Skip to content

Commit bddca4d

Browse files
committed
Ensure disassemble/reassemble cycle works in testProg.
1 parent 80bd5ed commit bddca4d

File tree

4 files changed

+56
-52
lines changed

4 files changed

+56
-52
lines changed

data/transactions/logic/assembler.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,7 @@ type disassembleState struct {
19381938
numericTargets bool
19391939
labelCount int
19401940
pendingLabels map[int]string
1941+
rerun bool
19411942

19421943
nextpc int
19431944
err error
@@ -1951,6 +1952,9 @@ func (dis *disassembleState) putLabel(label string, target int) {
19511952
dis.pendingLabels = make(map[int]string)
19521953
}
19531954
dis.pendingLabels[target] = label
1955+
if target <= dis.pc {
1956+
dis.rerun = true
1957+
}
19541958
}
19551959

19561960
func (dis *disassembleState) outputLabelIfNeeded() (err error) {
@@ -2412,11 +2416,13 @@ type disInfo struct {
24122416
hasStatefulOps bool
24132417
}
24142418

2415-
// disassembleInstrumented is like Disassemble, but additionally returns where
2416-
// each program counter value maps in the disassembly
2417-
func disassembleInstrumented(program []byte) (text string, ds disInfo, err error) {
2419+
// disassembleInstrumented is like Disassemble, but additionally
2420+
// returns where each program counter value maps in the
2421+
// disassembly. If the labels names are known, they may be passed in.
2422+
// When doing so, labels for all jump targets must be provided.
2423+
func disassembleInstrumented(program []byte, labels map[int]string) (text string, ds disInfo, err error) {
24182424
out := strings.Builder{}
2419-
dis := disassembleState{program: program, out: &out}
2425+
dis := disassembleState{program: program, out: &out, pendingLabels: labels}
24202426
version, vlen := binary.Uvarint(program)
24212427
if vlen <= 0 {
24222428
fmt.Fprintf(dis.out, "// invalid version\n")
@@ -2468,18 +2474,26 @@ func disassembleInstrumented(program []byte) (text string, ds disInfo, err error
24682474
}
24692475

24702476
text = out.String()
2477+
2478+
if dis.rerun {
2479+
if labels != nil {
2480+
err = errors.New("rerun even though we had labels")
2481+
return
2482+
}
2483+
return disassembleInstrumented(program, dis.pendingLabels)
2484+
}
24712485
return
24722486
}
24732487

24742488
// Disassemble produces a text form of program bytes.
24752489
// AssembleString(Disassemble()) should result in the same program bytes.
24762490
func Disassemble(program []byte) (text string, err error) {
2477-
text, _, err = disassembleInstrumented(program)
2491+
text, _, err = disassembleInstrumented(program, nil)
24782492
return
24792493
}
24802494

24812495
// HasStatefulOps checks if the program has stateful opcodes
24822496
func HasStatefulOps(program []byte) (bool, error) {
2483-
_, ds, err := disassembleInstrumented(program)
2497+
_, ds, err := disassembleInstrumented(program, nil)
24842498
return ds.hasStatefulOps, err
24852499
}

data/transactions/logic/assembler_test.go

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -384,19 +384,11 @@ func TestAssembleAlias(t *testing.T) {
384384
t.Parallel()
385385
source1 := `txn Accounts 0 // alias to txna
386386
pop
387-
gtxn 0 ApplicationArgs 0 // alias to gtxn
387+
gtxn 0 ApplicationArgs 0 // alias to gtxna
388388
pop
389389
`
390-
ops1, err := AssembleStringWithVersion(source1, AssemblerMaxVersion)
391-
require.NoError(t, err)
392-
393-
source2 := `txna Accounts 0
394-
pop
395-
gtxna 0 ApplicationArgs 0
396-
pop
397-
`
398-
ops2, err := AssembleStringWithVersion(source2, AssemblerMaxVersion)
399-
require.NoError(t, err)
390+
ops1 := testProg(t, source1, AssemblerMaxVersion)
391+
ops2 := testProg(t, strings.Replace(source1, "txn", "txna", -1), AssemblerMaxVersion)
400392

401393
require.Equal(t, ops1.Program, ops2.Program)
402394
}
@@ -431,6 +423,20 @@ func testProg(t testing.TB, source string, ver uint64, expected ...expect) *OpSt
431423
require.NoError(t, err)
432424
require.NotNil(t, ops)
433425
require.NotNil(t, ops.Program)
426+
// It should always be possible to Disassemble
427+
dis, err := Disassemble(ops.Program)
428+
require.NoError(t, err)
429+
// And, while the disassembly may not match input
430+
// exactly, the assembly of the disassembly should
431+
// give the same bytecode
432+
ops2, err := AssembleStringWithVersion(dis, ver)
433+
if len(ops2.Errors) > 0 || err != nil || ops2 == nil || ops2.Program == nil {
434+
t.Log(program)
435+
t.Log(dis)
436+
}
437+
require.Empty(t, ops2.Errors)
438+
require.NoError(t, err)
439+
require.Equal(t, ops.Program, ops2.Program)
434440
} else {
435441
require.Error(t, err)
436442
errors := ops.Errors
@@ -592,8 +598,7 @@ func TestAssembleInt(t *testing.T) {
592598
}
593599

594600
text := "int 0xcafebabe"
595-
ops, err := AssembleStringWithVersion(text, v)
596-
require.NoError(t, err)
601+
ops := testProg(t, text, v)
597602
s := hex.EncodeToString(ops.Program)
598603
require.Equal(t, mutateProgVersion(v, expected), s)
599604
})
@@ -643,8 +648,7 @@ func TestAssembleBytes(t *testing.T) {
643648
}
644649

645650
for _, vi := range variations {
646-
ops, err := AssembleStringWithVersion(vi, v)
647-
require.NoError(t, err)
651+
ops := testProg(t, vi, v)
648652
s := hex.EncodeToString(ops.Program)
649653
require.Equal(t, mutateProgVersion(v, expected), s)
650654
}
@@ -1184,8 +1188,7 @@ intc 0
11841188
intc 0
11851189
bnz done
11861190
done:`
1187-
ops, err := AssembleStringWithVersion(source, AssemblerMaxVersion)
1188-
require.NoError(t, err)
1191+
ops := testProg(t, source, AssemblerMaxVersion)
11891192
require.Equal(t, 9, len(ops.Program))
11901193
expectedProgBytes := []byte("\x01\x20\x01\x01\x22\x22\x40\x00\x00")
11911194
expectedProgBytes[0] = byte(AssemblerMaxVersion)

data/transactions/logic/debugger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func GetProgramID(program []byte) string {
8787
}
8888

8989
func makeDebugState(cx *evalContext) DebugState {
90-
disasm, dsInfo, err := disassembleInstrumented(cx.program)
90+
disasm, dsInfo, err := disassembleInstrumented(cx.program, nil)
9191
if err != nil {
9292
// Report disassembly error as program text
9393
disasm = err.Error()

data/transactions/logic/eval_test.go

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,7 @@ func TestGlobal(t *testing.T) {
10341034
addr, err := basics.UnmarshalChecksumAddress(testAddr)
10351035
require.NoError(t, err)
10361036
ledger.creatorAddr = addr
1037-
for v := uint64(0); v <= AssemblerMaxVersion; v++ {
1037+
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
10381038
_, ok := tests[v]
10391039
require.True(t, ok)
10401040
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
@@ -1055,9 +1055,6 @@ func TestGlobal(t *testing.T) {
10551055
txgroup := make([]transactions.SignedTxn, 1)
10561056
txgroup[0] = txn
10571057
sb := strings.Builder{}
1058-
block := bookkeeping.Block{}
1059-
block.BlockHeader.Round = 999999
1060-
block.BlockHeader.TimeStamp = 2069
10611058
proto := config.ConsensusParams{
10621059
MinTxnFee: 123,
10631060
MinBalance: 1000000,
@@ -2684,10 +2681,9 @@ func TestStackUnderflow(t *testing.T) {
26842681
t.Parallel()
26852682
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
26862683
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2687-
ops, err := AssembleStringWithVersion(`int 1`, v)
2684+
ops := testProg(t, `int 1`, v)
26882685
ops.Program = append(ops.Program, 0x08) // +
2689-
require.NoError(t, err)
2690-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2686+
err := Check(ops.Program, defaultEvalParams(nil, nil))
26912687
require.NoError(t, err)
26922688
sb := strings.Builder{}
26932689
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2707,10 +2703,9 @@ func TestWrongStackTypeRuntime(t *testing.T) {
27072703
t.Parallel()
27082704
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
27092705
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2710-
ops, err := AssembleStringWithVersion(`int 1`, v)
2711-
require.NoError(t, err)
2706+
ops := testProg(t, `int 1`, v)
27122707
ops.Program = append(ops.Program, 0x01, 0x15) // sha256, len
2713-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2708+
err := Check(ops.Program, defaultEvalParams(nil, nil))
27142709
require.NoError(t, err)
27152710
sb := strings.Builder{}
27162711
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2730,11 +2725,9 @@ func TestEqMismatch(t *testing.T) {
27302725
t.Parallel()
27312726
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
27322727
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2733-
ops, err := AssembleStringWithVersion(`byte 0x1234
2734-
int 1`, v)
2735-
require.NoError(t, err)
2728+
ops := testProg(t, `byte 0x1234; int 1`, v)
27362729
ops.Program = append(ops.Program, 0x12) // ==
2737-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2730+
err := Check(ops.Program, defaultEvalParams(nil, nil))
27382731
require.NoError(t, err) // TODO: Check should know the type stack was wrong
27392732
sb := strings.Builder{}
27402733
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2754,11 +2747,9 @@ func TestNeqMismatch(t *testing.T) {
27542747
t.Parallel()
27552748
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
27562749
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2757-
ops, err := AssembleStringWithVersion(`byte 0x1234
2758-
int 1`, v)
2759-
require.NoError(t, err)
2750+
ops := testProg(t, `byte 0x1234; int 1`, v)
27602751
ops.Program = append(ops.Program, 0x13) // !=
2761-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2752+
err := Check(ops.Program, defaultEvalParams(nil, nil))
27622753
require.NoError(t, err) // TODO: Check should know the type stack was wrong
27632754
sb := strings.Builder{}
27642755
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2778,11 +2769,9 @@ func TestWrongStackTypeRuntime2(t *testing.T) {
27782769
t.Parallel()
27792770
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
27802771
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2781-
ops, err := AssembleStringWithVersion(`byte 0x1234
2782-
int 1`, v)
2783-
require.NoError(t, err)
2772+
ops := testProg(t, `byte 0x1234; int 1`, v)
27842773
ops.Program = append(ops.Program, 0x08) // +
2785-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2774+
err := Check(ops.Program, defaultEvalParams(nil, nil))
27862775
require.NoError(t, err)
27872776
sb := strings.Builder{}
27882777
pass, _ := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2802,15 +2791,14 @@ func TestIllegalOp(t *testing.T) {
28022791
t.Parallel()
28032792
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
28042793
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2805-
ops, err := AssembleStringWithVersion(`int 1`, v)
2806-
require.NoError(t, err)
2794+
ops := testProg(t, `int 1`, v)
28072795
for opcode, spec := range opsByOpcode[v] {
28082796
if spec.op == nil {
28092797
ops.Program = append(ops.Program, byte(opcode))
28102798
break
28112799
}
28122800
}
2813-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2801+
err := Check(ops.Program, defaultEvalParams(nil, nil))
28142802
require.Error(t, err)
28152803
sb := strings.Builder{}
28162804
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))
@@ -2830,15 +2818,14 @@ func TestShortProgram(t *testing.T) {
28302818
t.Parallel()
28312819
for v := uint64(1); v <= AssemblerMaxVersion; v++ {
28322820
t.Run(fmt.Sprintf("v=%d", v), func(t *testing.T) {
2833-
ops, err := AssembleStringWithVersion(`int 1
2821+
ops := testProg(t, `int 1
28342822
bnz done
28352823
done:
28362824
int 1
28372825
`, v)
2838-
require.NoError(t, err)
28392826
// cut two last bytes - intc_1 and last byte of bnz
28402827
ops.Program = ops.Program[:len(ops.Program)-2]
2841-
err = Check(ops.Program, defaultEvalParams(nil, nil))
2828+
err := Check(ops.Program, defaultEvalParams(nil, nil))
28422829
require.Error(t, err)
28432830
sb := strings.Builder{}
28442831
pass, err := Eval(ops.Program, defaultEvalParams(&sb, nil))

0 commit comments

Comments
 (0)