diff --git a/sip/sip-006-runtime-cost-assessment.md b/sip/sip-006-runtime-cost-assessment.md index ef4edf6b2..f6b33d093 100644 --- a/sip/sip-006-runtime-cost-assessment.md +++ b/sip/sip-006-runtime-cost-assessment.md @@ -682,14 +682,14 @@ Memory is consumed by the following variable bindings: functions. Additionally, functions that perform _context changes_ also consume memory, -though the consume a constant amount: +though they consume a constant amount: * `as-contract` -* `as-block` +* `at-block` ## Type signature size -Types in Clarity may described using type signatures. For example, +Types in Clarity may be described using type signatures. For example, `(tuple (a int) (b int))` describes a tuple with two keys `a` and `b` of type `int`. These type descriptions are used by the Clarity analysis passes to check the type correctness of Clarity code. Clarity type signatures @@ -739,4 +739,10 @@ holding a write log in memory during the processing of a transaction. Operations that write data to the data store therefore consume memory _until the transaction completes_, and the write log is written to the -database. +database. The amount of memory consumed by operations on persisted data +types is defined as: + +* `data-var`: the size of the stored data var's value. +* `map`: the size of stored key + the size of the stored value. +* `nft`: the size of the NFT key +* `ft`: the size of a Clarity uint value. diff --git a/src/vm/tests/large_contract.rs b/src/vm/tests/large_contract.rs index 4c16012ce..87b2eba70 100644 --- a/src/vm/tests/large_contract.rs +++ b/src/vm/tests/large_contract.rs @@ -51,7 +51,7 @@ pub fn rollback_log_memory_test() { contract.push_str(&exploder); } - let (ct_ast, ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract).unwrap(); + let (ct_ast, _ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract).unwrap(); assert!(format!("{:?}", conn.initialize_smart_contract( &contract_identifier, &ct_ast, &contract, |_,_| false).unwrap_err()) @@ -94,10 +94,116 @@ pub fn let_memory_test() { contract.push_str(") 1)"); - let (ct_ast, ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract).unwrap(); + let (ct_ast, _ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract).unwrap(); assert!(format!("{:?}", conn.initialize_smart_contract( &contract_identifier, &ct_ast, &contract, |_,_| false).unwrap_err()) .contains("MemoryBalanceExceeded")); } } + +#[test] +pub fn argument_memory_test() { + let marf = MarfedKV::temporary(); + let mut clarity_instance = ClarityInstance::new(marf); + let EXPLODE_N = 100; + + let contract_identifier = QualifiedContractIdentifier::local("foo").unwrap(); + + { + let mut conn = clarity_instance.begin_block(&TrieFileStorage::block_sentinel(), + &BlockHeaderHash::from_bytes(&[0 as u8; 32]).unwrap(), + &NULL_HEADER_DB); + + let define_data_var = "(define-constant buff-0 \"a\")"; + + let mut contract = define_data_var.to_string(); + for i in 0..20 { + contract.push_str("\n"); + contract.push_str( + &format!("(define-constant buff-{} (concat buff-{} buff-{}))", + i+1, i, i)); + } + + contract.push_str("\n"); + contract.push_str("(is-eq "); + + for _i in 0..EXPLODE_N { + let exploder = "buff-20 "; + contract.push_str(exploder); + } + + contract.push_str(")"); + + let (ct_ast, _ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract).unwrap(); + assert!(format!("{:?}", + conn.initialize_smart_contract( + &contract_identifier, &ct_ast, &contract, |_,_| false).unwrap_err()) + .contains("MemoryBalanceExceeded")); + } +} + +#[test] +pub fn fcall_memory_test() { + let marf = MarfedKV::temporary(); + let mut clarity_instance = ClarityInstance::new(marf); + let COUNT_PER_FUNC = 10; + let FUNCS = 10; + + let contract_identifier = QualifiedContractIdentifier::local("foo").unwrap(); + + { + let mut conn = clarity_instance.begin_block(&TrieFileStorage::block_sentinel(), + &BlockHeaderHash::from_bytes(&[0 as u8; 32]).unwrap(), + &NULL_HEADER_DB); + + let define_data_var = "(define-constant buff-0 \"a\")"; + + let mut contract = define_data_var.to_string(); + for i in 0..20 { + contract.push_str("\n"); + contract.push_str( + &format!("(define-constant buff-{} (concat buff-{} buff-{}))", + i+1, i, i)); + } + + contract.push_str("\n"); + + for i in 0..FUNCS { + contract.push_str(&format!("(define-private (call-{})\n", i)); + + contract.push_str("(let ("); + + for j in 0..COUNT_PER_FUNC { + let exploder = format!("(var-{} buff-20) ", j); + contract.push_str(&exploder); + } + + if i == 0 { + contract.push_str(") 1) )\n"); + } else { + contract.push_str(&format!(") (call-{})) )\n", i - 1)); + } + } + + let mut contract_ok = contract.clone(); + let mut contract_err = contract.clone(); + + contract_ok.push_str("(call-0)"); + contract_err.push_str("(call-9)"); + + eprintln!("{}", contract_ok); + eprintln!("{}", contract_err); + + let (ct_ast, _ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract_ok).unwrap(); + conn.initialize_smart_contract( + // initialize the ok contract without errs, but still abort. + &contract_identifier, &ct_ast, &contract_ok, |_,_| true).unwrap(); + + let (ct_ast, _ct_analysis) = conn.analyze_smart_contract(&contract_identifier, &contract_err).unwrap(); + assert!(format!("{:?}", + conn.initialize_smart_contract( + &contract_identifier, &ct_ast, &contract_err, |_,_| false).unwrap_err()) + .contains("MemoryBalanceExceeded")); + } +} diff --git a/src/vm/types/signatures.rs b/src/vm/types/signatures.rs index ba2fceed5..f32f8165a 100644 --- a/src/vm/types/signatures.rs +++ b/src/vm/types/signatures.rs @@ -809,7 +809,7 @@ impl TypeSignature { UIntType => Some(16), BoolType => Some(1), PrincipalType => Some(148), // 20+128 - BufferType(len) => Some(1 + u32::from(len)), + BufferType(len) => Some(4 + u32::from(len)), TupleType(tuple_sig) => tuple_sig.inner_size(), ListType(list_type) => list_type.inner_size(), OptionalType(t) => t.size().checked_add(WRAPPER_VALUE_SIZE),