Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulate: Add opcode costs and budgets into response #5221

Merged
merged 15 commits into from
Mar 27, 2023

Conversation

algochoi
Copy link
Contributor

@algochoi algochoi commented Mar 21, 2023

Summary

This PR adds the following support for the simulator:

  • reporting total opcode budgets and costs for app calls in a grouped transaction
  • reporting individual opcode costs for each top level transaction
    • Inner app-call costs are "rolled-up" to the top level transaction
    • Logic sig costs in inner transactions are not reported, since they cannot be pooled anyway
  • adds budget-added, budget-consumed fields in the algod API spec

Closes #4971

Test Plan

Revised existing tests to report opcode budget and costs in the simulator and the handler. Added two new tests that tests over-budget app/logic sig calls to show behavior and how budget/costs are reported. Appreciate any other suggestions for tests as well 😄

@codecov
Copy link

codecov bot commented Mar 21, 2023

Codecov Report

Merging #5221 (45363e5) into master (e659181) will decrease coverage by 7.02%.
The diff coverage is 55.55%.

@@            Coverage Diff             @@
##           master    #5221      +/-   ##
==========================================
- Coverage   53.55%   46.53%   -7.02%     
==========================================
  Files         441      441              
  Lines       55123    55137      +14     
==========================================
- Hits        29520    25659    -3861     
- Misses      23317    27080    +3763     
- Partials     2286     2398     +112     
Impacted Files Coverage Δ
daemon/algod/api/server/v2/handlers.go 0.82% <ø> (ø)
daemon/algod/api/server/v2/utils.go 10.91% <0.00%> (-0.26%) ⬇️
ledger/simulation/trace.go 78.94% <ø> (ø)
ledger/simulation/simulator.go 80.59% <100.00%> (-2.22%) ⬇️
ledger/simulation/tracer.go 91.54% <100.00%> (+0.92%) ⬆️

... and 188 files with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@algochoi algochoi marked this pull request as ready for review March 22, 2023 17:57
}
tracer.cursorEvalTracer.BeforeTxnGroup(ep)

// Currently only supports one (first) txn group
if ep.PooledApplicationBudget != nil && tracer.result.TxnGroups[0].BudgetAdded == 0 {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We initialize the group budget by only accounting for top-level transactions. If we are in an inner level txn, then we dynamically add ep.Proto.MaxAppProgramCost to the budget each time. We might also want to think about how this can be handled when an app has depleted all its funds before it tries to "op-up" in an inner transaction and whether simulation should report an error in this case (like real execution), or simulate anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when an app has depleted all its funds before it tries to "op-up" in an inner transaction and whether simulation should report an error in this case

That is what we discussed yesterday about keeping track of budget as execution goes, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand that, the second if seems mutual exclusive with the first if, for first if condition makes BudgetAdded != 0, thus second if cannot be entered.

Could you explain more on what scenario the first if and second if will be triggered, such that it won't seem too abstract.

Copy link
Contributor Author

@algochoi algochoi Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to clarify this in the previous comment:

  • I considered a case where ep.PooledApplicationBudget might be nil because of an old consensus version (before pooling was enabled, this pointer would just be nil). Maybe for completion's sake, we should specify that the budget = ep.Proto.MaxAppProgramCost if this value is nil (since you cannot pool), and if it's not nil, we start adding the budget.
    • edit: I forgot there are also non-app calls that do not have a budget at all, so it's best to leave as is.
  • The BudgetAdded == 0 checks that we are always at the top-level transaction. If we are at an inner transaction, our PooledApplicationBudget could be something like 567 after being called midway through the app call. So we are saying that at the root-level transaction, set the BudgetAdded to a value that only accounts for the top level txns (e.g. 2100 for 3 txns) and then in the inner txn, we only add 700 once.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok to do nothing here if EnableAppCostPooling is false (i.e. the current code looks good to me).

If I understand the code correctly, BudgetAdded for the group would then be 0 and get omitted from the response. This makes sense, since if app budgets aren't pooled, there's not much use in aggregating the budgets for the whole group anyway. And of course individual transactions' BudgetUsed would still be accurate in that case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for explaining! that helps me a lot in understanding and catching up!

Copy link
Contributor

@ahangsu ahangsu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

taking a quick first pass, generally seems straightforward, have some minor questions as I am catching up.

ledger/simulation/simulator.go Outdated Show resolved Hide resolved
}
tracer.cursorEvalTracer.BeforeTxnGroup(ep)

// Currently only supports one (first) txn group
if ep.PooledApplicationBudget != nil && tracer.result.TxnGroups[0].BudgetAdded == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when an app has depleted all its funds before it tries to "op-up" in an inner transaction and whether simulation should report an error in this case

That is what we discussed yesterday about keeping track of budget as execution goes, right?

}
tracer.cursorEvalTracer.BeforeTxnGroup(ep)

// Currently only supports one (first) txn group
if ep.PooledApplicationBudget != nil && tracer.result.TxnGroups[0].BudgetAdded == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand that, the second if seems mutual exclusive with the first if, for first if condition makes BudgetAdded != 0, thus second if cannot be entered.

Could you explain more on what scenario the first if and second if will be triggered, such that it won't seem too abstract.

Copy link
Contributor

@jasonpaulos jasonpaulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, I just have some minor comments and a test suggestion

daemon/algod/api/algod.oas2.json Outdated Show resolved Hide resolved
daemon/algod/api/server/v2/utils.go Outdated Show resolved Hide resolved
daemon/algod/api/algod.oas2.json Outdated Show resolved Hide resolved
}
tracer.cursorEvalTracer.BeforeTxnGroup(ep)

// Currently only supports one (first) txn group
if ep.PooledApplicationBudget != nil && tracer.result.TxnGroups[0].BudgetAdded == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's ok to do nothing here if EnableAppCostPooling is false (i.e. the current code looks good to me).

If I understand the code correctly, BudgetAdded for the group would then be 0 and get omitted from the response. This makes sense, since if app budgets aren't pooled, there's not much use in aggregating the budgets for the whole group anyway. And of course individual transactions' BudgetUsed would still be accurate in that case.

ledger/simulation/simulation_eval_test.go Show resolved Hide resolved
jasonpaulos
jasonpaulos previously approved these changes Mar 24, 2023
Copy link
Contributor

@jasonpaulos jasonpaulos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks for including logicsig budget as well!

ahangsu
ahangsu previously approved these changes Mar 24, 2023
@algochoi algochoi dismissed stale reviews from ahangsu and jasonpaulos via 45363e5 March 27, 2023 16:35
daemon/algod/api/algod.oas3.yml Outdated Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Simulate - return opcode budget information
4 participants