├── .gitignore ├── README.md ├── evm.json ├── evm ├── Cargo.lock ├── Cargo.toml └── src │ ├── block.rs │ ├── consts.rs │ ├── context.rs │ ├── eval.rs │ ├── helpers.rs │ ├── jump_map.rs │ ├── lib.rs │ ├── machine.rs │ ├── main.rs │ ├── memory.rs │ ├── opcode.rs │ ├── stack.rs │ └── state.rs └── notes.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | evm/target 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EVM From Scratch 2 | 3 | This is an EVM implementation built from scratch using Rust. 4 | 5 | It does not implement gas calculations yet. 6 | 7 | Mainly it consumes EVM bytecode and blockchain state data (account with balances and code), and executes the EVM bytecode according to the Ethereum Yellow Paper spec. 8 | 9 | This is still a work in progress, though 98% of functionality is implemented there is still some refactoring work that needs to be done. 10 | 11 | ## Credits 12 | 13 | Used [w1nt3r-eth/evm-from-scratch](https://github.com/w1nt3r-eth/evm-from-scratch) as a starting point and for many of the tests. 14 | 15 | Also used [rust-blockchain/evm](https://github.com/rust-blockchain/evm) as inspiration and a helpful reference implementation. 16 | -------------------------------------------------------------------------------- /evm.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "STOP", 4 | "hint": "", 5 | "code": { 6 | "asm": "STOP", 7 | "bin": "00" 8 | }, 9 | "expect": { 10 | "success": true, 11 | "stack": [] 12 | } 13 | }, 14 | { 15 | "name": "PUSH", 16 | "hint": "Read \"Program Counter\" section of the course learning materials for an example on how to parse the bytecode", 17 | "code": { 18 | "asm": "PUSH1 1", 19 | "bin": "6001" 20 | }, 21 | "expect": { 22 | "stack": [ 23 | "0x1" 24 | ], 25 | "success": true 26 | } 27 | }, 28 | { 29 | "name": "PUSH2", 30 | "hint": "PUSH2 reads the next 2 bytes, don't forget to properly increment PC", 31 | "code": { 32 | "asm": "PUSH2 0x1122", 33 | "bin": "611122" 34 | }, 35 | "expect": { 36 | "stack": [ 37 | "0x1122" 38 | ], 39 | "success": true 40 | } 41 | }, 42 | { 43 | "name": "PUSH4", 44 | "hint": "PUSH4 reads the next 4 bytes", 45 | "code": { 46 | "asm": "PUSH4 0x11223344", 47 | "bin": "6311223344" 48 | }, 49 | "expect": { 50 | "stack": [ 51 | "0x11223344" 52 | ], 53 | "success": true 54 | } 55 | }, 56 | { 57 | "name": "PUSH6", 58 | "hint": "PUSH6 reads the next 6 bytes. Can you implement all PUSH1...PUSH32 using the same code?", 59 | "code": { 60 | "asm": "PUSH6 0x112233445566", 61 | "bin": "65112233445566" 62 | }, 63 | "expect": { 64 | "stack": [ 65 | "0x112233445566" 66 | ], 67 | "success": true 68 | } 69 | }, 70 | { 71 | "name": "PUSH10", 72 | "hint": "SIZE = OPCODE - PUSH1 + 1, then transform take the next SIZE bytes, PC += SIZE", 73 | "code": { 74 | "asm": "PUSH10 0x112233445566778899aa", 75 | "bin": "69112233445566778899aa" 76 | }, 77 | "expect": { 78 | "stack": [ 79 | "0x112233445566778899aa" 80 | ], 81 | "success": true 82 | } 83 | }, 84 | { 85 | "name": "PUSH11", 86 | "hint": "SIZE = OPCODE - PUSH1 + 1, program.slice(pc + 1, pc + 1 + size)", 87 | "code": { 88 | "asm": "PUSH11 0x112233445566778899aabb", 89 | "bin": "6a112233445566778899aabb" 90 | }, 91 | "expect": { 92 | "stack": [ 93 | "0x112233445566778899aabb" 94 | ], 95 | "success": true 96 | } 97 | }, 98 | { 99 | "name": "PUSH32", 100 | "hint": "PUSH32 reads the next 32 bytes (256 bits)", 101 | "code": { 102 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 103 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 104 | }, 105 | "expect": { 106 | "stack": [ 107 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 108 | ], 109 | "success": true 110 | } 111 | }, 112 | { 113 | "name": "PUSH32 no.2", 114 | "hint": "PUSH32 reads the next 32 bytes (256 bits)", 115 | "code": { 116 | "asm": "PUSH32 0xff00000000000000000000000000000000000000000000000000000000000000", 117 | "bin": "7fff00000000000000000000000000000000000000000000000000000000000000" 118 | }, 119 | "expect": { 120 | "stack": [ 121 | "0xff00000000000000000000000000000000000000000000000000000000000000" 122 | ], 123 | "success": true 124 | } 125 | }, 126 | { 127 | "name": "PUSH (twice)", 128 | "hint": "Note the order of items on the stack. The tests expect the top of the stack to be the first element", 129 | "code": { 130 | "asm": "PUSH1 1\nPUSH1 2", 131 | "bin": "60016002" 132 | }, 133 | "expect": { 134 | "stack": [ 135 | "0x2", 136 | "0x1" 137 | ], 138 | "success": true 139 | } 140 | }, 141 | { 142 | "name": "POP", 143 | "hint": "POP removes the top item from the stack and discards it", 144 | "code": { 145 | "asm": "PUSH1 1\nPUSH1 2\nPOP", 146 | "bin": "6001600250" 147 | }, 148 | "expect": { 149 | "stack": [ 150 | "0x1" 151 | ], 152 | "success": true 153 | } 154 | }, 155 | { 156 | "name": "STOP (midway)", 157 | "hint": "Note that the `PUSH1 2` didn't execute because the program stops after STOP opcode", 158 | "code": { 159 | "asm": "PUSH1 1\nSTOP\nPUSH1 2", 160 | "bin": "6001006002" 161 | }, 162 | "expect": { 163 | "stack": [ 164 | "0x1" 165 | ], 166 | "success": true 167 | } 168 | }, 169 | { 170 | "name": "ADD", 171 | "hint": "ADD takes the first 2 items from the stack, adds them together and pushes the result", 172 | "code": { 173 | "asm": "PUSH1 0x01\nPUSH1 0x02\nADD", 174 | "bin": "6001600201" 175 | }, 176 | "expect": { 177 | "stack": [ 178 | "0x3" 179 | ], 180 | "success": true 181 | } 182 | }, 183 | { 184 | "name": "ADD (overflow)", 185 | "hint": "EVM operates with uint256, if you add 2 to the max possible value it overflows and wraps around", 186 | "code": { 187 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0x02\nADD", 188 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600201" 189 | }, 190 | "expect": { 191 | "stack": [ 192 | "0x1" 193 | ], 194 | "success": true 195 | } 196 | }, 197 | { 198 | "name": "MUL", 199 | "code": { 200 | "asm": "PUSH1 0x02\nPUSH1 0x03\nMUL", 201 | "bin": "6002600302" 202 | }, 203 | "expect": { 204 | "stack": [ 205 | "0x6" 206 | ], 207 | "success": true 208 | }, 209 | "hint": "" 210 | }, 211 | { 212 | "name": "MUL (overflow)", 213 | "hint": "All math is performed with implicit [mod 2^256]", 214 | "code": { 215 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0x02\nMUL", 216 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600202" 217 | }, 218 | "expect": { 219 | "stack": [ 220 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" 221 | ], 222 | "success": true 223 | } 224 | }, 225 | { 226 | "name": "SUB", 227 | "hint": "SUB takes the first element from the stack and subtracts the second element from the stack", 228 | "code": { 229 | "asm": "PUSH1 0x02\nPUSH1 0x03\nSUB", 230 | "bin": "6002600303" 231 | }, 232 | "expect": { 233 | "stack": [ 234 | "0x1" 235 | ], 236 | "success": true 237 | } 238 | }, 239 | { 240 | "name": "SUB (underflow)", 241 | "hint": "Underflow works the same way as overflow, 3 - 2 wraps around and results in MAX_UINT256", 242 | "code": { 243 | "asm": "PUSH1 0x03\nPUSH1 0x02\nSUB", 244 | "bin": "6003600203" 245 | }, 246 | "expect": { 247 | "stack": [ 248 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 249 | ], 250 | "success": true 251 | } 252 | }, 253 | { 254 | "name": "DIV", 255 | "hint": "DIV takes the first element from the stack and divides it by the second element from the stack", 256 | "code": { 257 | "asm": "PUSH1 0x02\nPUSH1 0x06\nDIV", 258 | "bin": "6002600604" 259 | }, 260 | "expect": { 261 | "stack": [ 262 | "0x3" 263 | ], 264 | "success": true 265 | } 266 | }, 267 | { 268 | "name": "DIV (whole)", 269 | "hint": "Fraction part of the division is discarded", 270 | "code": { 271 | "asm": "PUSH1 0x06\nPUSH1 0x02\nDIV", 272 | "bin": "6006600204" 273 | }, 274 | "expect": { 275 | "stack": [ 276 | "0x0" 277 | ], 278 | "success": true 279 | } 280 | }, 281 | { 282 | "name": "DIV (by zero)", 283 | "hint": "In EVM you can divide by zero! Modern Solidity protects from this by adding instructions that check for zero", 284 | "code": { 285 | "asm": "PUSH1 0x00\nPUSH1 0x02\nDIV", 286 | "bin": "6000600204" 287 | }, 288 | "expect": { 289 | "stack": [ 290 | "0x0" 291 | ], 292 | "success": true 293 | } 294 | }, 295 | { 296 | "name": "MOD", 297 | "hint": "10 mod 3 = 1", 298 | "code": { 299 | "asm": "PUSH1 3\nPUSH1 10\nMOD", 300 | "bin": "6003600a06" 301 | }, 302 | "expect": { 303 | "stack": [ 304 | "0x1" 305 | ], 306 | "success": true 307 | } 308 | }, 309 | { 310 | "name": "MOD (by larger number)", 311 | "hint": "5 mod 17 = 5", 312 | "code": { 313 | "asm": "PUSH1 17\nPUSH1 5\nMOD", 314 | "bin": "6011600506" 315 | }, 316 | "expect": { 317 | "stack": [ 318 | "0x5" 319 | ], 320 | "success": true 321 | } 322 | }, 323 | { 324 | "name": "MOD (by zero)", 325 | "hint": "In EVM you can divide by zero! Modern Solidity protects from this by adding instructions that check for zero", 326 | "code": { 327 | "asm": "PUSH1 0\nPUSH1 2\nMOD", 328 | "bin": "6000600206" 329 | }, 330 | "expect": { 331 | "stack": [ 332 | "0x0" 333 | ], 334 | "success": true 335 | } 336 | }, 337 | { 338 | "name": "ADDMOD", 339 | "hint": "10 + 10 mod 8 = 4", 340 | "code": { 341 | "asm": "PUSH1 8\nPUSH1 10\nPUSH1 10\nADDMOD", 342 | "bin": "6008600a600a08" 343 | }, 344 | "expect": { 345 | "stack": [ 346 | "0x4" 347 | ], 348 | "success": true 349 | } 350 | }, 351 | { 352 | "name": "ADDMOD (wrapped)", 353 | "code": { 354 | "asm": "PUSH1 2\nPUSH1 2\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nADDMOD", 355 | "bin": "600260027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08" 356 | }, 357 | "expect": { 358 | "stack": [ 359 | "0x1" 360 | ], 361 | "success": true 362 | }, 363 | "hint": "" 364 | }, 365 | { 366 | "name": "MULMOD", 367 | "hint": "10 * 10 mod 8 = 4", 368 | "code": { 369 | "asm": "PUSH1 8\nPUSH1 10\nPUSH1 10\nMULMOD", 370 | "bin": "6008600a600a09" 371 | }, 372 | "expect": { 373 | "stack": [ 374 | "0x4" 375 | ], 376 | "success": true 377 | } 378 | }, 379 | { 380 | "name": "MULMOD (wrapped)", 381 | "code": { 382 | "asm": "PUSH1 12\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nMULMOD", 383 | "bin": "600c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09" 384 | }, 385 | "expect": { 386 | "stack": [ 387 | "0x9" 388 | ], 389 | "success": true 390 | }, 391 | "hint": "" 392 | }, 393 | { 394 | "name": "EXP", 395 | "code": { 396 | "asm": "PUSH1 2\nPUSH1 10\nEXP", 397 | "bin": "6002600a0a" 398 | }, 399 | "expect": { 400 | "stack": [ 401 | "0x64" 402 | ], 403 | "success": true 404 | }, 405 | "hint": "" 406 | }, 407 | { 408 | "name": "SIGNEXTEND (positive)", 409 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SIGNEXTEND has no effect on \"positive\" numbers", 410 | "code": { 411 | "asm": "PUSH1 0x7F\nPUSH1 0\nSIGNEXTEND", 412 | "bin": "607f60000b" 413 | }, 414 | "expect": { 415 | "stack": [ 416 | "0x7f" 417 | ], 418 | "success": true 419 | } 420 | }, 421 | { 422 | "name": "SIGNEXTEND (negative)", 423 | "hint": "Read \"Negative Numbers\" section of the course learning materials. The first bit of 0xFF is 1, so it is a negative number and needs to be padded by 1s in front", 424 | "code": { 425 | "asm": "PUSH1 0xFF\nPUSH1 0\nSIGNEXTEND", 426 | "bin": "60ff60000b" 427 | }, 428 | "expect": { 429 | "stack": [ 430 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 431 | ], 432 | "success": true 433 | } 434 | }, 435 | { 436 | "name": "SIGNEXTEND (negative) no. 2", 437 | "hint": "Read \"Negative Numbers\" section of the course learning materials. The first bit of 0xFF is 1, so it is a negative number and needs to be padded by 1s in front", 438 | "code": { 439 | "asm": "PUSH1 0xFE\nPUSH1 0\nSIGNEXTEND", 440 | "bin": "60fe60000b" 441 | }, 442 | "expect": { 443 | "stack": [ 444 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" 445 | ], 446 | "success": true 447 | } 448 | }, 449 | { 450 | "name": "SDIV", 451 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SDIV works like DIV for \"positive\" numbers", 452 | "code": { 453 | "asm": "PUSH1 10\nPUSH1 10\nSDIV", 454 | "bin": "600a600a05" 455 | }, 456 | "expect": { 457 | "stack": [ 458 | "0x1" 459 | ], 460 | "success": true 461 | } 462 | }, 463 | { 464 | "name": "SDIV (negative)", 465 | "hint": "Read \"Negative Numbers\" section of the course learning materials. -2 / -1 = 2", 466 | "code": { 467 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSDIV", 468 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe05" 469 | }, 470 | "expect": { 471 | "stack": [ 472 | "0x2" 473 | ], 474 | "success": true 475 | } 476 | }, 477 | { 478 | "name": "SDIV (mix of negative and positive)", 479 | "hint": "Read \"Negative Numbers\" section of the course learning materials. 10 / -2 = -5", 480 | "code": { 481 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nPUSH1 10\nSDIV", 482 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe600a05" 483 | }, 484 | "expect": { 485 | "stack": [ 486 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" 487 | ], 488 | "success": true 489 | } 490 | }, 491 | { 492 | "name": "SDIV (zero divided by negative number)", 493 | "hint": "Read \"Negative Numbers\" section of the course learning materials. 0 / -2 = 0", 494 | "code": { 495 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nPUSH1 0\nSDIV", 496 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe600005" 497 | }, 498 | "expect": { 499 | "stack": [ 500 | "0x0" 501 | ], 502 | "success": true 503 | } 504 | }, 505 | { 506 | "name": "SDIV (positive number divided by zero)", 507 | "hint": "10 / 0 = 0", 508 | "code": { 509 | "asm": "PUSH1 0\nPUSH1 10\nSDIV", 510 | "bin": "6000600a05" 511 | }, 512 | "expect": { 513 | "stack": [ 514 | "0x0" 515 | ], 516 | "success": true 517 | } 518 | }, 519 | { 520 | "name": "SMOD", 521 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SMOD works like MOD for \"positive\" numbers", 522 | "code": { 523 | "asm": "PUSH1 3\nPUSH1 10\nSMOD", 524 | "bin": "6003600a07" 525 | }, 526 | "expect": { 527 | "stack": [ 528 | "0x1" 529 | ], 530 | "success": true 531 | } 532 | }, 533 | { 534 | "name": "SMOD (negative)", 535 | "todo": "Open PR for changes to the below line", 536 | "hint": "Read \"Negative Numbers\" section of the course learning materials. -8 mod -3 = -2", 537 | "code": { 538 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8\nSMOD", 539 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff807" 540 | }, 541 | "expect": { 542 | "stack": [ 543 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" 544 | ], 545 | "success": true 546 | } 547 | }, 548 | { 549 | "name": "SDIV (by zero)", 550 | "hint": "In EVM you can divide by zero", 551 | "code": { 552 | "asm": "PUSH1 0x00\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nSDIV", 553 | "bin": "60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd05" 554 | }, 555 | "expect": { 556 | "stack": [ 557 | "0x0" 558 | ], 559 | "success": true 560 | } 561 | }, 562 | { 563 | "name": "SMOD (by zero)", 564 | "hint": "In EVM you can divide by zero", 565 | "code": { 566 | "asm": "PUSH1 0x00\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nSMOD", 567 | "bin": "60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd07" 568 | }, 569 | "expect": { 570 | "stack": [ 571 | "0x0" 572 | ], 573 | "success": true 574 | } 575 | }, 576 | { 577 | "name": "LT", 578 | "hint": "9 < 10 = true (1)", 579 | "code": { 580 | "asm": "PUSH1 10\nPUSH1 9\nLT", 581 | "bin": "600a600910" 582 | }, 583 | "expect": { 584 | "stack": [ 585 | "0x1" 586 | ], 587 | "success": true 588 | } 589 | }, 590 | { 591 | "name": "LT (equal)", 592 | "hint": "10 < 10 = false (0)", 593 | "code": { 594 | "asm": "PUSH1 10\nPUSH1 10\nLT", 595 | "bin": "600a600a10" 596 | }, 597 | "expect": { 598 | "stack": [ 599 | "0x0" 600 | ], 601 | "success": true 602 | } 603 | }, 604 | { 605 | "name": "LT (greater)", 606 | "hint": "11 < 10 = false (0)", 607 | "code": { 608 | "asm": "PUSH1 10\nPUSH1 11\nLT", 609 | "bin": "600a600b10" 610 | }, 611 | "expect": { 612 | "stack": [ 613 | "0x0" 614 | ], 615 | "success": true 616 | } 617 | }, 618 | { 619 | "name": "GT", 620 | "hint": "10 > 9 = true (1)", 621 | "code": { 622 | "asm": "PUSH1 9\nPUSH1 10\nGT", 623 | "bin": "6009600a11" 624 | }, 625 | "expect": { 626 | "stack": [ 627 | "0x1" 628 | ], 629 | "success": true 630 | } 631 | }, 632 | { 633 | "name": "GT (equal)", 634 | "hint": "10 > 10 = false (0)", 635 | "code": { 636 | "asm": "PUSH1 10\nPUSH1 10\nGT", 637 | "bin": "600a600a11" 638 | }, 639 | "expect": { 640 | "stack": [ 641 | "0x0" 642 | ], 643 | "success": true 644 | } 645 | }, 646 | { 647 | "name": "GT (less)", 648 | "hint": "10 > 11 = false (0)", 649 | "code": { 650 | "asm": "PUSH1 11\nPUSH1 10\nGT", 651 | "bin": "600b600a11" 652 | }, 653 | "expect": { 654 | "stack": [ 655 | "0x0" 656 | ], 657 | "success": true 658 | } 659 | }, 660 | { 661 | "name": "SLT", 662 | "hint": "Same as LT but treats arguments as signed numbers. -1 < 0 = true (1)", 663 | "code": { 664 | "asm": "PUSH1 0\nPUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nSLT", 665 | "bin": "60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12" 666 | }, 667 | "expect": { 668 | "stack": [ 669 | "0x1" 670 | ], 671 | "success": true 672 | } 673 | }, 674 | { 675 | "name": "SLT (equal)", 676 | "hint": "Same as LT but treats arguments as signed numbers. -1 < -1 = false (0)", 677 | "code": { 678 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nSLT", 679 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12" 680 | }, 681 | "expect": { 682 | "stack": [ 683 | "0x0" 684 | ], 685 | "success": true 686 | } 687 | }, 688 | { 689 | "name": "SLT (greater)", 690 | "hint": "Same as LT but treats arguments as signed numbers. -1 < -1 = false (0)", 691 | "code": { 692 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0\nSLT", 693 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600012" 694 | }, 695 | "expect": { 696 | "stack": [ 697 | "0x0" 698 | ], 699 | "success": true 700 | } 701 | }, 702 | { 703 | "name": "SGT", 704 | "hint": "Same as GT but treats arguments as signed numbers. No effect on \"positive\" numbers: 10 > 9 = true (1)", 705 | "code": { 706 | "asm": "PUSH1 9\nPUSH1 10\nSGT", 707 | "bin": "6009600a13" 708 | }, 709 | "expect": { 710 | "stack": [ 711 | "0x1" 712 | ], 713 | "success": true 714 | } 715 | }, 716 | { 717 | "name": "SGT (equal)", 718 | "hint": "Same as GT but treats arguments as signed numbers. -2 > -2 = false (0)", 719 | "code": { 720 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSGT", 721 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13" 722 | }, 723 | "expect": { 724 | "stack": [ 725 | "0x0" 726 | ], 727 | "success": true 728 | } 729 | }, 730 | { 731 | "name": "SGT (greater)", 732 | "hint": "Same as GT but treats arguments as signed numbers. -2 > -3 = true (1)", 733 | "code": { 734 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSGT", 735 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13" 736 | }, 737 | "expect": { 738 | "stack": [ 739 | "0x1" 740 | ], 741 | "success": true 742 | } 743 | }, 744 | { 745 | "name": "EQ", 746 | "hint": "10 == 10 = true (1)", 747 | "code": { 748 | "asm": "PUSH1 10\nPUSH1 10\nEQ", 749 | "bin": "600a600a14" 750 | }, 751 | "expect": { 752 | "stack": [ 753 | "0x1" 754 | ], 755 | "success": true 756 | } 757 | }, 758 | { 759 | "name": "EQ (not equal)", 760 | "hint": "10 == 9 = false (0)", 761 | "code": { 762 | "asm": "PUSH1 9\nPUSH1 10\nEQ", 763 | "bin": "6009600a14" 764 | }, 765 | "expect": { 766 | "stack": [ 767 | "0x0" 768 | ], 769 | "success": true 770 | } 771 | }, 772 | { 773 | "name": "ISZERO (not zero)", 774 | "hint": "If the top element on the stack is not zero, pushes 0", 775 | "code": { 776 | "asm": "PUSH1 9\nISZERO", 777 | "bin": "600915" 778 | }, 779 | "expect": { 780 | "stack": [ 781 | "0x0" 782 | ], 783 | "success": true 784 | } 785 | }, 786 | { 787 | "name": "ISZERO (zero)", 788 | "hint": "If the top element on the stack is zero, pushes 1", 789 | "code": { 790 | "asm": "PUSH1 0\nISZERO", 791 | "bin": "600015" 792 | }, 793 | "expect": { 794 | "stack": [ 795 | "0x1" 796 | ], 797 | "success": true 798 | } 799 | }, 800 | { 801 | "name": "NOT", 802 | "hint": "Bitwise NOT operation, flips every bit 1->0, 0->1", 803 | "code": { 804 | "asm": "PUSH1 0x0f\nNOT", 805 | "bin": "600f19" 806 | }, 807 | "expect": { 808 | "stack": [ 809 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" 810 | ], 811 | "success": true 812 | } 813 | }, 814 | { 815 | "name": "AND", 816 | "hint": "Bitwise AND operation of the top 2 items on the stack", 817 | "code": { 818 | "asm": "PUSH1 0xe\nPUSH1 0x3\nAND", 819 | "bin": "600e600316" 820 | }, 821 | "expect": { 822 | "stack": [ 823 | "0x2" 824 | ], 825 | "success": true 826 | } 827 | }, 828 | { 829 | "name": "OR", 830 | "hint": "Bitwise OR operation of the top 2 items on the stack", 831 | "code": { 832 | "asm": "PUSH1 0xe\nPUSH1 0x3\nOR", 833 | "bin": "600e600317" 834 | }, 835 | "expect": { 836 | "stack": [ 837 | "0xf" 838 | ], 839 | "success": true 840 | } 841 | }, 842 | { 843 | "name": "XOR", 844 | "hint": "Bitwise XOR operation of the top 2 items on the stack", 845 | "code": { 846 | "asm": "PUSH1 0xf0\nPUSH1 0x0f\nXOR", 847 | "bin": "60f0600f18" 848 | }, 849 | "expect": { 850 | "stack": [ 851 | "0xff" 852 | ], 853 | "success": true 854 | } 855 | }, 856 | { 857 | "name": "SHL", 858 | "hint": "Bitwise shift left, 1 << 1 = 2", 859 | "code": { 860 | "asm": "PUSH1 1\nPUSH1 1\nSHL", 861 | "bin": "600160011b" 862 | }, 863 | "expect": { 864 | "stack": [ 865 | "0x2" 866 | ], 867 | "success": true 868 | } 869 | }, 870 | { 871 | "name": "SHL (discards)", 872 | "hint": "Bits that end up outside MAX_UINT256 are discarded", 873 | "code": { 874 | "asm": "PUSH32 0xFF00000000000000000000000000000000000000000000000000000000000000\nPUSH1 4\nSHL", 875 | "bin": "7fff0000000000000000000000000000000000000000000000000000000000000060041b" 876 | }, 877 | "expect": { 878 | "stack": [ 879 | "0xf000000000000000000000000000000000000000000000000000000000000000" 880 | ], 881 | "success": true 882 | } 883 | }, 884 | { 885 | "name": "SHL (too large)", 886 | "hint": "When shift amount is too large, returns zero", 887 | "code": { 888 | "asm": "PUSH1 1\nPUSH4 0xFFFFFFFF\nSHL", 889 | "bin": "600163ffffffff1b" 890 | }, 891 | "expect": { 892 | "stack": [ 893 | "0x0" 894 | ], 895 | "success": true 896 | } 897 | }, 898 | { 899 | "name": "SHR", 900 | "hint": "Bitwise shift right, 2 >> 1 = 1", 901 | "code": { 902 | "asm": "PUSH1 2\nPUSH1 1\nSHR", 903 | "bin": "600260011c" 904 | }, 905 | "expect": { 906 | "stack": [ 907 | "0x1" 908 | ], 909 | "success": true 910 | } 911 | }, 912 | { 913 | "name": "SHR (discards)", 914 | "hint": "Bits that end up outside are discarded", 915 | "code": { 916 | "asm": "PUSH1 0xFF\nPUSH1 4\nSHR", 917 | "bin": "60ff60041c" 918 | }, 919 | "expect": { 920 | "stack": [ 921 | "0xf" 922 | ], 923 | "success": true 924 | } 925 | }, 926 | { 927 | "name": "SHR (too large)", 928 | "hint": "When shift amount is too large, returns zero", 929 | "code": { 930 | "asm": "PUSH1 1\nPUSH4 0xFFFFFFFF\nSHR", 931 | "bin": "600163ffffffff1c" 932 | }, 933 | "expect": { 934 | "stack": [ 935 | "0x0" 936 | ], 937 | "success": true 938 | } 939 | }, 940 | { 941 | "name": "SAR", 942 | "hint": "Like SHR but treats the argument as signed number. No effect on \"positive\" numbers, 2 >> 1 = 1", 943 | "code": { 944 | "asm": "PUSH1 2\nPUSH1 1\nSAR", 945 | "bin": "600260011d" 946 | }, 947 | "expect": { 948 | "stack": [ 949 | "0x1" 950 | ], 951 | "success": true 952 | } 953 | }, 954 | { 955 | "name": "SAR (fills 1s)", 956 | "hint": "Note that unlike SHR, it fills the empty space with 1s", 957 | "code": { 958 | "asm": "PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH1 4\nSAR", 959 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060041d" 960 | }, 961 | "expect": { 962 | "stack": [ 963 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" 964 | ], 965 | "success": true 966 | } 967 | }, 968 | { 969 | "name": "SAR (too large)", 970 | "hint": "When shift amount is too large and the first bit is 1, fills the whole number with 1s", 971 | "code": { 972 | "asm": "PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH4 0xFFFFFFFF\nSAR", 973 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0063ffffffff1d" 974 | }, 975 | "expect": { 976 | "stack": [ 977 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 978 | ], 979 | "success": true 980 | } 981 | }, 982 | { 983 | "name": "SAR (positive, too large)", 984 | "hint": "When shift amount is too large and the first bit is 0, fills the whole number with 0s", 985 | "code": { 986 | "asm": "PUSH32 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH4 0xFFFFFFFF\nSAR", 987 | "bin": "7f0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0063ffffffff1d" 988 | }, 989 | "expect": { 990 | "stack": [ 991 | "0x0" 992 | ], 993 | "success": true 994 | } 995 | }, 996 | { 997 | "name": "BYTE", 998 | "hint": "The value on the stack is treated as 32 bytes, take 31st (counting from the most significant one)", 999 | "code": { 1000 | "asm": "PUSH1 0xff\nPUSH1 31\nBYTE", 1001 | "bin": "60ff601f1a" 1002 | }, 1003 | "expect": { 1004 | "stack": [ 1005 | "0xff" 1006 | ], 1007 | "success": true 1008 | } 1009 | }, 1010 | { 1011 | "name": "BYTE (30th)", 1012 | "hint": "The value on the stack is treated as 32 bytes, take 30st (counting from the most significant one)", 1013 | "code": { 1014 | "asm": "PUSH2 0xff00\nPUSH1 30\nBYTE", 1015 | "bin": "61ff00601e1a" 1016 | }, 1017 | "expect": { 1018 | "stack": [ 1019 | "0xff" 1020 | ], 1021 | "success": true 1022 | } 1023 | }, 1024 | { 1025 | "name": "BYTE (29th)", 1026 | "hint": "Try to generalize your code to work with any argument", 1027 | "code": { 1028 | "asm": "PUSH3 0xff0000\nPUSH1 29\nBYTE", 1029 | "bin": "62ff0000601d1a" 1030 | }, 1031 | "expect": { 1032 | "stack": [ 1033 | "0xff" 1034 | ], 1035 | "success": true 1036 | } 1037 | }, 1038 | { 1039 | "name": "BYTE (out of range)", 1040 | "hint": "Treat other elements as zeros", 1041 | "code": { 1042 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 42\nBYTE", 1043 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602a1a" 1044 | }, 1045 | "expect": { 1046 | "stack": [ 1047 | "0x0" 1048 | ], 1049 | "success": true 1050 | } 1051 | }, 1052 | { 1053 | "name": "DUP1", 1054 | "hint": "Duplicate the first element from the stack and push it onto the stack", 1055 | "code": { 1056 | "asm": "PUSH1 1\nDUP1\nADD", 1057 | "bin": "60018001" 1058 | }, 1059 | "expect": { 1060 | "stack": [ 1061 | "0x2" 1062 | ], 1063 | "success": true 1064 | } 1065 | }, 1066 | { 1067 | "name": "DUP3", 1068 | "hint": "Duplicate the 3rd element from the stack and push it onto the stack", 1069 | "code": { 1070 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nDUP3", 1071 | "bin": "60016002600382" 1072 | }, 1073 | "expect": { 1074 | "stack": [ 1075 | "0x1", 1076 | "0x3", 1077 | "0x2", 1078 | "0x1" 1079 | ], 1080 | "success": true 1081 | } 1082 | }, 1083 | { 1084 | "name": "DUP5", 1085 | "hint": "Try to implement your code to handle any DUP1...DUP16", 1086 | "code": { 1087 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nDUP5", 1088 | "bin": "6001600260036004600584" 1089 | }, 1090 | "expect": { 1091 | "stack": [ 1092 | "0x1", 1093 | "0x5", 1094 | "0x4", 1095 | "0x3", 1096 | "0x2", 1097 | "0x1" 1098 | ], 1099 | "success": true 1100 | } 1101 | }, 1102 | { 1103 | "name": "DUP8", 1104 | "hint": "No seriously try to implement your code to handle any DUP1...DUP16 generically. You can do OPCODE - DUP1 + 1 to learn which item to take from the stack", 1105 | "code": { 1106 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nPUSH1 7\nPUSH1 8\nDUP8", 1107 | "bin": "6001600260036004600560066007600887" 1108 | }, 1109 | "expect": { 1110 | "stack": [ 1111 | "0x1", 1112 | "0x8", 1113 | "0x7", 1114 | "0x6", 1115 | "0x5", 1116 | "0x4", 1117 | "0x3", 1118 | "0x2", 1119 | "0x1" 1120 | ], 1121 | "success": true 1122 | } 1123 | }, 1124 | { 1125 | "name": "SWAP1", 1126 | "hint": "Swap the top item from the stack with the 1st one after that", 1127 | "code": { 1128 | "asm": "PUSH1 1\nPUSH1 2\nSWAP1", 1129 | "bin": "6001600290" 1130 | }, 1131 | "expect": { 1132 | "stack": [ 1133 | "0x1", 1134 | "0x2" 1135 | ], 1136 | "success": true 1137 | } 1138 | }, 1139 | { 1140 | "name": "SWAP3", 1141 | "hint": "Swap the top item from the stack with the 3rd one after that", 1142 | "code": { 1143 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nSWAP3", 1144 | "bin": "600160026003600492" 1145 | }, 1146 | "expect": { 1147 | "stack": [ 1148 | "0x1", 1149 | "0x3", 1150 | "0x2", 1151 | "0x4" 1152 | ], 1153 | "success": true 1154 | } 1155 | }, 1156 | { 1157 | "name": "SWAP5", 1158 | "hint": "Swap the top item from the stack with the 5th one after that. Try to implement SWAP1..SWAP16 with the same code", 1159 | "code": { 1160 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nSWAP5", 1161 | "bin": "60016002600360046005600694" 1162 | }, 1163 | "expect": { 1164 | "stack": [ 1165 | "0x1", 1166 | "0x5", 1167 | "0x4", 1168 | "0x3", 1169 | "0x2", 1170 | "0x6" 1171 | ], 1172 | "success": true 1173 | } 1174 | }, 1175 | { 1176 | "name": "SWAP7", 1177 | "hint": "No seriously try to implement your code to handle any SWAP1...SWAP16 generically. You can do OPCODE - SWAP1 + 2 to learn which item to take from the stack", 1178 | "code": { 1179 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nPUSH1 7\nPUSH1 8\nSWAP7", 1180 | "bin": "6001600260036004600560066007600896" 1181 | }, 1182 | "expect": { 1183 | "stack": [ 1184 | "0x1", 1185 | "0x7", 1186 | "0x6", 1187 | "0x5", 1188 | "0x4", 1189 | "0x3", 1190 | "0x2", 1191 | "0x8" 1192 | ], 1193 | "success": true 1194 | } 1195 | }, 1196 | { 1197 | "name": "INVALID", 1198 | "hint": "Invalid instruction. Note that your code is expected to return success = false, not throw exceptions", 1199 | "code": { 1200 | "asm": "INVALID", 1201 | "bin": "fe" 1202 | }, 1203 | "expect": { 1204 | "success": false, 1205 | "stack": [] 1206 | } 1207 | }, 1208 | { 1209 | "name": "PC", 1210 | "hint": "Read \"Program Counter\" section of the course learning materials", 1211 | "code": { 1212 | "asm": "PC", 1213 | "bin": "58" 1214 | }, 1215 | "expect": { 1216 | "stack": [ 1217 | "0x0" 1218 | ], 1219 | "success": true 1220 | } 1221 | }, 1222 | { 1223 | "name": "PC (more code)", 1224 | "hint": "`PUSH1 0` is counted as 2 bytes (even though it is a single instruction)", 1225 | "code": { 1226 | "asm": "PUSH1 0\nPOP\nPC", 1227 | "bin": "60005058" 1228 | }, 1229 | "expect": { 1230 | "stack": [ 1231 | "0x3" 1232 | ], 1233 | "success": true 1234 | } 1235 | }, 1236 | { 1237 | "name": "GAS", 1238 | "hint": "In this version of the tests, GAS is not supported yet and is always expected to return MAX_UINT256", 1239 | "code": { 1240 | "asm": "GAS", 1241 | "bin": "5a" 1242 | }, 1243 | "expect": { 1244 | "stack": [ 1245 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1246 | ], 1247 | "success": true 1248 | } 1249 | }, 1250 | { 1251 | "name": "JUMP", 1252 | "hint": "Set the Program Counter (PC) to the top value from the stack", 1253 | "code": { 1254 | "asm": "PUSH1 5\nJUMP\nPUSH1 1\nJUMPDEST\nPUSH1 2", 1255 | "bin": "60055660015b6002" 1256 | }, 1257 | "expect": { 1258 | "stack": [ 1259 | "0x2" 1260 | ], 1261 | "success": true 1262 | } 1263 | }, 1264 | { 1265 | "name": "JUMP (not JUMPDEST)", 1266 | "hint": "Offset 4 is not a valid JUMPDEST instruction", 1267 | "code": { 1268 | "asm": "PUSH1 3\nJUMP\nPUSH1 1", 1269 | "bin": "6003566001" 1270 | }, 1271 | "expect": { 1272 | "success": false, 1273 | "stack": [] 1274 | } 1275 | }, 1276 | { 1277 | "name": "JUMP (bad instruction boundry)", 1278 | "hint": "See \"9.4.3. Jump Destination Validity\" of the Yellow Paper https://ethereum.github.io/yellowpaper/paper.pdf", 1279 | "code": { 1280 | "asm": "PUSH1 4\nJUMP\nPUSH1 0x5b\nPUSH1 0xff", 1281 | "bin": "600456605b60ff" 1282 | }, 1283 | "expect": { 1284 | "success": false, 1285 | "stack": [] 1286 | } 1287 | }, 1288 | { 1289 | "name": "JUMPI (no jump)", 1290 | "hint": "Conditional JUMP, second argument is 0, not jumping", 1291 | "code": { 1292 | "asm": "PUSH1 0\nPUSH1 7\nJUMPI\nPUSH1 1\nJUMPDEST\nPUSH1 2\nPOP", 1293 | "bin": "600060075760015b600250" 1294 | }, 1295 | "expect": { 1296 | "stack": [ 1297 | "0x1" 1298 | ], 1299 | "success": true 1300 | } 1301 | }, 1302 | { 1303 | "name": "JUMPI (jump)", 1304 | "hint": "Conditional JUMP, second argument is not 0, jumping", 1305 | "code": { 1306 | "asm": "PUSH1 1\nPUSH1 7\nJUMPI\nPUSH1 1\nJUMPDEST\nPUSH1 2", 1307 | "bin": "600160075760015b6002" 1308 | }, 1309 | "expect": { 1310 | "stack": [ 1311 | "0x2" 1312 | ], 1313 | "success": true 1314 | } 1315 | }, 1316 | { 1317 | "name": "MSTORE", 1318 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1319 | "code": { 1320 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH1 0\nMLOAD", 1321 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600052600051" 1322 | }, 1323 | "expect": { 1324 | "stack": [ 1325 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 1326 | ], 1327 | "success": true 1328 | } 1329 | }, 1330 | { 1331 | "name": "MSTORE no.2", 1332 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1333 | "code": { 1334 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 1\nMSTORE\nPUSH1 0\nMLOAD", 1335 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600152600051" 1336 | }, 1337 | "expect": { 1338 | "stack": [ 1339 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" 1340 | ], 1341 | "success": true 1342 | } 1343 | }, 1344 | { 1345 | "name": "MSTORE no.3", 1346 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1347 | "code": { 1348 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 1\nMSTORE\nPUSH1 1\nMLOAD", 1349 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600152600151" 1350 | }, 1351 | "expect": { 1352 | "stack": [ 1353 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 1354 | ], 1355 | "success": true 1356 | } 1357 | }, 1358 | { 1359 | "name": "MSTORE two times load first", 1360 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1361 | "code": { 1362 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH32 0x0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f\nPUSH1 32\nMSTORE\nPUSH1 0\nMLOAD", 1363 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f206000527f0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f602052600051" 1364 | }, 1365 | "expect": { 1366 | "stack": [ 1367 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 1368 | ], 1369 | "success": true 1370 | } 1371 | }, 1372 | { 1373 | "name": "MSTORE two times load second", 1374 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1375 | "code": { 1376 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH32 0x0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f\nPUSH1 32\nMSTORE\nPUSH1 32\nMLOAD", 1377 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f206000527f0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f602052602051" 1378 | }, 1379 | "expect": { 1380 | "stack": [ 1381 | "0x202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f" 1382 | ], 1383 | "success": true 1384 | } 1385 | }, 1386 | { 1387 | "name": "MSTORE index 32 first, then index 0, load second", 1388 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1389 | "code": { 1390 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 32\nMSTORE\nPUSH32 0x0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f\nPUSH1 0\nMSTORE\nPUSH1 32\nMLOAD", 1391 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f206020527f0202030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f1f600052602051" 1392 | }, 1393 | "expect": { 1394 | "stack": [ 1395 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 1396 | ], 1397 | "success": true 1398 | } 1399 | }, 1400 | { 1401 | "name": "MSTORE (tail)", 1402 | "hint": "MLOAD starts from byte offset 31 and picks up the last byte (0x20), the rest of the memory is 00", 1403 | "code": { 1404 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH1 31\nMLOAD", 1405 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600052601f51" 1406 | }, 1407 | "expect": { 1408 | "stack": [ 1409 | "0x2000000000000000000000000000000000000000000000000000000000000000" 1410 | ], 1411 | "success": true 1412 | } 1413 | }, 1414 | { 1415 | "name": "MSTORE8", 1416 | "hint": "Store a single byte at the given offset", 1417 | "code": { 1418 | "asm": "PUSH1 0xff\nPUSH1 31\nMSTORE8\nPUSH1 0\nMLOAD", 1419 | "bin": "60ff601f53600051" 1420 | }, 1421 | "expect": { 1422 | "stack": [ 1423 | "0xff" 1424 | ], 1425 | "success": true 1426 | } 1427 | }, 1428 | { 1429 | "name": "MSIZE", 1430 | "hint": "No memory has been accessed, so the memory size is 0", 1431 | "code": { 1432 | "asm": "MSIZE", 1433 | "bin": "59" 1434 | }, 1435 | "expect": { 1436 | "stack": [ 1437 | "0x0" 1438 | ], 1439 | "success": true 1440 | } 1441 | }, 1442 | { 1443 | "name": "MSIZE (0x20)", 1444 | "hint": "The first 32-byte section has been accessed, so the memory size is 32 (0x20)", 1445 | "code": { 1446 | "asm": "PUSH1 0\nMLOAD\nPOP\nMSIZE", 1447 | "bin": "6000515059" 1448 | }, 1449 | "expect": { 1450 | "stack": [ 1451 | "0x20" 1452 | ], 1453 | "success": true 1454 | } 1455 | }, 1456 | { 1457 | "name": "MSIZE (0x60)", 1458 | "hint": "Memory is measured in 32-byte chunks", 1459 | "code": { 1460 | "asm": "PUSH1 0x39\nMLOAD\nPOP\nMSIZE", 1461 | "bin": "6039515059" 1462 | }, 1463 | "expect": { 1464 | "stack": [ 1465 | "0x60" 1466 | ], 1467 | "success": true 1468 | } 1469 | }, 1470 | { 1471 | "name": "MSIZE (after MSTORE8)", 1472 | "hint": "Any opcode touching memory should update MSIZE, including the future ones. Implement memory access in a way that automatically updates MSIZE no matter which opcode used it", 1473 | "code": { 1474 | "asm": "PUSH1 0xff\nPUSH1 0xff\nMSTORE8\nMSIZE", 1475 | "bin": "60ff60ff5359" 1476 | }, 1477 | "expect": { 1478 | "stack": [ 1479 | "0x100" 1480 | ], 1481 | "success": true 1482 | } 1483 | }, 1484 | { 1485 | "name": "SHA3", 1486 | "hint": "Use an existing library for your programming language. Note that even though the opcode is called SHA3, the algorythm used is keccak256", 1487 | "code": { 1488 | "asm": "PUSH32 0xffffffff00000000000000000000000000000000000000000000000000000000\nPUSH1 0\nMSTORE\nPUSH1 4\nPUSH1 0\nSHA3", 1489 | "bin": "7fffffffff000000000000000000000000000000000000000000000000000000006000526004600020" 1490 | }, 1491 | "expect": { 1492 | "stack": [ 1493 | "0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238" 1494 | ], 1495 | "success": true 1496 | } 1497 | }, 1498 | { 1499 | "name": "ADDRESS", 1500 | "hint": "Read \"Transaction\" section of the course learning materials. Change your evm function parameters list to include transaction data", 1501 | "tx": { 1502 | "to": "0x1000000000000000000000000000000000000aaa" 1503 | }, 1504 | "code": { 1505 | "asm": "ADDRESS", 1506 | "bin": "30" 1507 | }, 1508 | "expect": { 1509 | "stack": [ 1510 | "0x1000000000000000000000000000000000000aaa" 1511 | ], 1512 | "success": true 1513 | } 1514 | }, 1515 | { 1516 | "name": "CALLER", 1517 | "hint": "Solidity calls this msg.sender", 1518 | "tx": { 1519 | "from": "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 1520 | }, 1521 | "code": { 1522 | "asm": "CALLER", 1523 | "bin": "33" 1524 | }, 1525 | "expect": { 1526 | "stack": [ 1527 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 1528 | ], 1529 | "success": true 1530 | } 1531 | }, 1532 | { 1533 | "name": "ORIGIN", 1534 | "hint": "Solidity calls this tx.origin", 1535 | "tx": { 1536 | "origin": "0x1337" 1537 | }, 1538 | "code": { 1539 | "asm": "ORIGIN", 1540 | "bin": "32" 1541 | }, 1542 | "expect": { 1543 | "stack": [ 1544 | "0x1337" 1545 | ], 1546 | "success": true 1547 | } 1548 | }, 1549 | { 1550 | "name": "GASPRICE", 1551 | "tx": { 1552 | "gasprice": "0x99" 1553 | }, 1554 | "code": { 1555 | "asm": "GASPRICE", 1556 | "bin": "3a" 1557 | }, 1558 | "expect": { 1559 | "stack": [ 1560 | "0x99" 1561 | ], 1562 | "success": true 1563 | }, 1564 | "hint": "" 1565 | }, 1566 | { 1567 | "name": "BASEFEE", 1568 | "block": { 1569 | "basefee": "0x1" 1570 | }, 1571 | "code": { 1572 | "asm": "BASEFEE", 1573 | "bin": "48" 1574 | }, 1575 | "expect": { 1576 | "stack": [ 1577 | "0x1" 1578 | ], 1579 | "success": true 1580 | }, 1581 | "hint": "" 1582 | }, 1583 | { 1584 | "name": "COINBASE", 1585 | "hint": "Do not hardcode these numbers, pull them from the test cases", 1586 | "block": { 1587 | "coinbase": "0x777" 1588 | }, 1589 | "code": { 1590 | "asm": "COINBASE", 1591 | "bin": "41" 1592 | }, 1593 | "expect": { 1594 | "stack": [ 1595 | "0x777" 1596 | ], 1597 | "success": true 1598 | } 1599 | }, 1600 | { 1601 | "name": "COINBASE (different one)", 1602 | "hint": "Do not hardcode these numbers, pull them from the test cases", 1603 | "block": { 1604 | "coinbase": "0x888" 1605 | }, 1606 | "code": { 1607 | "asm": "COINBASE", 1608 | "bin": "41" 1609 | }, 1610 | "expect": { 1611 | "stack": [ 1612 | "0x888" 1613 | ], 1614 | "success": true 1615 | } 1616 | }, 1617 | { 1618 | "name": "TIMESTAMP", 1619 | "hint": "Solidity calls this block.timestamp", 1620 | "block": { 1621 | "timestamp": "0xe4e1c1" 1622 | }, 1623 | "code": { 1624 | "asm": "TIMESTAMP", 1625 | "bin": "42" 1626 | }, 1627 | "expect": { 1628 | "stack": [ 1629 | "0xe4e1c1" 1630 | ], 1631 | "success": true 1632 | } 1633 | }, 1634 | { 1635 | "name": "NUMBER", 1636 | "hint": "Solidity calls this block.number", 1637 | "block": { 1638 | "number": "0x1000001" 1639 | }, 1640 | "code": { 1641 | "asm": "NUMBER", 1642 | "bin": "43" 1643 | }, 1644 | "expect": { 1645 | "stack": [ 1646 | "0x1000001" 1647 | ], 1648 | "success": true 1649 | } 1650 | }, 1651 | { 1652 | "name": "DIFFICULTY", 1653 | "hint": "Also known as PREVRANDAO, not used in these test cases yet", 1654 | "block": { 1655 | "difficulty": "0x20000" 1656 | }, 1657 | "code": { 1658 | "asm": "DIFFICULTY", 1659 | "bin": "44" 1660 | }, 1661 | "expect": { 1662 | "stack": [ 1663 | "0x20000" 1664 | ], 1665 | "success": true 1666 | } 1667 | }, 1668 | { 1669 | "name": "GASLIMIT", 1670 | "block": { 1671 | "gaslimit": "0xffffffffffff" 1672 | }, 1673 | "code": { 1674 | "asm": "GASLIMIT", 1675 | "bin": "45" 1676 | }, 1677 | "expect": { 1678 | "stack": [ 1679 | "0xffffffffffff" 1680 | ], 1681 | "success": true 1682 | }, 1683 | "hint": "" 1684 | }, 1685 | { 1686 | "name": "CHAINID", 1687 | "block": { 1688 | "chainid": "0x1" 1689 | }, 1690 | "code": { 1691 | "asm": "CHAINID", 1692 | "bin": "46" 1693 | }, 1694 | "expect": { 1695 | "stack": [ 1696 | "0x1" 1697 | ], 1698 | "success": true 1699 | }, 1700 | "hint": "" 1701 | }, 1702 | { 1703 | "name": "BLOCKHASH", 1704 | "hint": "Not used in this test suite, can return 0", 1705 | "code": { 1706 | "asm": "PUSH1 0\nBLOCKHASH", 1707 | "bin": "600040" 1708 | }, 1709 | "expect": { 1710 | "stack": [ 1711 | "0x0" 1712 | ], 1713 | "success": true 1714 | } 1715 | }, 1716 | { 1717 | "name": "BALANCE", 1718 | "hint": "Read \"State\" section of the course learning materials. Modify your evm function to take state as one of the arguments, or turn it into a class", 1719 | "state": { 1720 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d": { 1721 | "balance": "0x100" 1722 | } 1723 | }, 1724 | "code": { 1725 | "asm": "PUSH20 0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d\nBALANCE", 1726 | "bin": "731e79b045dc29eae9fdc69673c9dcd7c53e5e159d31" 1727 | }, 1728 | "expect": { 1729 | "stack": [ 1730 | "0x100" 1731 | ], 1732 | "success": true 1733 | } 1734 | }, 1735 | { 1736 | "name": "BALANCE (empty)", 1737 | "hint": "Balance of accounts not present in state is zero", 1738 | "code": { 1739 | "asm": "PUSH20 0xaf69610ea9ddc95883f97a6a3171d52165b69b03\nBALANCE", 1740 | "bin": "73af69610ea9ddc95883f97a6a3171d52165b69b0331" 1741 | }, 1742 | "expect": { 1743 | "stack": [ 1744 | "0x0" 1745 | ], 1746 | "success": true 1747 | } 1748 | }, 1749 | { 1750 | "name": "CALLVALUE", 1751 | "hint": "Read \"Calls\" section of the course learning materials. Solidity calls this msg.value, it is amount of wei sent as part of this transaction", 1752 | "tx": { 1753 | "value": "0x1000" 1754 | }, 1755 | "code": { 1756 | "asm": "CALLVALUE", 1757 | "bin": "34" 1758 | }, 1759 | "expect": { 1760 | "stack": [ 1761 | "0x1000" 1762 | ], 1763 | "success": true 1764 | } 1765 | }, 1766 | { 1767 | "name": "CALLDATALOAD", 1768 | "hint": "Read \"Calls\" section of the course learning materials. Calldata is an array of bytes sent to the evm function", 1769 | "tx": { 1770 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1771 | }, 1772 | "code": { 1773 | "asm": "PUSH1 0\nCALLDATALOAD", 1774 | "bin": "600035" 1775 | }, 1776 | "expect": { 1777 | "stack": [ 1778 | "0x102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1779 | ], 1780 | "success": true 1781 | } 1782 | }, 1783 | { 1784 | "name": "CALLDATALOAD (tail)", 1785 | "hint": "Overflow bytes filled with zeros", 1786 | "tx": { 1787 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1788 | }, 1789 | "code": { 1790 | "asm": "PUSH1 31\nCALLDATALOAD", 1791 | "bin": "601f35" 1792 | }, 1793 | "expect": { 1794 | "stack": [ 1795 | "0xff00000000000000000000000000000000000000000000000000000000000000" 1796 | ], 1797 | "success": true 1798 | } 1799 | }, 1800 | { 1801 | "name": "CALLDATASIZE", 1802 | "hint": "Size (in bytes) of calldata buffer", 1803 | "tx": { 1804 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1805 | }, 1806 | "code": { 1807 | "asm": "CALLDATASIZE", 1808 | "bin": "36" 1809 | }, 1810 | "expect": { 1811 | "stack": [ 1812 | "0x20" 1813 | ], 1814 | "success": true 1815 | } 1816 | }, 1817 | { 1818 | "name": "CALLDATASIZE (no data)", 1819 | "code": { 1820 | "asm": "CALLDATASIZE", 1821 | "bin": "36" 1822 | }, 1823 | "expect": { 1824 | "stack": [ 1825 | "0x0" 1826 | ], 1827 | "success": true 1828 | }, 1829 | "hint": "" 1830 | }, 1831 | { 1832 | "name": "CALLDATACOPY", 1833 | "hint": "Copy 32-byte chunk of calldata into memory. Do not forget to update MSIZE after CALLDATACOPY", 1834 | "tx": { 1835 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1836 | }, 1837 | "code": { 1838 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nCALLDATACOPY\nPUSH1 0\nMLOAD", 1839 | "bin": "60206000600037600051" 1840 | }, 1841 | "expect": { 1842 | "stack": [ 1843 | "0x102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1844 | ], 1845 | "success": true 1846 | } 1847 | }, 1848 | { 1849 | "name": "CALLDATACOPY (tail)", 1850 | "hint": "Overflow bytes filled with zeros", 1851 | "tx": { 1852 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1853 | }, 1854 | "code": { 1855 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nCALLDATACOPY\nPUSH1 0\nMLOAD", 1856 | "bin": "6001601f600037600051" 1857 | }, 1858 | "expect": { 1859 | "stack": [ 1860 | "0xff00000000000000000000000000000000000000000000000000000000000000" 1861 | ], 1862 | "success": true 1863 | } 1864 | }, 1865 | { 1866 | "name": "CODESIZE (small)", 1867 | "hint": "Size of the bytecode running in the current context", 1868 | "code": { 1869 | "asm": "CODESIZE", 1870 | "bin": "38" 1871 | }, 1872 | "expect": { 1873 | "stack": [ 1874 | "0x1" 1875 | ], 1876 | "success": true 1877 | } 1878 | }, 1879 | { 1880 | "name": "CODESIZE", 1881 | "code": { 1882 | "asm": "PUSH20 0\nPOP\nCODESIZE", 1883 | "bin": "7300000000000000000000000000000000000000005038" 1884 | }, 1885 | "expect": { 1886 | "stack": [ 1887 | "0x17" 1888 | ], 1889 | "success": true 1890 | }, 1891 | "hint": "" 1892 | }, 1893 | { 1894 | "name": "CODECOPY", 1895 | "hint": "Copy your own code into memory. Implementing quines in EVM is really easy", 1896 | "code": { 1897 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nCODECOPY\nPUSH1 0\nMLOAD", 1898 | "bin": "60206000600039600051" 1899 | }, 1900 | "expect": { 1901 | "stack": [ 1902 | "0x6020600060003960005100000000000000000000000000000000000000000000" 1903 | ], 1904 | "success": true 1905 | } 1906 | }, 1907 | { 1908 | "name": "CODECOPY (tail)", 1909 | "hint": "Overflow bytes filled with zeros", 1910 | "code": { 1911 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPOP\nPUSH1 2\nPUSH1 32\nPUSH1 0\nCODECOPY\nPUSH1 0\nMLOAD", 1912 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5060026020600039600051" 1913 | }, 1914 | "expect": { 1915 | "stack": [ 1916 | "0xff50000000000000000000000000000000000000000000000000000000000000" 1917 | ], 1918 | "success": true 1919 | } 1920 | }, 1921 | { 1922 | "name": "EXTCODESIZE (empty)", 1923 | "code": { 1924 | "asm": "PUSH20 0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d\nEXTCODESIZE", 1925 | "bin": "731e79b045dc29eae9fdc69673c9dcd7c53e5e159d3b" 1926 | }, 1927 | "expect": { 1928 | "stack": [ 1929 | "0x0" 1930 | ], 1931 | "success": true 1932 | }, 1933 | "hint": "" 1934 | }, 1935 | { 1936 | "name": "EXTCODESIZE", 1937 | "hint": "Read \"State\" section of the course learning materials", 1938 | "state": { 1939 | "0x1000000000000000000000000000000000000aaa": { 1940 | "code": { 1941 | "asm": "PUSH1 1", 1942 | "bin": "6001" 1943 | } 1944 | } 1945 | }, 1946 | "code": { 1947 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODESIZE", 1948 | "bin": "731000000000000000000000000000000000000aaa3b" 1949 | }, 1950 | "expect": { 1951 | "stack": [ 1952 | "0x2" 1953 | ], 1954 | "success": true 1955 | } 1956 | }, 1957 | { 1958 | "name": "EXTCODECOPY", 1959 | "state": { 1960 | "0x1000000000000000000000000000000000000aaa": { 1961 | "code": { 1962 | "asm": null, 1963 | "bin": "6001" 1964 | } 1965 | } 1966 | }, 1967 | "code": { 1968 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODECOPY\nPUSH1 0\nMLOAD", 1969 | "bin": "602060006000731000000000000000000000000000000000000aaa3c600051" 1970 | }, 1971 | "expect": { 1972 | "stack": [ 1973 | "0x6001000000000000000000000000000000000000000000000000000000000000" 1974 | ], 1975 | "success": true 1976 | }, 1977 | "hint": "" 1978 | }, 1979 | { 1980 | "name": "EXTCODEHASH", 1981 | "hint": "Use the same library you used for SHA3 opcode", 1982 | "state": { 1983 | "0x1000000000000000000000000000000000000aaa": { 1984 | "code": { 1985 | "asm": null, 1986 | "bin": "FFFFFFFF" 1987 | } 1988 | } 1989 | }, 1990 | "code": { 1991 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODEHASH", 1992 | "bin": "731000000000000000000000000000000000000aaa3f" 1993 | }, 1994 | "expect": { 1995 | "stack": [ 1996 | "0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238" 1997 | ], 1998 | "success": true 1999 | } 2000 | }, 2001 | { 2002 | "name": "EXTCODEHASH (empty)", 2003 | "code": { 2004 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODEHASH", 2005 | "bin": "731000000000000000000000000000000000000aaa3f" 2006 | }, 2007 | "expect": { 2008 | "stack": [ 2009 | "0x0" 2010 | ], 2011 | "success": true 2012 | }, 2013 | "hint": "" 2014 | }, 2015 | { 2016 | "name": "SELFBALANCE", 2017 | "tx": { 2018 | "to": "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 2019 | }, 2020 | "state": { 2021 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d": { 2022 | "balance": "0x200" 2023 | } 2024 | }, 2025 | "code": { 2026 | "asm": "SELFBALANCE", 2027 | "bin": "47" 2028 | }, 2029 | "expect": { 2030 | "stack": [ 2031 | "0x200" 2032 | ], 2033 | "success": true 2034 | }, 2035 | "hint": "" 2036 | }, 2037 | { 2038 | "name": "SSTORE", 2039 | "hint": "Read \"Storage\" section of the course learning materials", 2040 | "code": { 2041 | "asm": "PUSH1 1\nPUSH1 0\nSSTORE\nPUSH1 0\nSLOAD", 2042 | "bin": "6001600055600054" 2043 | }, 2044 | "expect": { 2045 | "stack": [ 2046 | "0x1" 2047 | ], 2048 | "success": true 2049 | } 2050 | }, 2051 | { 2052 | "name": "SSTORE (non-zero location)", 2053 | "code": { 2054 | "asm": "PUSH1 2\nPUSH4 0x98fe5c2c\nSSTORE\nPUSH4 0x98fe5c2c\nSLOAD", 2055 | "bin": "60026398fe5c2c556398fe5c2c54" 2056 | }, 2057 | "expect": { 2058 | "stack": [ 2059 | "0x2" 2060 | ], 2061 | "success": true 2062 | }, 2063 | "hint": "" 2064 | }, 2065 | { 2066 | "name": "SLOAD (empty)", 2067 | "hint": "All storage is initialized to zeros", 2068 | "code": { 2069 | "asm": "PUSH1 0xff\nSLOAD", 2070 | "bin": "60ff54" 2071 | }, 2072 | "expect": { 2073 | "stack": [ 2074 | "0x0" 2075 | ], 2076 | "success": true 2077 | } 2078 | }, 2079 | { 2080 | "name": "LOG0", 2081 | "hint": "Make evm function return array of logs, modify the testing code to assert that the logs match", 2082 | "tx": { 2083 | "to": "0x1000000000000000000000000000000000000001" 2084 | }, 2085 | "code": { 2086 | "asm": "PUSH1 0xaa\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nLOG0", 2087 | "bin": "60aa6000526001601fa0" 2088 | }, 2089 | "expect": { 2090 | "logs": [ 2091 | { 2092 | "address": "0x1000000000000000000000000000000000000001", 2093 | "data": "aa", 2094 | "topics": [] 2095 | } 2096 | ], 2097 | "success": true 2098 | } 2099 | }, 2100 | { 2101 | "name": "LOG1", 2102 | "hint": "Make evm function return array of logs, modify the testing code to assert that the logs match", 2103 | "tx": { 2104 | "to": "0x1000000000000000000000000000000000000001" 2105 | }, 2106 | "code": { 2107 | "asm": "PUSH1 0xbb\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH1 1\nPUSH1 31\nLOG1", 2108 | "bin": "60bb6000527f11111111111111111111111111111111111111111111111111111111111111116001601fa1" 2109 | }, 2110 | "expect": { 2111 | "logs": [ 2112 | { 2113 | "address": "0x1000000000000000000000000000000000000001", 2114 | "data": "bb", 2115 | "topics": [ 2116 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2117 | ] 2118 | } 2119 | ], 2120 | "success": true 2121 | } 2122 | }, 2123 | { 2124 | "name": "LOG2", 2125 | "hint": "Use the same code to handle LOG1...LOG4 opcodes", 2126 | "tx": { 2127 | "to": "0x1000000000000000000000000000000000000001" 2128 | }, 2129 | "code": { 2130 | "asm": "PUSH1 0xcc\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH1 1\nPUSH1 31\nLOG2", 2131 | "bin": "60cc6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222226001601fa2" 2132 | }, 2133 | "expect": { 2134 | "logs": [ 2135 | { 2136 | "address": "0x1000000000000000000000000000000000000001", 2137 | "data": "cc", 2138 | "topics": [ 2139 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2140 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2141 | ] 2142 | } 2143 | ], 2144 | "success": true 2145 | } 2146 | }, 2147 | { 2148 | "name": "LOG3", 2149 | "hint": "N = OPCODE - LOG0, pop N items from the stack as topics", 2150 | "tx": { 2151 | "to": "0x1000000000000000000000000000000000000001" 2152 | }, 2153 | "code": { 2154 | "asm": "PUSH1 0xdd\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH32 0x3333333333333333333333333333333333333333333333333333333333333333\nPUSH1 1\nPUSH1 31\nLOG3", 2155 | "bin": "60dd6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222227f33333333333333333333333333333333333333333333333333333333333333336001601fa3" 2156 | }, 2157 | "expect": { 2158 | "logs": [ 2159 | { 2160 | "address": "0x1000000000000000000000000000000000000001", 2161 | "data": "dd", 2162 | "topics": [ 2163 | "0x3333333333333333333333333333333333333333333333333333333333333333", 2164 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2165 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2166 | ] 2167 | } 2168 | ], 2169 | "success": true 2170 | } 2171 | }, 2172 | { 2173 | "name": "LOG4", 2174 | "hint": "Refactoring code is always a good idea. Your code will become cleaner, and the tests will catch if something breaks", 2175 | "tx": { 2176 | "to": "0x1000000000000000000000000000000000000001" 2177 | }, 2178 | "code": { 2179 | "asm": "PUSH1 0xee\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH32 0x3333333333333333333333333333333333333333333333333333333333333333\nPUSH32 0x4444444444444444444444444444444444444444444444444444444444444444\nPUSH1 1\nPUSH1 31\nLOG4", 2180 | "bin": "60ee6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222227f33333333333333333333333333333333333333333333333333333333333333337f44444444444444444444444444444444444444444444444444444444444444446001601fa4" 2181 | }, 2182 | "expect": { 2183 | "logs": [ 2184 | { 2185 | "address": "0x1000000000000000000000000000000000000001", 2186 | "data": "ee", 2187 | "topics": [ 2188 | "0x4444444444444444444444444444444444444444444444444444444444444444", 2189 | "0x3333333333333333333333333333333333333333333333333333333333333333", 2190 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2191 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2192 | ] 2193 | } 2194 | ], 2195 | "success": true 2196 | } 2197 | }, 2198 | { 2199 | "name": "RETURN", 2200 | "hint": "Read \"Calls and Returns\" section of the course learning materials", 2201 | "code": { 2202 | "asm": "PUSH1 0xA2\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2203 | "bin": "60a26000526001601ff3" 2204 | }, 2205 | "expect": { 2206 | "success": true, 2207 | "return": "a2" 2208 | } 2209 | }, 2210 | { 2211 | "name": "REVERT", 2212 | "hint": "Note that this test expects `success` to be false", 2213 | "code": { 2214 | "asm": "PUSH1 0xF1\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nREVERT", 2215 | "bin": "60f16000526001601ffd" 2216 | }, 2217 | "expect": { 2218 | "success": false, 2219 | "return": "f1" 2220 | } 2221 | }, 2222 | { 2223 | "name": "CALL", 2224 | "hint": "Read \"Calls and Returns\" section of the course learning materials. Recursively call evm function from itself when handing this opcode", 2225 | "state": { 2226 | "0x1000000000000000000000000000000000000c42": { 2227 | "code": { 2228 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2229 | "bin": "60426000526001601ff3" 2230 | } 2231 | } 2232 | }, 2233 | "code": { 2234 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2235 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1600051" 2236 | }, 2237 | "expect": { 2238 | "stack": [ 2239 | "0x42", 2240 | "0x1" 2241 | ], 2242 | "success": true 2243 | } 2244 | }, 2245 | { 2246 | "name": "CALL (returns address)", 2247 | "hint": "In the inner context, the CALLER is the contract we are sending the initial transaction to", 2248 | "tx": { 2249 | "to": "0x1000000000000000000000000000000000000aaa" 2250 | }, 2251 | "state": { 2252 | "0x1000000000000000000000000000000000000c42": { 2253 | "code": { 2254 | "asm": "CALLER\nPUSH1 0\nMSTORE\nPUSH1 32\nPUSH1 0\nRETURN", 2255 | "bin": "3360005260206000f3" 2256 | } 2257 | } 2258 | }, 2259 | "code": { 2260 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2261 | "bin": "60206000600060006000731000000000000000000000000000000000000c426000f1600051" 2262 | }, 2263 | "expect": { 2264 | "stack": [ 2265 | "0x1000000000000000000000000000000000000aaa", 2266 | "0x1" 2267 | ], 2268 | "success": true 2269 | } 2270 | }, 2271 | { 2272 | "name": "CALL (reverts)", 2273 | "hint": "Reverts can also return data", 2274 | "state": { 2275 | "0x1000000000000000000000000000000000000c42": { 2276 | "code": { 2277 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nREVERT", 2278 | "bin": "60426000526001601ffd" 2279 | } 2280 | } 2281 | }, 2282 | "code": { 2283 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2284 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1600051" 2285 | }, 2286 | "expect": { 2287 | "stack": [ 2288 | "0x42", 2289 | "0x0" 2290 | ], 2291 | "success": true 2292 | } 2293 | }, 2294 | { 2295 | "name": "RETURNDATASIZE (empty)", 2296 | "code": { 2297 | "asm": "RETURNDATASIZE", 2298 | "bin": "3d" 2299 | }, 2300 | "expect": { 2301 | "stack": [ 2302 | "0x0" 2303 | ], 2304 | "success": true 2305 | }, 2306 | "hint": "" 2307 | }, 2308 | { 2309 | "name": "RETURNDATASIZE", 2310 | "state": { 2311 | "0x1000000000000000000000000000000000000c42": { 2312 | "code": { 2313 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2314 | "bin": "60426000526001601ff3" 2315 | } 2316 | } 2317 | }, 2318 | "code": { 2319 | "asm": "PUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPOP\nRETURNDATASIZE", 2320 | "bin": "60006000600060006000731000000000000000000000000000000000000c426000f1503d" 2321 | }, 2322 | "expect": { 2323 | "stack": [ 2324 | "0x1" 2325 | ], 2326 | "success": true 2327 | }, 2328 | "hint": "" 2329 | }, 2330 | { 2331 | "name": "RETURNDATACOPY", 2332 | "state": { 2333 | "0x1000000000000000000000000000000000000c42": { 2334 | "code": { 2335 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2336 | "bin": "60426000526001601ff3" 2337 | } 2338 | } 2339 | }, 2340 | "code": { 2341 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPOP\nPUSH1 1\nPUSH1 0\nPUSH1 0xff\nRETURNDATACOPY\nPUSH1 0xff\nMLOAD", 2342 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1506001600060ff3e60ff51" 2343 | }, 2344 | "expect": { 2345 | "stack": [ 2346 | "0x4200000000000000000000000000000000000000000000000000000000000000" 2347 | ], 2348 | "success": true 2349 | }, 2350 | "hint": "" 2351 | }, 2352 | { 2353 | "name": "DELEGATECALL", 2354 | "hint": "Like CALL, but keep the transaction data (from, origin, address) and use the code from the other account", 2355 | "tx": { 2356 | "to": "0x1000000000000000000000000000000000000aaa" 2357 | }, 2358 | "state": { 2359 | "0xdddddddddddddddddddddddddddddddddddddddd": { 2360 | "code": { 2361 | "asm": "ADDRESS\nPUSH1 0\nSSTORE", 2362 | "bin": "30600055" 2363 | } 2364 | } 2365 | }, 2366 | "code": { 2367 | "asm": "PUSH1 0\nDUP1\nDUP1\nDUP1\nPUSH20 0xdddddddddddddddddddddddddddddddddddddddd\nGAS\nDELEGATECALL\nPUSH1 0\nSLOAD", 2368 | "bin": "600080808073dddddddddddddddddddddddddddddddddddddddd5af4600054" 2369 | }, 2370 | "expect": { 2371 | "stack": [ 2372 | "0x1000000000000000000000000000000000000aaa", 2373 | "0x1" 2374 | ], 2375 | "success": true 2376 | } 2377 | }, 2378 | { 2379 | "name": "DELEGATECALL (with no code)", 2380 | "hint": "Like CALL, but keep the transaction data (from, origin, address) and use the code from the other account", 2381 | "tx": { 2382 | "to": "0x1000000000000000000000000000000000000aaa" 2383 | }, 2384 | "state": { 2385 | "0xdddddddddddddddddddddddddddddddddddddddd": { 2386 | } 2387 | }, 2388 | "code": { 2389 | "asm": "PUSH1 0\nDUP1\nDUP1\nDUP1\nPUSH20 0xdddddddddddddddddddddddddddddddddddddddd\nGAS\nDELEGATECALL\nPUSH1 0\nSLOAD", 2390 | "bin": "600080808073dddddddddddddddddddddddddddddddddddddddd5af4600054" 2391 | }, 2392 | "expect": { 2393 | "stack": [ 2394 | "0x0000000000000000000000000000000000000000", 2395 | "0x1" 2396 | ], 2397 | "success": true 2398 | } 2399 | }, 2400 | { 2401 | "name": "STATICCALL", 2402 | "hint": "Like CALL, but disable state modifications", 2403 | "state": { 2404 | "0x1000000000000000000000000000000000000c42": { 2405 | "code": { 2406 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2407 | "bin": "60426000526001601ff3" 2408 | } 2409 | } 2410 | }, 2411 | "code": { 2412 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nSTATICCALL\nPUSH1 0\nMLOAD", 2413 | "bin": "6001601f60006000731000000000000000000000000000000000000c426000fa600051" 2414 | }, 2415 | "expect": { 2416 | "stack": [ 2417 | "0x42", 2418 | "0x1" 2419 | ], 2420 | "success": true 2421 | } 2422 | }, 2423 | { 2424 | "name": "STATICCALL (reverts on write)", 2425 | "hint": "Use a flag to tell the evm function whenever the context is writeable (CALL) or not (STATICCALL)", 2426 | "state": { 2427 | "0x1000000000000000000000000000000000000c42": { 2428 | "code": { 2429 | "asm": "PUSH1 0x42\nPUSH1 0\nSSTORE", 2430 | "bin": "6042600055" 2431 | } 2432 | } 2433 | }, 2434 | "code": { 2435 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nSTATICCALL", 2436 | "bin": "6001601f60006000731000000000000000000000000000000000000c426000fa" 2437 | }, 2438 | "expect": { 2439 | "stack": [ 2440 | "0x0" 2441 | ], 2442 | "success": true 2443 | } 2444 | }, 2445 | { 2446 | "name": "CREATE (empty)", 2447 | "hint": "Read \"Creating new contracts\" section of the course learning materials. This code creates a new empty account with balance 9", 2448 | "tx": { 2449 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2450 | }, 2451 | "code": { 2452 | "asm": "PUSH1 0\nPUSH1 0\nPUSH1 9\nCREATE\nBALANCE", 2453 | "bin": "600060006009f031" 2454 | }, 2455 | "expect": { 2456 | "stack": [ 2457 | "0x9" 2458 | ], 2459 | "success": true 2460 | } 2461 | }, 2462 | { 2463 | "name": "CREATE (with 4x FF)", 2464 | "hint": "Read \"Creating new contracts\" section of the course learning materials. CALL with the given code, store the returned bytes as new contracts bytecode", 2465 | "tx": { 2466 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2467 | }, 2468 | "code": { 2469 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH13 0x63FFFFFFFF6000526004601CF3\nPUSH1 0\nMSTORE\nPUSH1 13\nPUSH1 19\nPUSH1 0\nCREATE\nEXTCODECOPY\nPUSH1 0\nMLOAD", 2470 | "bin": "6020600060006c63ffffffff6000526004601cf3600052600d60136000f03c600051" 2471 | }, 2472 | "expect": { 2473 | "stack": [ 2474 | "0xffffffff00000000000000000000000000000000000000000000000000000000" 2475 | ], 2476 | "success": true 2477 | } 2478 | }, 2479 | { 2480 | "name": "CREATE (reverts)", 2481 | "hint": "No address when constructor code reverts", 2482 | "tx": { 2483 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2484 | }, 2485 | "code": { 2486 | "asm": "PUSH13 0x63FFFFFFFF6000526004601CFD\nPUSH1 0\nMSTORE\nPUSH1 13\nPUSH1 19\nPUSH1 0\nCREATE", 2487 | "bin": "6c63ffffffff6000526004601cfd600052600d60136000f0" 2488 | }, 2489 | "expect": { 2490 | "stack": [ 2491 | "0x0" 2492 | ], 2493 | "success": true 2494 | } 2495 | }, 2496 | { 2497 | "name": "SELFDESTRUCT", 2498 | "hint": "Note that for simplicity, this opcode should delete the account from the state. In the real EVM this happens only after the transaction has been processed, but that would overcomplicate these tests", 2499 | "state": { 2500 | "0xdead00000000000000000000000000000000dead": { 2501 | "balance": "0x7", 2502 | "code": { 2503 | "asm": "PUSH20 0xa1c300000000000000000000000000000000a1c3\nSELFDESTRUCT", 2504 | "bin": "73a1c300000000000000000000000000000000a1c3ff" 2505 | } 2506 | } 2507 | }, 2508 | "code": { 2509 | "asm": "PUSH1 0\nDUP1\nDUP1\nDUP1\nDUP1\nPUSH20 0xdead00000000000000000000000000000000dead\nGAS\nCALL\nPOP\nPUSH20 0xa1c300000000000000000000000000000000a1c3\nBALANCE\nPUSH20 0xdead00000000000000000000000000000000dead\nEXTCODESIZE", 2510 | "bin": "60008080808073dead00000000000000000000000000000000dead5af15073a1c300000000000000000000000000000000a1c33173dead00000000000000000000000000000000dead3b" 2511 | }, 2512 | "expect": { 2513 | "stack": [ 2514 | "0x0", 2515 | "0x7" 2516 | ], 2517 | "success": true 2518 | } 2519 | } 2520 | ] -------------------------------------------------------------------------------- /evm/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.7.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 10 | 11 | [[package]] 12 | name = "autocfg" 13 | version = "1.1.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 16 | 17 | [[package]] 18 | name = "bitvec" 19 | version = "1.0.1" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 22 | dependencies = [ 23 | "funty", 24 | "radium", 25 | "tap", 26 | "wyz", 27 | ] 28 | 29 | [[package]] 30 | name = "block-buffer" 31 | version = "0.10.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" 34 | dependencies = [ 35 | "generic-array", 36 | ] 37 | 38 | [[package]] 39 | name = "byte-slice-cast" 40 | version = "1.2.2" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" 43 | 44 | [[package]] 45 | name = "byteorder" 46 | version = "1.4.3" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 49 | 50 | [[package]] 51 | name = "bytes" 52 | version = "1.4.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "cpufeatures" 64 | version = "0.2.5" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" 67 | dependencies = [ 68 | "libc", 69 | ] 70 | 71 | [[package]] 72 | name = "crunchy" 73 | version = "0.2.2" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 76 | 77 | [[package]] 78 | name = "crypto-common" 79 | version = "0.1.6" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 82 | dependencies = [ 83 | "generic-array", 84 | "typenum", 85 | ] 86 | 87 | [[package]] 88 | name = "digest" 89 | version = "0.10.6" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" 92 | dependencies = [ 93 | "block-buffer", 94 | "crypto-common", 95 | ] 96 | 97 | [[package]] 98 | name = "evm" 99 | version = "0.1.0" 100 | dependencies = [ 101 | "hex", 102 | "primitive-types", 103 | "rlp", 104 | "serde", 105 | "serde_json", 106 | "sha3", 107 | ] 108 | 109 | [[package]] 110 | name = "fixed-hash" 111 | version = "0.8.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" 114 | dependencies = [ 115 | "byteorder", 116 | "rand", 117 | "rustc-hex", 118 | "static_assertions", 119 | ] 120 | 121 | [[package]] 122 | name = "funty" 123 | version = "2.0.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 126 | 127 | [[package]] 128 | name = "generic-array" 129 | version = "0.14.6" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" 132 | dependencies = [ 133 | "typenum", 134 | "version_check", 135 | ] 136 | 137 | [[package]] 138 | name = "getrandom" 139 | version = "0.2.8" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 142 | dependencies = [ 143 | "cfg-if", 144 | "libc", 145 | "wasi", 146 | ] 147 | 148 | [[package]] 149 | name = "hashbrown" 150 | version = "0.12.3" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 153 | 154 | [[package]] 155 | name = "hex" 156 | version = "0.4.3" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 159 | 160 | [[package]] 161 | name = "impl-codec" 162 | version = "0.6.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" 165 | dependencies = [ 166 | "parity-scale-codec", 167 | ] 168 | 169 | [[package]] 170 | name = "impl-rlp" 171 | version = "0.3.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" 174 | dependencies = [ 175 | "rlp", 176 | ] 177 | 178 | [[package]] 179 | name = "impl-trait-for-tuples" 180 | version = "0.2.2" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" 183 | dependencies = [ 184 | "proc-macro2", 185 | "quote", 186 | "syn", 187 | ] 188 | 189 | [[package]] 190 | name = "indexmap" 191 | version = "1.9.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" 194 | dependencies = [ 195 | "autocfg", 196 | "hashbrown", 197 | ] 198 | 199 | [[package]] 200 | name = "itoa" 201 | version = "1.0.4" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 204 | 205 | [[package]] 206 | name = "keccak" 207 | version = "0.1.3" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" 210 | dependencies = [ 211 | "cpufeatures", 212 | ] 213 | 214 | [[package]] 215 | name = "libc" 216 | version = "0.2.135" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" 219 | 220 | [[package]] 221 | name = "memchr" 222 | version = "2.5.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 225 | 226 | [[package]] 227 | name = "once_cell" 228 | version = "1.17.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 231 | 232 | [[package]] 233 | name = "parity-scale-codec" 234 | version = "3.4.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "637935964ff85a605d114591d4d2c13c5d1ba2806dae97cea6bf180238a749ac" 237 | dependencies = [ 238 | "arrayvec", 239 | "bitvec", 240 | "byte-slice-cast", 241 | "impl-trait-for-tuples", 242 | "parity-scale-codec-derive", 243 | "serde", 244 | ] 245 | 246 | [[package]] 247 | name = "parity-scale-codec-derive" 248 | version = "3.1.4" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" 251 | dependencies = [ 252 | "proc-macro-crate", 253 | "proc-macro2", 254 | "quote", 255 | "syn", 256 | ] 257 | 258 | [[package]] 259 | name = "ppv-lite86" 260 | version = "0.2.17" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 263 | 264 | [[package]] 265 | name = "primitive-types" 266 | version = "0.12.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" 269 | dependencies = [ 270 | "fixed-hash", 271 | "impl-codec", 272 | "impl-rlp", 273 | "uint", 274 | ] 275 | 276 | [[package]] 277 | name = "proc-macro-crate" 278 | version = "1.3.1" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" 281 | dependencies = [ 282 | "once_cell", 283 | "toml_edit", 284 | ] 285 | 286 | [[package]] 287 | name = "proc-macro2" 288 | version = "1.0.47" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 291 | dependencies = [ 292 | "unicode-ident", 293 | ] 294 | 295 | [[package]] 296 | name = "quote" 297 | version = "1.0.21" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 300 | dependencies = [ 301 | "proc-macro2", 302 | ] 303 | 304 | [[package]] 305 | name = "radium" 306 | version = "0.7.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 309 | 310 | [[package]] 311 | name = "rand" 312 | version = "0.8.5" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 315 | dependencies = [ 316 | "libc", 317 | "rand_chacha", 318 | "rand_core", 319 | ] 320 | 321 | [[package]] 322 | name = "rand_chacha" 323 | version = "0.3.1" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 326 | dependencies = [ 327 | "ppv-lite86", 328 | "rand_core", 329 | ] 330 | 331 | [[package]] 332 | name = "rand_core" 333 | version = "0.6.4" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 336 | dependencies = [ 337 | "getrandom", 338 | ] 339 | 340 | [[package]] 341 | name = "rlp" 342 | version = "0.5.2" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" 345 | dependencies = [ 346 | "bytes", 347 | "rustc-hex", 348 | ] 349 | 350 | [[package]] 351 | name = "rustc-hex" 352 | version = "2.1.0" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" 355 | 356 | [[package]] 357 | name = "ryu" 358 | version = "1.0.11" 359 | source = "registry+https://github.com/rust-lang/crates.io-index" 360 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 361 | 362 | [[package]] 363 | name = "serde" 364 | version = "1.0.145" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" 367 | dependencies = [ 368 | "serde_derive", 369 | ] 370 | 371 | [[package]] 372 | name = "serde_derive" 373 | version = "1.0.145" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" 376 | dependencies = [ 377 | "proc-macro2", 378 | "quote", 379 | "syn", 380 | ] 381 | 382 | [[package]] 383 | name = "serde_json" 384 | version = "1.0.86" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074" 387 | dependencies = [ 388 | "itoa", 389 | "ryu", 390 | "serde", 391 | ] 392 | 393 | [[package]] 394 | name = "sha3" 395 | version = "0.10.6" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" 398 | dependencies = [ 399 | "digest", 400 | "keccak", 401 | ] 402 | 403 | [[package]] 404 | name = "static_assertions" 405 | version = "1.1.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 408 | 409 | [[package]] 410 | name = "syn" 411 | version = "1.0.102" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1" 414 | dependencies = [ 415 | "proc-macro2", 416 | "quote", 417 | "unicode-ident", 418 | ] 419 | 420 | [[package]] 421 | name = "tap" 422 | version = "1.0.1" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 425 | 426 | [[package]] 427 | name = "toml_datetime" 428 | version = "0.6.1" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" 431 | 432 | [[package]] 433 | name = "toml_edit" 434 | version = "0.19.4" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" 437 | dependencies = [ 438 | "indexmap", 439 | "toml_datetime", 440 | "winnow", 441 | ] 442 | 443 | [[package]] 444 | name = "typenum" 445 | version = "1.16.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" 448 | 449 | [[package]] 450 | name = "uint" 451 | version = "0.9.5" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" 454 | dependencies = [ 455 | "byteorder", 456 | "crunchy", 457 | "hex", 458 | "static_assertions", 459 | ] 460 | 461 | [[package]] 462 | name = "unicode-ident" 463 | version = "1.0.5" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 466 | 467 | [[package]] 468 | name = "version_check" 469 | version = "0.9.4" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 472 | 473 | [[package]] 474 | name = "wasi" 475 | version = "0.11.0+wasi-snapshot-preview1" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 478 | 479 | [[package]] 480 | name = "winnow" 481 | version = "0.3.5" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" 484 | dependencies = [ 485 | "memchr", 486 | ] 487 | 488 | [[package]] 489 | name = "wyz" 490 | version = "0.5.1" 491 | source = "registry+https://github.com/rust-lang/crates.io-index" 492 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 493 | dependencies = [ 494 | "tap", 495 | ] 496 | -------------------------------------------------------------------------------- /evm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "evm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | hex = "0.4.3" 10 | primitive-types = { version = "0.12", default-features = false, features = ["rlp", "std"] } 11 | rlp = "0.5.2" 12 | serde = { version = "1.0.145", features = ["derive"] } 13 | serde_json = "1.0.86" 14 | sha3 = "0.10.6" 15 | -------------------------------------------------------------------------------- /evm/src/block.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone)] 2 | pub struct Block<'a> { 3 | pub coinbase: &'a [u8], 4 | pub timestamp: &'a [u8], 5 | pub number: &'a [u8], 6 | pub difficulty: &'a [u8], 7 | pub gaslimit: &'a [u8], 8 | pub chainid: &'a [u8], 9 | pub basefee: &'a [u8], 10 | } 11 | 12 | impl<'a> Block<'a> { 13 | pub fn new( 14 | coinbase: &'a [u8], 15 | timestamp: &'a [u8], 16 | number: &'a [u8], 17 | difficulty: &'a [u8], 18 | gaslimit: &'a [u8], 19 | chainid: &'a [u8], 20 | basefee: &'a [u8], 21 | ) -> Self { 22 | Self { 23 | coinbase, 24 | timestamp, 25 | number, 26 | difficulty, 27 | gaslimit, 28 | chainid, 29 | basefee, 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /evm/src/consts.rs: -------------------------------------------------------------------------------- 1 | pub const WORD_BYTES: usize = 32; -------------------------------------------------------------------------------- /evm/src/context.rs: -------------------------------------------------------------------------------- 1 | use primitive_types::{U256, H160}; 2 | use crate::state::State; 3 | 4 | pub struct Context<'a> { 5 | pub address: H160, 6 | pub caller: H160, 7 | pub origin: H160, 8 | pub gasprice: U256, 9 | // TODO: update to U256, handle overlap with State.Account.balance -> maybe not 10 | pub value: U256, 11 | pub call_data: &'a String, 12 | pub state: State, 13 | pub is_static: bool, 14 | } 15 | 16 | // TODO: remove lifetime parameter where possible 17 | impl<'a> Context<'a> { 18 | pub fn new( 19 | address: H160, 20 | caller: H160, 21 | origin: H160, 22 | gasprice: U256, 23 | value: U256, 24 | // TODO: update data to call_data and store as hex not string 25 | call_data: &'a String, 26 | state: State, 27 | is_static: bool, 28 | ) -> Self { 29 | Self { 30 | address, 31 | caller, 32 | origin, 33 | gasprice, 34 | value, 35 | call_data, 36 | state, 37 | is_static, 38 | } 39 | } 40 | 41 | pub fn calldata_size(&self) -> U256 { 42 | let call_data_size = hex::decode(&self.call_data).unwrap().len(); 43 | call_data_size.into() 44 | } 45 | 46 | pub fn load_calldata(&self, byte_offset: usize, target_size: usize) -> U256 { 47 | let call_data = hex::decode(&self.call_data).unwrap(); 48 | let mut res: Vec = vec![0; target_size]; 49 | 50 | for i in 0..target_size { 51 | let data_index = i + byte_offset; 52 | if data_index < call_data.len() { 53 | let val = call_data[data_index]; 54 | res[i] = val; 55 | } 56 | } 57 | 58 | U256::from_big_endian(&res) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /evm/src/eval.rs: -------------------------------------------------------------------------------- 1 | use crate::consts::WORD_BYTES; 2 | use crate::context::Context; 3 | use crate::machine::{ControlFlow, EvmError, ExitSuccess, Log, Machine}; 4 | use crate::opcode::Opcode; 5 | use crate::{evm, helpers::*}; 6 | use primitive_types::{U256}; 7 | use sha3::{Digest, Keccak256}; 8 | 9 | pub fn eval(machine: &mut Machine) -> ControlFlow { 10 | let opcode = machine.opcode(); 11 | if machine.context.is_static && !Opcode::is_static(opcode) { 12 | return exit_error(EvmError::OpcodeNotStatic(opcode)); 13 | } 14 | match opcode { 15 | Opcode::STOP => stop(machine), 16 | Opcode::ADD => add(machine), 17 | Opcode::MUL => mul(machine), 18 | Opcode::SUB => sub(machine), 19 | Opcode::DIV => div(machine), 20 | Opcode::SDIV => sdiv(machine), 21 | Opcode::MOD => modulus(machine), 22 | Opcode::SMOD => smodulus(machine), 23 | Opcode::ADDMOD => add_modulus(machine), 24 | Opcode::MULMOD => mul_modulus(machine), 25 | Opcode::EXP => exp(machine), 26 | Opcode::SIGNEXTEND => sign_extend(machine), 27 | Opcode::LT => lt(machine), 28 | Opcode::GT => gt(machine), 29 | Opcode::SLT => slt(machine), 30 | Opcode::SGT => sgt(machine), 31 | Opcode::EQ => eq(machine), 32 | Opcode::ISZERO => iszero(machine), 33 | Opcode::AND => and(machine), 34 | Opcode::OR => or(machine), 35 | Opcode::XOR => xor(machine), 36 | Opcode::NOT => not(machine), 37 | Opcode::BYTE => byte(machine), 38 | Opcode::SHL => shl(machine), 39 | Opcode::SHR => shr(machine), 40 | Opcode::SAR => sar(machine), 41 | Opcode::KECCAK256 => keccak256(machine), 42 | Opcode::ADDRESS => address(machine), 43 | Opcode::BALANCE => balance(machine), 44 | Opcode::ORIGIN => origin(machine), 45 | Opcode::CALLER => caller(machine), 46 | Opcode::CALLVALUE => callvalue(machine), 47 | Opcode::CALLDATALOAD => calldataload(machine), 48 | Opcode::CALLDATASIZE => calldatasize(machine), 49 | Opcode::CALLDATACOPY => calldatacopy(machine), 50 | Opcode::CODESIZE => codesize(machine), 51 | Opcode::CODECOPY => codecopy(machine), 52 | Opcode::BLOCKHASH => blockhash(machine), 53 | Opcode::GASPRICE => gasprice(machine), 54 | Opcode::EXTCODESIZE => extcodesize(machine), 55 | Opcode::EXTCODECOPY => extcodecopy(machine), 56 | Opcode::EXTCODEHASH => extcodehash(machine), 57 | Opcode::RETURNDATASIZE => returndatasize(machine), 58 | Opcode::RETURNDATACOPY => returndatacopy(machine), 59 | Opcode::COINBASE => coinbase(machine), 60 | Opcode::TIMESTAMP => timestamp(machine), 61 | Opcode::NUMBER => number(machine), 62 | Opcode::DIFFICULTY => difficulty(machine), 63 | Opcode::GASLIMIT => gaslimit(machine), 64 | Opcode::CHAINID => chainid(machine), 65 | Opcode::SELFBALANCE => selfbalance(machine), 66 | Opcode::BASEFEE => basefee(machine), 67 | Opcode::POP => eval_pop(machine), 68 | Opcode::MLOAD => mload(machine), 69 | Opcode::MSTORE => mstore(machine), 70 | Opcode::SLOAD => sload(machine), 71 | Opcode::SSTORE => sstore(machine), 72 | Opcode::MSTORE8 => mstore8(machine), 73 | Opcode::JUMP => jump(machine), 74 | Opcode::JUMPI => jumpi(machine), 75 | Opcode::PC => pc(machine), 76 | Opcode::MSIZE => msize(machine), 77 | Opcode::GAS => gas(machine), 78 | Opcode::JUMPDEST => jumpdest(machine), 79 | Opcode::PUSH1..=Opcode::PUSH32 => eval_push(machine), 80 | Opcode::DUP1..=Opcode::DUP16 => dup(machine), 81 | Opcode::SWAP1..=Opcode::SWAP16 => swap(machine), 82 | Opcode::LOG0..=Opcode::LOG4 => log(machine), 83 | Opcode::CREATE => create(machine), 84 | Opcode::CALL => call(machine), 85 | Opcode::RETURN => eval_return(machine), 86 | Opcode::DELEGATECALL => delegatecall(machine), 87 | Opcode::STATICCALL => staticcall(machine), 88 | Opcode::REVERT => revert(machine), 89 | Opcode::INVALID => invalid(machine), 90 | Opcode::SELFDESTRUCT => selfdestruct(machine), 91 | _ => exit_error(EvmError::InvalidInstruction), 92 | } 93 | } 94 | 95 | // TODO: remove unwraps and handle failed stack pops 96 | // TODO: remove unnecessary mut references for machine 97 | // TODO: add and handle as_usize or fail 98 | // TODO: add 1024 stack limit 99 | 100 | fn stop(_machine: &mut Machine) -> ControlFlow { 101 | exit_success(ExitSuccess::Stop) 102 | } 103 | 104 | fn add(machine: &mut Machine) -> ControlFlow { 105 | let a = machine.stack.pop().unwrap(); 106 | let b = machine.stack.pop().unwrap(); 107 | let res = a.overflowing_add(b).0; 108 | machine.stack.push(res); 109 | 110 | ControlFlow::Continue(1) 111 | } 112 | 113 | fn mul(machine: &mut Machine) -> ControlFlow { 114 | let a = machine.stack.pop().unwrap(); 115 | let b = machine.stack.pop().unwrap(); 116 | let res = a.overflowing_mul(b).0; 117 | machine.stack.push(res); 118 | 119 | ControlFlow::Continue(1) 120 | } 121 | 122 | fn sub(machine: &mut Machine) -> ControlFlow { 123 | let a = machine.stack.pop().unwrap(); 124 | let b = machine.stack.pop().unwrap(); 125 | let res = a.overflowing_sub(b).0; 126 | machine.stack.push(res); 127 | 128 | ControlFlow::Continue(1) 129 | } 130 | 131 | fn div(machine: &mut Machine) -> ControlFlow { 132 | let a = machine.stack.pop().unwrap(); 133 | let b = machine.stack.pop().unwrap(); 134 | let res = a.checked_div(b); 135 | match res { 136 | Some(result) => machine.stack.push(result), 137 | None => machine.stack.push(0.into()), 138 | } 139 | 140 | ControlFlow::Continue(1) 141 | } 142 | 143 | fn sdiv(machine: &mut Machine) -> ControlFlow { 144 | let mut a = machine.stack.pop().unwrap(); 145 | let mut b = machine.stack.pop().unwrap(); 146 | 147 | // If the first bit is 1, then the value is negative, according to the rules of two's compliment 148 | let a_is_negative = is_negative(a); 149 | let b_is_negative = is_negative(b); 150 | 151 | // If the value is negative, we need to switch it into a positive value 152 | if a_is_negative { 153 | a = convert_twos_compliment(a); 154 | } 155 | // We do this for either of the numbers if they are negative, to find their absolute value 156 | if b_is_negative { 157 | b = convert_twos_compliment(b); 158 | } 159 | 160 | // now res = |a| / |b| 161 | let res = a.checked_div(b); 162 | 163 | match res { 164 | Some(mut result) => match result { 165 | // if the result is 0, push 0 straight onto stack 166 | i if i == 0.into() => machine.stack.push(i), 167 | _ => { 168 | // If only one of the numbers is negative, the result will be negative 169 | if a_is_negative ^ b_is_negative { 170 | // We need to perform two's compliment again to provide a negative result 171 | result = convert_twos_compliment(result); 172 | } 173 | machine.stack.push(result); 174 | } 175 | }, 176 | None => machine.stack.push(U256::zero()), 177 | } 178 | 179 | ControlFlow::Continue(1) 180 | } 181 | 182 | fn modulus(machine: &mut Machine) -> ControlFlow { 183 | let a = machine.stack.pop().unwrap(); 184 | let b = machine.stack.pop().unwrap(); 185 | let res = a.checked_rem(b); 186 | match res { 187 | Some(result) => machine.stack.push(result), 188 | None => machine.stack.push(0.into()), 189 | } 190 | 191 | ControlFlow::Continue(1) 192 | } 193 | 194 | fn smodulus(machine: &mut Machine) -> ControlFlow { 195 | let mut a = machine.stack.pop().unwrap(); 196 | let mut b = machine.stack.pop().unwrap(); 197 | 198 | let a_is_negative = is_negative(a); 199 | let b_is_negative = is_negative(b); 200 | 201 | if a_is_negative { 202 | a = convert_twos_compliment(a); 203 | } 204 | if b_is_negative { 205 | b = convert_twos_compliment(b); 206 | } 207 | 208 | let res = a.checked_rem(b); 209 | 210 | match res { 211 | Some(mut result) => match result { 212 | i if i == 0.into() => machine.stack.push(i), 213 | _ => { 214 | if a_is_negative { 215 | result = convert_twos_compliment(result); 216 | } 217 | machine.stack.push(result); 218 | } 219 | }, 220 | None => machine.stack.push(0.into()), 221 | } 222 | 223 | ControlFlow::Continue(1) 224 | } 225 | 226 | fn add_modulus(machine: &mut Machine) -> ControlFlow { 227 | let a = machine.stack.pop().unwrap(); 228 | let b = machine.stack.pop().unwrap(); 229 | let c = machine.stack.pop().unwrap(); 230 | let res = a.overflowing_add(b).0.checked_rem(c); 231 | match res { 232 | Some(result) => machine.stack.push(result), 233 | None => machine.stack.push(0.into()), 234 | } 235 | 236 | ControlFlow::Continue(1) 237 | } 238 | 239 | fn mul_modulus(machine: &mut Machine) -> ControlFlow { 240 | let a = machine.stack.pop().unwrap(); 241 | let b = machine.stack.pop().unwrap(); 242 | let c = machine.stack.pop().unwrap(); 243 | let res_mul = a.full_mul(b); 244 | let res_modulo = res_mul.checked_rem(c.into()); 245 | match res_modulo { 246 | Some(result) => machine 247 | .stack 248 | .push(result.try_into().expect( 249 | "c <= U256::MAX, result = res_mul % c, ∴ result < U256::MAX, ∴ overflow impossible; qed" 250 | )), 251 | None => machine.stack.push(0.into()), 252 | } 253 | 254 | ControlFlow::Continue(1) 255 | } 256 | 257 | fn exp(machine: &mut Machine) -> ControlFlow { 258 | let a = machine.stack.pop().unwrap(); 259 | let b = machine.stack.pop().unwrap(); 260 | let res = a.overflowing_pow(b).0; 261 | machine.stack.push(res); 262 | 263 | ControlFlow::Continue(1) 264 | } 265 | 266 | // extend a signed integer to 32 bytes 267 | // a = num_bytes = the number of bytes of the integer to extend - 1 268 | // b = int_to_extend = the integer to extend 269 | // e.g. 270 | // a = 0, b = 00000001, int_to_extend with bytes => 00000001 271 | // a = 1, b = 00000001, int_to_extend with bytes => 0000000000000001 272 | // a = 1, b = 11111111, int_to_extend with bytes => 0000000011111111 273 | // Full example: 274 | // a = 0, b = 11111110, int_to_extend with bytes => 11111110 275 | // bit_index = (8 * 0) + 7 = 7 276 | // bit = 1 277 | // mask = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111111 278 | // !mask = 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000 279 | // res = int_to_extend | !mask 280 | // = 11111110 | 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000000 281 | // = 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110 282 | // = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE 283 | fn sign_extend(machine: &mut Machine) -> ControlFlow { 284 | let num_bytes = machine.stack.pop().unwrap(); 285 | let int_to_extend = machine.stack.pop().unwrap(); 286 | 287 | if num_bytes >= U256::from(32) { 288 | // int is already fully extended, EVM is max 256 bits, 32 bytes = 256 bits 289 | // ∴ push int_to_extend straight to stack 290 | machine.stack.push(int_to_extend); 291 | } else { 292 | // t is the index from left to right of the first bit of the int_to_extend in a 32-byte word 293 | // x = num_bytes 294 | // t = 256 - 8(x + 1) 295 | // rearrange t to find the index from left to right 296 | // s = 255 - t = 8(x + 1) 297 | // where s is the index from left to right of the first bit of the int_to_extend in a 32-byte word 298 | // `low_u32` works since num_bytes < 32 299 | let bit_index = (8 * num_bytes.low_u32() + 7) as usize; 300 | // find whether the bit at bit_index is 1 or 0 301 | let bit = int_to_extend.bit(bit_index); 302 | // create a mask of 0s up to bit_index and then 1s from then on 303 | let mask = (U256::one() << bit_index) - U256::one(); 304 | if bit { 305 | // append 1s to int_to_extend 306 | machine.stack.push(int_to_extend | !mask); 307 | } else { 308 | // append 0s to int_to_extend 309 | machine.stack.push(int_to_extend & mask); 310 | } 311 | } 312 | ControlFlow::Continue(1) 313 | } 314 | 315 | fn lt(machine: &mut Machine) -> ControlFlow { 316 | let a = machine.stack.pop().unwrap(); 317 | let b = machine.stack.pop().unwrap(); 318 | let res = (a < b) as u32; 319 | machine.stack.push(U256::from(res)); 320 | 321 | ControlFlow::Continue(1) 322 | } 323 | 324 | fn gt(machine: &mut Machine) -> ControlFlow { 325 | let a = machine.stack.pop().unwrap(); 326 | let b = machine.stack.pop().unwrap(); 327 | let res = (a > b) as u32; 328 | machine.stack.push(U256::from(res)); 329 | 330 | ControlFlow::Continue(1) 331 | } 332 | 333 | fn slt(machine: &mut Machine) -> ControlFlow { 334 | let mut a = machine.stack.pop().unwrap(); 335 | let mut b = machine.stack.pop().unwrap(); 336 | 337 | if a == b { 338 | machine.stack.push(U256::zero()); 339 | return ControlFlow::Continue(1); 340 | } 341 | 342 | let a_is_negative = is_negative(a); 343 | let b_is_negative = is_negative(b); 344 | 345 | if a_is_negative && !b_is_negative { 346 | machine.stack.push(U256::one()); 347 | return ControlFlow::Continue(1); 348 | } else if !a_is_negative && b_is_negative { 349 | machine.stack.push(U256::zero()); 350 | return ControlFlow::Continue(1); 351 | } 352 | 353 | if a_is_negative { 354 | a = convert_twos_compliment(a); 355 | } 356 | if b_is_negative { 357 | b = convert_twos_compliment(b); 358 | } 359 | 360 | let mut res = a < b; 361 | 362 | if a_is_negative && b_is_negative { 363 | res = !res; 364 | } 365 | 366 | machine.stack.push(U256::from(res as u32)); 367 | 368 | ControlFlow::Continue(1) 369 | } 370 | 371 | fn sgt(machine: &mut Machine) -> ControlFlow { 372 | let mut a = machine.stack.pop().unwrap(); 373 | let mut b = machine.stack.pop().unwrap(); 374 | 375 | if a == b { 376 | machine.stack.push(U256::zero()); 377 | return ControlFlow::Continue(1); 378 | } 379 | 380 | let a_is_negative = is_negative(a); 381 | let b_is_negative = is_negative(b); 382 | 383 | if a_is_negative && !b_is_negative { 384 | machine.stack.push(U256::zero()); 385 | return ControlFlow::Continue(1); 386 | } else if !a_is_negative && b_is_negative { 387 | machine.stack.push(U256::one()); 388 | return ControlFlow::Continue(1); 389 | } 390 | 391 | if a_is_negative { 392 | a = convert_twos_compliment(a); 393 | } 394 | if b_is_negative { 395 | b = convert_twos_compliment(b); 396 | } 397 | 398 | let mut res = a > b; 399 | 400 | if a_is_negative && b_is_negative { 401 | res = !res; 402 | } 403 | 404 | machine.stack.push(U256::from(res as u32)); 405 | 406 | ControlFlow::Continue(1) 407 | } 408 | 409 | fn eq(machine: &mut Machine) -> ControlFlow { 410 | let a = machine.stack.pop().unwrap(); 411 | let b = machine.stack.pop().unwrap(); 412 | 413 | if a == b { 414 | machine.stack.push(U256::one()); 415 | } else { 416 | machine.stack.push(U256::zero()); 417 | } 418 | 419 | ControlFlow::Continue(1) 420 | } 421 | 422 | fn iszero(machine: &mut Machine) -> ControlFlow { 423 | let a = machine.stack.pop().unwrap(); 424 | 425 | if a == U256::zero() { 426 | machine.stack.push(U256::one()); 427 | } else { 428 | machine.stack.push(U256::zero()); 429 | } 430 | 431 | ControlFlow::Continue(1) 432 | } 433 | 434 | fn not(machine: &mut Machine) -> ControlFlow { 435 | let a = machine.stack.pop().unwrap(); 436 | 437 | machine.stack.push(!a); 438 | 439 | ControlFlow::Continue(1) 440 | } 441 | 442 | fn byte(machine: &mut Machine) -> ControlFlow { 443 | let byte_offset = machine.stack.pop().unwrap(); 444 | let value = machine.stack.pop().unwrap(); 445 | 446 | if byte_offset >= 32.into() { 447 | machine.stack.push(U256::zero()); 448 | return ControlFlow::Continue(1); 449 | } 450 | 451 | let byte_index = U256::from(31) - byte_offset; 452 | 453 | let res = value.byte(byte_index.as_usize()); 454 | 455 | machine.stack.push(res.into()); 456 | 457 | ControlFlow::Continue(1) 458 | } 459 | 460 | fn and(machine: &mut Machine) -> ControlFlow { 461 | let a = machine.stack.pop().unwrap(); 462 | let b = machine.stack.pop().unwrap(); 463 | 464 | machine.stack.push(a & b); 465 | 466 | ControlFlow::Continue(1) 467 | } 468 | 469 | fn or(machine: &mut Machine) -> ControlFlow { 470 | let a = machine.stack.pop().unwrap(); 471 | let b = machine.stack.pop().unwrap(); 472 | 473 | machine.stack.push(a | b); 474 | 475 | ControlFlow::Continue(1) 476 | } 477 | 478 | fn xor(machine: &mut Machine) -> ControlFlow { 479 | let a = machine.stack.pop().unwrap(); 480 | let b = machine.stack.pop().unwrap(); 481 | 482 | machine.stack.push(a ^ b); 483 | 484 | ControlFlow::Continue(1) 485 | } 486 | 487 | fn shl(machine: &mut Machine) -> ControlFlow { 488 | let shift = machine.stack.pop().unwrap(); 489 | let value = machine.stack.pop().unwrap(); 490 | 491 | let shifted = value << shift; 492 | machine.stack.push(shifted); 493 | 494 | ControlFlow::Continue(1) 495 | } 496 | 497 | fn shr(machine: &mut Machine) -> ControlFlow { 498 | let shift = machine.stack.pop().unwrap(); 499 | let value = machine.stack.pop().unwrap(); 500 | 501 | let shifted = value >> shift; 502 | machine.stack.push(shifted); 503 | 504 | ControlFlow::Continue(1) 505 | } 506 | 507 | fn sar(machine: &mut Machine) -> ControlFlow { 508 | // shift value is unsigned 509 | let shift = machine.stack.pop().unwrap(); 510 | // value is signed 511 | let mut value = machine.stack.pop().unwrap(); 512 | 513 | let value_is_negative = is_negative(value); 514 | 515 | if value_is_negative { 516 | value = convert_twos_compliment(value); 517 | } 518 | 519 | let mut shifted = value >> shift; 520 | 521 | if value_is_negative { 522 | shifted = convert_twos_compliment(shifted); 523 | } 524 | 525 | machine.stack.push(shifted); 526 | 527 | ControlFlow::Continue(1) 528 | } 529 | 530 | fn keccak256(machine: &mut Machine) -> ControlFlow { 531 | let offset = machine.stack.pop().unwrap(); 532 | let size = machine.stack.pop().unwrap(); 533 | 534 | let data_to_hash = machine.memory.get(offset.as_usize(), size.as_usize()); 535 | let hashed_data = Keccak256::digest(data_to_hash); 536 | 537 | machine.stack.push(U256::from_big_endian(&hashed_data)); 538 | 539 | ControlFlow::Continue(1) 540 | } 541 | 542 | fn address(machine: &mut Machine) -> ControlFlow { 543 | machine 544 | .stack 545 | .push(machine.context.address.to_u256()); 546 | 547 | ControlFlow::Continue(1) 548 | } 549 | 550 | fn balance(machine: &mut Machine) -> ControlFlow { 551 | let address = machine.stack.pop().unwrap().to_h160(); 552 | let balance = machine.context.state.get_account_balance(address); 553 | 554 | machine.stack.push(balance); 555 | 556 | ControlFlow::Continue(1) 557 | } 558 | 559 | fn origin(machine: &mut Machine) -> ControlFlow { 560 | machine 561 | .stack 562 | .push(machine.context.origin.to_u256()); 563 | 564 | ControlFlow::Continue(1) 565 | } 566 | 567 | fn caller(machine: &mut Machine) -> ControlFlow { 568 | machine 569 | .stack 570 | .push(machine.context.caller.to_u256()); 571 | 572 | ControlFlow::Continue(1) 573 | } 574 | 575 | fn callvalue(machine: &mut Machine) -> ControlFlow { 576 | machine.stack.push(machine.context.value); 577 | 578 | ControlFlow::Continue(1) 579 | } 580 | 581 | fn calldataload(machine: &mut Machine) -> ControlFlow { 582 | let byte_offset = machine.stack.pop().unwrap(); 583 | 584 | machine.stack.push( 585 | machine 586 | .context 587 | .load_calldata(byte_offset.as_usize(), WORD_BYTES), 588 | ); 589 | 590 | ControlFlow::Continue(1) 591 | } 592 | 593 | fn calldatasize(machine: &mut Machine) -> ControlFlow { 594 | machine.stack.push(machine.context.calldata_size()); 595 | 596 | ControlFlow::Continue(1) 597 | } 598 | 599 | // TODO: move all possible .as_usize()'s to the initial values 600 | fn calldatacopy(machine: &mut Machine) -> ControlFlow { 601 | let dest_offset = machine.stack.pop().unwrap(); 602 | let offset = machine.stack.pop().unwrap(); 603 | let size = machine.stack.pop().unwrap(); 604 | 605 | let calldata = machine 606 | .context 607 | .load_calldata(offset.as_usize(), size.as_usize()); 608 | 609 | machine 610 | .memory 611 | .set(dest_offset.as_usize(), calldata, size.as_usize()); 612 | 613 | ControlFlow::Continue(1) 614 | } 615 | 616 | fn codesize(machine: &mut Machine) -> ControlFlow { 617 | machine.stack.push(machine.code.len().into()); 618 | 619 | ControlFlow::Continue(1) 620 | } 621 | 622 | fn codecopy(machine: &mut Machine) -> ControlFlow { 623 | let dest_offset = machine.stack.pop().unwrap().as_usize(); 624 | let offset = machine.stack.pop().unwrap().as_usize(); 625 | let size = machine.stack.pop().unwrap().as_usize(); 626 | 627 | let code = arr_slice_extend(machine.code, offset, size); 628 | 629 | machine.memory.set(dest_offset, code, size); 630 | 631 | ControlFlow::Continue(1) 632 | } 633 | 634 | fn blockhash(_machine: &mut Machine) -> ControlFlow { 635 | // TODO: implement blockhash properly 636 | ControlFlow::Continue(1) 637 | } 638 | 639 | fn gasprice(machine: &mut Machine) -> ControlFlow { 640 | // TODO: implement gas price properly 641 | machine 642 | .stack 643 | .push(machine.context.gasprice); 644 | 645 | ControlFlow::Continue(1) 646 | } 647 | 648 | fn extcodesize(machine: &mut Machine) -> ControlFlow { 649 | let address = machine.stack.pop().unwrap().to_h160(); 650 | 651 | let code = machine.context.state.get_account_code(address); 652 | 653 | machine.stack.push(code.len().into()); 654 | 655 | ControlFlow::Continue(1) 656 | } 657 | 658 | fn extcodecopy(machine: &mut Machine) -> ControlFlow { 659 | let address = machine.stack.pop().unwrap().to_h160(); 660 | let dest_offset = machine.stack.pop().unwrap().as_usize(); 661 | let offset = machine.stack.pop().unwrap().as_usize(); 662 | let size = machine.stack.pop().unwrap().as_usize(); 663 | 664 | let account_code = machine.context.state.get_account_code(address); 665 | let code = arr_slice_extend(&account_code[..], offset, size); 666 | 667 | // TODO: set vec instead of U256 => code could be longer. update in other place as well 668 | machine.memory.set(dest_offset, code, size); 669 | ControlFlow::Continue(1) 670 | } 671 | 672 | fn extcodehash(machine: &mut Machine) -> ControlFlow { 673 | let address = machine.stack.pop().unwrap().to_h160(); 674 | 675 | let account_code = machine.context.state.get_account_code(address); 676 | if account_code.len() == 0 { 677 | machine.stack.push(0.into()); 678 | } else { 679 | let hashed_code = Keccak256::digest(account_code); 680 | machine.stack.push(U256::from_big_endian(&hashed_code)); 681 | } 682 | 683 | ControlFlow::Continue(1) 684 | } 685 | 686 | fn returndatasize(machine: &mut Machine) -> ControlFlow { 687 | machine.stack.push(machine.return_data_buffer.len().into()); 688 | 689 | ControlFlow::Continue(1) 690 | } 691 | 692 | fn returndatacopy(machine: &mut Machine) -> ControlFlow { 693 | let dest_offset = machine.stack.pop().unwrap().as_usize(); 694 | let offset = machine.stack.pop().unwrap().as_usize(); 695 | let size = machine.stack.pop().unwrap().as_usize(); 696 | 697 | let return_data = U256::from_big_endian(&machine.return_data_buffer[offset..]); 698 | 699 | machine.memory.set(dest_offset, return_data, size); 700 | 701 | ControlFlow::Continue(1) 702 | } 703 | 704 | fn coinbase(machine: &mut Machine) -> ControlFlow { 705 | machine 706 | .stack 707 | .push(U256::from_big_endian(machine.block.coinbase)); 708 | 709 | ControlFlow::Continue(1) 710 | } 711 | 712 | fn timestamp(machine: &mut Machine) -> ControlFlow { 713 | machine 714 | .stack 715 | .push(U256::from_big_endian(machine.block.timestamp)); 716 | 717 | ControlFlow::Continue(1) 718 | } 719 | 720 | fn number(machine: &mut Machine) -> ControlFlow { 721 | machine 722 | .stack 723 | .push(U256::from_big_endian(machine.block.number)); 724 | 725 | ControlFlow::Continue(1) 726 | } 727 | 728 | fn difficulty(machine: &mut Machine) -> ControlFlow { 729 | machine 730 | .stack 731 | .push(U256::from_big_endian(machine.block.difficulty)); 732 | 733 | ControlFlow::Continue(1) 734 | } 735 | 736 | fn gaslimit(machine: &mut Machine) -> ControlFlow { 737 | machine 738 | .stack 739 | .push(U256::from_big_endian(machine.block.gaslimit)); 740 | 741 | ControlFlow::Continue(1) 742 | } 743 | 744 | fn chainid(machine: &mut Machine) -> ControlFlow { 745 | machine 746 | .stack 747 | .push(U256::from_big_endian(machine.block.chainid)); 748 | 749 | ControlFlow::Continue(1) 750 | } 751 | 752 | fn selfbalance(machine: &mut Machine) -> ControlFlow { 753 | let address = machine.context.address; 754 | let balance = machine 755 | .context 756 | .state 757 | .get_account_balance(address); 758 | 759 | machine.stack.push(balance); 760 | 761 | ControlFlow::Continue(1) 762 | } 763 | 764 | fn basefee(machine: &mut Machine) -> ControlFlow { 765 | machine 766 | .stack 767 | .push(U256::from_big_endian(machine.block.basefee)); 768 | 769 | ControlFlow::Continue(1) 770 | } 771 | 772 | fn eval_pop(machine: &mut Machine) -> ControlFlow { 773 | machine.stack.pop(); 774 | 775 | ControlFlow::Continue(1) 776 | } 777 | 778 | fn mload(machine: &mut Machine) -> ControlFlow { 779 | let byte_offset = machine.stack.pop().unwrap(); 780 | 781 | let res = machine.memory.get(byte_offset.as_usize(), WORD_BYTES); 782 | let res_word = U256::from_big_endian(res); 783 | 784 | machine.stack.push(res_word); 785 | ControlFlow::Continue(1) 786 | } 787 | 788 | fn mstore(machine: &mut Machine) -> ControlFlow { 789 | let byte_offset = machine.stack.pop().unwrap(); 790 | let value = machine.stack.pop().unwrap(); 791 | 792 | machine 793 | .memory 794 | .set(byte_offset.as_usize(), value, WORD_BYTES); 795 | 796 | ControlFlow::Continue(1) 797 | } 798 | 799 | fn mstore8(machine: &mut Machine) -> ControlFlow { 800 | let byte_offset = machine.stack.pop().unwrap(); 801 | let value = machine.stack.pop().unwrap(); 802 | 803 | machine.memory.set(byte_offset.as_usize(), value, 1); 804 | 805 | ControlFlow::Continue(1) 806 | } 807 | 808 | fn sload(machine: &mut Machine) -> ControlFlow { 809 | let key = machine.stack.pop().unwrap(); 810 | 811 | let res = machine.storage.get(&key); 812 | 813 | match res { 814 | Some(result) => machine.stack.push(*result), 815 | None => machine.stack.push(0.into()), 816 | } 817 | 818 | ControlFlow::Continue(1) 819 | } 820 | 821 | fn sstore(machine: &mut Machine) -> ControlFlow { 822 | let key = machine.stack.pop().unwrap(); 823 | let value = machine.stack.pop().unwrap(); 824 | 825 | machine.storage.insert(key, value); 826 | 827 | ControlFlow::Continue(1) 828 | } 829 | 830 | fn jump(machine: &mut Machine) -> ControlFlow { 831 | let a = machine.stack.pop().unwrap(); 832 | let is_valid = machine.jump_map.is_valid(a); 833 | 834 | if is_valid { 835 | ControlFlow::Jump(a.as_usize()) 836 | } else { 837 | exit_error(EvmError::InvalidJump) 838 | } 839 | } 840 | 841 | fn jumpi(machine: &mut Machine) -> ControlFlow { 842 | let jump_to = machine.stack.pop().unwrap(); 843 | let should_jump = machine.stack.pop().unwrap(); 844 | 845 | if should_jump.is_zero() { 846 | return ControlFlow::Continue(1); 847 | } 848 | 849 | let is_valid = machine.jump_map.is_valid(jump_to); 850 | 851 | if is_valid { 852 | ControlFlow::Jump(jump_to.as_usize()) 853 | } else { 854 | exit_error(EvmError::InvalidJump) 855 | } 856 | } 857 | 858 | fn pc(machine: &mut Machine) -> ControlFlow { 859 | machine.stack.push(machine.pc.into()); 860 | 861 | ControlFlow::Continue(1) 862 | } 863 | 864 | fn msize(machine: &mut Machine) -> ControlFlow { 865 | let res = machine.memory.size(); 866 | machine.stack.push(res.into()); 867 | 868 | ControlFlow::Continue(1) 869 | } 870 | 871 | fn gas(machine: &mut Machine) -> ControlFlow { 872 | // TODO: update to calculate gas properly (update tests first) 873 | machine.stack.push(U256::MAX); 874 | 875 | ControlFlow::Continue(1) 876 | } 877 | fn jumpdest(_machine: &mut Machine) -> ControlFlow { 878 | ControlFlow::Continue(1) 879 | } 880 | 881 | fn eval_push(machine: &mut Machine) -> ControlFlow { 882 | let n = usize::from(machine.opcode() - (Opcode::PUSH1 - 1)); 883 | let start = machine.pc + 1; 884 | let end = start + n; 885 | let bytes = &machine.code[start..end]; 886 | let val_to_push = U256::from_big_endian(bytes); 887 | machine.stack.push(val_to_push); 888 | 889 | ControlFlow::Continue(n + 1) 890 | } 891 | 892 | fn dup(machine: &mut Machine) -> ControlFlow { 893 | let n = usize::from(machine.opcode() - Opcode::DUP1); 894 | 895 | let a = machine.stack.peek(n); 896 | 897 | match a { 898 | Ok(val) => machine.stack.push(val), 899 | Err(error) => return exit_error(error), 900 | } 901 | 902 | ControlFlow::Continue(1) 903 | } 904 | 905 | fn swap(machine: &mut Machine) -> ControlFlow { 906 | let n = usize::from(machine.opcode() - (Opcode::SWAP1 - 1)); 907 | 908 | let a = match machine.stack.peek(0) { 909 | Ok(val) => val, 910 | Err(err) => return exit_error(err), 911 | }; 912 | let b = match machine.stack.peek(n) { 913 | Ok(val) => val, 914 | Err(err) => return exit_error(err), 915 | }; 916 | 917 | match machine.stack.set(a, n) { 918 | Ok(()) => (), 919 | Err(err) => return exit_error(err), 920 | } 921 | match machine.stack.set(b, 0) { 922 | Ok(()) => (), 923 | Err(err) => return exit_error(err), 924 | } 925 | 926 | ControlFlow::Continue(1) 927 | } 928 | 929 | fn log(machine: &mut Machine) -> ControlFlow { 930 | let n = usize::from(machine.opcode() - Opcode::LOG0); 931 | 932 | let offset = machine.stack.pop().unwrap().as_usize(); 933 | let size = machine.stack.pop().unwrap().as_usize(); 934 | 935 | let data = machine.memory.get(offset, size); 936 | 937 | let mut new_log = Log::new(machine.context.address, data); 938 | 939 | for _i in 0..n { 940 | let topic = machine.stack.pop().unwrap(); 941 | new_log.add_topic(topic); 942 | } 943 | 944 | machine.logs.push(new_log); 945 | 946 | ControlFlow::Continue(1) 947 | } 948 | 949 | // TODO: sender_nonce => starts as 0 but increments each CREATE: todo: increment & test incrementation of this value 950 | fn create(machine: &mut Machine) -> ControlFlow { 951 | let value = machine.stack.pop().unwrap(); 952 | let offset = machine.stack.pop().unwrap().as_usize(); 953 | let size = machine.stack.pop().unwrap().as_usize(); 954 | 955 | let initialisation_code = machine.memory.get(offset, size); 956 | 957 | let address = create_address(machine.context.address, 0.into()); 958 | 959 | let mut value_bytes: [u8; 32] = [0; 32]; 960 | U256::to_big_endian(&value, &mut value_bytes); 961 | 962 | let res = evm( 963 | initialisation_code, 964 | Context::new( 965 | address, 966 | machine.context.address, 967 | machine.context.origin, 968 | machine.context.gasprice, 969 | // QUESTION??? should value be 0 at this point? And then just sent after? 970 | U256::from_big_endian(&value_bytes), 971 | &String::new(), 972 | machine.context.state.clone(), 973 | false, 974 | ), 975 | machine.block, 976 | None, 977 | ); 978 | 979 | if !res.success { 980 | machine.stack.push(0.into()); 981 | return ControlFlow::Continue(1); 982 | } 983 | 984 | machine.context.state = res.state; 985 | 986 | // code = return value of initialisation code 987 | match &res.return_val { 988 | // TODO: deal with code over 32_bytes long => update return val type to Vec 989 | Some(code) => { 990 | // TODO: what if code is more than 32 bytes???? 991 | let mut code_bytes: [u8; 32] = [0; 32]; 992 | U256::to_big_endian(code, &mut code_bytes); 993 | 994 | // This could cut off an (unlikely) initial stop opcode. Update return_val to be a Vec 995 | let code_vec = code.to_vec_u8_without_padding(); 996 | 997 | machine 998 | .context 999 | .state 1000 | .add_or_update_account(address, value, code_vec); 1001 | } 1002 | None => { 1003 | machine 1004 | .context 1005 | .state 1006 | .add_or_update_account(address, value, Vec::new()); 1007 | } 1008 | } 1009 | 1010 | // UPDATE STATE 1011 | 1012 | machine.stack.push(address.to_u256()); 1013 | 1014 | ControlFlow::Continue(1) 1015 | } 1016 | 1017 | // enum CallType { 1018 | // CALL, 1019 | // DELEGATECALL, 1020 | // STATICCALL, 1021 | // CREATE 1022 | // } 1023 | 1024 | // fn message_call (machine: &mut Machine, call_type: CallType) -> ControlFlow { 1025 | // ControlFlow::Continue(1) 1026 | // } 1027 | 1028 | fn call(machine: &mut Machine) -> ControlFlow { 1029 | // TODO: handle gas 1030 | let _gas = machine.stack.pop().unwrap(); 1031 | let address = machine.stack.pop().unwrap().to_h160(); 1032 | let value = machine.stack.pop().unwrap(); 1033 | let args_offset = machine.stack.pop().unwrap().as_usize(); 1034 | let args_size = machine.stack.pop().unwrap().as_usize(); 1035 | let ret_offset = machine.stack.pop().unwrap().as_usize(); 1036 | let ret_size = machine.stack.pop().unwrap().as_usize(); 1037 | 1038 | let data = machine.memory.get(args_offset, args_size); 1039 | 1040 | let code = machine.context.state.get_account_code(address); 1041 | 1042 | 1043 | // TODO: use trait for this 1044 | let mut value_bytes: [u8; 32] = [0; 32]; 1045 | U256::to_big_endian(&value, &mut value_bytes); 1046 | 1047 | let data_string = hex::encode(data); 1048 | 1049 | let res = evm( 1050 | code, 1051 | Context::new( 1052 | address, 1053 | machine.context.address, 1054 | machine.context.origin, 1055 | machine.context.gasprice, 1056 | U256::from_big_endian(&value_bytes), 1057 | &data_string, 1058 | machine.context.state.clone(), 1059 | false, 1060 | ), 1061 | machine.block, 1062 | None, 1063 | ); 1064 | 1065 | match &res.return_val { 1066 | Some(value) => { 1067 | let return_val_without_padding = value.to_vec_u8_without_padding(); 1068 | 1069 | machine.memory.set(ret_offset, *value, ret_size); 1070 | machine.return_data_buffer = return_val_without_padding; 1071 | } 1072 | None => { 1073 | machine.return_data_buffer = Vec::new(); 1074 | } 1075 | }; 1076 | 1077 | if res.success { 1078 | machine.context.state = res.state; 1079 | machine.stack.push(1.into()); 1080 | } else { 1081 | machine.stack.push(0.into()); 1082 | } 1083 | 1084 | ControlFlow::Continue(1) 1085 | } 1086 | 1087 | fn eval_return(machine: &mut Machine) -> ControlFlow { 1088 | let offset = machine.stack.pop().unwrap().as_usize(); 1089 | let size = machine.stack.pop().unwrap().as_usize(); 1090 | 1091 | let res = machine.memory.get(offset, size); 1092 | 1093 | exit_success(ExitSuccess::Return(U256::from_big_endian(res))) 1094 | } 1095 | 1096 | fn delegatecall(machine: &mut Machine) -> ControlFlow { 1097 | // TODO: handle gas 1098 | let _gas = machine.stack.pop().unwrap(); 1099 | let address = machine.stack.pop().unwrap().to_h160(); 1100 | let args_offset = machine.stack.pop().unwrap().as_usize(); 1101 | let args_size = machine.stack.pop().unwrap().as_usize(); 1102 | let ret_offset = machine.stack.pop().unwrap().as_usize(); 1103 | let ret_size = machine.stack.pop().unwrap().as_usize(); 1104 | 1105 | let data = machine.memory.get(args_offset, args_size); 1106 | 1107 | let code = machine.context.state.get_account_code(address); 1108 | 1109 | 1110 | let data_string = hex::encode(data); 1111 | 1112 | let res = evm( 1113 | code, 1114 | Context::new( 1115 | machine.context.address, 1116 | machine.context.caller, 1117 | machine.context.origin, 1118 | machine.context.gasprice, 1119 | machine.context.value, 1120 | &data_string, 1121 | machine.context.state.clone(), 1122 | false, 1123 | ), 1124 | machine.block, 1125 | Some(&mut machine.storage), 1126 | ); 1127 | 1128 | match &res.return_val { 1129 | Some(value) => { 1130 | let return_val_without_padding = value.to_vec_u8_without_padding(); 1131 | 1132 | machine.memory.set(ret_offset, *value, ret_size); 1133 | machine.return_data_buffer = return_val_without_padding; 1134 | } 1135 | None => { 1136 | machine.return_data_buffer = Vec::new(); 1137 | } 1138 | }; 1139 | 1140 | if res.success { 1141 | machine.context.state = res.state; 1142 | machine.stack.push(1.into()); 1143 | } else { 1144 | machine.stack.push(0.into()); 1145 | } 1146 | 1147 | ControlFlow::Continue(1) 1148 | } 1149 | 1150 | // TODO: merge call opcode shared logic into a single call function with a type enum passed in 1151 | fn staticcall(machine: &mut Machine) -> ControlFlow { 1152 | // TODO: handle gas 1153 | let _gas = machine.stack.pop().unwrap(); 1154 | let address = machine.stack.pop().unwrap().to_h160(); 1155 | let args_offset = machine.stack.pop().unwrap().as_usize(); 1156 | let args_size = machine.stack.pop().unwrap().as_usize(); 1157 | let ret_offset = machine.stack.pop().unwrap().as_usize(); 1158 | let ret_size = machine.stack.pop().unwrap().as_usize(); 1159 | 1160 | let data = machine.memory.get(args_offset, args_size); 1161 | 1162 | let code = machine.context.state.get_account_code(address); 1163 | 1164 | let data_string = hex::encode(data); 1165 | 1166 | let res = evm( 1167 | code, 1168 | Context::new( 1169 | address, 1170 | machine.context.address, 1171 | machine.context.origin, 1172 | machine.context.gasprice, 1173 | 0.into(), 1174 | &data_string, 1175 | machine.context.state.clone(), 1176 | true, 1177 | ), 1178 | machine.block, 1179 | None, 1180 | ); 1181 | 1182 | match &res.return_val { 1183 | Some(value) => { 1184 | let return_val_without_padding = value.to_vec_u8_without_padding(); 1185 | 1186 | machine.memory.set(ret_offset, *value, ret_size); 1187 | machine.return_data_buffer = return_val_without_padding; 1188 | } 1189 | None => { 1190 | machine.return_data_buffer = Vec::new(); 1191 | } 1192 | }; 1193 | 1194 | if res.success { 1195 | machine.context.state = res.state; 1196 | machine.stack.push(1.into()); 1197 | } else { 1198 | machine.stack.push(0.into()); 1199 | } 1200 | 1201 | ControlFlow::Continue(1) 1202 | } 1203 | 1204 | fn revert(machine: &mut Machine) -> ControlFlow { 1205 | let offset = machine.stack.pop().unwrap().as_usize(); 1206 | let size = machine.stack.pop().unwrap().as_usize(); 1207 | 1208 | let res = machine.memory.get(offset, size); 1209 | 1210 | exit_error(EvmError::Revert(U256::from_big_endian(res))) 1211 | } 1212 | 1213 | fn invalid(_machine: &mut Machine) -> ControlFlow { 1214 | exit_error(EvmError::InvalidInstruction) 1215 | } 1216 | 1217 | fn selfdestruct(machine: &mut Machine) -> ControlFlow { 1218 | let address = machine.stack.pop().unwrap(); 1219 | 1220 | let balance = machine 1221 | .context 1222 | .state 1223 | .destruct_account(machine.context.address); 1224 | 1225 | machine 1226 | .context 1227 | .state 1228 | .increment_balance(address.to_h160(), balance + machine.context.value); 1229 | 1230 | ControlFlow::Continue(1) 1231 | } 1232 | -------------------------------------------------------------------------------- /evm/src/helpers.rs: -------------------------------------------------------------------------------- 1 | use crate::machine::{ControlFlow, EvmError, ExitReason, ExitSuccess}; 2 | use primitive_types::{H160, H256, U256}; 3 | use sha3::{Digest, Keccak256}; 4 | use std::ops::{Add, Div, Sub}; 5 | 6 | // TODO: follow this pattern elsewhere 7 | // TODO: extract traits into own file 8 | pub trait Convert { 9 | fn to_h160(&self) -> H160; 10 | fn to_u256(&self) -> U256; 11 | } 12 | 13 | pub trait ToBytes { 14 | fn to_vec_u8_without_padding(&self) -> Vec; 15 | } 16 | 17 | // TODO: update to use into 18 | impl Convert for U256 { 19 | fn to_h160(&self) -> H160 { 20 | let mut bytes: [u8; 32] = [0; 32]; 21 | self.to_big_endian(&mut bytes); 22 | H160::from_slice(&remove_padding(&bytes)) 23 | } 24 | fn to_u256(&self) -> U256 { 25 | *self 26 | } 27 | } 28 | 29 | impl ToBytes for U256 { 30 | fn to_vec_u8_without_padding(&self) -> Vec { 31 | let mut return_val_bytes: [u8; 32] = [0; 32]; 32 | U256::to_big_endian(&self, &mut return_val_bytes); 33 | let return_val_without_padding: Vec = remove_padding(&return_val_bytes); 34 | return_val_without_padding 35 | } 36 | } 37 | 38 | impl Convert for H160 { 39 | fn to_h160(&self) -> H160 { 40 | *self 41 | } 42 | fn to_u256(&self) -> U256 { 43 | U256::from_big_endian(self.as_bytes()) 44 | } 45 | } 46 | 47 | impl Convert for String { 48 | fn to_h160(&self) -> H160 { 49 | let hex_decoded = hex_decode_with_prefix(&self); 50 | let res = add_padding(&hex_decoded, 20); 51 | H160::from_slice(&res) 52 | } 53 | 54 | fn to_u256(&self) -> U256 { 55 | let hex_decoded = hex_decode_with_prefix(&self); 56 | U256::from_big_endian(&hex_decoded) 57 | } 58 | } 59 | 60 | pub fn hex_decode_with_prefix(data: &String) -> Vec { 61 | let slice = if data.contains('x') { 62 | &data[2..] 63 | } else { 64 | &data[..] 65 | }; 66 | 67 | let mut res = String::new(); 68 | if slice.len() % 2 == 1 { 69 | res.push('0'); 70 | } 71 | res.push_str(slice); 72 | hex::decode(res).unwrap() 73 | } 74 | 75 | pub fn remove_padding(list: &[u8]) -> Vec { 76 | list.to_vec().into_iter().skip_while(|x| *x == 0).collect() 77 | } 78 | 79 | pub fn create_address(caller: H160, nonce: U256) -> H160 { 80 | let mut stream = rlp::RlpStream::new_list(2); 81 | stream.append(&caller); 82 | stream.append(&nonce); 83 | H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() 84 | } 85 | 86 | pub fn exit_error(err: EvmError) -> ControlFlow { 87 | ControlFlow::Exit(ExitReason::Error(err)) 88 | } 89 | pub fn exit_success(success: ExitSuccess) -> ControlFlow { 90 | ControlFlow::Exit(ExitReason::Success(success)) 91 | } 92 | 93 | pub fn add_padding(arr: &[u8], size: usize) -> Vec { 94 | if arr.len() >= size { 95 | return arr.to_vec(); 96 | } 97 | let mut res = vec![0; size]; 98 | let start_index = size - arr.len(); 99 | for (arr_index, res_index) in (start_index..res.len()).into_iter().enumerate() { 100 | res[res_index] = arr[arr_index]; 101 | } 102 | res 103 | } 104 | 105 | pub fn arr_slice_extend(arr: &[u8], offset: usize, size: usize) -> U256 { 106 | let mut res = vec![0; size]; 107 | for i in 0..size { 108 | let code_index = i + offset; 109 | if code_index < arr.len() { 110 | let data = arr[code_index]; 111 | res[i] = data; 112 | } 113 | } 114 | U256::from_big_endian(&res) 115 | } 116 | 117 | pub fn convert_twos_compliment(x: U256) -> U256 { 118 | // Note, normally the twos compliment of 0 is 0 119 | // However according to the EVM spec it seems to want this behaviour 120 | // I am uncertain if this is a misunderstanding by me/edge case for the SAR opcode 121 | // TODO: research this behaviour 122 | if x == U256::zero() { 123 | return !x; 124 | } 125 | // We do this by first doing a bitwise negation then adding one 126 | !x + U256::one() 127 | } 128 | 129 | pub fn is_negative(x: U256) -> bool { 130 | // check the first bit, if it is 1, it is negative 131 | // according to the rules of twos_compliment 132 | x.bit(255) 133 | } 134 | 135 | pub fn ceil_divide(a: T, b: T) -> T { 136 | (a + b - T::one()) / b 137 | } 138 | 139 | pub trait Int: 140 | Add + Sub + Div + PartialEq + Copy 141 | { 142 | fn zero() -> Self; 143 | fn one() -> Self; 144 | } 145 | 146 | impl Int for U256 { 147 | fn zero() -> Self { 148 | U256::zero() 149 | } 150 | fn one() -> Self { 151 | U256::one() 152 | } 153 | } 154 | 155 | impl Int for usize { 156 | fn zero() -> Self { 157 | 0 158 | } 159 | fn one() -> Self { 160 | 1 161 | } 162 | } 163 | 164 | impl Int for u32 { 165 | fn zero() -> Self { 166 | 0 167 | } 168 | fn one() -> Self { 169 | 1 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /evm/src/jump_map.rs: -------------------------------------------------------------------------------- 1 | use primitive_types::U256; 2 | 3 | use crate::opcode::Opcode; 4 | 5 | pub struct JumpMap(Vec); 6 | 7 | impl JumpMap { 8 | pub fn new(code: &[u8]) -> Self { 9 | Self(Self::generate_map(code)) 10 | } 11 | 12 | fn generate_map(code: &[u8]) -> Vec { 13 | let mut map: Vec = vec![]; 14 | let mut steps_to_block = 0; 15 | for i in 0..code.len() { 16 | if steps_to_block > 0 { 17 | steps_to_block -= 1; 18 | } 19 | 20 | let opcode = code[i]; 21 | 22 | match opcode { 23 | Opcode::PUSH1..=Opcode::PUSH32 => { 24 | steps_to_block = usize::from(opcode) - usize::from(Opcode::PUSH1) + 2; 25 | } 26 | _ => (), 27 | } 28 | 29 | if steps_to_block == 0 && opcode == Opcode::JUMPDEST { 30 | map.push(true); 31 | } else { 32 | map.push(false); 33 | } 34 | } 35 | 36 | map 37 | } 38 | 39 | pub fn is_valid(&self, index: U256) -> bool { 40 | if index < self.0.len().into() { 41 | self.0[index.as_usize()] 42 | } else { 43 | false 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /evm/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | mod consts; 3 | pub mod context; 4 | mod eval; 5 | pub mod helpers; 6 | mod jump_map; 7 | mod machine; 8 | mod memory; 9 | mod opcode; 10 | mod stack; 11 | pub mod state; 12 | 13 | use std::collections::HashMap; 14 | use primitive_types::U256; 15 | use crate::block::Block; 16 | use crate::context::Context; 17 | use crate::machine::EvmResult; 18 | use crate::machine::Machine; 19 | 20 | // TODO: refactor to create Runtime struct with Machine within it 21 | pub fn evm( 22 | code: impl AsRef<[u8]>, 23 | context: Context, 24 | block: Block, 25 | storage: Option<&mut HashMap>, 26 | ) -> EvmResult { 27 | let mut new_storage = HashMap::new(); 28 | let mut machine: Machine = match storage { 29 | Some(store) => Machine::new(code.as_ref(), context, block, store), 30 | None => Machine::new(code.as_ref(), context, block, &mut new_storage) 31 | }; 32 | return machine.execute(); 33 | } 34 | -------------------------------------------------------------------------------- /evm/src/machine.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{block::Block, context::Context}; 4 | use crate::eval::eval; 5 | use crate::jump_map::JumpMap; 6 | use crate::memory::Memory; 7 | use crate::stack::Stack; 8 | use crate::state::State; 9 | use primitive_types::{U256, H160}; 10 | 11 | pub enum ControlFlow { 12 | Continue(usize), 13 | Jump(usize), 14 | Exit(ExitReason), 15 | } 16 | 17 | pub enum ExitReason { 18 | Error(EvmError), 19 | Success(ExitSuccess), 20 | } 21 | 22 | pub enum ExitSuccess { 23 | Stop, 24 | Return(U256), 25 | } 26 | 27 | #[derive(Debug, Copy, Clone)] 28 | pub enum EvmError { 29 | StackUnderflow, 30 | InvalidInstruction, 31 | InvalidJump, 32 | Revert(U256), 33 | OpcodeNotStatic(u8), 34 | } 35 | 36 | enum EvmStatus { 37 | Running, 38 | Exited(ExitReason), 39 | } 40 | 41 | pub struct EvmResult { 42 | pub stack: Vec, 43 | pub success: bool, 44 | pub error: Option, 45 | pub logs: Vec, 46 | pub state: State, 47 | pub return_val: Option, 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | pub struct Log { 52 | pub address: String, 53 | pub data: String, 54 | pub topics: Vec, 55 | } 56 | 57 | impl Log { 58 | pub fn new(address: H160, data: &[u8]) -> Self { 59 | Self { 60 | address: address.to_string(), 61 | data: hex::encode(data), 62 | topics: Vec::new(), 63 | } 64 | } 65 | 66 | pub fn add_topic(&mut self, topic: U256) { 67 | let mut bytes: [u8; 32] = [0; 32]; 68 | topic.to_big_endian(&mut bytes); 69 | 70 | let topic_string = hex::encode(bytes); 71 | 72 | // TODO: check if this can be done using H160 or similar, maybe H256 73 | let mut prepended_topic: String = "0x".to_owned(); 74 | prepended_topic.push_str(&topic_string); 75 | 76 | self.topics.push(prepended_topic); 77 | } 78 | } 79 | 80 | pub struct Machine<'a> { 81 | pub stack: Stack, 82 | pub memory: Memory, 83 | pub storage: &'a mut HashMap, 84 | pub return_data_buffer: Vec, 85 | pub context: Context<'a>, 86 | pub block: Block<'a>, 87 | pub jump_map: JumpMap, 88 | pub code: &'a [u8], 89 | pub logs: Vec, 90 | pub pc: usize, 91 | } 92 | 93 | impl<'a> Machine<'a> { 94 | pub fn new( 95 | code: &'a [u8], 96 | context: Context<'a>, 97 | block: Block<'a>, 98 | storage: &'a mut HashMap, 99 | ) -> Self { 100 | Self { 101 | stack: Stack::new(), 102 | memory: Memory::new(), 103 | jump_map: JumpMap::new(code), 104 | return_data_buffer: Vec::new(), 105 | logs: Vec::new(), 106 | storage, 107 | context, 108 | block, 109 | code, 110 | pc: 0, 111 | } 112 | } 113 | 114 | fn stack(&self) -> Vec { 115 | self.stack.data() 116 | } 117 | 118 | pub fn opcode(&self) -> u8 { 119 | self.code[self.pc] 120 | } 121 | 122 | fn step(&mut self) -> EvmStatus { 123 | match eval(self) { 124 | ControlFlow::Continue(steps) => { 125 | self.pc += steps; 126 | EvmStatus::Running 127 | } 128 | ControlFlow::Jump(position) => { 129 | self.pc = position; 130 | EvmStatus::Running 131 | } 132 | ControlFlow::Exit(reason) => EvmStatus::Exited(reason), 133 | } 134 | } 135 | 136 | pub fn execute(&mut self) -> EvmResult { 137 | while self.pc < self.code.len() { 138 | match self.step() { 139 | EvmStatus::Running => continue, 140 | EvmStatus::Exited(reason) => match reason { 141 | ExitReason::Success(success) => match success { 142 | ExitSuccess::Stop => break, 143 | ExitSuccess::Return(val) => { 144 | return EvmResult { 145 | stack: self.stack(), 146 | success: true, 147 | error: None, 148 | logs: self.logs.clone(), 149 | state: self.context.state.clone(), 150 | return_val: Some(val), 151 | } 152 | } 153 | }, 154 | ExitReason::Error(error) => { 155 | return EvmResult { 156 | stack: self.stack(), 157 | success: false, 158 | error: Some(error), 159 | logs: self.logs.clone(), 160 | state: self.context.state.clone(), 161 | return_val: match &error { 162 | EvmError::Revert(val) => Some(*val), 163 | _ => None, 164 | }, 165 | } 166 | } 167 | }, 168 | } 169 | } 170 | 171 | return EvmResult { 172 | stack: self.stack(), 173 | success: true, 174 | error: None, 175 | logs: self.logs.clone(), 176 | state: self.context.state.clone(), 177 | return_val: None, 178 | }; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /evm/src/main.rs: -------------------------------------------------------------------------------- 1 | use evm::{ 2 | evm, 3 | block::Block, 4 | context::Context, 5 | helpers::{hex_decode_with_prefix, Convert}, 6 | state::State, 7 | }; 8 | use primitive_types::{H160, U256}; 9 | use serde::Deserialize; 10 | use std::{collections::HashMap, str::FromStr}; 11 | 12 | #[derive(Debug, Deserialize)] 13 | struct Evmtest { 14 | name: String, 15 | hint: String, 16 | code: Code, 17 | tx: Option, 18 | block: Option, 19 | state: Option, 20 | expect: Expect, 21 | } 22 | 23 | #[derive(Debug, Deserialize)] 24 | struct Tx { 25 | to: Option, 26 | from: Option, 27 | origin: Option, 28 | gasprice: Option, 29 | value: Option, 30 | data: Option, 31 | } 32 | 33 | #[derive(Debug, Deserialize)] 34 | struct BlockData { 35 | basefee: Option, 36 | coinbase: Option, 37 | timestamp: Option, 38 | number: Option, 39 | difficulty: Option, 40 | gaslimit: Option, 41 | chainid: Option, 42 | } 43 | 44 | #[derive(Debug, Deserialize)] 45 | struct StateData(HashMap); 46 | 47 | impl StateData { 48 | pub fn new() -> Self { 49 | Self(HashMap::new()) 50 | } 51 | 52 | pub fn account_data_list(&self) -> Vec<(H160, Vec, Vec)> { 53 | self.0 54 | .iter() 55 | .map(|(address, account_data)| { 56 | ( 57 | H160::from_str(address).unwrap(), 58 | account_data.hex_decode_balance(), 59 | account_data.hex_decode_code(), 60 | ) 61 | }) 62 | .collect() 63 | } 64 | } 65 | 66 | #[derive(Debug, Deserialize, Clone)] 67 | struct AccountData { 68 | balance: Option, 69 | code: Option, 70 | } 71 | 72 | impl AccountData { 73 | pub fn hex_decode_balance(&self) -> Vec { 74 | match &self.balance { 75 | Some(balance) => hex_decode_with_prefix(&balance), 76 | None => vec![], 77 | } 78 | } 79 | pub fn hex_decode_code(&self) -> Vec { 80 | let default = String::new(); 81 | 82 | match &self.code { 83 | Some(code_state) => hex::decode(match &code_state.bin { 84 | Some(bin) => &bin, 85 | None => &default, 86 | }) 87 | .unwrap(), 88 | None => vec![], 89 | } 90 | } 91 | } 92 | 93 | #[derive(Debug, Deserialize, Clone)] 94 | struct CodeState { 95 | // asm: Option, 96 | bin: Option, 97 | } 98 | 99 | #[derive(Debug, Deserialize)] 100 | struct Code { 101 | asm: String, 102 | bin: String, 103 | } 104 | 105 | #[derive(Debug, Deserialize)] 106 | struct Expect { 107 | stack: Option>, 108 | success: bool, 109 | logs: Option>, 110 | #[serde(rename = "return")] 111 | ret: Option, 112 | } 113 | 114 | #[derive(Debug, Deserialize)] 115 | struct Log { 116 | address: Option, 117 | data: Option, 118 | topics: Option>, 119 | } 120 | 121 | fn main() { 122 | let text = std::fs::read_to_string("../evm.json").unwrap(); 123 | let data: Vec = serde_json::from_str(&text).unwrap(); 124 | 125 | let total = data.len(); 126 | 127 | for (index, test) in data.iter().enumerate() { 128 | println!("Test {} of {}: {}", index + 1, total, test.name); 129 | 130 | let code: Vec = hex::decode(&test.code.bin).unwrap(); 131 | 132 | // TODO: update to use macro here 133 | let address = match &test.tx { 134 | Some(tx) => match &tx.to { 135 | Some(to) => to.to_h160(), 136 | None => H160::zero(), 137 | }, 138 | None => H160::zero(), 139 | }; 140 | let caller = match &test.tx { 141 | Some(tx) => match &tx.from { 142 | Some(from) => from.to_h160(), 143 | None => H160::zero(), 144 | }, 145 | None => H160::zero(), 146 | }; 147 | let origin: H160 = match &test.tx { 148 | Some(tx) => match &tx.origin { 149 | Some(origin) => origin.to_h160(), 150 | None => H160::zero(), 151 | }, 152 | None => H160::zero(), 153 | }; 154 | let gasprice = match &test.tx { 155 | Some(tx) => match &tx.gasprice { 156 | Some(gasprice) => gasprice.to_u256(), 157 | None => U256::zero(), 158 | }, 159 | None => U256::zero(), 160 | }; 161 | let value = match &test.tx { 162 | Some(tx) => match &tx.value { 163 | Some(value) => value.to_u256(), 164 | None => U256::zero(), 165 | }, 166 | None => U256::zero(), 167 | }; 168 | let data = match &test.tx { 169 | Some(tx) => match &tx.data { 170 | Some(data) => data.clone(), 171 | None => String::new(), 172 | }, 173 | None => String::new(), 174 | }; 175 | 176 | let basefee = match &test.block { 177 | Some(tx) => match &tx.basefee { 178 | Some(basefee) => hex_decode_with_prefix(basefee), 179 | None => vec![], 180 | }, 181 | None => vec![], 182 | }; 183 | let coinbase = match &test.block { 184 | Some(tx) => match &tx.coinbase { 185 | Some(coinbase) => hex_decode_with_prefix(coinbase), 186 | None => vec![], 187 | }, 188 | None => vec![], 189 | }; 190 | let timestamp = match &test.block { 191 | Some(tx) => match &tx.timestamp { 192 | Some(timestamp) => hex_decode_with_prefix(timestamp), 193 | None => vec![], 194 | }, 195 | None => vec![], 196 | }; 197 | let number = match &test.block { 198 | Some(tx) => match &tx.number { 199 | Some(number) => hex_decode_with_prefix(number), 200 | None => vec![], 201 | }, 202 | None => vec![], 203 | }; 204 | let difficulty = match &test.block { 205 | Some(tx) => match &tx.difficulty { 206 | Some(difficulty) => hex_decode_with_prefix(difficulty), 207 | None => vec![], 208 | }, 209 | None => vec![], 210 | }; 211 | let gaslimit = match &test.block { 212 | Some(tx) => match &tx.gaslimit { 213 | Some(gaslimit) => hex_decode_with_prefix(gaslimit), 214 | None => vec![], 215 | }, 216 | None => vec![], 217 | }; 218 | let chainid = match &test.block { 219 | Some(tx) => match &tx.chainid { 220 | Some(chainid) => hex_decode_with_prefix(chainid), 221 | None => vec![], 222 | }, 223 | None => vec![], 224 | }; 225 | 226 | let account_data_list = match &test.state { 227 | Some(state) => state.account_data_list(), 228 | None => StateData::new().account_data_list(), 229 | }; 230 | 231 | let mut state: State = State::new(); 232 | 233 | state.add_accounts(&account_data_list); 234 | 235 | let result = evm( 236 | &code, 237 | Context::new( 238 | address, caller, origin, gasprice, value, &data, state, false, 239 | ), 240 | Block::new( 241 | &coinbase, 242 | ×tamp, 243 | &number, 244 | &difficulty, 245 | &gaslimit, 246 | &chainid, 247 | &basefee, 248 | ), 249 | None, 250 | ); 251 | 252 | let mut expected_stack: Vec = Vec::new(); 253 | if let Some(ref stacks) = test.expect.stack { 254 | for value in stacks { 255 | expected_stack.push(U256::from_str_radix(value, 16).unwrap()); 256 | } 257 | } 258 | 259 | let mut matching = result.stack.len() == expected_stack.len(); 260 | if matching { 261 | for i in 0..result.stack.len() { 262 | if result.stack[i] != expected_stack[i] { 263 | matching = false; 264 | break; 265 | } 266 | } 267 | } 268 | 269 | let mut logs_match = true; 270 | 271 | match &test.expect.logs { 272 | Some(logs) => { 273 | for (i, log) in logs.iter().enumerate() { 274 | match &log.address { 275 | Some(address) => { 276 | if i >= result.logs.len() { 277 | logs_match = false; 278 | } else { 279 | logs_match = address == &result.logs[i].address; 280 | } 281 | } 282 | None => (), 283 | } 284 | match &log.data { 285 | Some(data) => { 286 | if i >= result.logs.len() { 287 | logs_match = false; 288 | } else { 289 | logs_match = data == &result.logs[i].data 290 | } 291 | } 292 | None => (), 293 | } 294 | match &log.topics { 295 | Some(data) => { 296 | if i >= result.logs.len() { 297 | logs_match = false; 298 | } else { 299 | for (j, topic) in data.iter().enumerate() { 300 | if j >= result.logs[i].topics.len() { 301 | logs_match = false 302 | } else { 303 | logs_match = topic == &result.logs[i].topics[j] 304 | } 305 | } 306 | } 307 | } 308 | None => (), 309 | } 310 | } 311 | } 312 | None => (), 313 | } 314 | 315 | let mut return_matches = true; 316 | 317 | match &test.expect.ret { 318 | Some(ret) => { 319 | let expected_ret = U256::from_str_radix(ret, 16).unwrap(); 320 | let actual_ret = result.return_val; 321 | match actual_ret { 322 | Some(actual_ret) => { 323 | return_matches = actual_ret == expected_ret; 324 | } 325 | None => { 326 | return_matches = false; 327 | } 328 | } 329 | } 330 | None => (), 331 | } 332 | 333 | matching = matching 334 | && result.success == test.expect.success 335 | && (test.expect.success && result.error.is_none() 336 | || !test.expect.success && !result.error.is_none()) 337 | && logs_match 338 | && return_matches; 339 | 340 | if !matching { 341 | println!("Instructions: \n{}\n", test.code.asm); 342 | 343 | println!( 344 | "Expected error: {:?}", 345 | if test.expect.success { None } else { Some(()) } 346 | ); 347 | println!("Expected success: {:?}", test.expect.success); 348 | match &test.expect.ret { 349 | Some(ret) => { 350 | println!("Expected return: {:?}", ret) 351 | } 352 | None => println!("Expected return: None"), 353 | } 354 | println!("Expected stack: ["); 355 | for v in expected_stack { 356 | println!(" {:#X},", v); 357 | } 358 | println!("]"); 359 | match &test.expect.logs { 360 | Some(logs) => { 361 | println!("Expected logs: ["); 362 | for log in logs { 363 | match &log.address { 364 | Some(addr) => println!(" address: {:?},", addr), 365 | None => (), 366 | } 367 | match &log.data { 368 | Some(data) => println!(" data: {:?},", data), 369 | None => (), 370 | } 371 | match &log.topics { 372 | Some(topics) => { 373 | if topics.len() > 0 { 374 | println!(" topics: ["); 375 | for topic in topics { 376 | println!(" {:?}", topic); 377 | } 378 | println!(" ]"); 379 | } else { 380 | println!(" topics: {:?},", topics); 381 | } 382 | } 383 | None => (), 384 | } 385 | } 386 | println!("]\n"); 387 | } 388 | None => { 389 | println!("\n"); 390 | } 391 | } 392 | 393 | println!("Actual error: {:?}", result.error); 394 | println!("Actual success: {:?}", result.success); 395 | println!("Actual return: {:?}", result.return_val); 396 | println!("Actual stack: ["); 397 | for v in result.stack { 398 | println!(" {:#X},", v); 399 | } 400 | println!("]"); 401 | if result.logs.len() > 0 { 402 | println!("Actual logs: ["); 403 | for log in result.logs { 404 | println!(" address: {:?},", log.address); 405 | println!(" data: {:?},", log.data); 406 | if log.topics.len() > 0 { 407 | println!(" topics: ["); 408 | for topic in log.topics { 409 | println!(" {:?}", topic); 410 | } 411 | println!(" ]"); 412 | } else { 413 | println!(" topics: []") 414 | } 415 | } 416 | println!("]\n"); 417 | } else { 418 | println!("\n") 419 | } 420 | 421 | println!("\nHint: {}\n", test.hint); 422 | println!("Progress: {}/{}\n\n", index, total); 423 | panic!("Test failed"); 424 | } 425 | println!("PASS"); 426 | } 427 | println!("Congratulations!"); 428 | } 429 | -------------------------------------------------------------------------------- /evm/src/memory.rs: -------------------------------------------------------------------------------- 1 | use crate::{consts::WORD_BYTES, helpers::ceil_divide}; 2 | use primitive_types::U256; 3 | 4 | pub struct Memory { 5 | data: Vec, 6 | len_words: usize, 7 | } 8 | 9 | impl Memory { 10 | pub fn new() -> Self { 11 | Self { 12 | data: Vec::new(), 13 | len_words: 0, 14 | } 15 | } 16 | 17 | pub fn size(&self) -> usize { 18 | self.len_words * WORD_BYTES 19 | } 20 | 21 | fn resize(&mut self, length: usize) { 22 | if length >= self.len_words * WORD_BYTES { 23 | self.data.resize(length, 0); 24 | self.len_words = ceil_divide(length, WORD_BYTES); 25 | } 26 | } 27 | 28 | // memory′[offset . . . (offset + 31)] ≡ value 29 | // num_words_in_mem′≡max(num_words_in_mem, ceil( (offset+32)÷32 ) ) 30 | pub fn set(&mut self, byte_offset: usize, value: U256, target_size: usize) { 31 | self.resize(byte_offset + target_size); 32 | 33 | for i in 0..target_size { 34 | let byte = value.byte(target_size - 1 - i); 35 | self.data[byte_offset + i] = byte; 36 | } 37 | } 38 | 39 | pub fn get(&mut self, byte_offset: usize, target_size: usize) -> &[u8] { 40 | let end_index = byte_offset + target_size; 41 | self.resize(end_index); 42 | &self.data[byte_offset..end_index] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /evm/src/opcode.rs: -------------------------------------------------------------------------------- 1 | pub struct Opcode; 2 | 3 | impl Opcode { 4 | pub const STOP: u8 = 0x00; 5 | pub const ADD: u8 = 0x01; 6 | pub const MUL: u8 = 0x02; 7 | pub const SUB: u8 = 0x03; 8 | pub const DIV: u8 = 0x04; 9 | pub const SDIV: u8 = 0x05; 10 | pub const MOD: u8 = 0x06; 11 | pub const SMOD: u8 = 0x07; 12 | pub const ADDMOD: u8 = 0x08; 13 | pub const MULMOD: u8 = 0x09; 14 | pub const EXP: u8 = 0x0a; 15 | pub const SIGNEXTEND: u8 = 0x0b; 16 | pub const LT: u8 = 0x10; 17 | pub const GT: u8 = 0x11; 18 | pub const SLT: u8 = 0x12; 19 | pub const SGT: u8 = 0x13; 20 | pub const EQ: u8 = 0x14; 21 | pub const ISZERO: u8 = 0x15; 22 | pub const AND: u8 = 0x16; 23 | pub const OR: u8 = 0x17; 24 | pub const XOR: u8 = 0x18; 25 | pub const NOT: u8 = 0x19; 26 | pub const BYTE: u8 = 0x1a; 27 | pub const SHL: u8 = 0x1b; 28 | pub const SHR: u8 = 0x1c; 29 | pub const SAR: u8 = 0x1d; 30 | pub const KECCAK256: u8 = 0x20; 31 | pub const ADDRESS: u8 = 0x30; 32 | pub const BALANCE: u8 = 0x31; 33 | pub const ORIGIN: u8 = 0x32; 34 | pub const CALLER: u8 = 0x33; 35 | pub const CALLVALUE: u8 = 0x34; 36 | pub const CALLDATALOAD: u8 = 0x35; 37 | pub const CALLDATASIZE: u8 = 0x36; 38 | pub const CALLDATACOPY: u8 = 0x37; 39 | pub const CODESIZE: u8 = 0x38; 40 | pub const CODECOPY: u8 = 0x39; 41 | pub const GASPRICE: u8 = 0x3a; 42 | pub const EXTCODESIZE: u8 = 0x3b; 43 | pub const EXTCODECOPY: u8 = 0x3c; 44 | pub const RETURNDATASIZE: u8 = 0x3d; 45 | pub const RETURNDATACOPY: u8 = 0x3e; 46 | pub const EXTCODEHASH: u8 = 0x3f; 47 | pub const BLOCKHASH: u8 = 0x40; 48 | pub const COINBASE: u8 = 0x41; 49 | pub const TIMESTAMP: u8 = 0x42; 50 | pub const NUMBER: u8 = 0x43; 51 | pub const DIFFICULTY: u8 = 0x44; 52 | pub const GASLIMIT: u8 = 0x45; 53 | pub const CHAINID: u8 = 0x46; 54 | pub const SELFBALANCE: u8 = 0x47; 55 | pub const BASEFEE: u8 = 0x48; 56 | pub const POP: u8 = 0x50; 57 | pub const MLOAD: u8 = 0x51; 58 | pub const MSTORE: u8 = 0x52; 59 | pub const MSTORE8: u8 = 0x53; 60 | pub const SLOAD: u8 = 0x54; 61 | pub const SSTORE: u8 = 0x55; 62 | pub const JUMP: u8 = 0x56; 63 | pub const JUMPI: u8 = 0x57; 64 | pub const PC: u8 = 0x58; 65 | pub const MSIZE: u8 = 0x59; 66 | pub const GAS: u8 = 0x5a; 67 | pub const JUMPDEST: u8 = 0x5b; 68 | pub const PUSH1: u8 = 0x60; 69 | pub const PUSH32: u8 = 0x7f; 70 | pub const DUP1: u8 = 0x80; 71 | pub const DUP16: u8 = 0x8f; 72 | pub const SWAP1: u8 = 0x90; 73 | pub const SWAP16: u8 = 0x9f; 74 | pub const LOG0: u8 = 0xa0; 75 | pub const LOG4: u8 = 0xa4; 76 | pub const CREATE: u8 = 0xf0; 77 | pub const CALL: u8 = 0xf1; 78 | pub const RETURN: u8 = 0xf3; 79 | pub const DELEGATECALL: u8 = 0xf4; 80 | pub const CREATE2: u8 = 0xf5; 81 | pub const STATICCALL: u8 = 0xfa; 82 | pub const REVERT: u8 = 0xfd; 83 | pub const INVALID: u8 = 0xfe; 84 | pub const SELFDESTRUCT: u8 = 0xff; 85 | } 86 | 87 | impl Opcode { 88 | pub fn is_static(value: u8) -> bool { 89 | match value { 90 | Opcode::CREATE => false, 91 | Opcode::CREATE2 => false, 92 | Opcode::LOG0..=Opcode::LOG4 => false, 93 | Opcode::SSTORE => false, 94 | Opcode::SELFDESTRUCT => false, 95 | Opcode::CALL => false, 96 | _ => true, 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /evm/src/stack.rs: -------------------------------------------------------------------------------- 1 | use crate::machine::EvmError; 2 | use primitive_types::U256; 3 | 4 | pub struct Stack { 5 | data: Vec, 6 | } 7 | 8 | impl Stack { 9 | pub fn new() -> Self { 10 | Self { data: Vec::new() } 11 | } 12 | 13 | pub fn push(&mut self, value: U256) { 14 | self.data.push(value); 15 | } 16 | 17 | pub fn pop(&mut self) -> Option { 18 | self.data.pop() 19 | } 20 | 21 | pub fn set(&mut self, value: U256, i: usize) -> Result<(), EvmError> { 22 | let len = self.data.len(); 23 | if len > i { 24 | self.data[len - i - 1] = value; 25 | Ok(()) 26 | } else { 27 | Err(EvmError::StackUnderflow) 28 | } 29 | } 30 | pub fn peek(&self, i: usize) -> Result { 31 | if self.data.len() > i { 32 | Ok(self.data[self.data.len() - i - 1]) 33 | } else { 34 | Err(EvmError::StackUnderflow) 35 | } 36 | } 37 | 38 | pub fn data(&self) -> Vec { 39 | self.data.to_vec().into_iter().rev().collect() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /evm/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use primitive_types::{U256, H160}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Account { 6 | pub balance: U256, 7 | pub code: Vec, 8 | } 9 | 10 | impl Account { 11 | pub fn new(balance: U256, code: Vec) -> Self { 12 | Self { balance, code } 13 | } 14 | } 15 | 16 | // TODO: Update to use BTreeMap 17 | // TODO: Move out of context 18 | #[derive(Clone)] 19 | pub struct State(pub HashMap); 20 | 21 | impl State { 22 | pub fn new() -> Self { 23 | let map = HashMap::::new(); 24 | Self(map) 25 | } 26 | 27 | pub fn add_accounts(&mut self, address_balances: &Vec<(H160, Vec, Vec)>) { 28 | for (address, balance, code) in address_balances { 29 | self.add_account( 30 | *address, 31 | U256::from_big_endian(balance), 32 | code.clone(), 33 | ); 34 | } 35 | } 36 | 37 | pub fn add_account(&mut self, address: H160, balance: U256, code: Vec) { 38 | self.0.insert(address, Account::new(balance, code)); 39 | } 40 | 41 | pub fn destruct_account(&mut self, address: H160) -> U256 { 42 | let account = self.0.remove(&address); 43 | match account { 44 | Some(account) => account.balance, 45 | None => 0.into() 46 | } 47 | } 48 | 49 | pub fn add_or_update_account(&mut self, address: H160, balance: U256, code: Vec) { 50 | let prev_balance = self.get_account_balance(address); 51 | let new_balance = balance + prev_balance; 52 | 53 | self.0.insert(address, Account::new(new_balance, code)); 54 | } 55 | 56 | pub fn get_account_code(&self, address: H160) -> Vec { 57 | let balance = self.0.get(&address); 58 | 59 | match balance { 60 | Some(account) => account.code.clone(), 61 | None => vec![], 62 | } 63 | } 64 | 65 | pub fn get_account_balance(&self, address: H160) -> U256 { 66 | let account = self.0.get(&address); 67 | 68 | match account { 69 | Some(account) => account.balance, 70 | None => U256::zero(), 71 | } 72 | } 73 | 74 | pub fn get_account(&self, address: H160) -> Option<&Account> { 75 | let account = self.0.get(&address); 76 | 77 | account 78 | } 79 | 80 | pub fn increment_balance(&mut self, address: H160, extra: U256) { 81 | let account = self.get_account(address); 82 | match account { 83 | Some(account) => { 84 | let new_balance = account.balance + extra; 85 | self.add_account(address, new_balance, account.code.clone()) 86 | } 87 | None => { 88 | self.add_account(address, extra, Vec::new()) 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## Eth Yellow Paper 4 | 5 | ### Execution Environment - 9.3 - pg 13 6 | 7 | - σ - the system state 8 | - *g* - the remaining gas for computation 9 | - A - accrued substate 10 | - *I* - tuple with important execution env. information - Ia => address of smart contract, Io => sender address etc 11 | - o - resultant output 12 | - Ξ - execution function 13 | 14 | ``` 15 | (σ′, g′, A′, o) ≡ Ξ(σ, g, A, I) 16 | ``` 17 | 18 | ### Execution Overview - 9.4 - pg 13 19 | 20 | - Execution function is formally defined with a recursive function `X` 21 | - This uses an iterator function `O` (which defines the result of a single cycle of the state machine) 22 | - Functions `Z` which determines if the present state is an exceptional halting state 23 | - `H` specifying the output data of the instruction if and only if the present state is a normal halting state 24 | 25 | - μ - the machine state 26 | - μg - gas, μ′g => remaining machine gas 27 | - () - empty sequence 28 | - ∅ - empty set 29 | - different to () => the output of H, which evaluates to ∅ when execution is to continue but a series (potentially empty) when execution should halt. 30 | 31 | ``` 32 | Ξ(σ, g, A, I) ≡ (σ′,μ′g, A′, o) 33 | (σ′, μ′, A′, ..., o) ≡ X((σ, μ, A, I)) 34 | μg ≡ g 35 | μpc ≡ 0 36 | μm ≡ (0, 0, ...) 37 | μi ≡ 0 38 | μs ≡ () 39 | μo ≡ () 40 | ``` 41 | 42 | ### 9.4.1. Machine State - pg 13 43 | 44 | - The machine state μ is defined as the tuple: 45 | - (g, pc, m, i, s) 46 | - g - which are the gas available 47 | - pc -the program counter pc ∈ N256 48 | - m - the memory contents 49 | - i - the active number of words in memory (counting continuously from position 0) 50 | - s - the stack contents. 51 | 52 | - μm - The memory contents are a series of zeroes of size 256 53 | - w - the current operation to be executed 54 | - δ - the stack items removed 55 | - α - the stack items added 56 | - C - instruction cost function evaluating to the full cost, in gas, of executing the given instruction. 57 | 58 | 59 | (w = JUMP ∧ μs[0] ∈/ D(Ib)) 60 | 61 | ## Execution evn info 62 | 63 | • Ia, the address of the account which owns the code that is executing. 64 | • Io, the sender address of the transaction that orig- 65 | inated this execution. (141) 66 | • Ip, the price of gas in the transaction that origi- nated this execution. 67 | • Id, the byte array that is the input data to this execution; if the execution agent is a transaction, this would be the transaction data. 68 | • Is, the address of the account which caused the code to be executing; if the execution agent is a transaction, this would be the transaction sender. 69 | • Iv, the value, in Wei, passed to this account as part of the same procedure as execution; if the execution agent is a transaction, this would be the transaction value. 70 | • Ib, the byte array that is the machine code to be executed. 71 | • IH, the block header of the present block. 72 | • Ie, the depth of the present message-call or contract-creation (i.e. the number of CALLs or 73 | CREATE(2)s being executed at present). 74 | • Iw, the permission to make modifications to the 75 | state. --------------------------------------------------------------------------------