github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/vm/vm_test.go (about)

     1  package vm
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  	"testing/quick"
    10  
    11  	"github.com/bytom/bytom/errors"
    12  	"github.com/bytom/bytom/testutil"
    13  )
    14  
    15  type tracebuf struct {
    16  	bytes.Buffer
    17  }
    18  
    19  func (t tracebuf) dump() {
    20  	os.Stdout.Write(t.Bytes())
    21  }
    22  
    23  // Programs that run without error.
    24  func TestProgramOK(t *testing.T) {
    25  	doOKNotOK(t, true)
    26  }
    27  
    28  // Programs that return an ErrFalseVMResult.
    29  func TestProgramNotOK(t *testing.T) {
    30  	doOKNotOK(t, false)
    31  }
    32  
    33  func doOKNotOK(t *testing.T, expectOK bool) {
    34  	cases := []struct {
    35  		prog string
    36  		args [][]byte
    37  	}{
    38  		{"TRUE", nil},
    39  
    40  		// bitwise ops
    41  		{"INVERT 0xfef0 EQUAL", [][]byte{{0x01, 0x0f}}},
    42  
    43  		{"AND 0x02 EQUAL", [][]byte{{0x03}, {0x06}}},
    44  		{"AND 0x02 EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
    45  
    46  		{"OR 0x07 EQUAL", [][]byte{{0x03}, {0x06}}},
    47  		{"OR 0x07ff EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
    48  
    49  		{"XOR 0x05 EQUAL", [][]byte{{0x03}, {0x06}}},
    50  		{"XOR 0x05ff EQUAL", [][]byte{{0x03, 0xff}, {0x06}}},
    51  
    52  		// numeric and logical ops
    53  		{"1ADD 2 NUMEQUAL", [][]byte{Int64Bytes(1)}},
    54  		{"1ADD 0 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
    55  
    56  		{"1SUB 1 NUMEQUAL", [][]byte{Int64Bytes(2)}},
    57  		{"1SUB -1 NUMEQUAL", [][]byte{Int64Bytes(0)}},
    58  
    59  		{"2MUL 2 NUMEQUAL", [][]byte{Int64Bytes(1)}},
    60  		{"2MUL 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
    61  		{"2MUL -2 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
    62  
    63  		{"2DIV 1 NUMEQUAL", [][]byte{Int64Bytes(2)}},
    64  		{"2DIV 0 NUMEQUAL", [][]byte{Int64Bytes(1)}},
    65  		{"2DIV 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
    66  		{"2DIV -1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
    67  		{"2DIV -1 NUMEQUAL", [][]byte{Int64Bytes(-2)}},
    68  
    69  		{"NEGATE -1 NUMEQUAL", [][]byte{Int64Bytes(1)}},
    70  		{"NEGATE 1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
    71  		{"NEGATE 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
    72  
    73  		{"ABS 1 NUMEQUAL", [][]byte{Int64Bytes(1)}},
    74  		{"ABS 1 NUMEQUAL", [][]byte{Int64Bytes(-1)}},
    75  		{"ABS 0 NUMEQUAL", [][]byte{Int64Bytes(0)}},
    76  
    77  		{"0NOTEQUAL", [][]byte{Int64Bytes(1)}},
    78  		{"0NOTEQUAL NOT", [][]byte{Int64Bytes(0)}},
    79  
    80  		{"ADD 5 NUMEQUAL", [][]byte{Int64Bytes(2), Int64Bytes(3)}},
    81  
    82  		{"SUB 2 NUMEQUAL", [][]byte{Int64Bytes(5), Int64Bytes(3)}},
    83  
    84  		{"MUL 6 NUMEQUAL", [][]byte{Int64Bytes(2), Int64Bytes(3)}},
    85  
    86  		{"DIV 2 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(3)}},
    87  
    88  		{"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(2)}},
    89  		{"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(-6), Int64Bytes(2)}},
    90  		{"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(6), Int64Bytes(-2)}},
    91  		{"MOD 0 NUMEQUAL", [][]byte{Int64Bytes(-6), Int64Bytes(-2)}},
    92  		{"MOD 2 NUMEQUAL", [][]byte{Int64Bytes(12), Int64Bytes(10)}},
    93  		{"MOD 8 NUMEQUAL", [][]byte{Int64Bytes(-12), Int64Bytes(10)}},
    94  		{"MOD -8 NUMEQUAL", [][]byte{Int64Bytes(12), Int64Bytes(-10)}},
    95  		{"MOD -2 NUMEQUAL", [][]byte{Int64Bytes(-12), Int64Bytes(-10)}},
    96  
    97  		{"LSHIFT 2 NUMEQUAL", [][]byte{Int64Bytes(1), Int64Bytes(1)}},
    98  		{"LSHIFT 4 NUMEQUAL", [][]byte{Int64Bytes(1), Int64Bytes(2)}},
    99  		{"LSHIFT -2 NUMEQUAL", [][]byte{Int64Bytes(-1), Int64Bytes(1)}},
   100  		{"LSHIFT -4 NUMEQUAL", [][]byte{Int64Bytes(-1), Int64Bytes(2)}},
   101  
   102  		{"1 1 BOOLAND", nil},
   103  		{"1 0 BOOLAND NOT", nil},
   104  		{"0 1 BOOLAND NOT", nil},
   105  		{"0 0 BOOLAND NOT", nil},
   106  
   107  		{"1 1 BOOLOR", nil},
   108  		{"1 0 BOOLOR", nil},
   109  		{"0 1 BOOLOR", nil},
   110  		{"0 0 BOOLOR NOT", nil},
   111  
   112  		{"1 2 OR 3 EQUAL", nil},
   113  
   114  		// splice ops
   115  		{"0 CATPUSHDATA 0x0000 EQUAL", [][]byte{{0x00}}},
   116  		{"0 0xff CATPUSHDATA 0x01ff EQUAL", nil},
   117  		{"CATPUSHDATA 0x050105 EQUAL", [][]byte{{0x05}, {0x05}}},
   118  		{"CATPUSHDATA 0xff01ff EQUAL", [][]byte{{0xff}, {0xff}}},
   119  		{"0 0xcccccc CATPUSHDATA 0x03cccccc EQUAL", nil},
   120  		{"0x05 0x05 SWAP 0xdeadbeef CATPUSHDATA DROP 0x05 EQUAL", nil},
   121  		{"0x05 0x05 SWAP 0xdeadbeef CATPUSHDATA DROP 0x05 EQUAL", nil},
   122  
   123  		// // control flow ops
   124  		{"1 JUMP:7 0 1 EQUAL", nil},                                                       // jumps over 0
   125  		{"1 JUMP:$target 0 $target 1 EQUAL", nil},                                         // jumps over 0
   126  		{"1 1 JUMPIF:8 0 1 EQUAL", nil},                                                   // jumps over 0
   127  		{"1 1 JUMPIF:$target 0 $target 1 EQUAL", nil},                                     // jumps over 0
   128  		{"1 0 JUMPIF:8 0 1 EQUAL NOT", nil},                                               // doesn't jump over 0
   129  		{"1 0 JUMPIF:$target 0 $target 1 EQUAL NOT", nil},                                 // doesn't jump over 0
   130  		{"1 0 JUMPIF:1", nil},                                                             // doesn't jump, so no infinite loop
   131  		{"1 $target 0 JUMPIF:$target", nil},                                               // doesn't jump, so no infinite loop
   132  		{"4 1 JUMPIF:14 5 EQUAL JUMP:16 4 EQUAL", nil},                                    // if (true) { return x == 4; } else { return x == 5; }
   133  		{"4 1 JUMPIF:$true 5 EQUAL JUMP:$end $true 4 EQUAL $end", nil},                    // if (true) { return x == 4; } else { return x == 5; }
   134  		{"5 0 JUMPIF:14 5 EQUAL JUMP:16 4 EQUAL", nil},                                    // if (false) { return x == 4; } else { return x == 5; }
   135  		{"5 0 JUMPIF:$true 5 EQUAL JUMP:$end $true 4 $test EQUAL $end", nil},              // if (false) { return x == 4; } else { return x == 5; }
   136  		{"0 1 2 3 4 5 6 JUMP:13 DROP DUP 0 NUMNOTEQUAL JUMPIF:12 1", nil},                 // same as "0 1 2 3 4 5 6 WHILE DROP ENDWHILE 1"
   137  		{"0 1 2 3 4 5 6 JUMP:$dup $drop DROP $dup DUP 0 NUMNOTEQUAL JUMPIF:$drop 1", nil}, // same as "0 1 2 3 4 5 6 WHILE DROP ENDWHILE 1"
   138  		{"0 JUMP:7 1ADD DUP 10 LESSTHAN JUMPIF:6 10 NUMEQUAL", nil},                       // fixed version of "0 1 WHILE DROP 1ADD DUP 10 LESSTHAN ENDWHILE 10 NUMEQUAL"
   139  		{"0 JUMP:$dup $add 1ADD $dup DUP 10 LESSTHAN JUMPIF:$add 10 NUMEQUAL", nil},       // fixed version of "0 1 WHILE DROP 1ADD DUP 10 LESSTHAN ENDWHILE 10 NUMEQUAL"
   140  
   141  	}
   142  	for i, c := range cases {
   143  		progSrc := c.prog
   144  		if !expectOK {
   145  			progSrc += " NOT"
   146  		}
   147  		prog, err := Assemble(progSrc)
   148  		if err != nil {
   149  			t.Fatal(err)
   150  		}
   151  		fmt.Printf("* case %d, prog [%s] [%x]\n", i, progSrc, prog)
   152  		trace := new(tracebuf)
   153  		TraceOut = trace
   154  		vm := &virtualMachine{
   155  			program:   prog,
   156  			runLimit:  int64(10000),
   157  			dataStack: append([][]byte{}, c.args...),
   158  		}
   159  		err = vm.run()
   160  		if err == nil && vm.falseResult() {
   161  			err = ErrFalseVMResult
   162  		}
   163  		if expectOK && err != nil {
   164  			trace.dump()
   165  			t.Errorf("case %d [%s]: expected success, got error %s", i, progSrc, err)
   166  		} else if !expectOK && err != ErrFalseVMResult {
   167  			trace.dump()
   168  			t.Errorf("case %d [%s]: expected ErrFalseVMResult, got %s", i, progSrc, err)
   169  		}
   170  	}
   171  }
   172  
   173  func TestVerifyTxInput(t *testing.T) {
   174  	cases := []struct {
   175  		vctx    *Context
   176  		wantErr error
   177  		gasLeft int64
   178  	}{
   179  		{
   180  			vctx: &Context{
   181  				VMVersion: 1,
   182  				Code:      []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
   183  				Arguments: [][]byte{{2}, {3}},
   184  			},
   185  			gasLeft: 9986,
   186  		},
   187  		{
   188  			vctx:    &Context{VMVersion: 2},
   189  			wantErr: ErrUnsupportedVM,
   190  			gasLeft: 10000,
   191  		},
   192  		{
   193  			vctx: &Context{
   194  				VMVersion: 1,
   195  				Code:      []byte{byte(OP_ADD), byte(OP_5), byte(OP_NUMEQUAL)},
   196  				Arguments: [][]byte{make([]byte, 50001)},
   197  			},
   198  			wantErr: ErrRunLimitExceeded,
   199  			gasLeft: 0,
   200  		},
   201  	}
   202  
   203  	for _, c := range cases {
   204  		gasLeft, gotErr := Verify(c.vctx, 10000)
   205  		if errors.Root(gotErr) != c.wantErr {
   206  			t.Errorf("VerifyTxInput(%+v) err = %v want %v", c.vctx, gotErr, c.wantErr)
   207  		}
   208  		if gasLeft != c.gasLeft {
   209  			t.Errorf("VerifyTxInput(%+v) err = gasLeft doesn't match", c.vctx)
   210  		}
   211  	}
   212  }
   213  
   214  func TestRun(t *testing.T) {
   215  	cases := []struct {
   216  		vm      *virtualMachine
   217  		wantErr error
   218  	}{{
   219  		vm: &virtualMachine{runLimit: 50000, program: []byte{byte(OP_TRUE)}},
   220  	}, {
   221  		vm:      &virtualMachine{runLimit: 50000, program: []byte{byte(OP_ADD)}},
   222  		wantErr: ErrDataStackUnderflow,
   223  	}}
   224  
   225  	for i, c := range cases {
   226  		gotErr := c.vm.run()
   227  
   228  		if gotErr != c.wantErr {
   229  			t.Errorf("run test %d: got err = %v want %v", i, gotErr, c.wantErr)
   230  			continue
   231  		}
   232  
   233  		if c.wantErr != nil {
   234  			continue
   235  		}
   236  	}
   237  }
   238  
   239  func TestStep(t *testing.T) {
   240  	txVMContext := &Context{DestPos: new(uint64)}
   241  	cases := []struct {
   242  		startVM *virtualMachine
   243  		wantVM  *virtualMachine
   244  		wantErr error
   245  	}{{
   246  		startVM: &virtualMachine{
   247  			program:  []byte{byte(OP_TRUE)},
   248  			runLimit: 50000,
   249  		},
   250  		wantVM: &virtualMachine{
   251  			program:   []byte{byte(OP_TRUE)},
   252  			runLimit:  49990,
   253  			dataStack: [][]byte{{1}},
   254  			pc:        1,
   255  			nextPC:    1,
   256  			data:      []byte{1},
   257  		},
   258  	}, {
   259  		startVM: &virtualMachine{
   260  			program:   []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
   261  			runLimit:  49990,
   262  			dataStack: [][]byte{},
   263  			pc:        1,
   264  		},
   265  		wantVM: &virtualMachine{
   266  			program:      []byte{byte(OP_TRUE), byte(OP_JUMP), byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
   267  			runLimit:     49989,
   268  			dataStack:    [][]byte{},
   269  			data:         []byte{byte(0xff), byte(0x00), byte(0x00), byte(0x00)},
   270  			pc:           255,
   271  			nextPC:       255,
   272  			deferredCost: 0,
   273  		},
   274  	}, {
   275  		startVM: &virtualMachine{
   276  			program:   []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   277  			runLimit:  49995,
   278  			dataStack: [][]byte{{1}},
   279  			pc:        1,
   280  		},
   281  		wantVM: &virtualMachine{
   282  			program:      []byte{byte(OP_TRUE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   283  			runLimit:     50003,
   284  			dataStack:    [][]byte{},
   285  			pc:           0,
   286  			nextPC:       0,
   287  			data:         []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   288  			deferredCost: -9,
   289  		},
   290  	}, {
   291  		startVM: &virtualMachine{
   292  			program:   []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   293  			runLimit:  49995,
   294  			dataStack: [][]byte{{}},
   295  			pc:        1,
   296  		},
   297  		wantVM: &virtualMachine{
   298  			program:      []byte{byte(OP_FALSE), byte(OP_JUMPIF), byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   299  			runLimit:     50002,
   300  			dataStack:    [][]byte{},
   301  			pc:           6,
   302  			nextPC:       6,
   303  			data:         []byte{byte(0x00), byte(0x00), byte(0x00), byte(0x00)},
   304  			deferredCost: -8,
   305  		},
   306  	}, {
   307  		startVM: &virtualMachine{
   308  			program:   []byte{255},
   309  			runLimit:  50000,
   310  			dataStack: [][]byte{},
   311  		},
   312  		wantVM: &virtualMachine{
   313  			program:   []byte{255},
   314  			runLimit:  49999,
   315  			pc:        1,
   316  			nextPC:    1,
   317  			dataStack: [][]byte{},
   318  		},
   319  	}, {
   320  		startVM: &virtualMachine{
   321  			program:  []byte{byte(OP_ADD)},
   322  			runLimit: 50000,
   323  		},
   324  		wantErr: ErrDataStackUnderflow,
   325  	}, {
   326  		startVM: &virtualMachine{
   327  			program:  []byte{byte(OP_INDEX)},
   328  			runLimit: 1,
   329  			context:  txVMContext,
   330  		},
   331  		wantErr: ErrRunLimitExceeded,
   332  	}, {
   333  		startVM: &virtualMachine{
   334  			program:           []byte{255},
   335  			runLimit:          100,
   336  			expansionReserved: true,
   337  		},
   338  		wantErr: ErrDisallowedOpcode,
   339  	}, {
   340  		startVM: &virtualMachine{
   341  			program:  []byte{255},
   342  			runLimit: 100,
   343  		},
   344  		wantVM: &virtualMachine{
   345  			program:  []byte{255},
   346  			runLimit: 99,
   347  			pc:       1,
   348  			nextPC:   1,
   349  		},
   350  	}}
   351  
   352  	for i, c := range cases {
   353  		gotErr := c.startVM.step()
   354  		gotVM := c.startVM
   355  
   356  		if gotErr != c.wantErr {
   357  			t.Errorf("step test %d: got err = %v want %v", i, gotErr, c.wantErr)
   358  			continue
   359  		}
   360  
   361  		if c.wantErr != nil {
   362  			continue
   363  		}
   364  
   365  		if !testutil.DeepEqual(gotVM, c.wantVM) {
   366  			t.Errorf("step test %d:\n\tgot vm:  %+v\n\twant vm: %+v", i, gotVM, c.wantVM)
   367  		}
   368  	}
   369  }
   370  
   371  func decompile(prog []byte) string {
   372  	var strs []string
   373  	for i := uint32(0); i < uint32(len(prog)); { // update i inside the loop
   374  		inst, err := ParseOp(prog, i)
   375  		if err != nil {
   376  			strs = append(strs, fmt.Sprintf("<%x>", prog[i]))
   377  			i++
   378  			continue
   379  		}
   380  		var str string
   381  		if len(inst.Data) > 0 {
   382  			str = fmt.Sprintf("0x%x", inst.Data)
   383  		} else {
   384  			str = inst.Op.String()
   385  		}
   386  		strs = append(strs, str)
   387  		i += inst.Len
   388  	}
   389  	return strings.Join(strs, " ")
   390  }
   391  
   392  func TestVerifyTxInputQuickCheck(t *testing.T) {
   393  	f := func(program []byte, witnesses [][]byte) (ok bool) {
   394  		defer func() {
   395  			if err := recover(); err != nil {
   396  				t.Log(decompile(program))
   397  				for i := range witnesses {
   398  					t.Logf("witness %d: %x\n", i, witnesses[i])
   399  				}
   400  				t.Log(err)
   401  				ok = false
   402  			}
   403  		}()
   404  
   405  		vctx := &Context{
   406  			VMVersion: 1,
   407  			Code:      program,
   408  			Arguments: witnesses,
   409  		}
   410  		Verify(vctx, 10000)
   411  
   412  		return true
   413  	}
   414  	if err := quick.Check(f, nil); err != nil {
   415  		t.Error(err)
   416  	}
   417  }
   418  
   419  func TestVerifyBlockHeaderQuickCheck(t *testing.T) {
   420  	f := func(program []byte, witnesses [][]byte) (ok bool) {
   421  		defer func() {
   422  			if err := recover(); err != nil {
   423  				t.Log(decompile(program))
   424  				for i := range witnesses {
   425  					t.Logf("witness %d: %x\n", i, witnesses[i])
   426  				}
   427  				t.Log(err)
   428  				ok = false
   429  			}
   430  		}()
   431  		context := &Context{
   432  			VMVersion: 1,
   433  			Code:      program,
   434  			Arguments: witnesses,
   435  		}
   436  		Verify(context, 10000)
   437  		return true
   438  	}
   439  	if err := quick.Check(f, nil); err != nil {
   440  		t.Error(err)
   441  	}
   442  }