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

Pool App call budget in group transactions #2711

Merged
merged 15 commits into from
Aug 12, 2021

Conversation

algochoi
Copy link
Contributor

@algochoi algochoi commented Aug 7, 2021

Summary

  • Adds feature to allow App calls to pool their budget in a grouped transaction (e.g. in a 16 app call txn group, the entire group has access to 11200 units of pooled cost).
  • Allows ed25519verify to be called in Application calls, given that there is enough budget in the pool
  • Added vFuture consensus parameter

Closes #2636

Test Plan

  • Added unit tests in ledger/ to check that group transactions pool the App call budget (700 * number of app calls)
  • Added tests to check that pooling is not allowed in V28
  • Added tests to check that ed25519verify is allowed in App calls, given enough budget in the pool

@algochoi algochoi changed the title Algochoi/pool tx costs Pool App call budget in group transactions Aug 7, 2021
Copy link
Contributor

@jannotti jannotti left a comment

Choose a reason for hiding this comment

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

Let's discuss the shared budget pointer idea. I think it fits more closely with how we have done some other recent features. (In fact, see my PR for how I'm handling the excess fee credit in app txn creation: Look for "FeeCredit" in https://github.com/algorand/go-algorand/pull/2661/files)

{approvalProgram2, "dynamic cost budget exceeded, executing keccak256: remaining budget is 700 but program cost was 783"},
}

for _, testCase := range cases {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think at one point we wanted to do the protocol checks in data/transactions/logic/evalStateful_test.go, but there is some ledger code that also needs to be tested (prepareEvalParams) for correct protocol, so I put this test in the ledger tests.

Copy link
Contributor

@algorandskiy algorandskiy Aug 10, 2021

Choose a reason for hiding this comment

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

these tests look good but I think we should not test everything here because in ledger package we only set pooled budget for multiple app calls in one group. And the decisions are made in logic.
Let's do the following:

  1. a unit test forprepareEvalParams call on a group with 2 txns with app calls on old and new protos. No ledger construction/blocks needed.
  2. Preserve a high-level test with block evaluator: no pooling on old proto and pooling on new proto. Programs should be simple but valid, something like "byte "a"\n" + strings.Repeat("keccak256\n", 50).
  3. Make more precise testing on ed25519verify in logic package - it is needed anyway, and the EvalParams with proto and budget can be easily set there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the prepareEvalParams tests above like you have suggested. Just to be clear, we should probably only check for correct pooling here, and check for cost/budget in the data/transactions/logic/evalStateful_test.go file?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is always a good idea to check both happy case and error case. The idea is to have 1) minimal sufficient coverage 2) without knowing to much details about stuff from other packeges.
I I suggest to use the following code to generate test programs of difference cost by varying N:

prog := "byte "a\n" + strings.Repeat("keccak256\n", N) + "pop\nint 1\n";

So here in ledger the following four cases would be enough:

  1. cost ~701 + cost ~100 => FAIL on v29.
  2. same combo OK on vFuture
  3. ~701 + ~701 => FAIL on both v29 and vFuture

Comment on lines 364 to 367
if pass && cx.EvalParams.Proto.EnableAppCostPooling {
// if eval passes, then budget is always greater than cost, so should not have underflow
*cx.EvalParams.PooledApplicationBudget -= cx.cost
}
Copy link
Contributor

@jannotti jannotti Aug 9, 2021

Choose a reason for hiding this comment

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

If an app doesn't pass, everything's probably going to get aborted. Nonetheless, I don't see a good reason not to decrease the pooled budget.

This also needs to be super careful about underflow. Going negative here almost certainly leads to the possibility of a the budget being interpreted as a high number elsewhere.

@codecov-commenter
Copy link

codecov-commenter commented Aug 10, 2021

Codecov Report

Merging #2711 (4c08880) into master (58bc231) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #2711   +/-   ##
=======================================
  Coverage   47.09%   47.10%           
=======================================
  Files         350      350           
  Lines       56309    56321   +12     
=======================================
+ Hits        26520    26528    +8     
- Misses      26817    26821    +4     
  Partials     2972     2972           
Impacted Files Coverage Δ
data/transactions/logic/opcodes.go 96.00% <ø> (ø)
config/consensus.go 84.06% <100.00%> (+0.05%) ⬆️
data/transactions/logic/eval.go 90.64% <100.00%> (+0.02%) ⬆️
ledger/eval.go 75.97% <100.00%> (+0.21%) ⬆️
ledger/blockqueue.go 82.18% <0.00%> (-2.88%) ⬇️
catchup/service.go 68.57% <0.00%> (-1.56%) ⬇️
network/requestTracker.go 70.25% <0.00%> (-0.44%) ⬇️
network/wsPeer.go 74.37% <0.00%> (+2.22%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 58bc231...4c08880. Read the comment docs.

Copy link
Contributor

@algorandskiy algorandskiy left a comment

Choose a reason for hiding this comment

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

Looks good, some work needed: 1) ed25519verify must be only allowed in stateful TEAL v5+ 2) restructure your TEAL tests in legder: it only should check evaluation on old/new proto with as trivial programs as possible. All TEAL heavy-lifting should go into logic package. Especially this change does not depends on ledger interface for TEAL, only EvalParams preparation, so this preparation in isolation, and a big picture needed to be tested there.

{approvalProgram2, "dynamic cost budget exceeded, executing keccak256: remaining budget is 700 but program cost was 783"},
}

for _, testCase := range cases {
Copy link
Contributor

@algorandskiy algorandskiy Aug 10, 2021

Choose a reason for hiding this comment

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

these tests look good but I think we should not test everything here because in ledger package we only set pooled budget for multiple app calls in one group. And the decisions are made in logic.
Let's do the following:

  1. a unit test forprepareEvalParams call on a group with 2 txns with app calls on old and new protos. No ledger construction/blocks needed.
  2. Preserve a high-level test with block evaluator: no pooling on old proto and pooling on new proto. Programs should be simple but valid, something like "byte "a"\n" + strings.Repeat("keccak256\n", 50).
  3. Make more precise testing on ed25519verify in logic package - it is needed anyway, and the EvalParams with proto and budget can be easily set there.

Comment on lines 437 to 438
for i, testCase := range cases {
t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not must but something you might to consider: there are two almost identical loops.
If fact, it can be done as a single loop over cases.

  1. Remove fake proto from eval construction above
  2. Make params array consisting on 3 elements: fake params with Application: true, MaxAppProgramCost: 700, vFuture, v29.
  3. Modify evalTestCase struct and add number of app calls in tx group (numAppCalls)
  4. Run loops over cases, params, and calculate pooled cost as proto.MaxAppProgramCost * numAppCalls, and compare against a value EvalParams

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, will refactor this part.

Copy link
Contributor

@algorandskiy algorandskiy left a comment

Choose a reason for hiding this comment

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

LGTM, one question about pushint in tests.

@algochoi algochoi requested a review from algorandskiy August 11, 2021 18:22
algorandskiy
algorandskiy previously approved these changes Aug 11, 2021
@jannotti jannotti self-requested a review August 12, 2021 01:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Pool computational budget for application call transactions
4 participants