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 }