Skip to content

Commit 9f8b4eb

Browse files
committed
pass epoch along for cost eval
1 parent 23fce5f commit 9f8b4eb

File tree

3 files changed

+93
-44
lines changed

3 files changed

+93
-44
lines changed

clarity/src/vm/ast/static_cost/mod.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod trait_counter;
22
use std::collections::HashMap;
33

44
use clarity_types::types::{CharType, SequenceData};
5+
use stacks_common::types::StacksEpochId;
56
pub use trait_counter::{
67
TraitCount, TraitCountCollector, TraitCountContext, TraitCountPropagator, TraitCountVisitor,
78
};
@@ -97,10 +98,12 @@ pub(crate) fn calculate_value_cost(value: &Value) -> Result<StaticCost, String>
9798
pub(crate) fn calculate_function_cost_from_native_function(
9899
native_function: NativeFunctions,
99100
arg_count: u64,
100-
clarity_version: &ClarityVersion,
101+
epoch: StacksEpochId,
101102
) -> Result<StaticCost, String> {
103+
// Derive clarity_version from epoch for lookup_reserved_functions
104+
let clarity_version = ClarityVersion::default_for_epoch(epoch);
102105
let cost_function =
103-
match lookup_reserved_functions(native_function.to_string().as_str(), clarity_version) {
106+
match lookup_reserved_functions(native_function.to_string().as_str(), &clarity_version) {
104107
Some(CallableType::NativeFunction(_, _, cost_fn)) => cost_fn,
105108
Some(CallableType::NativeFunction205(_, _, cost_fn, _)) => cost_fn,
106109
Some(CallableType::SpecialFunction(_, _)) => return Ok(StaticCost::ZERO),
@@ -110,11 +113,22 @@ pub(crate) fn calculate_function_cost_from_native_function(
110113
}
111114
};
112115

113-
let cost = match clarity_version {
114-
ClarityVersion::Clarity1 => cost_function.eval::<Costs1>(arg_count),
115-
ClarityVersion::Clarity2 => cost_function.eval::<Costs2>(arg_count),
116-
ClarityVersion::Clarity3 => cost_function.eval::<Costs3>(arg_count),
117-
ClarityVersion::Clarity4 => cost_function.eval::<Costs4>(arg_count),
116+
let cost = match epoch {
117+
StacksEpochId::Epoch20 => cost_function.eval::<Costs1>(arg_count),
118+
StacksEpochId::Epoch2_05 => cost_function.eval::<Costs2>(arg_count),
119+
StacksEpochId::Epoch21
120+
| StacksEpochId::Epoch22
121+
| StacksEpochId::Epoch23
122+
| StacksEpochId::Epoch24
123+
| StacksEpochId::Epoch25
124+
| StacksEpochId::Epoch30
125+
| StacksEpochId::Epoch31
126+
| StacksEpochId::Epoch32 => cost_function.eval::<Costs3>(arg_count),
127+
StacksEpochId::Epoch33 => cost_function.eval::<Costs4>(arg_count),
128+
StacksEpochId::Epoch10 => {
129+
// fallback to costs 1 since epoch 1 doesn't have direct cost mapping
130+
cost_function.eval::<Costs1>(arg_count)
131+
}
118132
}
119133
.map_err(|e| format!("Cost calculation error: {:?}", e))?;
120134
Ok(StaticCost {

clarity/src/vm/costs/analysis.rs

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ pub fn static_cost(
234234
let epoch = env.global_context.epoch_id;
235235
let ast = make_ast(&contract_source, epoch, clarity_version)?;
236236

237-
static_cost_tree_from_ast(&ast, clarity_version)
237+
static_cost_tree_from_ast(&ast, clarity_version, epoch)
238238
}
239239

240240
/// same idea as `static_cost` but returns the root of the cost analysis tree for each function
@@ -265,14 +265,15 @@ pub fn static_cost_tree(
265265
let epoch = env.global_context.epoch_id;
266266
let ast = make_ast(&contract_source, epoch, clarity_version)?;
267267

268-
static_cost_tree_from_ast(&ast, clarity_version)
268+
static_cost_tree_from_ast(&ast, clarity_version, epoch)
269269
}
270270

271271
pub fn static_cost_from_ast(
272272
contract_ast: &crate::vm::ast::ContractAST,
273273
clarity_version: &ClarityVersion,
274+
epoch: StacksEpochId,
274275
) -> Result<HashMap<String, (StaticCost, Option<TraitCount>)>, String> {
275-
let cost_trees_with_traits = static_cost_tree_from_ast(contract_ast, clarity_version)?;
276+
let cost_trees_with_traits = static_cost_tree_from_ast(contract_ast, clarity_version, epoch)?;
276277

277278
// Extract trait_count from the first entry (all entries have the same trait_count)
278279
let trait_count = cost_trees_with_traits
@@ -298,6 +299,7 @@ pub fn static_cost_from_ast(
298299
pub(crate) fn static_cost_tree_from_ast(
299300
ast: &crate::vm::ast::ContractAST,
300301
clarity_version: &ClarityVersion,
302+
epoch: StacksEpochId,
301303
) -> Result<HashMap<String, (CostAnalysisNode, Option<TraitCount>)>, String> {
302304
let exprs = &ast.expressions;
303305
let user_args = UserArgumentsContext::new();
@@ -313,7 +315,7 @@ pub(crate) fn static_cost_tree_from_ast(
313315
for expr in exprs {
314316
if let Some(function_name) = extract_function_name(expr) {
315317
let (_, cost_analysis_tree) =
316-
build_cost_analysis_tree(expr, &user_args, &costs_map, clarity_version)?;
318+
build_cost_analysis_tree(expr, &user_args, &costs_map, clarity_version, epoch)?;
317319
costs.insert(function_name, Some(cost_analysis_tree));
318320
}
319321
}
@@ -353,6 +355,7 @@ pub fn build_cost_analysis_tree(
353355
user_args: &UserArgumentsContext,
354356
cost_map: &HashMap<String, Option<StaticCost>>,
355357
clarity_version: &ClarityVersion,
358+
epoch: StacksEpochId,
356359
) -> Result<(Option<String>, CostAnalysisNode), String> {
357360
match &expr.expr {
358361
SymbolicExpressionType::List(list) => {
@@ -364,6 +367,7 @@ pub fn build_cost_analysis_tree(
364367
user_args,
365368
cost_map,
366369
clarity_version,
370+
epoch,
367371
)?;
368372
Ok((Some(returned_function_name), cost_analysis_tree))
369373
} else {
@@ -372,12 +376,18 @@ pub fn build_cost_analysis_tree(
372376
user_args,
373377
cost_map,
374378
clarity_version,
379+
epoch,
375380
)?;
376381
Ok((None, cost_analysis_tree))
377382
}
378383
} else {
379-
let cost_analysis_tree =
380-
build_listlike_cost_analysis_tree(list, user_args, cost_map, clarity_version)?;
384+
let cost_analysis_tree = build_listlike_cost_analysis_tree(
385+
list,
386+
user_args,
387+
cost_map,
388+
clarity_version,
389+
epoch,
390+
)?;
381391
Ok((None, cost_analysis_tree))
382392
}
383393
}
@@ -439,6 +449,7 @@ fn build_function_definition_cost_analysis_tree(
439449
_user_args: &UserArgumentsContext,
440450
cost_map: &HashMap<String, Option<StaticCost>>,
441451
clarity_version: &ClarityVersion,
452+
epoch: StacksEpochId,
442453
) -> Result<(String, CostAnalysisNode), String> {
443454
let define_type = list[0]
444455
.match_atom()
@@ -476,7 +487,7 @@ fn build_function_definition_cost_analysis_tree(
476487

477488
// Process the function body with the function's user arguments context
478489
let (_, body_tree) =
479-
build_cost_analysis_tree(body, &function_user_args, cost_map, clarity_version)?;
490+
build_cost_analysis_tree(body, &function_user_args, cost_map, clarity_version, epoch)?;
480491
children.push(body_tree);
481492

482493
// Get the function name from the signature
@@ -508,20 +519,22 @@ fn build_listlike_cost_analysis_tree(
508519
user_args: &UserArgumentsContext,
509520
cost_map: &HashMap<String, Option<StaticCost>>,
510521
clarity_version: &ClarityVersion,
522+
epoch: StacksEpochId,
511523
) -> Result<CostAnalysisNode, String> {
512524
let mut children = Vec::new();
513525

514526
// Build children for all exprs
515527
for expr in exprs[1..].iter() {
516-
let (_, child_tree) = build_cost_analysis_tree(expr, user_args, cost_map, clarity_version)?;
528+
let (_, child_tree) =
529+
build_cost_analysis_tree(expr, user_args, cost_map, clarity_version, epoch)?;
517530
children.push(child_tree);
518531
}
519532

520533
let (expr_node, cost) = match &exprs[0].expr {
521534
SymbolicExpressionType::List(_) => {
522535
// Recursively analyze the nested list structure
523536
let (_, nested_tree) =
524-
build_cost_analysis_tree(&exprs[0], user_args, cost_map, clarity_version)?;
537+
build_cost_analysis_tree(&exprs[0], user_args, cost_map, clarity_version, epoch)?;
525538
// Add the nested tree as a child (its cost will be included when summing children)
526539
children.insert(0, nested_tree);
527540
// The root cost is zero - the actual cost comes from the nested expression
@@ -537,7 +550,7 @@ fn build_listlike_cost_analysis_tree(
537550
let cost = calculate_function_cost_from_native_function(
538551
native_function,
539552
children.len() as u64,
540-
clarity_version,
553+
epoch,
541554
)?;
542555
(CostExprNode::NativeFunction(native_function), cost)
543556
} else {
@@ -608,7 +621,7 @@ mod tests {
608621
let user_args = UserArgumentsContext::new();
609622
let expr = &exprs[0];
610623
let (_, cost_analysis_tree) =
611-
build_cost_analysis_tree(&expr, &user_args, &cost_map, clarity_version)?;
624+
build_cost_analysis_tree(&expr, &user_args, &cost_map, clarity_version, epoch)?;
612625

613626
let summing_cost = calculate_total_cost_with_branching(&cost_analysis_tree);
614627
Ok(summing_cost.into())
@@ -620,7 +633,7 @@ mod tests {
620633
) -> Result<HashMap<String, StaticCost>, String> {
621634
let epoch = StacksEpochId::latest();
622635
let ast = make_ast(source, epoch, clarity_version)?;
623-
let costs = static_cost_from_ast(&ast, clarity_version)?;
636+
let costs = static_cost_from_ast(&ast, clarity_version, epoch)?;
624637
Ok(costs
625638
.into_iter()
626639
.map(|(name, (cost, _trait_count))| (name, cost))
@@ -713,9 +726,15 @@ mod tests {
713726
let expr = &ast.expressions[0];
714727
let user_args = UserArgumentsContext::new();
715728
let cost_map = HashMap::new(); // Empty cost map for tests
716-
let (_, cost_tree) =
717-
build_cost_analysis_tree(expr, &user_args, &cost_map, &ClarityVersion::Clarity3)
718-
.unwrap();
729+
let epoch = StacksEpochId::Epoch32;
730+
let (_, cost_tree) = build_cost_analysis_tree(
731+
expr,
732+
&user_args,
733+
&cost_map,
734+
&ClarityVersion::Clarity3,
735+
epoch,
736+
)
737+
.unwrap();
719738

720739
// Root should be an If node
721740
assert!(matches!(
@@ -760,9 +779,15 @@ mod tests {
760779
let expr = &ast.expressions[0];
761780
let user_args = UserArgumentsContext::new();
762781
let cost_map = HashMap::new(); // Empty cost map for tests
763-
let (_, cost_tree) =
764-
build_cost_analysis_tree(expr, &user_args, &cost_map, &ClarityVersion::Clarity3)
765-
.unwrap();
782+
let epoch = StacksEpochId::Epoch32;
783+
let (_, cost_tree) = build_cost_analysis_tree(
784+
expr,
785+
&user_args,
786+
&cost_map,
787+
&ClarityVersion::Clarity3,
788+
epoch,
789+
)
790+
.unwrap();
766791

767792
assert!(matches!(
768793
cost_tree.expr,
@@ -793,9 +818,15 @@ mod tests {
793818
let expr = &ast.expressions[0];
794819
let user_args = UserArgumentsContext::new();
795820
let cost_map = HashMap::new(); // Empty cost map for tests
796-
let (_, cost_tree) =
797-
build_cost_analysis_tree(expr, &user_args, &cost_map, &ClarityVersion::Clarity3)
798-
.unwrap();
821+
let epoch = StacksEpochId::Epoch32;
822+
let (_, cost_tree) = build_cost_analysis_tree(
823+
expr,
824+
&user_args,
825+
&cost_map,
826+
&ClarityVersion::Clarity3,
827+
epoch,
828+
)
829+
.unwrap();
799830

800831
assert!(matches!(
801832
cost_tree.expr,
@@ -816,9 +847,15 @@ mod tests {
816847
let expr = &ast.expressions[0];
817848
let user_args = UserArgumentsContext::new();
818849
let cost_map = HashMap::new(); // Empty cost map for tests
819-
let (_, cost_tree) =
820-
build_cost_analysis_tree(expr, &user_args, &cost_map, &ClarityVersion::Clarity3)
821-
.unwrap();
850+
let epoch = StacksEpochId::Epoch32;
851+
let (_, cost_tree) = build_cost_analysis_tree(
852+
expr,
853+
&user_args,
854+
&cost_map,
855+
&ClarityVersion::Clarity3,
856+
epoch,
857+
)
858+
.unwrap();
822859

823860
assert_eq!(cost_tree.children.len(), 3);
824861

clarity/src/vm/tests/analysis.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ fn test_build_cost_analysis_tree_function_definition() {
3333
let cost_map = HashMap::new();
3434

3535
let clarity_version = ClarityVersion::Clarity3;
36-
let result = build_cost_analysis_tree(expr, &user_args, &cost_map, &clarity_version);
36+
let epoch = StacksEpochId::Epoch32;
37+
let result = build_cost_analysis_tree(expr, &user_args, &cost_map, &clarity_version, epoch);
3738

3839
match result {
3940
Ok((function_name, node)) => {
@@ -71,7 +72,7 @@ fn test_dependent_function_calls() {
7172
epoch,
7273
)
7374
.unwrap();
74-
let function_map = static_cost_from_ast(&ast, &ClarityVersion::Clarity3).unwrap();
75+
let function_map = static_cost_from_ast(&ast, &ClarityVersion::Clarity3, epoch).unwrap();
7576

7677
let (add_one_cost, _) = function_map.get("add-one").unwrap();
7778
let (somefunc_cost, _) = function_map.get("somefunc").unwrap();
@@ -103,7 +104,8 @@ fn test_get_trait_count_direct() {
103104
)
104105
.unwrap();
105106

106-
let costs = static_cost_tree_from_ast(&ast, &ClarityVersion::Clarity3).unwrap();
107+
let costs =
108+
static_cost_tree_from_ast(&ast, &ClarityVersion::Clarity3, StacksEpochId::Epoch32).unwrap();
107109

108110
// Extract trait_count from the result (all entries have the same trait_count)
109111
let trait_count = costs
@@ -133,15 +135,11 @@ fn test_trait_counting() {
133135
(define-private (send (trait <trait-name>) (addr principal)) (trait addr))
134136
"#;
135137
let contract_id = QualifiedContractIdentifier::local("trait-counting").unwrap();
136-
let ast = crate::vm::ast::build_ast(
137-
&contract_id,
138-
src,
139-
&mut (),
140-
ClarityVersion::Clarity3,
141-
StacksEpochId::Epoch32,
142-
)
143-
.unwrap();
144-
let static_cost = static_cost_from_ast(&ast, &ClarityVersion::Clarity3)
138+
let epoch = StacksEpochId::Epoch32;
139+
let ast =
140+
crate::vm::ast::build_ast(&contract_id, src, &mut (), ClarityVersion::Clarity3, epoch)
141+
.unwrap();
142+
let static_cost = static_cost_from_ast(&ast, &ClarityVersion::Clarity3, epoch)
145143
.unwrap()
146144
.clone();
147145
let send_trait_count_map = static_cost.get("send").unwrap().1.clone().unwrap();
@@ -230,7 +228,7 @@ fn test_pox_4_costs() {
230228
)
231229
.expect("Failed to build AST from pox-4.clar");
232230

233-
let cost_map = static_cost_from_ast(&ast, &clarity_version)
231+
let cost_map = static_cost_from_ast(&ast, &clarity_version, epoch)
234232
.expect("Failed to get static cost analysis for pox-4.clar");
235233

236234
// Check some functions in the cost map

0 commit comments

Comments
 (0)