From 4957a9530bd5d644814045f3e215f7f6430a1901 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 25 Apr 2023 13:40:30 -0400 Subject: [PATCH 1/6] Show opcode context for logicsigs (not just apps) --- data/transactions/logic/eval.go | 49 +++++++++++++++++++++++----- data/transactions/logic/eval_test.go | 37 +++++++-------------- ledger/apply/application.go | 4 +-- ledger/apply/application_test.go | 2 +- ledger/eval/appcow.go | 14 ++------ ledger/ledgercore/error.go | 15 --------- 6 files changed, 57 insertions(+), 64 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index f40f60fce5..0a822a2c71 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -678,19 +678,38 @@ func (st StackType) Typed() bool { return false } -// PanicError wraps a recover() catching a panic() -type PanicError struct { +// panicError wraps a recover() catching a panic() +type panicError struct { PanicValue interface{} StackTrace string } -func (pe PanicError) Error() string { +func (pe panicError) Error() string { return fmt.Sprintf("panic in TEAL Eval: %v\n%s", pe.PanicValue, pe.StackTrace) } var errLogicSigNotSupported = errors.New("LogicSig not supported") var errTooManyArgs = errors.New("LogicSig has too many arguments") +// EvalError indicates TEAL evaluation failure +type EvalError struct { + Err error + Details string +} + +// Error satisfies builtin interface `error` +func (err EvalError) Error() string { + msg := fmt.Sprintf("logic eval error: %v", err.Err) + if len(err.Details) > 0 { + msg = fmt.Sprintf("%s. Details: %s", msg, err.Details) + } + return msg +} + +func (err EvalError) Unwrap() error { + return err.Err +} + // ClearStateBudgetError allows evaluation to signal that the caller should // reject the transaction. Normally, an error in evaluation would not cause a // ClearState txn to fail. However, callers fail a txn for ClearStateBudgetError @@ -776,6 +795,11 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam fmt.Fprintf(cx.Trace, "--- enter %d %s %v\n", aid, cx.txn.Txn.OnCompletion, cx.txn.Txn.ApplicationArgs) } pass, err := eval(program, &cx) + if err != nil { + pc, det := cx.PcDetails() + details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + err = EvalError{err, details} + } if cx.Trace != nil && cx.caller != nil { fmt.Fprintf(cx.Trace, "--- exit %d accept=%t\n", aid, pass) @@ -798,7 +822,7 @@ func EvalApp(program []byte, gi int, aid basics.AppIndex, params *EvalParams) (b // EvalSignatureFull evaluates the logicsig of the ith transaction in params. // A program passes successfully if it finishes with one int element on the stack that is non-zero. // It returns EvalContext suitable for obtaining additional info about the execution. -func EvalSignatureFull(gi int, params *EvalParams) (pass bool, pcx *EvalContext, err error) { +func EvalSignatureFull(gi int, params *EvalParams) (bool, *EvalContext, error) { if params.SigLedger == nil { return false, nil, errors.New("no sig ledger in signature eval") } @@ -808,14 +832,21 @@ func EvalSignatureFull(gi int, params *EvalParams) (pass bool, pcx *EvalContext, groupIndex: gi, txn: ¶ms.TxnGroup[gi], } - pass, err = eval(cx.txn.Lsig.Logic, &cx) + pass, err := eval(cx.txn.Lsig.Logic, &cx) + + if err != nil { + pc, det := cx.PcDetails() + details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + err = EvalError{err, details} + } + return pass, &cx, err } // EvalSignature evaluates the logicsig of the ith transaction in params. // A program passes successfully if it finishes with one int element on the stack that is non-zero. -func EvalSignature(gi int, params *EvalParams) (pass bool, err error) { - pass, _, err = EvalSignatureFull(gi, params) +func EvalSignature(gi int, params *EvalParams) (bool, error) { + pass, _, err := EvalSignatureFull(gi, params) return pass, err } @@ -831,7 +862,7 @@ func eval(program []byte, cx *EvalContext) (pass bool, err error) { if cx.Trace != nil { errstr += cx.Trace.String() } - err = PanicError{x, errstr} + err = panicError{x, errstr} cx.EvalParams.log().Errorf("recovered panic in Eval: %v", err) } }() @@ -941,7 +972,7 @@ func check(program []byte, params *EvalParams, mode RunMode) (err error) { if params.Trace != nil { errstr += params.Trace.String() } - err = PanicError{x, errstr} + err = panicError{x, errstr} params.log().Errorf("recovered panic in Check: %s", err) } }() diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 29231d3448..e55d580c84 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -2961,7 +2961,7 @@ func isNotPanic(t *testing.T, err error) { if err == nil { return } - if pe, ok := err.(PanicError); ok { + if pe, ok := err.(panicError); ok { t.Error(pe) } } @@ -3188,14 +3188,10 @@ func TestPanic(t *testing.T) { //nolint:paralleltest // Uses withPanicOpcode params := defaultEvalParams() params.TxnGroup[0].Lsig.Logic = ops.Program err := CheckSignature(0, params) - require.Error(t, err) - if pe, ok := err.(PanicError); ok { - require.Equal(t, panicString, pe.PanicValue) - pes := pe.Error() - require.True(t, strings.Contains(pes, "panic")) - } else { - t.Errorf("expected PanicError object but got %T %#v", err, err) - } + var pe panicError + require.ErrorAs(t, err, &pe) + require.Equal(t, panicString, pe.PanicValue) + require.ErrorContains(t, pe, "panic") var txn transactions.SignedTxn txn.Lsig.Logic = ops.Program @@ -3206,13 +3202,9 @@ func TestPanic(t *testing.T) { //nolint:paralleltest // Uses withPanicOpcode t.Log(params.Trace.String()) } require.False(t, pass) - if pe, ok := err.(PanicError); ok { - require.Equal(t, panicString, pe.PanicValue) - pes := pe.Error() - require.True(t, strings.Contains(pes, "panic")) - } else { - t.Errorf("expected PanicError object but got %T %#v", err, err) - } + require.ErrorAs(t, err, &pe) + require.Equal(t, panicString, pe.PanicValue) + require.ErrorContains(t, pe, "panic") if v >= appsEnabledVersion { txn = transactions.SignedTxn{ @@ -3224,13 +3216,9 @@ func TestPanic(t *testing.T) { //nolint:paralleltest // Uses withPanicOpcode params.Ledger = NewLedger(nil) pass, err = EvalApp(ops.Program, 0, 1, params) require.False(t, pass) - if pe, ok := err.(PanicError); ok { - require.Equal(t, panicString, pe.PanicValue) - pes := pe.Error() - require.True(t, strings.Contains(pes, "panic")) - } else { - t.Errorf("expected PanicError object but got %T %#v", err, err) - } + require.ErrorAs(t, err, &pe) + require.Equal(t, panicString, pe.PanicValue) + require.ErrorContains(t, pe, "panic") } }) }) @@ -5789,8 +5777,7 @@ func TestOpJSONRef(t *testing.T) { pass, _, err := EvalContract(ops.Program, 0, 888, ep) require.False(t, pass) - require.Error(t, err) - require.EqualError(t, err, s.error) + require.ErrorContains(t, err, s.error) // reset pooled budget for new "app call" *ep.PooledApplicationBudget = ep.Proto.MaxAppProgramCost diff --git a/ledger/apply/application.go b/ledger/apply/application.go index 3d03349ebe..a887309a45 100644 --- a/ledger/apply/application.go +++ b/ledger/apply/application.go @@ -413,8 +413,8 @@ func ApplicationCall(ac transactions.ApplicationCallTxnFields, header transactio if exists { pass, evalDelta, err := balances.StatefulEval(gi, evalParams, appIdx, params.ClearStateProgram) if err != nil { - // Fail on non-logic eval errors and ignore LogicEvalError errors - if _, ok := err.(ledgercore.LogicEvalError); !ok { + // ClearStateProgram evaluation can't make the txn fail. + if _, ok := err.(logic.EvalError); !ok { return err } } diff --git a/ledger/apply/application_test.go b/ledger/apply/application_test.go index bc8eb74ae9..54f7941081 100644 --- a/ledger/apply/application_test.go +++ b/ledger/apply/application_test.go @@ -895,7 +895,7 @@ func TestAppCallClearState(t *testing.T) { // one to opt out, one deallocate, no error from ApplicationCall b.pass = true b.delta = transactions.EvalDelta{GlobalDelta: nil} - b.err = ledgercore.LogicEvalError{Err: fmt.Errorf("test error")} + b.err = logic.EvalError{Err: fmt.Errorf("test error")} err = ApplicationCall(ac, h, b, ad, 0, &ep, txnCounter) a.NoError(err) a.Equal(1, b.put) diff --git a/ledger/eval/appcow.go b/ledger/eval/appcow.go index 0e22704521..14815e8e2c 100644 --- a/ledger/eval/appcow.go +++ b/ledger/eval/appcow.go @@ -467,19 +467,9 @@ func (cb *roundCowState) StatefulEval(gi int, params *logic.EvalParams, aidx bas params.Ledger = calf params.SigLedger = calf - // Eval the program - pass, cx, err := logic.EvalContract(program, gi, aidx, params) + pass, err = logic.EvalApp(program, gi, aidx, params) if err != nil { - var details string - if cx != nil { - pc, det := cx.PcDetails() - details = fmt.Sprintf("pc=%d, opcodes=%s", pc, det) - } - // Don't wrap ClearStateBudgetError, so it will be taken seriously - if _, ok := err.(logic.ClearStateBudgetError); ok { - return false, transactions.EvalDelta{}, err - } - return false, transactions.EvalDelta{}, ledgercore.LogicEvalError{Err: err, Details: details} + return false, transactions.EvalDelta{}, err } // If program passed, build our eval delta, and commit to state changes diff --git a/ledger/ledgercore/error.go b/ledger/ledgercore/error.go index b1dff71a4a..c98312a9a1 100644 --- a/ledger/ledgercore/error.go +++ b/ledger/ledgercore/error.go @@ -93,21 +93,6 @@ func (err ErrNoEntry) Error() string { return fmt.Sprintf("ledger does not have entry %d (latest %d, committed %d)", err.Round, err.Latest, err.Committed) } -// LogicEvalError indicates TEAL evaluation failure -type LogicEvalError struct { - Err error - Details string -} - -// Error satisfies builtin interface `error` -func (err LogicEvalError) Error() string { - msg := fmt.Sprintf("logic eval error: %v", err.Err) - if len(err.Details) > 0 { - msg = fmt.Sprintf("%s. Details: %s", msg, err.Details) - } - return msg -} - // ErrNonSequentialBlockEval provides feedback when the evaluator cannot be created for // stale/future rounds. type ErrNonSequentialBlockEval struct { From e07461f1d96453683b3695568feecef8fa72a5c2 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Tue, 25 Apr 2023 14:35:09 -0400 Subject: [PATCH 2/6] Newspeak for TEAL --- data/transactions/logic/backwardCompat_test.go | 4 ++-- data/transactions/logic/eval.go | 2 +- ledger/eval/cow.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/backwardCompat_test.go b/data/transactions/logic/backwardCompat_test.go index df6bd68210..e8353e5d27 100644 --- a/data/transactions/logic/backwardCompat_test.go +++ b/data/transactions/logic/backwardCompat_test.go @@ -284,7 +284,7 @@ func TestBackwardCompatTEALv1(t *testing.T) { ep.TxnGroup[0].Lsig.Logic = program ep.TxnGroup[0].Lsig.Args = [][]byte{data[:], sig[:], pk[:], tx.Sender[:], tx.Note} - // ensure v1 program runs well on latest TEAL evaluator + // ensure v1 program runs well on latest evaluator require.Equal(t, uint8(1), program[0]) // Cost should stay exactly 2140 @@ -315,7 +315,7 @@ func TestBackwardCompatTEALv1(t *testing.T) { ep2.Proto.LogicSigMaxCost = 2308 testLogicBytes(t, opsV2.Program, ep2) - // ensure v0 program runs well on latest TEAL evaluator + // ensure v0 program runs well on latest evaluator ep, tx, _ = makeSampleEnv() program[0] = 0 sig = c.Sign(Msg{ diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 0a822a2c71..7e49687f04 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -691,7 +691,7 @@ func (pe panicError) Error() string { var errLogicSigNotSupported = errors.New("LogicSig not supported") var errTooManyArgs = errors.New("LogicSig has too many arguments") -// EvalError indicates TEAL evaluation failure +// EvalError indicates AVM evaluation failure type EvalError struct { Err error Details string diff --git a/ledger/eval/cow.go b/ledger/eval/cow.go index 4a04eb83eb..c58f65fc51 100644 --- a/ledger/eval/cow.go +++ b/ledger/eval/cow.go @@ -80,7 +80,7 @@ type roundCowState struct { // storage deltas populated as side effects of AppCall transaction // 1. Opt-in/Close actions (see Allocate/Deallocate) - // 2. Stateful TEAL evaluation (see setKey/delKey) + // 2. Application evaluation (see setKey/delKey) // must be incorporated into mods.accts before passing deltas forward sdeltas map[basics.Address]map[storagePtr]*storageDelta From c8dbb4c36fe1ad4b69cfbeffd4d7b90546ab2333 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 26 Apr 2023 11:43:29 -0400 Subject: [PATCH 3/6] Convey more info in EvalError --- data/transactions/logic/eval.go | 31 +++++++++++++++++----------- data/transactions/logic/eval_test.go | 2 +- data/transactions/verify/txn.go | 2 +- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 7e49687f04..6f301c6ef8 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -693,17 +693,24 @@ var errTooManyArgs = errors.New("LogicSig has too many arguments") // EvalError indicates AVM evaluation failure type EvalError struct { - Err error - Details string + Err error + details string + groupIndex int + logicsig bool } // Error satisfies builtin interface `error` func (err EvalError) Error() string { - msg := fmt.Sprintf("logic eval error: %v", err.Err) - if len(err.Details) > 0 { - msg = fmt.Sprintf("%s. Details: %s", msg, err.Details) + var msg string + if err.logicsig { + msg = fmt.Sprintf("rejected by logic err=%v", err.Err) + } else { + msg = fmt.Sprintf("logic eval error: %v", err.Err) + } + if err.details == "" { + return msg } - return msg + return msg + ". Details: " + err.details } func (err EvalError) Unwrap() error { @@ -796,9 +803,9 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam } pass, err := eval(program, &cx) if err != nil { - pc, det := cx.PcDetails() + pc, det := cx.pcDetails() details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) - err = EvalError{err, details} + err = EvalError{err, details, gi, false} } if cx.Trace != nil && cx.caller != nil { @@ -835,9 +842,9 @@ func EvalSignatureFull(gi int, params *EvalParams) (bool, *EvalContext, error) { pass, err := eval(cx.txn.Lsig.Logic, &cx) if err != nil { - pc, det := cx.PcDetails() + pc, det := cx.pcDetails() details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) - err = EvalError{err, details} + err = EvalError{err, details, gi, true} } return pass, &cx, err @@ -5553,8 +5560,8 @@ func opBlock(cx *EvalContext) error { } } -// PcDetails return PC and disassembled instructions at PC up to 2 opcodes back -func (cx *EvalContext) PcDetails() (pc int, dis string) { +// pcDetails return PC and disassembled instructions at PC up to 2 opcodes back +func (cx *EvalContext) pcDetails() (pc int, dis string) { const maxNumAdditionalOpcodes = 2 text, ds, err := disassembleInstrumented(cx.program, nil) if err != nil { diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index e55d580c84..3f4782462c 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -5158,7 +5158,7 @@ func TestPcDetails(t *testing.T) { assert.Equal(t, test.pc, cx.pc, ep.Trace.String()) - pc, det := cx.PcDetails() + pc, det := cx.pcDetails() assert.Equal(t, test.pc, pc) assert.Equal(t, test.det, det) }) diff --git a/data/transactions/verify/txn.go b/data/transactions/verify/txn.go index 1d36055210..7c75ffd9ec 100644 --- a/data/transactions/verify/txn.go +++ b/data/transactions/verify/txn.go @@ -450,7 +450,7 @@ func logicSigVerify(txn *transactions.SignedTxn, groupIndex int, groupCtx *Group pass, cx, err := logic.EvalSignatureFull(groupIndex, &ep) if err != nil { logicErrTotal.Inc(nil) - return LogicSigError{groupIndex, fmt.Errorf("transaction %v: rejected by logic err=%w", txn.ID(), err)} + return LogicSigError{groupIndex, fmt.Errorf("transaction %v: %w", txn.ID(), err)} } if !pass { logicRejTotal.Inc(nil) From 855c116d9a752b043d75fb3d4aa4decc012f4d28 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 26 Apr 2023 12:14:05 -0400 Subject: [PATCH 4/6] Cleanup ClearStateBudgetError --- config/consensus.go | 4 ++++ data/transactions/logic/eval.go | 23 +++++++---------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/config/consensus.go b/config/consensus.go index c3739a5903..e3bf4d527f 100644 --- a/config/consensus.go +++ b/config/consensus.go @@ -488,6 +488,9 @@ type ConsensusParams struct { // returning false, if pubkey is not on the curve. EnablePrecheckECDSACurve bool + // EnableBareBudgetError specifies that I/O budget overruns should not be considered EvalError + EnableBareBudgetError bool + // StateProofUseTrackerVerification specifies whether the node will use data from state proof verification tracker // in order to verify state proofs. StateProofUseTrackerVerification bool @@ -1262,6 +1265,7 @@ func initConsensusProtocols() { vFuture.LogicSigVersion = 9 // When moving this to a release, put a new higher LogicSigVersion here vFuture.EnablePrecheckECDSACurve = true + vFuture.EnableBareBudgetError = true vFuture.StateProofUseTrackerVerification = true vFuture.EnableCatchpointsWithSPContexts = true diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 6f301c6ef8..76bf6a8d27 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -717,20 +717,7 @@ func (err EvalError) Unwrap() error { return err.Err } -// ClearStateBudgetError allows evaluation to signal that the caller should -// reject the transaction. Normally, an error in evaluation would not cause a -// ClearState txn to fail. However, callers fail a txn for ClearStateBudgetError -// because the transaction has not provided enough budget to let ClearState do -// its job. -type ClearStateBudgetError struct { - offered int -} - -func (e ClearStateBudgetError) Error() string { - return fmt.Sprintf("Attempted ClearState execution with low OpcodeBudget %d", e.offered) -} - -// EvalContract executes stateful TEAL program as the gi'th transaction in params +// EvalContract executes stateful program as the gi'th transaction in params func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParams) (bool, *EvalContext, error) { if params.Ledger == nil { return false, nil, errors.New("no ledger in contract eval") @@ -751,7 +738,7 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam if cx.Proto.IsolateClearState && cx.txn.Txn.OnCompletion == transactions.ClearStateOC { if cx.PooledApplicationBudget != nil && *cx.PooledApplicationBudget < cx.Proto.MaxAppProgramCost { - return false, nil, ClearStateBudgetError{*cx.PooledApplicationBudget} + return false, nil, fmt.Errorf("Attempted ClearState execution with low OpcodeBudget %d", *cx.PooledApplicationBudget) } } @@ -792,7 +779,11 @@ func EvalContract(program []byte, gi int, aid basics.AppIndex, params *EvalParam used = basics.AddSaturate(used, size) if used > cx.ioBudget { - return false, nil, fmt.Errorf("box read budget (%d) exceeded", cx.ioBudget) + err = fmt.Errorf("box read budget (%d) exceeded", cx.ioBudget) + if !cx.Proto.EnableBareBudgetError { + err = EvalError{err, "", gi, false} + } + return false, nil, err } } cx.readBudgetChecked = true From e4ce16c289416014a3aab94102341f521dfcfae9 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 26 Apr 2023 13:01:34 -0400 Subject: [PATCH 5/6] Pretty up those opcodes --- data/transactions/logic/eval.go | 2 +- data/transactions/logic/eval_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 76bf6a8d27..f05fa2cc76 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -5576,7 +5576,7 @@ func (cx *EvalContext) pcDetails() (pc int, dis string) { break } } - return cx.pc, dis + return cx.pc, strings.ReplaceAll(strings.TrimSuffix(dis, "\n"), "\n", "; ") } func base64Decode(encoded []byte, encoding *base64.Encoding) ([]byte, error) { diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 3f4782462c..42bd615444 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -5138,9 +5138,9 @@ func TestPcDetails(t *testing.T) { pc int det string }{ - {"int 1; int 2; -", 5, "pushint 1\npushint 2\n-\n"}, - {"int 1; err", 3, "pushint 1\nerr\n"}, - {"int 1; dup; int 2; -; +", 6, "dup\npushint 2\n-\n"}, + {"int 1; int 2; -", 5, "pushint 1; pushint 2; -"}, + {"int 1; err", 3, "pushint 1; err"}, + {"int 1; dup; int 2; -; +", 6, "dup; pushint 2; -"}, {"b end; end:", 4, ""}, } for i, test := range tests { From 374dec6ebb0ef466b7d1a9fdd04f807a93f1e133 Mon Sep 17 00:00:00 2001 From: John Jannotti Date: Wed, 26 Apr 2023 13:26:12 -0400 Subject: [PATCH 6/6] Adjust errors from \n to ; --- ledger/boxtxn_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index fc6ec0cc31..b892a21f08 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -171,28 +171,28 @@ func TestBoxCreate(t *testing.T) { } dl.txn(adam.Args("check", "adam", "\x00\x00")) - dl.txgroup("box_create\nassert", adam.Noted("one"), adam.Noted("two")) + dl.txgroup("box_create; assert", adam.Noted("one"), adam.Noted("two")) bobo := call.Args("create", "bobo") dl.txn(bobo, fmt.Sprintf("invalid Box reference %#x", "bobo")) bobo.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("bobo")}} dl.txn(bobo) - dl.txgroup("box_create\nassert", bobo.Noted("one"), bobo.Noted("two")) + dl.txgroup("box_create; assert", bobo.Noted("one"), bobo.Noted("two")) dl.beginBlock() chaz := call.Args("create", "chaz") chaz.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("chaz")}} dl.txn(chaz) - dl.txn(chaz.Noted("again"), "box_create\nassert") + dl.txn(chaz.Noted("again"), "box_create; assert") dl.endBlock() // new block - dl.txn(chaz.Noted("again"), "box_create\nassert") + dl.txn(chaz.Noted("again"), "box_create; assert") dogg := call.Args("create", "dogg") dogg.Boxes = []transactions.BoxRef{{Index: 0, Name: []byte("dogg")}} dl.txn(dogg, "below min") dl.txn(chaz.Args("delete", "chaz")) - dl.txn(chaz.Args("delete", "chaz").Noted("again"), "box_del\nassert") + dl.txn(chaz.Args("delete", "chaz").Noted("again"), "box_del; assert") dl.txn(dogg) dl.txn(bobo.Args("delete", "bobo")) @@ -231,7 +231,7 @@ func TestBoxRecreate(t *testing.T) { create := call.Args("create", "adam", "\x04") // box value size is 4 bytes recreate := call.Args("recreate", "adam", "\x04") - dl.txn(recreate, "box_create\n!\nassert") + dl.txn(recreate, "box_create; !; assert") dl.txn(create) dl.txn(recreate) dl.txn(call.Args("set", "adam", "\x01\x02\x03\x04"))