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

Dryrun: Split dryrun cost field into BudgetConsumed and BudgetAdded #3957

Merged
merged 20 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions cmd/goal/clerk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,8 +1211,11 @@ var dryrunRemoteCmd = &cobra.Command{
trace = *txnResult.LogicSigTrace
}
}
if txnResult.Cost != nil {
fmt.Fprintf(os.Stdout, "tx[%d] cost: %d\n", i, *txnResult.Cost)
if txnResult.BudgetCredit != nil {
fmt.Fprintf(os.Stdout, "tx[%d] budget credit: %d\n", i, *txnResult.BudgetCredit)
}
if txnResult.BudgetDebit != nil {
fmt.Fprintf(os.Stdout, "tx[%d] budget debit: %d\n", i, *txnResult.BudgetDebit)
}

fmt.Fprintf(os.Stdout, "tx[%d] messages:\n", i)
Expand Down
8 changes: 6 additions & 2 deletions daemon/algod/api/algod.oas2.json
Original file line number Diff line number Diff line change
Expand Up @@ -2295,8 +2295,12 @@
"format": "byte"
}
},
"cost": {
"description": "Execution cost of app call transaction",
"budget-debit": {
"description": "Budget added during execution of app call transaction.",
"type": "integer"
},
"budget-credit": {
"description": "Budget consumed during execution of app call transaction.",
"type": "integer"
}
}
Expand Down
8 changes: 6 additions & 2 deletions daemon/algod/api/algod.oas3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1328,8 +1328,12 @@
},
"type": "array"
},
"cost": {
"description": "Execution cost of app call transaction",
"budget-credit": {
"description": "Budget consumed during execution of app call transaction.",
"type": "integer"
},
"budget-debit": {
"description": "Budget added during execution of app call transaction.",
"type": "integer"
},
"disassembly": {
Expand Down
17 changes: 15 additions & 2 deletions daemon/algod/api/server/v2/dryrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,11 @@ func doDryrunRequest(dr *DryrunRequest, response *generated.DryrunResponse) {
err = fmt.Errorf("cost budget exceeded: budget is %d but program cost was %d", allowedBudget-cumulativeCost, cost)
}
}
cost64 := uint64(cost)
result.Cost = &cost64
// amount the budget was increased
budgetDebit := uint64(proto.MaxAppProgramCost * numInnerTxns(delta))
budgetCredit := uint64(cost) + budgetDebit
Copy link
Contributor

Choose a reason for hiding this comment

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

Why isn't budgetCredted just cost? It is because cost is really credit - debit at this point? This might warrant a comment.

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 point, I'll add a comment to clarify. You're right, cost = credit - debit

result.BudgetDebit = &budgetDebit
result.BudgetCredit = &budgetCredit
maxCurrentBudget = pooledAppBudget
cumulativeCost += cost

Expand Down Expand Up @@ -624,3 +627,13 @@ func MergeAppParams(base *basics.AppParams, update *basics.AppParams) {
base.GlobalStateSchema = update.GlobalStateSchema
}
}

// count all inner transactions contained within the eval delta
func numInnerTxns(delta transactions.EvalDelta) (cnt int) {
cnt = len(delta.InnerTxns)
for _, itxn := range delta.InnerTxns {
cnt += numInnerTxns(itxn.EvalDelta)
}

return
}
65 changes: 53 additions & 12 deletions daemon/algod/api/server/v2/dryrun_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1260,27 +1260,47 @@ func TestDryrunCost(t *testing.T) {
msg string
numHashes int
}{
{"REJECT", 12},
{"PASS", 5},
{"REJECT", 22},
{"PASS", 16},
}

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
costs := make([]uint64, 2)
expectedCosts := make([]int64, 3)
expectedDebit := make([]uint64, 3)

ops, err := logic.AssembleString("#pragma version 5\nbyte 0x41\n" + strings.Repeat("keccak256\n", test.numHashes) + "pop\nint 1\n")
require.NoError(t, err)
approval := ops.Program
costs[0] = 3 + uint64(test.numHashes)*130
app1 := ops.Program
expectedCosts[0] = 3 + int64(test.numHashes)*130
expectedDebit[0] = 0

ops, err = logic.AssembleString("int 1")
require.NoError(t, err)
clst := ops.Program

ops, err = logic.AssembleString("#pragma version 5 \nint 1 \nint 2 \npop")
require.NoError(t, err)
approv := ops.Program
costs[1] = 3
app2 := ops.Program
expectedCosts[1] = 3
expectedDebit[1] = 0

ops, err = logic.AssembleString(`#pragma version 6
itxn_begin
int appl
itxn_field TypeEnum
int DeleteApplication
itxn_field OnCompletion
byte 0x068101 // #pragma version 6; int 1;
itxn_field ApprovalProgram
byte 0x068101 // #pragma version 6; int 1;
itxn_field ClearStateProgram
itxn_submit
int 1`)
require.NoError(t, err)
app3 := ops.Program
expectedCosts[2] = -687
expectedDebit[2] = 700

var appIdx basics.AppIndex = 1
creator := randomAddress()
Expand All @@ -1307,13 +1327,23 @@ func TestDryrunCost(t *testing.T) {
},
},
},
{
Txn: transactions.Transaction{
Header: transactions.Header{Sender: sender},
Type: protocol.ApplicationCallTx,
ApplicationCallTxnFields: transactions.ApplicationCallTxnFields{
ApplicationID: appIdx + 2,
OnCompletion: transactions.OptInOC,
},
},
},
},
Apps: []generated.Application{
{
Id: uint64(appIdx),
Params: generated.ApplicationParams{
Creator: creator.String(),
ApprovalProgram: approval,
ApprovalProgram: app1,
ClearStateProgram: clst,
LocalStateSchema: &generated.ApplicationStateSchema{NumByteSlice: 1},
},
Expand All @@ -1322,7 +1352,16 @@ func TestDryrunCost(t *testing.T) {
Id: uint64(appIdx + 1),
Params: generated.ApplicationParams{
Creator: creator.String(),
ApprovalProgram: approv,
ApprovalProgram: app2,
ClearStateProgram: clst,
LocalStateSchema: &generated.ApplicationStateSchema{NumByteSlice: 1},
},
},
{
Id: uint64(appIdx + 2),
Params: generated.ApplicationParams{
Creator: creator.String(),
ApprovalProgram: app3,
ClearStateProgram: clst,
LocalStateSchema: &generated.ApplicationStateSchema{NumByteSlice: 1},
},
Expand All @@ -1340,13 +1379,15 @@ func TestDryrunCost(t *testing.T) {
var response generated.DryrunResponse
doDryrunRequest(&dr, &response)
require.Empty(t, response.Error)
require.Equal(t, 2, len(response.Txns))
require.Equal(t, 3, len(response.Txns))

for i, txn := range response.Txns {
messages := *txn.AppCallMessages
require.GreaterOrEqual(t, len(messages), 1)
require.NotNil(t, *txn.Cost)
require.Equal(t, costs[i], *txn.Cost)
cost := int64(*txn.BudgetCredit) - int64(*txn.BudgetDebit)
require.NotNil(t, cost)
require.Equal(t, expectedCosts[i], cost)
require.Equal(t, expectedDebit[i], *txn.BudgetDebit)
statusMatches := false
costExceedFound := false
for _, msg := range messages {
Expand Down
Loading