From 8219abbeb5bfff32bd88f8bb7a5e04f6ce5f6bc1 Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Fri, 15 Nov 2024 21:42:52 -0500 Subject: [PATCH] add DetailedEvalErrors method to EvalTracer interface --- data/pools/transactionPool.go | 6 +++++- data/transactions/logic/eval.go | 12 ++++++++++-- data/transactions/logic/evalStateful_test.go | 3 ++- data/transactions/logic/mocktracer/tracer.go | 2 ++ data/transactions/logic/tracer.go | 13 +++++++++++++ ledger/apptxn_test.go | 2 +- ledger/boxtxn_test.go | 12 ++++++------ ledger/simple_test.go | 2 ++ ledger/simulation/tracer.go | 2 ++ 9 files changed, 43 insertions(+), 11 deletions(-) diff --git a/data/pools/transactionPool.go b/data/pools/transactionPool.go index a2eef08bc3..8446291973 100644 --- a/data/pools/transactionPool.go +++ b/data/pools/transactionPool.go @@ -62,6 +62,7 @@ type TransactionPool struct { cond sync.Cond expiredTxCount map[basics.Round]int pendingBlockEvaluator BlockEvaluator + evalTracer logic.EvalTracer numPendingWholeBlocks basics.Round feePerByte atomic.Uint64 feeThresholdMultiplier uint64 @@ -140,6 +141,9 @@ func MakeTransactionPool(ledger *ledger.Ledger, cfg config.Local, log logging.Lo log: log, vac: vac, } + if cfg.EnableDeveloperAPI { + pool.evalTracer = logic.EvalErrorDetailsTracer{} + } pool.cond.L = &pool.mu pool.assemblyCond.L = &pool.assemblyMu pool.recomputeBlockEvaluator(nil, 0) @@ -732,7 +736,7 @@ func (pool *TransactionPool) recomputeBlockEvaluator(committedTxIDs map[transact if hint < 0 || int(knownCommitted) < 0 { hint = 0 } - pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, hint, 0, nil) + pool.pendingBlockEvaluator, err = pool.ledger.StartEvaluator(next.BlockHeader, hint, 0, pool.evalTracer) if err != nil { // The pendingBlockEvaluator is an interface, and in case of an evaluator error // we want to remove the interface itself rather then keeping an interface diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index a05d41748d..5edb5b8421 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -1027,8 +1027,16 @@ func (err EvalError) Unwrap() error { } func (cx *EvalContext) evalError(err error) error { - pc := cx.pc - details := fmt.Sprintf("pc=%d", pc) + var pc int + var details string + if cx.Tracer != nil && cx.Tracer.DetailedEvalErrors() { + var det string + pc, det = cx.pcDetails() + details = fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + } else { + pc = cx.pc + details = fmt.Sprintf("pc=%d", pc) + } err = basics.Annotate(err, "pc", pc, diff --git a/data/transactions/logic/evalStateful_test.go b/data/transactions/logic/evalStateful_test.go index 73ddeb8d02..36ac82adf1 100644 --- a/data/transactions/logic/evalStateful_test.go +++ b/data/transactions/logic/evalStateful_test.go @@ -443,6 +443,7 @@ func testApps(t *testing.T, programs []string, txgroup []transactions.SignedTxn, } } ep := NewAppEvalParams(transactions.WrapSignedTxnsWithAD(txgroup), proto, &transactions.SpecialAddresses{}) + ep.Tracer = EvalErrorDetailsTracer{} if ledger == nil { ledger = NewLedger(nil) } @@ -929,7 +930,7 @@ itxn_submit Stack: []any{uint64(777)}, }, }, - "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26", + "inner-msg": "logic eval error: cannot compare (uint64 to []byte). Details: app=5000, pc=26, opcodes=pushint 100; pushbytes 0x0201 // 0x0201; ==", "inner-attrs": map[string]any{ "pc": 26, "group-index": 0, diff --git a/data/transactions/logic/mocktracer/tracer.go b/data/transactions/logic/mocktracer/tracer.go index 8ac31cb3a3..45fb3e6380 100644 --- a/data/transactions/logic/mocktracer/tracer.go +++ b/data/transactions/logic/mocktracer/tracer.go @@ -232,6 +232,8 @@ func (d *Tracer) AfterBlock(hdr *bookkeeping.BlockHeader) { d.Events = append(d.Events, AfterBlock(hdr.Round)) } +func (d *Tracer) DisassembleEvalError() bool { return false } + // copyDeltas makes a deep copy of the given ledgercore.StateDelta pointer, if it's not nil. // This is inefficient, but it should only be used for testing. func copyDeltas(deltas *ledgercore.StateDelta) *ledgercore.StateDelta { diff --git a/data/transactions/logic/tracer.go b/data/transactions/logic/tracer.go index c8996f316c..551ea67e10 100644 --- a/data/transactions/logic/tracer.go +++ b/data/transactions/logic/tracer.go @@ -162,6 +162,10 @@ type EvalTracer interface { // AfterBlock is called after the block has finished evaluation. It will not be called in the event that an evalError // stops evaluation of the block. AfterBlock(hdr *bookkeeping.BlockHeader) + + // DetailedEvalErrors permits the tracer to enable detailed EvalError messages (including PC with disassembled + // opcodes) by returning true. + DetailedEvalErrors() bool } // NullEvalTracer implements EvalTracer, but all of its hook methods do nothing @@ -198,3 +202,12 @@ func (n NullEvalTracer) AfterOpcode(cx *EvalContext, evalError error) {} // AfterBlock does nothing func (n NullEvalTracer) AfterBlock(hdr *bookkeeping.BlockHeader) {} + +// DetailedEvalErrors does nothing +func (n NullEvalTracer) DetailedEvalErrors() bool { return false } + +// EvalErrorDetailsTracer enables disassembled details in EvalError messages, and nothing else. +type EvalErrorDetailsTracer struct{ NullEvalTracer } + +// DetailedEvalErrors returns true. +func (EvalErrorDetailsTracer) DetailedEvalErrors() bool { return true } diff --git a/ledger/apptxn_test.go b/ledger/apptxn_test.go index a970156c3f..a7b3b15214 100644 --- a/ledger/apptxn_test.go +++ b/ledger/apptxn_test.go @@ -566,7 +566,7 @@ func TestRekeyActionCloseAccount(t *testing.T) { // do it again, to ensure the lack of authorization is in the right // place, by matching on the opcode that comes before the itxn_submit we // want to know failed (it'll be in the error). - dl.txn(&useacct, "logic eval error") + dl.txn(&useacct, "itxn_field Receiver") }) } diff --git a/ledger/boxtxn_test.go b/ledger/boxtxn_test.go index 264515baa2..7f42beb143 100644 --- a/ledger/boxtxn_test.go +++ b/ledger/boxtxn_test.go @@ -169,28 +169,28 @@ func TestBoxCreate(t *testing.T) { } dl.txn(adam.Args("check", "adam", "\x00\x00")) - dl.txgroup("logic eval error: assert failed", 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("logic eval error: assert failed", 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"), "logic eval error: assert failed") + dl.txn(chaz.Noted("again"), "box_create; assert") dl.endBlock() // new block - dl.txn(chaz.Noted("again"), "logic eval error: assert failed") + 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"), "logic eval error: assert failed") + dl.txn(chaz.Args("delete", "chaz").Noted("again"), "box_del; assert") dl.txn(dogg) dl.txn(bobo.Args("delete", "bobo")) @@ -229,7 +229,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, "logic eval error: assert failed") + dl.txn(recreate, "box_create; !; assert") dl.txn(create) dl.txn(recreate) dl.txn(call.Args("set", "adam", "\x01\x02\x03\x04")) diff --git a/ledger/simple_test.go b/ledger/simple_test.go index 8af40eaaf3..9d4a480b49 100644 --- a/ledger/simple_test.go +++ b/ledger/simple_test.go @@ -29,6 +29,7 @@ import ( "github.com/algorand/go-algorand/data/bookkeeping" "github.com/algorand/go-algorand/data/committee" "github.com/algorand/go-algorand/data/transactions" + "github.com/algorand/go-algorand/data/transactions/logic" "github.com/algorand/go-algorand/data/transactions/verify" "github.com/algorand/go-algorand/data/txntest" "github.com/algorand/go-algorand/ledger/eval" @@ -91,6 +92,7 @@ func nextBlock(t testing.TB, ledger *Ledger) *eval.BlockEvaluator { eval, err := eval.StartEvaluator(ledger, nextHdr, eval.EvaluatorOptions{ Generate: true, Validate: true, // Do the complete checks that a new txn would be subject to + Tracer: logic.EvalErrorDetailsTracer{}, }) require.NoError(t, err) return eval diff --git a/ledger/simulation/tracer.go b/ledger/simulation/tracer.go index 94fa1394f7..8a9b7dda7b 100644 --- a/ledger/simulation/tracer.go +++ b/ledger/simulation/tracer.go @@ -555,3 +555,5 @@ func (tracer *evalTracer) AfterProgram(cx *logic.EvalContext, pass bool, evalErr } } } + +func (tracer *evalTracer) DetailedEvalErrors() bool { return true }