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