From f59516ac3d58f81c167289fb642b44d6d6a14526 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Wed, 29 Sep 2021 22:00:04 -0400 Subject: [PATCH 1/2] Extended logic eval error --- data/transactions/logic/eval.go | 26 +++++++++++++++++++ data/transactions/logic/eval_test.go | 38 ++++++++++++++++++++++++++++ ledger/appcow.go | 4 ++- ledger/ledgercore/error.go | 9 +++++-- 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index aa2744ddc4..04e20ccf60 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -3938,3 +3938,29 @@ func opTxSubmit(cx *EvalContext) { }) cx.subtxn = nil } + +// 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 { + for i := 0; i < len(ds.pcOffset); i++ { + if ds.pcOffset[i].PC == cx.pc { + start := 0 + if i >= maxNumAdditionalOpcodes { + start = i - maxNumAdditionalOpcodes + } + + startTextPos := ds.pcOffset[start].Offset + endTextPos := len(text) + if i+1 < len(ds.pcOffset) { + endTextPos = ds.pcOffset[i+1].Offset + } + + dis = text[startTextPos:endTextPos] + break + } + } + } + return cx.pc, dis +} diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index 3e1fe56af3..a245047ea3 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4842,3 +4842,41 @@ func TestLog(t *testing.T) { require.False(t, pass) } } + +func TestPcDetails(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + var tests = []struct { + source string + 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"}, + } + for i, test := range tests { + t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { + ops := testProg(t, test.source, LogicVersion) + txn := makeSampleTxn() + txgroup := makeSampleTxnGroup(txn) + txn.Lsig.Logic = ops.Program + sb := strings.Builder{} + ep := defaultEvalParams(&sb, &txn) + ep.TxnGroup = txgroup + + var cx EvalContext + cx.EvalParams = ep + cx.runModeFlags = runModeSignature + + pass, err := eval(ops.Program, &cx) + require.Error(t, err) + require.False(t, pass) + + pc, det := cx.PcDetails() + require.Equal(t, test.pc, pc) + require.Equal(t, test.det, det) + }) + } +} diff --git a/ledger/appcow.go b/ledger/appcow.go index 64e718c7db..60ffee91e1 100644 --- a/ledger/appcow.go +++ b/ledger/appcow.go @@ -490,7 +490,9 @@ func (cb *roundCowState) StatefulEval(params logic.EvalParams, aidx basics.AppIn var cx *logic.EvalContext pass, cx, err = logic.EvalStatefulCx(program, params) if err != nil { - return false, transactions.EvalDelta{}, ledgercore.LogicEvalError{Err: err} + pc, det := cx.PcDetails() + details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + return false, transactions.EvalDelta{}, ledgercore.LogicEvalError{Err: err, Details: details} } // 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 c0e02b1956..c54c6173cd 100644 --- a/ledger/ledgercore/error.go +++ b/ledger/ledgercore/error.go @@ -79,12 +79,17 @@ func (err ErrNoEntry) Error() string { // LogicEvalError indicates TEAL evaluation failure type LogicEvalError struct { - Err error + Err error + Details string } // Error satisfies builtin interface `error` func (err LogicEvalError) Error() string { - return fmt.Sprintf("logic eval error: %v", err.Err) + 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 From b351f0ade60039275bad56909bc9733262e769de Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Thu, 30 Sep 2021 15:26:37 -0400 Subject: [PATCH 2/2] CR fixes --- data/transactions/logic/eval.go | 30 +++++++++++++++------------- data/transactions/logic/eval_test.go | 1 + ledger/appcow.go | 7 +++++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/data/transactions/logic/eval.go b/data/transactions/logic/eval.go index 04e20ccf60..1309988e36 100644 --- a/data/transactions/logic/eval.go +++ b/data/transactions/logic/eval.go @@ -3943,23 +3943,25 @@ func opTxSubmit(cx *EvalContext) { func (cx *EvalContext) PcDetails() (pc int, dis string) { const maxNumAdditionalOpcodes = 2 text, ds, err := disassembleInstrumented(cx.program, nil) - if err == nil { - for i := 0; i < len(ds.pcOffset); i++ { - if ds.pcOffset[i].PC == cx.pc { - start := 0 - if i >= maxNumAdditionalOpcodes { - start = i - maxNumAdditionalOpcodes - } + if err != nil { + return cx.pc, dis + } - startTextPos := ds.pcOffset[start].Offset - endTextPos := len(text) - if i+1 < len(ds.pcOffset) { - endTextPos = ds.pcOffset[i+1].Offset - } + for i := 0; i < len(ds.pcOffset); i++ { + if ds.pcOffset[i].PC == cx.pc { + start := 0 + if i >= maxNumAdditionalOpcodes { + start = i - maxNumAdditionalOpcodes + } - dis = text[startTextPos:endTextPos] - break + startTextPos := ds.pcOffset[start].Offset + endTextPos := len(text) + if i+1 < len(ds.pcOffset) { + endTextPos = ds.pcOffset[i+1].Offset } + + dis = text[startTextPos:endTextPos] + break } } return cx.pc, dis diff --git a/data/transactions/logic/eval_test.go b/data/transactions/logic/eval_test.go index a245047ea3..903201183d 100644 --- a/data/transactions/logic/eval_test.go +++ b/data/transactions/logic/eval_test.go @@ -4855,6 +4855,7 @@ func TestPcDetails(t *testing.T) { {"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"}, + {"b end; end:", 4, ""}, } for i, test := range tests { t.Run(fmt.Sprintf("i=%d", i), func(t *testing.T) { diff --git a/ledger/appcow.go b/ledger/appcow.go index 60ffee91e1..18a0f6f0a8 100644 --- a/ledger/appcow.go +++ b/ledger/appcow.go @@ -490,8 +490,11 @@ func (cb *roundCowState) StatefulEval(params logic.EvalParams, aidx basics.AppIn var cx *logic.EvalContext pass, cx, err = logic.EvalStatefulCx(program, params) if err != nil { - pc, det := cx.PcDetails() - details := fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + var details string + if cx != nil { + pc, det := cx.PcDetails() + details = fmt.Sprintf("pc=%d, opcodes=%s", pc, det) + } return false, transactions.EvalDelta{}, ledgercore.LogicEvalError{Err: err, Details: details} }