├── .gitignore ├── .vscode └── settings.json ├── README.md ├── src ├── evm.py └── lib.py └── test └── tests.json /.gitignore: -------------------------------------------------------------------------------- 1 | src/__pycache__ 2 | 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.extraPaths": [ 3 | "./src", 4 | ] 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # EVM From Scratch 3 | 4 | Welcome to [0xMonsoon](https://twitter.com/0xmonsoon)'s implementation of the Ethereum Virtual Machine in python. Its based on this [template](https://github.com/w1nt3r-eth/evm-from-scratch). 5 | 6 | **Note:** 7 | - All the opcodes have been implemented. 8 | - Gas metering has not been implemented yet. Its a work in progress. 9 | - The code is poorly documented right now. Working on adding rationale for design choices. 10 | 11 | ## Credits 12 | 13 | All the test cases in this repository are made by [w1nt3r.eth](https://twitter.com/w1nt3r_eth). This repository is part of the "EVM From Scratch" course (release date TBD). 14 | 15 | Implementation inspired by [smol-evm](https://github.com/karmacoma-eth/smol-evm) and [jaglinux](https://github.com/jaglinux/evm-from-scratch). 16 | 17 | ## Commands to run 18 | 19 | ``` 20 | cd src 21 | python3 evm.py 22 | ``` 23 | This will run the Ethereum Virtual Machine and test it against the test cases contained in `test/tests.json`. -------------------------------------------------------------------------------- /src/evm.py: -------------------------------------------------------------------------------- 1 | # Implementation inspired by https://github.com/karmacoma-eth/smol-evm 2 | # Implementation inspired by https://github.com/jaglinux/evm-from-scratch 3 | # all opcodes implemented 4 | 5 | import json 6 | import os 7 | import sha3 8 | import copy 9 | from lib import Account, WorldState, Context, OpcodeResponse, OpcodeData 10 | from lib import unsigned_to_signed, signed_to_unsigned 11 | from lib import UINT256MAX, STATICCALL_DISSALOWED_OPCODES 12 | 13 | 14 | def opcodeStop(ctx, info): 15 | return OpcodeResponse(success=True, encounteredStop=True,returnData=None) 16 | 17 | 18 | def opcodePush(ctx, info, numericPartOfName): 19 | data = 0 20 | for i in range(numericPartOfName): 21 | data = (data << 8) | ctx.code[ctx.pc + 1] 22 | ctx.pc += 1 23 | ctx.stack.push(data) 24 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 25 | 26 | 27 | def opcodePop(ctx, info): 28 | data = ctx.stack.pop() 29 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 30 | 31 | 32 | def opcodeAdd(ctx, info): 33 | a = ctx.stack.pop() 34 | b = ctx.stack.pop() 35 | result = (a + b) 36 | # overflow condition 37 | result &= UINT256MAX 38 | ctx.stack.push(result) 39 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 40 | 41 | 42 | def opcodeMul(ctx, info): 43 | a = ctx.stack.pop() 44 | b = ctx.stack.pop() 45 | result = (a * b) 46 | # overflow condition 47 | result &= UINT256MAX 48 | ctx.stack.push(result) 49 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 50 | 51 | 52 | def opcodeSub(ctx, info): 53 | a = ctx.stack.pop() 54 | b = ctx.stack.pop() 55 | result = (a - b) 56 | result &= UINT256MAX 57 | ctx.stack.push(result) 58 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 59 | 60 | 61 | def opcodeDiv(ctx, info): 62 | a = ctx.stack.pop() 63 | b = ctx.stack.pop() 64 | # Handle Divide by 0 65 | if b == 0: 66 | result = 0 67 | else: 68 | result = int(a / b) 69 | ctx.stack.push(result) 70 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 71 | 72 | 73 | def opcodeMod(ctx, info): 74 | a = ctx.stack.pop() 75 | b = ctx.stack.pop() 76 | if b == 0: 77 | result = 0 78 | else: 79 | result = a % b 80 | ctx.stack.push(result) 81 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 82 | 83 | 84 | def opcodeAddMod(ctx, info): 85 | a = ctx.stack.pop() 86 | b = ctx.stack.pop() 87 | result = a + b 88 | c = ctx.stack.pop() 89 | result = result % c 90 | result &= UINT256MAX 91 | ctx.stack.push(result) 92 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 93 | 94 | 95 | def opcodeMulMod(ctx, info): 96 | a = ctx.stack.pop() 97 | b = ctx.stack.pop() 98 | result = a * b 99 | c = ctx.stack.pop() 100 | result = result % c 101 | result &= UINT256MAX 102 | ctx.stack.push(result) 103 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 104 | 105 | 106 | def opcodeExp(ctx, info): 107 | a = ctx.stack.pop() 108 | exponent = ctx.stack.pop() 109 | result = a ** exponent 110 | result &= UINT256MAX 111 | ctx.stack.push(result) 112 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 113 | 114 | 115 | def opcodeSignExtend(ctx, info): 116 | a = ctx.stack.pop() 117 | b = ctx.stack.pop() 118 | b = b & ((1 << (a + 1) * 8) - 1) 119 | if (b >> ((a + 1) * 8 - 1)) != 0: 120 | mask = UINT256MAX ^ ((1 << (a + 1) * 8) - 1) 121 | b = b | mask 122 | ctx.stack.push(b) 123 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 124 | 125 | 126 | def opcodeSDiv(ctx, info): 127 | a = unsigned_to_signed(ctx.stack.pop()) 128 | b = unsigned_to_signed(ctx.stack.pop()) 129 | ctx.stack.push(signed_to_unsigned(a // b) if b != 0 else 0) 130 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 131 | 132 | 133 | def opcodeSMod(ctx, info): 134 | a = unsigned_to_signed(ctx.stack.pop()) 135 | b = unsigned_to_signed(ctx.stack.pop()) 136 | ctx.stack.push(signed_to_unsigned(a % b) if b != 0 else 0) 137 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 138 | 139 | 140 | def opcodeLT(ctx, info): 141 | a = ctx.stack.pop() 142 | b = ctx.stack.pop() 143 | ctx.stack.push(1 if a < b else 0) 144 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 145 | 146 | 147 | def opcodeGT(ctx, info): 148 | a = ctx.stack.pop() 149 | b = ctx.stack.pop() 150 | ctx.stack.push(1 if a > b else 0) 151 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 152 | 153 | 154 | def opcodeSLT(ctx, info): 155 | a = unsigned_to_signed(ctx.stack.pop()) 156 | b = unsigned_to_signed(ctx.stack.pop()) 157 | ctx.stack.push(1 if a < b else 0) 158 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 159 | 160 | 161 | def opcodeSGT(ctx, info): 162 | a = unsigned_to_signed(ctx.stack.pop()) 163 | b = unsigned_to_signed(ctx.stack.pop()) 164 | ctx.stack.push(1 if a > b else 0) 165 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 166 | 167 | 168 | def opcodeEQ(ctx, info): 169 | ctx.stack.push(1 if ctx.stack.pop() == ctx.stack.pop() else 0) 170 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 171 | 172 | 173 | def opcodeIsZero(ctx, info): 174 | ctx.stack.push(1 if ctx.stack.pop() == 0 else 0) 175 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 176 | 177 | 178 | def opcodeAnd(ctx, info): 179 | ctx.stack.push((ctx.stack.pop() & ctx.stack.pop())) 180 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 181 | 182 | 183 | def opcodeOR(ctx, info): 184 | ctx.stack.push((ctx.stack.pop() | ctx.stack.pop())) 185 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 186 | 187 | 188 | def opcodeXOR(ctx, info): 189 | ctx.stack.push((ctx.stack.pop() ^ ctx.stack.pop())) 190 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 191 | 192 | 193 | def opcodeNot(ctx, info): 194 | ctx.stack.push(UINT256MAX ^ ctx.stack.pop()) 195 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 196 | 197 | 198 | def opcodeSHL(ctx, info): 199 | a = ctx.stack.pop() 200 | b = ctx.stack.pop() 201 | ctx.stack.push(0 if a >= 256 else ((b << a) % 2 ** 256)) 202 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 203 | 204 | 205 | def opcodeSHR(ctx, info): 206 | a = ctx.stack.pop() 207 | b = ctx.stack.pop() 208 | ctx.stack.push(b >> a) 209 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 210 | 211 | 212 | def opcodeSAR(ctx, info): 213 | shift, signed_value = ctx.stack.pop(), unsigned_to_signed(ctx.stack.pop()) 214 | ctx.stack.push(signed_to_unsigned(signed_value >> shift)) 215 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 216 | 217 | 218 | def opcodeByte(ctx, info): 219 | offset, value = ctx.stack.pop(), ctx.stack.pop() 220 | if offset < 32: 221 | ctx.stack.push((value >> ((31 - offset) * 8)) & 0xFF) 222 | else: 223 | ctx.stack.push(0) 224 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 225 | 226 | 227 | def opcodeDup(ctx, info, numericPartOfName): 228 | a = ctx.stack.access_at_index(numericPartOfName * -1) 229 | ctx.stack.push(a) 230 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 231 | 232 | 233 | def opcodeSwap(ctx, info, numericPartOfName): 234 | a = ctx.stack.access_at_index((numericPartOfName + 1) * -1) 235 | b = ctx.stack.pop() 236 | ctx.stack.push(a) 237 | ctx.stack.set_at_index((numericPartOfName + 1) * -1, b) 238 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 239 | 240 | 241 | def opcodeInvalid(ctx, info): 242 | return OpcodeResponse(success=False, encounteredStop=False,returnData=None) 243 | 244 | 245 | def opcodePC(ctx, info): 246 | ctx.stack.push(ctx.pc) 247 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 248 | 249 | 250 | def opcodeGas(ctx, info): 251 | ctx.stack.push(UINT256MAX) 252 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 253 | 254 | 255 | def opcodeJump(ctx, info): 256 | a = ctx.stack.pop() 257 | if a in ctx.valid_jumpdests_set: 258 | ctx.set_pc(a) 259 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 260 | else: 261 | return OpcodeResponse(success=False, encounteredStop=False,returnData=None) 262 | 263 | 264 | def opcodeJumpIf(ctx, info): 265 | a = ctx.stack.pop() 266 | b = ctx.stack.pop() 267 | if b == 0: 268 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 269 | else: 270 | if a in ctx.valid_jumpdests_set: 271 | ctx.set_pc(a) 272 | return OpcodeResponse( 273 | success=True, encounteredStop=False,returnData=None) 274 | else: 275 | return OpcodeResponse( 276 | success=False, 277 | encounteredStop=False, 278 | data=None) 279 | 280 | 281 | def opcodeJumpDest(ctx, info): 282 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 283 | 284 | 285 | def opcodeMLoad(ctx, info): 286 | a = ctx.stack.pop() 287 | b = int(ctx.memory.load(a, 32).hex(), 16) 288 | ctx.stack.push(b) 289 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 290 | 291 | 292 | def opcodeMStore(ctx, info): 293 | a = ctx.stack.pop() 294 | b = hex(ctx.stack.pop())[2:] 295 | if len(b) % 2 == 1: 296 | b = "0" + b 297 | b = bytes.fromhex(b) 298 | 299 | size = len(b) 300 | b = bytes(32 - size) + b 301 | 302 | ctx.memory.store(a, b) 303 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 304 | 305 | 306 | def opcodeMStore8(ctx, info): 307 | a = ctx.stack.pop() 308 | b = hex(ctx.stack.pop())[2:] 309 | if len(b) % 2 == 1: 310 | b = "0" + b 311 | b = bytes.fromhex(b) 312 | 313 | size = len(b) 314 | 315 | if size == 1: 316 | ctx.memory.store(a, b) 317 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 318 | else: 319 | return OpcodeResponse(success=False, encounteredStop=False,returnData=None) 320 | 321 | 322 | def opcodeMSize(ctx, info): 323 | ctx.stack.push(ctx.memory.msize()) 324 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 325 | 326 | 327 | def opcodeSHA3(ctx, info): 328 | a = ctx.stack.pop() 329 | b = ctx.stack.pop() 330 | c = ctx.memory.load(a, b) 331 | ctx.stack.push(int(sha3.keccak_256(bytes(c)).digest().hex(), 16)) 332 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 333 | 334 | 335 | def opcodeAddress(ctx, info): 336 | a = int(info["tx"]["to"], 16) 337 | ctx.stack.push(a) 338 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 339 | 340 | 341 | def opcodeCaller(ctx, info): 342 | a = int(info["tx"]["from"], 16) 343 | ctx.stack.push(a) 344 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 345 | 346 | 347 | def opcodeOrigin(ctx, info): 348 | a = int(info["tx"]["origin"], 16) 349 | ctx.stack.push(a) 350 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 351 | 352 | 353 | def opcodeGasPrice(ctx, info): 354 | a = int(info["tx"]["gasprice"], 16) 355 | ctx.stack.push(a) 356 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 357 | 358 | 359 | def opcodeBaseFee(ctx, info): 360 | a = int(info["block"]["basefee"], 16) 361 | ctx.stack.push(a) 362 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 363 | 364 | 365 | def opcodeCoinBase(ctx, info): 366 | a = int(info["block"]["coinbase"], 16) 367 | ctx.stack.push(a) 368 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 369 | 370 | 371 | def opcodeTimeStamp(ctx, info): 372 | a = int(info["block"]["timestamp"], 16) 373 | ctx.stack.push(a) 374 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 375 | 376 | 377 | def opcodeNumber(ctx, info): 378 | a = int(info["block"]["number"], 16) 379 | ctx.stack.push(a) 380 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 381 | 382 | 383 | def opcodeDifficulty(ctx, info): 384 | a = int(info["block"]["difficulty"], 16) 385 | ctx.stack.push(a) 386 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 387 | 388 | 389 | def opcodeGasLimit(ctx, info): 390 | a = int(info["block"]["gaslimit"], 16) 391 | ctx.stack.push(a) 392 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 393 | 394 | 395 | def opcodeChainId(ctx, info): 396 | a = int(info["block"]["chainid"], 16) 397 | ctx.stack.push(a) 398 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 399 | 400 | 401 | def opcodeBlockHash(ctx, info): 402 | ctx.stack.push(0x0) 403 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 404 | 405 | 406 | def opcodeBalance(ctx, info): 407 | a = ctx.stack.pop() 408 | if not ctx.world_state.get(a): 409 | ctx.stack.push(0) 410 | else: 411 | 412 | b = ctx.world_state.get(a).getBalance() 413 | ctx.stack.push(b) 414 | 415 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 416 | 417 | 418 | def opcodeCallValue(ctx, info): 419 | a = int(info["tx"]["value"], 16) 420 | ctx.stack.push(a) 421 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 422 | 423 | 424 | def opcodeCallDataLoad(ctx, info): 425 | a = ctx.stack.pop() 426 | b = int(ctx.calldata.load(a, 32).hex(), 16) 427 | ctx.stack.push(b) 428 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 429 | 430 | 431 | def opcodeCallDataSize(ctx, info): 432 | a = ctx.calldata.size() 433 | ctx.stack.push(a) 434 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 435 | 436 | 437 | def opcodeCallDataCopy(ctx, info): 438 | a = ctx.stack.pop() 439 | b = ctx.stack.pop() 440 | c = ctx.stack.pop() 441 | calldata = ctx.calldata.load(b, c) 442 | ctx.memory.store(a, calldata) 443 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 444 | 445 | 446 | def opcodeCodeSize(ctx, info): 447 | a = len(ctx.code) 448 | ctx.stack.push(a) 449 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 450 | 451 | 452 | def opcodeCodeCopy(ctx, info): 453 | a = ctx.stack.pop() 454 | b = ctx.stack.pop() 455 | c = ctx.stack.pop() 456 | size = len(ctx.code) 457 | code = bytearray(ctx.code) 458 | if b + c > size: 459 | code.extend(bytes((b + c) - size)) 460 | 461 | ctx.memory.store(a, code[b:b + c]) 462 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 463 | 464 | 465 | def opcodeExtCodeSize(ctx, info): 466 | a = ctx.stack.pop() 467 | if not ctx.world_state.get(a): 468 | ctx.stack.push(0) 469 | else: 470 | ctx.stack.push(len(ctx.world_state.get(a).code)) 471 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 472 | 473 | 474 | def opcodeExtCodeCopy(ctx, info): 475 | a = ctx.stack.pop() 476 | b = ctx.stack.pop() 477 | c = ctx.stack.pop() 478 | d = ctx.stack.pop() 479 | if not ctx.world_state.get(a): 480 | ctx.memory.store(b, bytes()) 481 | else: 482 | size = len(ctx.world_state.get(a).code) 483 | code = bytearray(ctx.world_state.get(a).code) 484 | if c + d > size: 485 | code.extend(bytes((c + d) - size)) 486 | ctx.memory.store(b, code[c:c + d]) 487 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 488 | 489 | 490 | def opcodeExtCodeHash(ctx, info): 491 | a = ctx.stack.pop() 492 | if not ctx.world_state.get(a): 493 | ctx.stack.push(0) 494 | else: 495 | code = bytearray(ctx.world_state.get(a).code) 496 | ctx.stack.push(int(sha3.keccak_256(bytes(code)).digest().hex(), 16)) 497 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 498 | 499 | 500 | def opcodeSelfBalance(ctx, info): 501 | a = int(info["tx"]["to"], 16) 502 | if not ctx.world_state.get(a): 503 | ctx.stack.push(0) 504 | else: 505 | b = ctx.world_state.get(a).getBalance() 506 | ctx.stack.push(b) 507 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 508 | 509 | 510 | def opcodeSStore(ctx, info): 511 | a = ctx.stack.pop() 512 | b = ctx.stack.pop() 513 | ctx.storage.store(a, b) 514 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 515 | 516 | 517 | def opcodeSLoad(ctx, info): 518 | a = ctx.stack.pop() 519 | b = ctx.storage.load(a) 520 | ctx.stack.push(b) 521 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 522 | 523 | 524 | def opcodeLog(ctx, info, numericPartOfName): 525 | a = ctx.stack.pop() 526 | b = ctx.stack.pop() 527 | c = int(ctx.memory.load(a, b).hex(), 16) 528 | log = {} 529 | log["address"] = int(info["tx"]["to"], 16) 530 | log["data"] = c 531 | log["topics"] = [] 532 | for i in range(numericPartOfName): 533 | log["topics"].append(ctx.stack.pop()) 534 | ctx.logs.append(log) 535 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 536 | 537 | 538 | def opcodeReturn(ctx, info): 539 | a = ctx.stack.pop() 540 | b = ctx.stack.pop() 541 | c = int(ctx.memory.load(a, b).hex(), 16) 542 | return OpcodeResponse(success=True, encounteredStop=False,returnData=c) 543 | 544 | 545 | def opcodeRevert(ctx, info): 546 | a = ctx.stack.pop() 547 | b = ctx.stack.pop() 548 | c = int(ctx.memory.load(a, b).hex(), 16) 549 | return OpcodeResponse(success=False, encounteredStop=False,returnData=c) 550 | 551 | 552 | def opcodeCall(ctx, info): 553 | gas = ctx.stack.pop() 554 | address = ctx.stack.pop() 555 | value = ctx.stack.pop() 556 | if info["isStaticCall"] and value != 0: 557 | return OpcodeResponse(success=False, encounteredStop=False,returnData=None) 558 | argOffset = ctx.stack.pop() 559 | argSize = ctx.stack.pop() 560 | retOffset = ctx.stack.pop() 561 | retSize = ctx.stack.pop() 562 | new_info = copy.deepcopy(info) 563 | 564 | if info.get("tx") and info.get("tx").get("to"): 565 | new_info["tx"]["from"] = str(info["tx"]["to"]) 566 | if new_info.get("tx"): 567 | new_info["tx"]["to"] = hex(address)[2:] 568 | else: 569 | new_info["tx"] = {"to": hex(address)[2:]} 570 | 571 | code = ctx.world_state.get(address).code 572 | (success, stack, logs, returndata, after_execution_world_state) = evm( 573 | code = code, info = new_info, outputStackLen = 0, isStaticCall = False) 574 | 575 | if success: 576 | ctx.world_state = after_execution_world_state 577 | 578 | if returndata: 579 | returndata = hex(returndata)[2:] 580 | if len(returndata) % 2 == 1: 581 | returndata = "0" + returndata 582 | 583 | returndata = returndata[:retSize * 2] 584 | returndata = bytearray.fromhex(returndata) 585 | 586 | ctx.returndata.setreturndata(returndata) 587 | ctx.memory.store(retOffset, returndata) 588 | ctx.stack.push(int(success)) 589 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 590 | 591 | 592 | def opcodeReturnDataSize(ctx, info): 593 | a = ctx.returndata.size() 594 | ctx.stack.push(a) 595 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 596 | 597 | 598 | def opcodeReturnDataCopy(ctx, info): 599 | a = ctx.stack.pop() 600 | b = ctx.stack.pop() 601 | c = ctx.stack.pop() 602 | returndata = ctx.returndata.load(b, c) 603 | ctx.memory.store(a, returndata) 604 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 605 | 606 | 607 | def opcodeDelegateCall(ctx, info): 608 | gas = ctx.stack.pop() 609 | address = ctx.stack.pop() 610 | argOffset = ctx.stack.pop() 611 | argSize = ctx.stack.pop() 612 | retOffset = ctx.stack.pop() 613 | retSize = ctx.stack.pop() 614 | new_info = copy.deepcopy(info) 615 | 616 | code = ctx.world_state.get(address).code 617 | (success, stack, logs, returndata, after_execution_world_state) = evm( 618 | code = code, info = new_info, outputStackLen = 0, isStaticCall = False) 619 | 620 | if success: 621 | ctx.world_state = after_execution_world_state 622 | 623 | if returndata: 624 | returndata = hex(returndata)[2:] 625 | if len(returndata) % 2 == 1: 626 | returndata = "0" + returndata 627 | 628 | returndata = returndata[:retSize * 2] 629 | returndata = bytearray.fromhex(returndata) 630 | 631 | ctx.returndata.setreturndata(returndata) 632 | ctx.memory.store(retOffset, returndata) 633 | ctx.stack.push(int(success)) 634 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 635 | 636 | 637 | def opcodeStaticCall(ctx, info): 638 | gas = ctx.stack.pop() 639 | address = ctx.stack.pop() 640 | argOffset = ctx.stack.pop() 641 | argSize = ctx.stack.pop() 642 | retOffset = ctx.stack.pop() 643 | retSize = ctx.stack.pop() 644 | new_info = copy.deepcopy(info) 645 | 646 | if info.get("tx") and info.get("tx").get("to"): 647 | new_info["tx"]["from"] = str(info["tx"]["to"]) 648 | if new_info.get("tx"): 649 | new_info["tx"]["to"] = hex(address)[2:] 650 | else: 651 | new_info["tx"] = {"to": hex(address)[2:]} 652 | 653 | code = ctx.world_state.get(address).code 654 | (success, stack, logs, returndata, after_execution_world_state) = evm( 655 | code = code, info = new_info, outputStackLen = 0, isStaticCall = True) 656 | 657 | if success: 658 | ctx.world_state = after_execution_world_state 659 | if returndata: 660 | returndata = hex(returndata)[2:] 661 | if len(returndata) % 2 == 1: 662 | returndata = "0" + returndata 663 | 664 | returndata = returndata[:retSize * 2] 665 | returndata = bytearray.fromhex(returndata) 666 | 667 | ctx.returndata.setreturndata(returndata) 668 | ctx.memory.store(retOffset, returndata) 669 | ctx.stack.push(int(success)) 670 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 671 | 672 | 673 | def opcodeCreate(ctx, info): 674 | value = ctx.stack.pop() 675 | offset = ctx.stack.pop() 676 | size = ctx.stack.pop() 677 | 678 | current_address = info["tx"]["to"][2:] 679 | if ctx.world_state.get(current_address): 680 | nonce = ctx.world_state.get(current_address).nonce 681 | else: 682 | nonce = 0 683 | current_address = bytes.fromhex(current_address) 684 | 685 | nonce = hex(nonce)[2:] 686 | if len(nonce) % 2 == 1: 687 | nonce = "0" + nonce 688 | nonce = bytes.fromhex(nonce) 689 | contract_address = int( 690 | sha3.keccak_256( 691 | bytes( 692 | current_address + 693 | nonce)).digest().hex(), 694 | 16) 695 | 696 | code = ctx.memory.load(offset, size) 697 | (success, stack, logs, returndata, after_execution_world_state) = evm( 698 | code = code, info = info, outputStackLen = 0, isStaticCall = False) 699 | 700 | if not success: 701 | ctx.stack.push(0) 702 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 703 | 704 | if not returndata: 705 | returndata = bytes() 706 | else: 707 | returndata = bytes.fromhex(hex(returndata)[2:]) 708 | 709 | ctx.world_state.set( 710 | contract_address, 711 | Account( 712 | balance=value, 713 | code=returndata)) 714 | ctx.stack.push(contract_address) 715 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 716 | 717 | 718 | def opcodeSelfDestruct(ctx, info): 719 | a = ctx.stack.pop() 720 | current_address = int(info["tx"]["to"], 16) 721 | 722 | if ctx.world_state.get(a): 723 | ctx.world_state.get( 724 | a).balance += ctx.world_state.get(current_address).balance 725 | else: 726 | ctx.world_state.set( 727 | a, Account( 728 | balance=ctx.world_state.get(current_address).balance)) 729 | 730 | ctx.world_state.get(current_address).balance = 0 731 | ctx.world_state.get(current_address).code = bytes() 732 | return OpcodeResponse(success=True, encounteredStop=False,returnData=None) 733 | 734 | 735 | opcode = {} 736 | opcode[0x00] = OpcodeData(0x00, "STOP", opcodeStop) 737 | opcode[0x60] = OpcodeData(0x60, "PUSH1", opcodePush, 1) 738 | opcode[0x61] = OpcodeData(0x61, "PUSH2", opcodePush, 2) 739 | opcode[0x62] = OpcodeData(0x62, "PUSH3", opcodePush, 3) 740 | opcode[0x63] = OpcodeData(0x63, "PUSH4", opcodePush, 4) 741 | opcode[0x64] = OpcodeData(0x64, "PUSH5", opcodePush, 5) 742 | opcode[0x65] = OpcodeData(0x65, "PUSH6", opcodePush, 6) 743 | opcode[0x66] = OpcodeData(0x66, "PUSH7", opcodePush, 7) 744 | opcode[0x67] = OpcodeData(0x67, "PUSH8", opcodePush, 8) 745 | opcode[0x68] = OpcodeData(0x68, "PUSH9", opcodePush, 9) 746 | opcode[0x69] = OpcodeData(0x69, "PUSH10", opcodePush, 10) 747 | opcode[0x6A] = OpcodeData(0x6A, "PUSH11", opcodePush, 11) 748 | opcode[0x6B] = OpcodeData(0x6B, "PUSH12", opcodePush, 12) 749 | opcode[0x6C] = OpcodeData(0x6C, "PUSH13", opcodePush, 13) 750 | opcode[0x6D] = OpcodeData(0x6D, "PUSH14", opcodePush, 14) 751 | opcode[0x6E] = OpcodeData(0x6E, "PUSH15", opcodePush, 15) 752 | opcode[0x6F] = OpcodeData(0x6F, "PUSH16", opcodePush, 16) 753 | opcode[0x70] = OpcodeData(0x70, "PUSH17", opcodePush, 17) 754 | opcode[0x71] = OpcodeData(0x71, "PUSH18", opcodePush, 18) 755 | opcode[0x72] = OpcodeData(0x72, "PUSH19", opcodePush, 19) 756 | opcode[0x73] = OpcodeData(0x73, "PUSH20", opcodePush, 20) 757 | opcode[0x74] = OpcodeData(0x74, "PUSH21", opcodePush, 21) 758 | opcode[0x75] = OpcodeData(0x75, "PUSH22", opcodePush, 22) 759 | opcode[0x76] = OpcodeData(0x76, "PUSH23", opcodePush, 23) 760 | opcode[0x77] = OpcodeData(0x77, "PUSH24", opcodePush, 24) 761 | opcode[0x78] = OpcodeData(0x78, "PUSH25", opcodePush, 25) 762 | opcode[0x79] = OpcodeData(0x79, "PUSH26", opcodePush, 26) 763 | opcode[0x7A] = OpcodeData(0x7A, "PUSH27", opcodePush, 27) 764 | opcode[0x7B] = OpcodeData(0x7B, "PUSH28", opcodePush, 28) 765 | opcode[0x7C] = OpcodeData(0x7C, "PUSH29", opcodePush, 29) 766 | opcode[0x7D] = OpcodeData(0x7D, "PUSH30", opcodePush, 30) 767 | opcode[0x7E] = OpcodeData(0x7E, "PUSH31", opcodePush, 31) 768 | opcode[0x7F] = OpcodeData(0x7F, "PUSH32", opcodePush, 32) 769 | opcode[0x50] = OpcodeData(0x50, "POP", opcodePop) 770 | opcode[0x01] = OpcodeData(0x01, "ADD", opcodeAdd) 771 | opcode[0x02] = OpcodeData(0x02, "MUL", opcodeMul) 772 | opcode[0x03] = OpcodeData(0x03, "SUB", opcodeSub) 773 | opcode[0x04] = OpcodeData(0x04, "DIV", opcodeDiv) 774 | opcode[0x06] = OpcodeData(0x06, "MOD", opcodeMod) 775 | opcode[0x08] = OpcodeData(0x08, "MODADD", opcodeAddMod) 776 | opcode[0x09] = OpcodeData(0x09, "MODMUL", opcodeMulMod) 777 | opcode[0x0a] = OpcodeData(0xa, "EXP", opcodeExp) 778 | opcode[0x0b] = OpcodeData(0xa, "SIGNEXTEND", opcodeSignExtend) 779 | opcode[0x05] = OpcodeData(0x5, "SDIV", opcodeSDiv) 780 | opcode[0x07] = OpcodeData(0x7, "SMOD", opcodeSMod) 781 | opcode[0x10] = OpcodeData(0x10, "LT", opcodeLT) 782 | opcode[0x11] = OpcodeData(0x11, "GT", opcodeGT) 783 | opcode[0x12] = OpcodeData(0x12, "SLT", opcodeSLT) 784 | opcode[0x13] = OpcodeData(0x13, "SGT", opcodeSGT) 785 | opcode[0x14] = OpcodeData(0x14, "EQ", opcodeEQ) 786 | opcode[0x15] = OpcodeData(0x15, "ISZERO", opcodeIsZero) 787 | opcode[0x16] = OpcodeData(0x16, "AND", opcodeAnd) 788 | opcode[0x17] = OpcodeData(0x17, "OR", opcodeOR) 789 | opcode[0x18] = OpcodeData(0x18, "XOR", opcodeXOR) 790 | opcode[0x19] = OpcodeData(0x19, "NOT", opcodeNot) 791 | opcode[0x1b] = OpcodeData(0x1b, "SHL", opcodeSHL) 792 | opcode[0x1c] = OpcodeData(0x1c, "SHR", opcodeSHR) 793 | opcode[0x1d] = OpcodeData(0x1d, "SAR", opcodeSAR) 794 | opcode[0x1a] = OpcodeData(0x1a, "BYTE", opcodeByte) 795 | opcode[0x80] = OpcodeData(0x80, "DUP1", opcodeDup, 1) 796 | opcode[0x81] = OpcodeData(0x81, "DUP2", opcodeDup, 2) 797 | opcode[0x82] = OpcodeData(0x82, "DUP3", opcodeDup, 3) 798 | opcode[0x83] = OpcodeData(0x83, "DUP4", opcodeDup, 4) 799 | opcode[0x84] = OpcodeData(0x84, "DUP5", opcodeDup, 5) 800 | opcode[0x85] = OpcodeData(0x85, "DUP6", opcodeDup, 6) 801 | opcode[0x86] = OpcodeData(0x86, "DUP7", opcodeDup, 7) 802 | opcode[0x87] = OpcodeData(0x87, "DUP8", opcodeDup, 8) 803 | opcode[0x88] = OpcodeData(0x88, "DUP9", opcodeDup, 9) 804 | opcode[0x89] = OpcodeData(0x89, "DUP10", opcodeDup, 10) 805 | opcode[0x8A] = OpcodeData(0x8A, "DUP11", opcodeDup, 11) 806 | opcode[0x8B] = OpcodeData(0x8B, "DUP12", opcodeDup, 12) 807 | opcode[0x8C] = OpcodeData(0x8C, "DUP13", opcodeDup, 13) 808 | opcode[0x8D] = OpcodeData(0x8D, "DUP14", opcodeDup, 14) 809 | opcode[0x8E] = OpcodeData(0x8E, "DUP15", opcodeDup, 15) 810 | opcode[0x8F] = OpcodeData(0x8F, "DUP16", opcodeDup, 16) 811 | opcode[0x90] = OpcodeData(0x90, "SWAP1", opcodeSwap, 1) 812 | opcode[0x91] = OpcodeData(0x91, "SWAP2", opcodeSwap, 2) 813 | opcode[0x92] = OpcodeData(0x92, "SWAP3", opcodeSwap, 3) 814 | opcode[0x93] = OpcodeData(0x93, "SWAP4", opcodeSwap, 4) 815 | opcode[0x94] = OpcodeData(0x94, "SWAP5", opcodeSwap, 5) 816 | opcode[0x95] = OpcodeData(0x95, "SWAP6", opcodeSwap, 6) 817 | opcode[0x96] = OpcodeData(0x96, "SWAP7", opcodeSwap, 7) 818 | opcode[0x97] = OpcodeData(0x97, "SWAP8", opcodeSwap, 8) 819 | opcode[0x98] = OpcodeData(0x98, "SWAP9", opcodeSwap, 9) 820 | opcode[0x99] = OpcodeData(0x99, "SWAP10", opcodeSwap, 10) 821 | opcode[0x9A] = OpcodeData(0x9A, "SWAP11", opcodeSwap, 11) 822 | opcode[0x9B] = OpcodeData(0x9B, "SWAP12", opcodeSwap, 12) 823 | opcode[0x9C] = OpcodeData(0x9C, "SWAP13", opcodeSwap, 13) 824 | opcode[0x9D] = OpcodeData(0x9D, "SWAP14", opcodeSwap, 14) 825 | opcode[0x9E] = OpcodeData(0x9E, "SWAP15", opcodeSwap, 15) 826 | opcode[0x9F] = OpcodeData(0x9F, "SWAP16", opcodeSwap, 16) 827 | opcode[0xfe] = OpcodeData(0xfe, "INVALID", opcodeInvalid) 828 | opcode[0x58] = OpcodeData(0x58, "PC", opcodePC) 829 | opcode[0x5a] = OpcodeData(0x5a, "GAS", opcodeGas) 830 | opcode[0x56] = OpcodeData(0x56, "JUMP", opcodeJump) 831 | opcode[0x57] = OpcodeData(0x57, "JUMPI", opcodeJumpIf) 832 | opcode[0x5b] = OpcodeData(0x57, "JUMPDEST", opcodeJumpDest) 833 | opcode[0x51] = OpcodeData(0x51, "MLOAD", opcodeMLoad) 834 | opcode[0x52] = OpcodeData(0x52, "MSTORE", opcodeMStore) 835 | opcode[0x53] = OpcodeData(0x53, "MSTORE8", opcodeMStore8) 836 | opcode[0x59] = OpcodeData(0x59, "MSIZE", opcodeMSize) 837 | opcode[0x20] = OpcodeData(0x20, "SHA3", opcodeSHA3) 838 | opcode[0x30] = OpcodeData(0x30, "ADDRESS", opcodeAddress) 839 | opcode[0x33] = OpcodeData(0x33, "CALLER", opcodeCaller) 840 | opcode[0x32] = OpcodeData(0x32, "ORIGIN", opcodeOrigin) 841 | opcode[0x3a] = OpcodeData(0x99, "GASPRICE", opcodeGasPrice) 842 | opcode[0x48] = OpcodeData(0x48, "BASEFEE", opcodeBaseFee) 843 | opcode[0x41] = OpcodeData(0x41, "COINBASE", opcodeCoinBase) 844 | opcode[0x42] = OpcodeData(0x42, "TIMESTAMP", opcodeTimeStamp) 845 | opcode[0x43] = OpcodeData(0x43, "NUMBER", opcodeNumber) 846 | opcode[0x44] = OpcodeData(0x44, "DIFFICULTY", opcodeDifficulty) 847 | opcode[0x45] = OpcodeData(0x45, "GASLIMIT", opcodeGasLimit) 848 | opcode[0x46] = OpcodeData(0x46, "CHAINID", opcodeChainId) 849 | opcode[0x40] = OpcodeData(0x40, "BLOCKHASH", opcodeBlockHash) 850 | opcode[0x31] = OpcodeData(0x31, "BALANCE", opcodeBalance) 851 | opcode[0x34] = OpcodeData(0x34, "CALLVALUE", opcodeCallValue) 852 | opcode[0x35] = OpcodeData(0x35, "CALLDATALOAD", opcodeCallDataLoad) 853 | opcode[0x36] = OpcodeData(0x36, "CALLDATASIZE", opcodeCallDataSize) 854 | opcode[0x37] = OpcodeData(0x37, "CALLDATACOPY", opcodeCallDataCopy) 855 | opcode[0x38] = OpcodeData(0x38, "CODESIZE", opcodeCodeSize) 856 | opcode[0x39] = OpcodeData(0x39, "CODECOPY", opcodeCodeCopy) 857 | opcode[0x3b] = OpcodeData(0x3b, "EXTCODESIZE", opcodeExtCodeSize) 858 | opcode[0x3c] = OpcodeData(0x3c, "EXTCODECOPY", opcodeExtCodeCopy) 859 | opcode[0x3f] = OpcodeData(0x3f, "EXTCODEHASH", opcodeExtCodeHash) 860 | opcode[0x47] = OpcodeData(0x47, "SELFBALANCE", opcodeSelfBalance) 861 | opcode[0x54] = OpcodeData(0x54, "SLOAD", opcodeSLoad) 862 | opcode[0x55] = OpcodeData(0x55, "SSTORE", opcodeSStore) 863 | opcode[0xa0] = OpcodeData(0xa0, "LOG0", opcodeLog, 0) 864 | opcode[0xa1] = OpcodeData(0xa1, "LOG1", opcodeLog, 1) 865 | opcode[0xa2] = OpcodeData(0xa2, "LOG2", opcodeLog, 2) 866 | opcode[0xa3] = OpcodeData(0xa3, "LOG3", opcodeLog, 3) 867 | opcode[0xa4] = OpcodeData(0xa4, "LOG4", opcodeLog, 4) 868 | opcode[0xf3] = OpcodeData(0xf3, "RETURN", opcodeReturn) 869 | opcode[0xfd] = OpcodeData(0xfd, "REVERT", opcodeRevert) 870 | opcode[0xf1] = OpcodeData(0xf1, "CALL", opcodeCall) 871 | opcode[0x3d] = OpcodeData(0x3d, "RETURNDATASIZE", opcodeReturnDataSize) 872 | opcode[0x3e] = OpcodeData(0x3e, "RETURNDATACOPY", opcodeReturnDataCopy) 873 | opcode[0xf4] = OpcodeData(0xf4, "DELEGATECALL", opcodeDelegateCall) 874 | opcode[0xfa] = OpcodeData(0xfa, "STATICCALL", opcodeStaticCall) 875 | opcode[0xf0] = OpcodeData(0xfa, "CREATE", opcodeCreate) 876 | opcode[0xff] = OpcodeData(0xff, "SELFDESTRUCT", opcodeSelfDestruct) 877 | 878 | 879 | def evm(code, info, outputStackLen, isStaticCall): 880 | 881 | opcodeReturn = OpcodeResponse(True, False, None) 882 | info["isStaticCall"] = False 883 | account_states = dict() 884 | 885 | if info.get("state"): 886 | state_info = info.get("state") 887 | for address in state_info.keys(): 888 | state_code = bytes() 889 | if state_info.get(address).get("code") and state_info.get( 890 | address).get("code").get("bin"): 891 | state_code = bytes.fromhex( 892 | state_info.get(address).get("code").get("bin")) 893 | account_tuple = Account( 894 | state_info.get(address).get("nonce"), 895 | state_info.get(address).get("balance"), 896 | state_info.get(address).get("storage"), 897 | state_code 898 | ) 899 | account_states[int(address, 16)] = account_tuple 900 | 901 | world_state = WorldState(account_states) 902 | calldata = bytes() 903 | if info.get("tx") and info.get("tx").get("data"): 904 | calldata = bytes.fromhex(info.get("tx").get("data")) 905 | ctx = Context(world_state=world_state, code=code, calldata=calldata) 906 | 907 | while ctx.pc < len(code): 908 | op = code[ctx.pc] 909 | if isStaticCall: 910 | info["isStaticCall"] = True 911 | if op in STATICCALL_DISSALOWED_OPCODES: 912 | opcodeReturn.success = False 913 | break 914 | # pc will always increment by 1 here 915 | # pc can also be incremented in PUSH opcodes 916 | opcodeObj = opcode.get(op) 917 | if opcodeObj: 918 | print( 919 | f'\033[0;39mRunning opcode {hex(opcodeObj.opcode)} {opcodeObj.name}') 920 | 921 | if opcodeObj.numericPartOfName is None: 922 | opcodeReturn = opcodeObj.run(ctx, info) 923 | else: 924 | opcodeReturn = opcodeObj.run( 925 | ctx, info, opcodeObj.numericPartOfName) 926 | if opcodeReturn.encounteredStop: 927 | break 928 | if not opcodeReturn.success: 929 | break 930 | else: 931 | print("Opcode implementation not found for ", hex(op)) 932 | # return fake success but empty stack so that test case 933 | # panics with proper test name and error message 934 | return (True, [], [], None, WorldState()) 935 | ctx.pc += 1 936 | 937 | result = [] 938 | logs = ctx.logs 939 | returndata = opcodeReturn.returnData 940 | 941 | if not opcodeReturn.success: 942 | return (opcodeReturn.success, result, logs, returndata, world_state) 943 | 944 | if len(ctx.stack.list): 945 | if outputStackLen > 1: 946 | # output format is different if output stack is greater than 2 947 | # check evm.json for more details. 948 | while len(ctx.stack.list): 949 | result.append(ctx.stack.pop()) 950 | else: 951 | tempList = [f'{i:x}' for i in ctx.stack.list] 952 | result.append(int(''.join(tempList), 16)) 953 | return (opcodeReturn.success, result, logs, returndata, world_state) 954 | 955 | 956 | def test(): 957 | script_dirname = os.path.dirname(os.path.abspath(__file__)) 958 | json_file = os.path.join(script_dirname, "..", "test", "tests.json") 959 | with open(json_file) as f: 960 | data = json.load(f) 961 | total = len(data) 962 | 963 | for i, test in enumerate(data): 964 | # Note: as the test cases get more complex, you'll need to modify this 965 | # to pass down more arguments to the evm function 966 | code = bytes.fromhex(test['code']['bin']) 967 | info = test 968 | expected_stack = test['expect'].get('stack', []) 969 | expected_logs = test['expect'].get('logs', []) 970 | expected_return = test['expect'].get('return', None) 971 | 972 | (success, stack, logs, returndata, after_execution_world_state) = evm( 973 | code = code, info = info, outputStackLen = len(expected_stack), isStaticCall = False) 974 | expected_stack = [int(x, 16) for x in expected_stack] 975 | if len(expected_logs) > 0: 976 | for log in expected_logs: 977 | log["address"] = int(log["address"], 16) 978 | log["data"] = int(log["data"], 16) 979 | log["topics"] = [int(x, 16) for x in log["topics"]] if len( 980 | log["topics"]) > 0 else [] 981 | 982 | if expected_return: 983 | expected_return = int(expected_return, 16) 984 | 985 | if stack != expected_stack or success != test['expect'][ 986 | 'success'] or logs != expected_logs or returndata != expected_return: 987 | print(f"❌ Test #{i + 1}/{total} {test['name']}") 988 | if stack != expected_stack: 989 | print("Stack doesn't match") 990 | print(" expected:", expected_stack) 991 | print(" actual:", stack) 992 | if logs != expected_logs: 993 | print("Logs don't match") 994 | print(" expected: " + str(expected_logs)) 995 | print(" actual: " + str(logs)) 996 | 997 | if success != test['expect']['success']: 998 | print("Success doesn't match") 999 | print(" expected:", test['expect']['success']) 1000 | print(" actual:", success) 1001 | 1002 | if returndata != expected_return: 1003 | print("Returndata doesn't match") 1004 | print(" expected:", expected_return) 1005 | print(" actual:", returndata) 1006 | 1007 | print("") 1008 | print("Test code:") 1009 | print(test['code']['asm']) 1010 | print("") 1011 | print("Hint:", test['hint']) 1012 | print("") 1013 | print(f"Progress: {i}/{len(data)}") 1014 | print("") 1015 | break 1016 | else: 1017 | print(f"\033[1;32m✓ Test #{i + 1}/{total} {test['name']}") 1018 | print("") 1019 | 1020 | 1021 | if __name__ == '__main__': 1022 | test() -------------------------------------------------------------------------------- /src/lib.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | 4 | ############# 5 | # CONSTANTS # 6 | ############# 7 | 8 | UINT256MAX = (2 ** 256) - 1 9 | # CREATE, CREATE2, LOG0, LOG1, LOG2, LOG3, LOG4, SSTORE, SELFDESTRUCT 10 | # Check https://www.evm.codes/#fa for reference 11 | STATICCALL_DISSALOWED_OPCODES = ( 12 | 0xa0, 13 | 0xa1, 14 | 0xa2, 15 | 0xa3, 16 | 0xa4, 17 | 0xf0, 18 | 0xf5, 19 | 0x55, 20 | 0xff) 21 | 22 | 23 | ########### 24 | # CLASSES # 25 | ########### 26 | 27 | class WorldState: 28 | def __init__(self, initial_state=dict()): 29 | self.accounts = initial_state 30 | 31 | def get(self, address): 32 | return self.accounts.get(address) 33 | 34 | def set(self, address, account): 35 | self.accounts[address] = account 36 | 37 | 38 | class Account: 39 | def __init__(self, nonce=0, balance=0, storage=None, code=bytes()): 40 | self.nonce = nonce 41 | self.balance = balance 42 | self.storage = storage if storage else Storage() 43 | self.code = code 44 | 45 | def getNonce(self): 46 | return int(self.nonce, 16) 47 | 48 | def getBalance(self): 49 | if isinstance(self.balance, int): 50 | return self.balance 51 | else: 52 | return int(self.balance, 16) 53 | 54 | def is_empty(self): 55 | return self.nonce == 0 and self.balance == 0 and self.code == b"" 56 | 57 | 58 | class Storage: 59 | def __init__(self, init=dict()): 60 | self.data = init 61 | 62 | def load(self, slot): 63 | return self.data.get(slot, 0) 64 | 65 | def store(self, slot, value): 66 | self.data[slot] = value 67 | 68 | 69 | class Memory: 70 | def __init__(self): 71 | self.array = bytearray() 72 | 73 | def ceildiv(self, a, b): 74 | return -(a // -b) 75 | 76 | def msize(self): 77 | assert (len(self.array) % 32 == 0) 78 | return len(self.array) 79 | 80 | def expand_memory_if_required(self, offset, size): 81 | active_words = self.msize() // 32 82 | new_words = self.ceildiv((offset + size), 32) 83 | 84 | if new_words - active_words > 0: 85 | self.array.extend(bytes(32 * (new_words - active_words))) 86 | 87 | def store(self, offset, value): 88 | self.expand_memory_if_required(offset, len(value)) 89 | self.array[offset:offset + len(value)] = value 90 | 91 | def load(self, offset, size): 92 | self.expand_memory_if_required(offset, size) 93 | a = bytearray() 94 | for i in range(offset, offset + size): 95 | b = hex(self.array[i])[2:] 96 | if len(b) % 2 == 1: 97 | b = "0" + b 98 | a += bytes.fromhex(b) 99 | return a 100 | 101 | 102 | class Stack: 103 | def __init__(self, size=1024): 104 | self.list = [] 105 | self.maxSize = size 106 | 107 | # Push max 32 bytes 108 | def push(self, item): 109 | if item > UINT256MAX: 110 | # TODO: handle error 111 | return False 112 | self.list.append(item) 113 | 114 | def pop(self): 115 | return self.list.pop() 116 | 117 | def access_at_index(self, index): 118 | return self.list[index] 119 | 120 | def set_at_index(self, index, item): 121 | self.list[index] = item 122 | 123 | 124 | class Calldata: 125 | def __init__(self, data=bytes()) -> None: 126 | self.data = data 127 | 128 | def size(self): 129 | return len(self.data) 130 | 131 | def load(self, offset, size): 132 | a = bytearray() 133 | for i in range(offset, len(self.data)): 134 | b = hex(self.data[i])[2:] 135 | if len(b) % 2 == 1: 136 | b = "0" + b 137 | a += bytes.fromhex(b) 138 | 139 | if len(a) < size: 140 | a.extend(bytes(size - len(a))) 141 | return a 142 | 143 | 144 | class Returndata: 145 | def __init__(self, data=bytes()) -> None: 146 | self.data = data 147 | 148 | def size(self): 149 | return len(self.data) 150 | 151 | def setreturndata(self, data): 152 | if data is None: 153 | self.data = bytes() 154 | else: 155 | self.data = data 156 | 157 | def load(self, offset, size): 158 | a = bytearray() 159 | for i in range(offset, len(self.data)): 160 | b = hex(self.data[i])[2:] 161 | if len(b) % 2 == 1: 162 | b = "0" + b 163 | a += bytes.fromhex(b) 164 | 165 | if len(a) < size: 166 | a.extend(bytes(size - len(a))) 167 | return a 168 | 169 | 170 | class Context: 171 | def __init__( 172 | self, 173 | world_state=dict(), 174 | code=bytes(), 175 | pc=0, 176 | stack=None, 177 | calldata=None, 178 | ): 179 | 180 | self.world_state = world_state 181 | self.stack = stack if stack else Stack(1024) 182 | self.memory = Memory() 183 | self.code = code 184 | self.pc = pc 185 | self.valid_jumpdests_set = self.valid_jumpdests(code) 186 | self.calldata = Calldata(calldata) 187 | self.storage = Storage() 188 | self.logs = [] 189 | self.returndata = Returndata() 190 | 191 | def set_pc(self, pc): 192 | self.pc = pc 193 | 194 | def valid_jumpdests(self, code): 195 | valid_dests = set() 196 | pc = 0 197 | while pc < len(code): 198 | op = code[pc] 199 | if op >= 0x60 and op < 0x70: 200 | pc += op - 0x60 + 1 201 | if pc < len(code) and op == 0x5b: 202 | valid_dests.add(pc) 203 | pc += 1 204 | 205 | return valid_dests 206 | 207 | 208 | @dataclass 209 | class OpcodeResponse: 210 | success: bool # whether current execution completed successfully 211 | encounteredStop: bool # stop will be True for stop opcode 212 | returnData: int # pop, return etc. opcodes return data 213 | 214 | 215 | class OpcodeData: 216 | def __init__(self, opcode, name, run, numericPartOfName=None): 217 | self.opcode = opcode 218 | self.name = name 219 | self.run = run # function pointer 220 | # if opcode like push4 or dup2 or swap5 221 | self.numericPartOfName = numericPartOfName 222 | 223 | 224 | ############# 225 | # FUNCTIONS # 226 | ############# 227 | 228 | def unsigned_to_signed(n): 229 | if (n >> 255) != 0: 230 | return -(UINT256MAX + 1 - (n & UINT256MAX)) 231 | return n & UINT256MAX 232 | 233 | 234 | def signed_to_unsigned(n): 235 | if n < 0: 236 | n = UINT256MAX + n + 1 237 | return n & UINT256MAX 238 | -------------------------------------------------------------------------------- /test/tests.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": "PUSH (twice)", 114 | "hint": "Note the order of items on the stack. The tests expect the top of the stack to be the first element", 115 | "code": { 116 | "asm": "PUSH1 1\nPUSH1 2", 117 | "bin": "60016002" 118 | }, 119 | "expect": { 120 | "stack": [ 121 | "0x2", 122 | "0x1" 123 | ], 124 | "success": true 125 | } 126 | }, 127 | { 128 | "name": "POP", 129 | "hint": "POP removes the top item from the stack and discards it", 130 | "code": { 131 | "asm": "PUSH1 1\nPUSH1 2\nPOP", 132 | "bin": "6001600250" 133 | }, 134 | "expect": { 135 | "stack": [ 136 | "0x1" 137 | ], 138 | "success": true 139 | } 140 | }, 141 | { 142 | "name": "STOP (midway)", 143 | "hint": "Note that the `PUSH1 2` didn't execute because the program stops after STOP opcode", 144 | "code": { 145 | "asm": "PUSH1 1\nSTOP\nPUSH1 2", 146 | "bin": "6001006002" 147 | }, 148 | "expect": { 149 | "stack": [ 150 | "0x1" 151 | ], 152 | "success": true 153 | } 154 | }, 155 | { 156 | "name": "ADD", 157 | "hint": "ADD takes the first 2 items from the stack, adds them together and pushes the result", 158 | "code": { 159 | "asm": "PUSH1 0x01\nPUSH1 0x02\nADD", 160 | "bin": "6001600201" 161 | }, 162 | "expect": { 163 | "stack": [ 164 | "0x3" 165 | ], 166 | "success": true 167 | } 168 | }, 169 | { 170 | "name": "ADD (overflow)", 171 | "hint": "EVM operates with uint256, if you add 2 to the max possible value it overflows and wraps around", 172 | "code": { 173 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0x02\nADD", 174 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600201" 175 | }, 176 | "expect": { 177 | "stack": [ 178 | "0x1" 179 | ], 180 | "success": true 181 | } 182 | }, 183 | { 184 | "name": "MUL", 185 | "code": { 186 | "asm": "PUSH1 0x02\nPUSH1 0x03\nMUL", 187 | "bin": "6002600302" 188 | }, 189 | "expect": { 190 | "stack": [ 191 | "0x6" 192 | ], 193 | "success": true 194 | }, 195 | "hint": "" 196 | }, 197 | { 198 | "name": "MUL (overflow)", 199 | "hint": "All math is performed with implicit [mod 2^256]", 200 | "code": { 201 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0x02\nMUL", 202 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600202" 203 | }, 204 | "expect": { 205 | "stack": [ 206 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" 207 | ], 208 | "success": true 209 | } 210 | }, 211 | { 212 | "name": "SUB", 213 | "hint": "SUB takes the first element from the stack and subtracts the second element from the stack", 214 | "code": { 215 | "asm": "PUSH1 0x02\nPUSH1 0x03\nSUB", 216 | "bin": "6002600303" 217 | }, 218 | "expect": { 219 | "stack": [ 220 | "0x1" 221 | ], 222 | "success": true 223 | } 224 | }, 225 | { 226 | "name": "SUB (underflow)", 227 | "hint": "Underflow works the same way as overflow, 3 - 2 wraps around and results in MAX_UINT256", 228 | "code": { 229 | "asm": "PUSH1 0x03\nPUSH1 0x02\nSUB", 230 | "bin": "6003600203" 231 | }, 232 | "expect": { 233 | "stack": [ 234 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 235 | ], 236 | "success": true 237 | } 238 | }, 239 | { 240 | "name": "DIV", 241 | "hint": "DIV takes the first element from the stack and divides it by the second element from the stack", 242 | "code": { 243 | "asm": "PUSH1 0x02\nPUSH1 0x06\nDIV", 244 | "bin": "6002600604" 245 | }, 246 | "expect": { 247 | "stack": [ 248 | "0x3" 249 | ], 250 | "success": true 251 | } 252 | }, 253 | { 254 | "name": "DIV (whole)", 255 | "hint": "Fraction part of the division is discarded", 256 | "code": { 257 | "asm": "PUSH1 0x06\nPUSH1 0x02\nDIV", 258 | "bin": "6006600204" 259 | }, 260 | "expect": { 261 | "stack": [ 262 | "0x0" 263 | ], 264 | "success": true 265 | } 266 | }, 267 | { 268 | "name": "DIV (by zero)", 269 | "hint": "In EVM you can divide by zero! Modern Solidity protects from this by adding instructions that check for zero", 270 | "code": { 271 | "asm": "PUSH1 0x00\nPUSH1 0x02\nDIV", 272 | "bin": "6000600204" 273 | }, 274 | "expect": { 275 | "stack": [ 276 | "0x0" 277 | ], 278 | "success": true 279 | } 280 | }, 281 | { 282 | "name": "MOD", 283 | "hint": "10 mod 3 = 1", 284 | "code": { 285 | "asm": "PUSH1 3\nPUSH1 10\nMOD", 286 | "bin": "6003600a06" 287 | }, 288 | "expect": { 289 | "stack": [ 290 | "0x1" 291 | ], 292 | "success": true 293 | } 294 | }, 295 | { 296 | "name": "MOD (by larger number)", 297 | "hint": "5 mod 17 = 5", 298 | "code": { 299 | "asm": "PUSH1 17\nPUSH1 5\nMOD", 300 | "bin": "6011600506" 301 | }, 302 | "expect": { 303 | "stack": [ 304 | "0x5" 305 | ], 306 | "success": true 307 | } 308 | }, 309 | { 310 | "name": "MOD (by zero)", 311 | "hint": "In EVM you can divide by zero! Modern Solidity protects from this by adding instructions that check for zero", 312 | "code": { 313 | "asm": "PUSH1 0\nPUSH1 2\nMOD", 314 | "bin": "6000600206" 315 | }, 316 | "expect": { 317 | "stack": [ 318 | "0x0" 319 | ], 320 | "success": true 321 | } 322 | }, 323 | { 324 | "name": "ADDMOD", 325 | "hint": "10 + 10 mod 8 = 4", 326 | "code": { 327 | "asm": "PUSH1 8\nPUSH1 10\nPUSH1 10\nADDMOD", 328 | "bin": "6008600a600a08" 329 | }, 330 | "expect": { 331 | "stack": [ 332 | "0x4" 333 | ], 334 | "success": true 335 | } 336 | }, 337 | { 338 | "name": "ADDMOD (wrapped)", 339 | "code": { 340 | "asm": "PUSH1 2\nPUSH1 2\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nADDMOD", 341 | "bin": "600260027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08" 342 | }, 343 | "expect": { 344 | "stack": [ 345 | "0x1" 346 | ], 347 | "success": true 348 | }, 349 | "hint": "" 350 | }, 351 | { 352 | "name": "MULMOD", 353 | "hint": "10 * 10 mod 8 = 4", 354 | "code": { 355 | "asm": "PUSH1 8\nPUSH1 10\nPUSH1 10\nMULMOD", 356 | "bin": "6008600a600a09" 357 | }, 358 | "expect": { 359 | "stack": [ 360 | "0x4" 361 | ], 362 | "success": true 363 | } 364 | }, 365 | { 366 | "name": "MULMOD (wrapped)", 367 | "code": { 368 | "asm": "PUSH1 12\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nPUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nMULMOD", 369 | "bin": "600c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff09" 370 | }, 371 | "expect": { 372 | "stack": [ 373 | "0x9" 374 | ], 375 | "success": true 376 | }, 377 | "hint": "" 378 | }, 379 | { 380 | "name": "EXP", 381 | "code": { 382 | "asm": "PUSH1 2\nPUSH1 10\nEXP", 383 | "bin": "6002600a0a" 384 | }, 385 | "expect": { 386 | "stack": [ 387 | "0x64" 388 | ], 389 | "success": true 390 | }, 391 | "hint": "" 392 | }, 393 | { 394 | "name": "SIGNEXTEND (positive)", 395 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SIGNEXTEND has no effect on \"positive\" numbers", 396 | "code": { 397 | "asm": "PUSH1 0x7F\nPUSH1 0\nSIGNEXTEND", 398 | "bin": "607f60000b" 399 | }, 400 | "expect": { 401 | "stack": [ 402 | "0x7f" 403 | ], 404 | "success": true 405 | } 406 | }, 407 | { 408 | "name": "SIGNEXTEND (negative)", 409 | "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", 410 | "code": { 411 | "asm": "PUSH1 0xFF\nPUSH1 0\nSIGNEXTEND", 412 | "bin": "60ff60000b" 413 | }, 414 | "expect": { 415 | "stack": [ 416 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 417 | ], 418 | "success": true 419 | } 420 | }, 421 | { 422 | "name": "SDIV", 423 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SDIV works like DIV for \"positive\" numbers", 424 | "code": { 425 | "asm": "PUSH1 10\nPUSH1 10\nSDIV", 426 | "bin": "600a600a05" 427 | }, 428 | "expect": { 429 | "stack": [ 430 | "0x1" 431 | ], 432 | "success": true 433 | } 434 | }, 435 | { 436 | "name": "SDIV (negative)", 437 | "hint": "Read \"Negative Numbers\" section of the course learning materials. -2 / -1 = 2", 438 | "code": { 439 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSDIV", 440 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe05" 441 | }, 442 | "expect": { 443 | "stack": [ 444 | "0x2" 445 | ], 446 | "success": true 447 | } 448 | }, 449 | { 450 | "name": "SDIV (mix of negative and positive)", 451 | "hint": "Read \"Negative Numbers\" section of the course learning materials. 10 / -2 = -5", 452 | "code": { 453 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nPUSH1 10\nSDIV", 454 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe600a05" 455 | }, 456 | "expect": { 457 | "stack": [ 458 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb" 459 | ], 460 | "success": true 461 | } 462 | }, 463 | { 464 | "name": "SMOD", 465 | "hint": "Read \"Negative Numbers\" section of the course learning materials. SMOD works like MOD for \"positive\" numbers", 466 | "code": { 467 | "asm": "PUSH1 3\nPUSH1 10\nSMOD", 468 | "bin": "6003600a07" 469 | }, 470 | "expect": { 471 | "stack": [ 472 | "0x1" 473 | ], 474 | "success": true 475 | } 476 | }, 477 | { 478 | "name": "SMOD (negative)", 479 | "hint": "Read \"Negative Numbers\" section of the course learning materials. -10 mod -3 = -1", 480 | "code": { 481 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8\nSMOD", 482 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff807" 483 | }, 484 | "expect": { 485 | "stack": [ 486 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" 487 | ], 488 | "success": true 489 | } 490 | }, 491 | { 492 | "name": "SDIV (by zero)", 493 | "hint": "In EVM you can divide by zero", 494 | "code": { 495 | "asm": "PUSH1 0x00\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nSDIV", 496 | "bin": "60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd05" 497 | }, 498 | "expect": { 499 | "stack": [ 500 | "0x0" 501 | ], 502 | "success": true 503 | } 504 | }, 505 | { 506 | "name": "SMOD (by zero)", 507 | "hint": "In EVM you can divide by zero", 508 | "code": { 509 | "asm": "PUSH1 0x00\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nSMOD", 510 | "bin": "60007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd07" 511 | }, 512 | "expect": { 513 | "stack": [ 514 | "0x0" 515 | ], 516 | "success": true 517 | } 518 | }, 519 | { 520 | "name": "LT", 521 | "hint": "9 < 10 = true (1)", 522 | "code": { 523 | "asm": "PUSH1 10\nPUSH1 9\nLT", 524 | "bin": "600a600910" 525 | }, 526 | "expect": { 527 | "stack": [ 528 | "0x1" 529 | ], 530 | "success": true 531 | } 532 | }, 533 | { 534 | "name": "LT (equal)", 535 | "hint": "10 < 10 = false (0)", 536 | "code": { 537 | "asm": "PUSH1 10\nPUSH1 10\nLT", 538 | "bin": "600a600a10" 539 | }, 540 | "expect": { 541 | "stack": [ 542 | "0x0" 543 | ], 544 | "success": true 545 | } 546 | }, 547 | { 548 | "name": "LT (greater)", 549 | "hint": "11 < 10 = false (0)", 550 | "code": { 551 | "asm": "PUSH1 10\nPUSH1 11\nLT", 552 | "bin": "600a600b10" 553 | }, 554 | "expect": { 555 | "stack": [ 556 | "0x0" 557 | ], 558 | "success": true 559 | } 560 | }, 561 | { 562 | "name": "GT", 563 | "hint": "10 > 9 = true (1)", 564 | "code": { 565 | "asm": "PUSH1 9\nPUSH1 10\nGT", 566 | "bin": "6009600a11" 567 | }, 568 | "expect": { 569 | "stack": [ 570 | "0x1" 571 | ], 572 | "success": true 573 | } 574 | }, 575 | { 576 | "name": "GT (equal)", 577 | "hint": "10 > 10 = false (0)", 578 | "code": { 579 | "asm": "PUSH1 10\nPUSH1 10\nGT", 580 | "bin": "600a600a11" 581 | }, 582 | "expect": { 583 | "stack": [ 584 | "0x0" 585 | ], 586 | "success": true 587 | } 588 | }, 589 | { 590 | "name": "GT (less)", 591 | "hint": "10 > 11 = false (0)", 592 | "code": { 593 | "asm": "PUSH1 11\nPUSH1 10\nGT", 594 | "bin": "600b600a11" 595 | }, 596 | "expect": { 597 | "stack": [ 598 | "0x0" 599 | ], 600 | "success": true 601 | } 602 | }, 603 | { 604 | "name": "SLT", 605 | "hint": "Same as LT but treats arguments as signed numbers. -1 < 0 = true (1)", 606 | "code": { 607 | "asm": "PUSH1 0\nPUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nSLT", 608 | "bin": "60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12" 609 | }, 610 | "expect": { 611 | "stack": [ 612 | "0x1" 613 | ], 614 | "success": true 615 | } 616 | }, 617 | { 618 | "name": "SLT (equal)", 619 | "hint": "Same as LT but treats arguments as signed numbers. -1 < -1 = false (0)", 620 | "code": { 621 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nSLT", 622 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff12" 623 | }, 624 | "expect": { 625 | "stack": [ 626 | "0x0" 627 | ], 628 | "success": true 629 | } 630 | }, 631 | { 632 | "name": "SLT (greater)", 633 | "hint": "Same as LT but treats arguments as signed numbers. -1 < -1 = false (0)", 634 | "code": { 635 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 0\nSLT", 636 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600012" 637 | }, 638 | "expect": { 639 | "stack": [ 640 | "0x0" 641 | ], 642 | "success": true 643 | } 644 | }, 645 | { 646 | "name": "SGT", 647 | "hint": "Same as GT but treats arguments as signed numbers. No effect on \"positive\" numbers: 10 > 9 = true (1)", 648 | "code": { 649 | "asm": "PUSH1 9\nPUSH1 10\nSGT", 650 | "bin": "6009600a13" 651 | }, 652 | "expect": { 653 | "stack": [ 654 | "0x1" 655 | ], 656 | "success": true 657 | } 658 | }, 659 | { 660 | "name": "SGT (equal)", 661 | "hint": "Same as GT but treats arguments as signed numbers. -2 > -2 = false (0)", 662 | "code": { 663 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSGT", 664 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13" 665 | }, 666 | "expect": { 667 | "stack": [ 668 | "0x0" 669 | ], 670 | "success": true 671 | } 672 | }, 673 | { 674 | "name": "SGT (greater)", 675 | "hint": "Same as GT but treats arguments as signed numbers. -2 > -3 = true (1)", 676 | "code": { 677 | "asm": "PUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd\nPUSH32 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe\nSGT", 678 | "bin": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe13" 679 | }, 680 | "expect": { 681 | "stack": [ 682 | "0x1" 683 | ], 684 | "success": true 685 | } 686 | }, 687 | { 688 | "name": "EQ", 689 | "hint": "10 == 10 = true (1)", 690 | "code": { 691 | "asm": "PUSH1 10\nPUSH1 10\nEQ", 692 | "bin": "600a600a14" 693 | }, 694 | "expect": { 695 | "stack": [ 696 | "0x1" 697 | ], 698 | "success": true 699 | } 700 | }, 701 | { 702 | "name": "EQ (not equal)", 703 | "hint": "10 == 9 = false (0)", 704 | "code": { 705 | "asm": "PUSH1 9\nPUSH1 10\nEQ", 706 | "bin": "6009600a14" 707 | }, 708 | "expect": { 709 | "stack": [ 710 | "0x0" 711 | ], 712 | "success": true 713 | } 714 | }, 715 | { 716 | "name": "ISZERO (not zero)", 717 | "hint": "If the top element on the stack is not zero, pushes 0", 718 | "code": { 719 | "asm": "PUSH1 9\nISZERO", 720 | "bin": "600915" 721 | }, 722 | "expect": { 723 | "stack": [ 724 | "0x0" 725 | ], 726 | "success": true 727 | } 728 | }, 729 | { 730 | "name": "ISZERO (zero)", 731 | "hint": "If the top element on the stack is zero, pushes 1", 732 | "code": { 733 | "asm": "PUSH1 0\nISZERO", 734 | "bin": "600015" 735 | }, 736 | "expect": { 737 | "stack": [ 738 | "0x1" 739 | ], 740 | "success": true 741 | } 742 | }, 743 | { 744 | "name": "NOT", 745 | "hint": "Bitwise NOT operation, flips every bit 1->0, 0->1", 746 | "code": { 747 | "asm": "PUSH1 0x0f\nNOT", 748 | "bin": "600f19" 749 | }, 750 | "expect": { 751 | "stack": [ 752 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" 753 | ], 754 | "success": true 755 | } 756 | }, 757 | { 758 | "name": "AND", 759 | "hint": "Bitwise AND operation of the top 2 items on the stack", 760 | "code": { 761 | "asm": "PUSH1 0xe\nPUSH1 0x3\nAND", 762 | "bin": "600e600316" 763 | }, 764 | "expect": { 765 | "stack": [ 766 | "0x2" 767 | ], 768 | "success": true 769 | } 770 | }, 771 | { 772 | "name": "OR", 773 | "hint": "Bitwise OR operation of the top 2 items on the stack", 774 | "code": { 775 | "asm": "PUSH1 0xe\nPUSH1 0x3\nOR", 776 | "bin": "600e600317" 777 | }, 778 | "expect": { 779 | "stack": [ 780 | "0xf" 781 | ], 782 | "success": true 783 | } 784 | }, 785 | { 786 | "name": "XOR", 787 | "hint": "Bitwise XOR operation of the top 2 items on the stack", 788 | "code": { 789 | "asm": "PUSH1 0xf0\nPUSH1 0x0f\nXOR", 790 | "bin": "60f0600f18" 791 | }, 792 | "expect": { 793 | "stack": [ 794 | "0xff" 795 | ], 796 | "success": true 797 | } 798 | }, 799 | { 800 | "name": "SHL", 801 | "hint": "Bitwise shift left, 1 << 1 = 2", 802 | "code": { 803 | "asm": "PUSH1 1\nPUSH1 1\nSHL", 804 | "bin": "600160011b" 805 | }, 806 | "expect": { 807 | "stack": [ 808 | "0x2" 809 | ], 810 | "success": true 811 | } 812 | }, 813 | { 814 | "name": "SHL (discards)", 815 | "hint": "Bits that end up outside MAX_UINT256 are discarded", 816 | "code": { 817 | "asm": "PUSH32 0xFF00000000000000000000000000000000000000000000000000000000000000\nPUSH1 4\nSHL", 818 | "bin": "7fff0000000000000000000000000000000000000000000000000000000000000060041b" 819 | }, 820 | "expect": { 821 | "stack": [ 822 | "0xf000000000000000000000000000000000000000000000000000000000000000" 823 | ], 824 | "success": true 825 | } 826 | }, 827 | { 828 | "name": "SHL (too large)", 829 | "hint": "When shift amount is too large, returns zero", 830 | "code": { 831 | "asm": "PUSH1 1\nPUSH4 0xFFFFFFFF\nSHL", 832 | "bin": "600163ffffffff1b" 833 | }, 834 | "expect": { 835 | "stack": [ 836 | "0x0" 837 | ], 838 | "success": true 839 | } 840 | }, 841 | { 842 | "name": "SHR", 843 | "hint": "Bitwise shift right, 2 >> 1 = 1", 844 | "code": { 845 | "asm": "PUSH1 2\nPUSH1 1\nSHR", 846 | "bin": "600260011c" 847 | }, 848 | "expect": { 849 | "stack": [ 850 | "0x1" 851 | ], 852 | "success": true 853 | } 854 | }, 855 | { 856 | "name": "SHR (discards)", 857 | "hint": "Bits that end up outside are discarded", 858 | "code": { 859 | "asm": "PUSH1 0xFF\nPUSH1 4\nSHR", 860 | "bin": "60ff60041c" 861 | }, 862 | "expect": { 863 | "stack": [ 864 | "0xf" 865 | ], 866 | "success": true 867 | } 868 | }, 869 | { 870 | "name": "SHR (too large)", 871 | "hint": "When shift amount is too large, returns zero", 872 | "code": { 873 | "asm": "PUSH1 1\nPUSH4 0xFFFFFFFF\nSHR", 874 | "bin": "600163ffffffff1c" 875 | }, 876 | "expect": { 877 | "stack": [ 878 | "0x0" 879 | ], 880 | "success": true 881 | } 882 | }, 883 | { 884 | "name": "SAR", 885 | "hint": "Like SHR but treats the argument as signed number. No effect on \"positive\" numbers, 2 >> 1 = 1", 886 | "code": { 887 | "asm": "PUSH1 2\nPUSH1 1\nSAR", 888 | "bin": "600260011d" 889 | }, 890 | "expect": { 891 | "stack": [ 892 | "0x1" 893 | ], 894 | "success": true 895 | } 896 | }, 897 | { 898 | "name": "SAR (fills 1s)", 899 | "hint": "Note that unlike SHR, it fills the empty space with 1s", 900 | "code": { 901 | "asm": "PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH1 4\nSAR", 902 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060041d" 903 | }, 904 | "expect": { 905 | "stack": [ 906 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0" 907 | ], 908 | "success": true 909 | } 910 | }, 911 | { 912 | "name": "SAR (too large)", 913 | "hint": "When shift amount is too large and the first bit is 1, fills the whole number with 1s", 914 | "code": { 915 | "asm": "PUSH32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH4 0xFFFFFFFF\nSAR", 916 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0063ffffffff1d" 917 | }, 918 | "expect": { 919 | "stack": [ 920 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 921 | ], 922 | "success": true 923 | } 924 | }, 925 | { 926 | "name": "SAR (positive, too large)", 927 | "hint": "When shift amount is too large and the first bit is 0, fills the whole number with 0s", 928 | "code": { 929 | "asm": "PUSH32 0x0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00\nPUSH4 0xFFFFFFFF\nSAR", 930 | "bin": "7f0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0063ffffffff1d" 931 | }, 932 | "expect": { 933 | "stack": [ 934 | "0x0" 935 | ], 936 | "success": true 937 | } 938 | }, 939 | { 940 | "name": "BYTE", 941 | "hint": "The value on the stack is treated as 32 bytes, take 31st (counting from the most significant one)", 942 | "code": { 943 | "asm": "PUSH1 0xff\nPUSH1 31\nBYTE", 944 | "bin": "60ff601f1a" 945 | }, 946 | "expect": { 947 | "stack": [ 948 | "0xff" 949 | ], 950 | "success": true 951 | } 952 | }, 953 | { 954 | "name": "BYTE (30th)", 955 | "hint": "The value on the stack is treated as 32 bytes, take 30st (counting from the most significant one)", 956 | "code": { 957 | "asm": "PUSH2 0xff00\nPUSH1 30\nBYTE", 958 | "bin": "61ff00601e1a" 959 | }, 960 | "expect": { 961 | "stack": [ 962 | "0xff" 963 | ], 964 | "success": true 965 | } 966 | }, 967 | { 968 | "name": "BYTE (29th)", 969 | "hint": "Try to generalize your code to work with any argument", 970 | "code": { 971 | "asm": "PUSH3 0xff0000\nPUSH1 29\nBYTE", 972 | "bin": "62ff0000601d1a" 973 | }, 974 | "expect": { 975 | "stack": [ 976 | "0xff" 977 | ], 978 | "success": true 979 | } 980 | }, 981 | { 982 | "name": "BYTE (out of range)", 983 | "hint": "Treat other elements as zeros", 984 | "code": { 985 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPUSH1 42\nBYTE", 986 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602a1a" 987 | }, 988 | "expect": { 989 | "stack": [ 990 | "0x0" 991 | ], 992 | "success": true 993 | } 994 | }, 995 | { 996 | "name": "DUP1", 997 | "hint": "Duplicate the first element from the stack and push it onto the stack", 998 | "code": { 999 | "asm": "PUSH1 1\nDUP1\nADD", 1000 | "bin": "60018001" 1001 | }, 1002 | "expect": { 1003 | "stack": [ 1004 | "0x2" 1005 | ], 1006 | "success": true 1007 | } 1008 | }, 1009 | { 1010 | "name": "DUP3", 1011 | "hint": "Duplicate the 3rd element from the stack and push it onto the stack", 1012 | "code": { 1013 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nDUP3", 1014 | "bin": "60016002600382" 1015 | }, 1016 | "expect": { 1017 | "stack": [ 1018 | "0x1", 1019 | "0x3", 1020 | "0x2", 1021 | "0x1" 1022 | ], 1023 | "success": true 1024 | } 1025 | }, 1026 | { 1027 | "name": "DUP5", 1028 | "hint": "Try to implement your code to handle any DUP1...DUP16", 1029 | "code": { 1030 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nDUP5", 1031 | "bin": "6001600260036004600584" 1032 | }, 1033 | "expect": { 1034 | "stack": [ 1035 | "0x1", 1036 | "0x5", 1037 | "0x4", 1038 | "0x3", 1039 | "0x2", 1040 | "0x1" 1041 | ], 1042 | "success": true 1043 | } 1044 | }, 1045 | { 1046 | "name": "DUP8", 1047 | "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", 1048 | "code": { 1049 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nPUSH1 7\nPUSH1 8\nDUP8", 1050 | "bin": "6001600260036004600560066007600887" 1051 | }, 1052 | "expect": { 1053 | "stack": [ 1054 | "0x1", 1055 | "0x8", 1056 | "0x7", 1057 | "0x6", 1058 | "0x5", 1059 | "0x4", 1060 | "0x3", 1061 | "0x2", 1062 | "0x1" 1063 | ], 1064 | "success": true 1065 | } 1066 | }, 1067 | { 1068 | "name": "SWAP", 1069 | "hint": "Swap the top item from the stack with the 1st one after that", 1070 | "code": { 1071 | "asm": "PUSH1 1\nPUSH1 2\nSWAP1", 1072 | "bin": "6001600290" 1073 | }, 1074 | "expect": { 1075 | "stack": [ 1076 | "0x1", 1077 | "0x2" 1078 | ], 1079 | "success": true 1080 | } 1081 | }, 1082 | { 1083 | "name": "SWAP3", 1084 | "hint": "Swap the top item from the stack with the 3rd one after that", 1085 | "code": { 1086 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nSWAP3", 1087 | "bin": "600160026003600492" 1088 | }, 1089 | "expect": { 1090 | "stack": [ 1091 | "0x1", 1092 | "0x3", 1093 | "0x2", 1094 | "0x4" 1095 | ], 1096 | "success": true 1097 | } 1098 | }, 1099 | { 1100 | "name": "SWAP5", 1101 | "hint": "Swap the top item from the stack with the 5th one after that. Try to implement SWAP1..SWAP16 with the same code", 1102 | "code": { 1103 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nSWAP5", 1104 | "bin": "60016002600360046005600694" 1105 | }, 1106 | "expect": { 1107 | "stack": [ 1108 | "0x1", 1109 | "0x5", 1110 | "0x4", 1111 | "0x3", 1112 | "0x2", 1113 | "0x6" 1114 | ], 1115 | "success": true 1116 | } 1117 | }, 1118 | { 1119 | "name": "SWAP7", 1120 | "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", 1121 | "code": { 1122 | "asm": "PUSH1 1\nPUSH1 2\nPUSH1 3\nPUSH1 4\nPUSH1 5\nPUSH1 6\nPUSH1 7\nPUSH1 8\nSWAP7", 1123 | "bin": "6001600260036004600560066007600896" 1124 | }, 1125 | "expect": { 1126 | "stack": [ 1127 | "0x1", 1128 | "0x7", 1129 | "0x6", 1130 | "0x5", 1131 | "0x4", 1132 | "0x3", 1133 | "0x2", 1134 | "0x8" 1135 | ], 1136 | "success": true 1137 | } 1138 | }, 1139 | { 1140 | "name": "INVALID", 1141 | "hint": "Invalid instruction. Note that your code is expected to return success = false, not throw exceptions", 1142 | "code": { 1143 | "asm": "INVALID", 1144 | "bin": "fe" 1145 | }, 1146 | "expect": { 1147 | "success": false, 1148 | "stack": [] 1149 | } 1150 | }, 1151 | { 1152 | "name": "PC", 1153 | "hint": "Read \"Program Counter\" section of the course learning materials", 1154 | "code": { 1155 | "asm": "PC", 1156 | "bin": "58" 1157 | }, 1158 | "expect": { 1159 | "stack": [ 1160 | "0x0" 1161 | ], 1162 | "success": true 1163 | } 1164 | }, 1165 | { 1166 | "name": "PC (more code)", 1167 | "hint": "`PUSH1 0` is counted as 2 bytes (even though it is a single instruction)", 1168 | "code": { 1169 | "asm": "PUSH1 0\nPOP\nPC", 1170 | "bin": "60005058" 1171 | }, 1172 | "expect": { 1173 | "stack": [ 1174 | "0x3" 1175 | ], 1176 | "success": true 1177 | } 1178 | }, 1179 | { 1180 | "name": "GAS", 1181 | "hint": "In this version of the tests, GAS is not supported yet and is always expected to return MAX_UINT256", 1182 | "code": { 1183 | "asm": "GAS", 1184 | "bin": "5a" 1185 | }, 1186 | "expect": { 1187 | "stack": [ 1188 | "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 1189 | ], 1190 | "success": true 1191 | } 1192 | }, 1193 | { 1194 | "name": "JUMP", 1195 | "hint": "Set the Program Counter (PC) to the top value from the stack", 1196 | "code": { 1197 | "asm": "PUSH1 5\nJUMP\nPUSH1 1\nJUMPDEST\nPUSH1 2", 1198 | "bin": "60055660015b6002" 1199 | }, 1200 | "expect": { 1201 | "stack": [ 1202 | "0x2" 1203 | ], 1204 | "success": true 1205 | } 1206 | }, 1207 | { 1208 | "name": "JUMP (not JUMPDEST)", 1209 | "hint": "Offset 4 is not a valid JUMPDEST instruction", 1210 | "code": { 1211 | "asm": "PUSH1 3\nJUMP\nPUSH1 1", 1212 | "bin": "6003566001" 1213 | }, 1214 | "expect": { 1215 | "success": false, 1216 | "stack": [] 1217 | } 1218 | }, 1219 | { 1220 | "name": "JUMP (bad instruction boundry)", 1221 | "hint": "See \"9.4.3. Jump Destination Validity\" of the Yellow Paper https://ethereum.github.io/yellowpaper/paper.pdf", 1222 | "code": { 1223 | "asm": "PUSH1 4\nJUMP\nPUSH1 0x5b\nPUSH1 0xff", 1224 | "bin": "600456605b60ff" 1225 | }, 1226 | "expect": { 1227 | "success": false, 1228 | "stack": [] 1229 | } 1230 | }, 1231 | { 1232 | "name": "JUMPI (no jump)", 1233 | "hint": "Conditional JUMP, second argument is 0, not jumping", 1234 | "code": { 1235 | "asm": "PUSH1 0\nPUSH1 7\nJUMPI\nPUSH1 1\nJUMPDEST\nPUSH1 2\nPOP", 1236 | "bin": "600060075760015b600250" 1237 | }, 1238 | "expect": { 1239 | "stack": [ 1240 | "0x1" 1241 | ], 1242 | "success": true 1243 | } 1244 | }, 1245 | { 1246 | "name": "JUMPI (jump)", 1247 | "hint": "Conditional JUMP, second argument is not 0, jumping", 1248 | "code": { 1249 | "asm": "PUSH1 1\nPUSH1 7\nJUMPI\nPUSH1 1\nJUMPDEST\nPUSH1 2", 1250 | "bin": "600160075760015b6002" 1251 | }, 1252 | "expect": { 1253 | "stack": [ 1254 | "0x2" 1255 | ], 1256 | "success": true 1257 | } 1258 | }, 1259 | { 1260 | "name": "MSTORE", 1261 | "hint": "Read \"Memory\" section of the course learning materials before implementing memory opcodes", 1262 | "code": { 1263 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH1 0\nMLOAD", 1264 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600052600051" 1265 | }, 1266 | "expect": { 1267 | "stack": [ 1268 | "0x102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" 1269 | ], 1270 | "success": true 1271 | } 1272 | }, 1273 | { 1274 | "name": "MSTORE (tail)", 1275 | "hint": "MLOAD starts from byte offset 31 and picks up the last byte (0x20), the rest of the memory is 00", 1276 | "code": { 1277 | "asm": "PUSH32 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\nPUSH1 0\nMSTORE\nPUSH1 31\nMLOAD", 1278 | "bin": "7f0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20600052601f51" 1279 | }, 1280 | "expect": { 1281 | "stack": [ 1282 | "0x2000000000000000000000000000000000000000000000000000000000000000" 1283 | ], 1284 | "success": true 1285 | } 1286 | }, 1287 | { 1288 | "name": "MSTORE8", 1289 | "hint": "Store a single byte at the given offset", 1290 | "code": { 1291 | "asm": "PUSH1 0xff\nPUSH1 31\nMSTORE8\nPUSH1 0\nMLOAD", 1292 | "bin": "60ff601f53600051" 1293 | }, 1294 | "expect": { 1295 | "stack": [ 1296 | "0xff" 1297 | ], 1298 | "success": true 1299 | } 1300 | }, 1301 | { 1302 | "name": "MSIZE", 1303 | "hint": "No memory has been accessed, so the memory size is 0", 1304 | "code": { 1305 | "asm": "MSIZE", 1306 | "bin": "59" 1307 | }, 1308 | "expect": { 1309 | "stack": [ 1310 | "0x0" 1311 | ], 1312 | "success": true 1313 | } 1314 | }, 1315 | { 1316 | "name": "MSIZE (0x20)", 1317 | "hint": "The first 32-byte section has been accessed, so the memory size is 32 (0x20)", 1318 | "code": { 1319 | "asm": "PUSH1 0\nMLOAD\nPOP\nMSIZE", 1320 | "bin": "6000515059" 1321 | }, 1322 | "expect": { 1323 | "stack": [ 1324 | "0x20" 1325 | ], 1326 | "success": true 1327 | } 1328 | }, 1329 | { 1330 | "name": "MSIZE (0x60)", 1331 | "hint": "Memory is measured in 32-byte chunks", 1332 | "code": { 1333 | "asm": "PUSH1 0x39\nMLOAD\nPOP\nMSIZE", 1334 | "bin": "6039515059" 1335 | }, 1336 | "expect": { 1337 | "stack": [ 1338 | "0x60" 1339 | ], 1340 | "success": true 1341 | } 1342 | }, 1343 | { 1344 | "name": "MSIZE (after MSTORE8)", 1345 | "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", 1346 | "code": { 1347 | "asm": "PUSH1 0xff\nPUSH1 0xff\nMSTORE8\nMSIZE", 1348 | "bin": "60ff60ff5359" 1349 | }, 1350 | "expect": { 1351 | "stack": [ 1352 | "0x100" 1353 | ], 1354 | "success": true 1355 | } 1356 | }, 1357 | { 1358 | "name": "SHA3", 1359 | "hint": "Use an existing library for your programming language. Note that even though the opcode is called SHA3, the algorythm used is keccak256", 1360 | "code": { 1361 | "asm": "PUSH32 0xffffffff00000000000000000000000000000000000000000000000000000000\nPUSH1 0\nMSTORE\nPUSH1 4\nPUSH1 0\nSHA3", 1362 | "bin": "7fffffffff000000000000000000000000000000000000000000000000000000006000526004600020" 1363 | }, 1364 | "expect": { 1365 | "stack": [ 1366 | "0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238" 1367 | ], 1368 | "success": true 1369 | } 1370 | }, 1371 | { 1372 | "name": "ADDRESS", 1373 | "hint": "Read \"Transaction\" section of the course learning materials. Change your evm function parameters list to include transaction data", 1374 | "tx": { 1375 | "to": "0x1000000000000000000000000000000000000aaa" 1376 | }, 1377 | "code": { 1378 | "asm": "ADDRESS", 1379 | "bin": "30" 1380 | }, 1381 | "expect": { 1382 | "stack": [ 1383 | "0x1000000000000000000000000000000000000aaa" 1384 | ], 1385 | "success": true 1386 | } 1387 | }, 1388 | { 1389 | "name": "CALLER", 1390 | "hint": "Solidity calls this msg.sender", 1391 | "tx": { 1392 | "from": "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 1393 | }, 1394 | "code": { 1395 | "asm": "CALLER", 1396 | "bin": "33" 1397 | }, 1398 | "expect": { 1399 | "stack": [ 1400 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 1401 | ], 1402 | "success": true 1403 | } 1404 | }, 1405 | { 1406 | "name": "ORIGIN", 1407 | "hint": "Solidity calls this tx.origin", 1408 | "tx": { 1409 | "origin": "0x1337" 1410 | }, 1411 | "code": { 1412 | "asm": "ORIGIN", 1413 | "bin": "32" 1414 | }, 1415 | "expect": { 1416 | "stack": [ 1417 | "0x1337" 1418 | ], 1419 | "success": true 1420 | } 1421 | }, 1422 | { 1423 | "name": "GASPRICE", 1424 | "tx": { 1425 | "gasprice": "0x99" 1426 | }, 1427 | "code": { 1428 | "asm": "GASPRICE", 1429 | "bin": "3a" 1430 | }, 1431 | "expect": { 1432 | "stack": [ 1433 | "0x99" 1434 | ], 1435 | "success": true 1436 | }, 1437 | "hint": "" 1438 | }, 1439 | { 1440 | "name": "BASEFEE", 1441 | "block": { 1442 | "basefee": "0x1" 1443 | }, 1444 | "code": { 1445 | "asm": "BASEFEE", 1446 | "bin": "48" 1447 | }, 1448 | "expect": { 1449 | "stack": [ 1450 | "0x1" 1451 | ], 1452 | "success": true 1453 | }, 1454 | "hint": "" 1455 | }, 1456 | { 1457 | "name": "COINBASE", 1458 | "hint": "Do not hardcode these numbers, pull them from the test cases", 1459 | "block": { 1460 | "coinbase": "0x777" 1461 | }, 1462 | "code": { 1463 | "asm": "COINBASE", 1464 | "bin": "41" 1465 | }, 1466 | "expect": { 1467 | "stack": [ 1468 | "0x777" 1469 | ], 1470 | "success": true 1471 | } 1472 | }, 1473 | { 1474 | "name": "COINBASE (different one)", 1475 | "hint": "Do not hardcode these numbers, pull them from the test cases", 1476 | "block": { 1477 | "coinbase": "0x888" 1478 | }, 1479 | "code": { 1480 | "asm": "COINBASE", 1481 | "bin": "41" 1482 | }, 1483 | "expect": { 1484 | "stack": [ 1485 | "0x888" 1486 | ], 1487 | "success": true 1488 | } 1489 | }, 1490 | { 1491 | "name": "TIMESTAMP", 1492 | "hint": "Solidity calls this block.timestamp", 1493 | "block": { 1494 | "timestamp": "0xe4e1c1" 1495 | }, 1496 | "code": { 1497 | "asm": "TIMESTAMP", 1498 | "bin": "42" 1499 | }, 1500 | "expect": { 1501 | "stack": [ 1502 | "0xe4e1c1" 1503 | ], 1504 | "success": true 1505 | } 1506 | }, 1507 | { 1508 | "name": "NUMBER", 1509 | "hint": "Solidity calls this block.number", 1510 | "block": { 1511 | "number": "0x1000001" 1512 | }, 1513 | "code": { 1514 | "asm": "NUMBER", 1515 | "bin": "43" 1516 | }, 1517 | "expect": { 1518 | "stack": [ 1519 | "0x1000001" 1520 | ], 1521 | "success": true 1522 | } 1523 | }, 1524 | { 1525 | "name": "DIFFICULTY", 1526 | "hint": "Also known as PREVRANDAO, not used in these test cases yet", 1527 | "block": { 1528 | "difficulty": "0x20000" 1529 | }, 1530 | "code": { 1531 | "asm": "DIFFICULTY", 1532 | "bin": "44" 1533 | }, 1534 | "expect": { 1535 | "stack": [ 1536 | "0x20000" 1537 | ], 1538 | "success": true 1539 | } 1540 | }, 1541 | { 1542 | "name": "GASLIMIT", 1543 | "block": { 1544 | "gaslimit": "0xffffffffffff" 1545 | }, 1546 | "code": { 1547 | "asm": "GASLIMIT", 1548 | "bin": "45" 1549 | }, 1550 | "expect": { 1551 | "stack": [ 1552 | "0xffffffffffff" 1553 | ], 1554 | "success": true 1555 | }, 1556 | "hint": "" 1557 | }, 1558 | { 1559 | "name": "CHAINID", 1560 | "block": { 1561 | "chainid": "0x1" 1562 | }, 1563 | "code": { 1564 | "asm": "CHAINID", 1565 | "bin": "46" 1566 | }, 1567 | "expect": { 1568 | "stack": [ 1569 | "0x1" 1570 | ], 1571 | "success": true 1572 | }, 1573 | "hint": "" 1574 | }, 1575 | { 1576 | "name": "BLOCKHASH", 1577 | "hint": "Not used in this test suite, can return 0", 1578 | "code": { 1579 | "asm": "PUSH1 0\nBLOCKHASH", 1580 | "bin": "600040" 1581 | }, 1582 | "expect": { 1583 | "stack": [ 1584 | "0x0" 1585 | ], 1586 | "success": true 1587 | } 1588 | }, 1589 | { 1590 | "name": "BALANCE", 1591 | "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", 1592 | "state": { 1593 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d": { 1594 | "balance": "0x100" 1595 | } 1596 | }, 1597 | "code": { 1598 | "asm": "PUSH20 0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d\nBALANCE", 1599 | "bin": "731e79b045dc29eae9fdc69673c9dcd7c53e5e159d31" 1600 | }, 1601 | "expect": { 1602 | "stack": [ 1603 | "0x100" 1604 | ], 1605 | "success": true 1606 | } 1607 | }, 1608 | { 1609 | "name": "BALANCE (empty)", 1610 | "hint": "Balance of accounts not present in state is zero", 1611 | "code": { 1612 | "asm": "PUSH20 0xaf69610ea9ddc95883f97a6a3171d52165b69b03\nBALANCE", 1613 | "bin": "73af69610ea9ddc95883f97a6a3171d52165b69b0331" 1614 | }, 1615 | "expect": { 1616 | "stack": [ 1617 | "0x0" 1618 | ], 1619 | "success": true 1620 | } 1621 | }, 1622 | { 1623 | "name": "CALLVALUE", 1624 | "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", 1625 | "tx": { 1626 | "value": "0x1000" 1627 | }, 1628 | "code": { 1629 | "asm": "CALLVALUE", 1630 | "bin": "34" 1631 | }, 1632 | "expect": { 1633 | "stack": [ 1634 | "0x1000" 1635 | ], 1636 | "success": true 1637 | } 1638 | }, 1639 | { 1640 | "name": "CALLDATALOAD", 1641 | "hint": "Read \"Calls\" section of the course learning materials. Calldata is an array of bytes sent to the evm function", 1642 | "tx": { 1643 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1644 | }, 1645 | "code": { 1646 | "asm": "PUSH1 0\nCALLDATALOAD", 1647 | "bin": "600035" 1648 | }, 1649 | "expect": { 1650 | "stack": [ 1651 | "0x102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1652 | ], 1653 | "success": true 1654 | } 1655 | }, 1656 | { 1657 | "name": "CALLDATALOAD (tail)", 1658 | "hint": "Overflow bytes filled with zeros", 1659 | "tx": { 1660 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1661 | }, 1662 | "code": { 1663 | "asm": "PUSH1 31\nCALLDATALOAD", 1664 | "bin": "601f35" 1665 | }, 1666 | "expect": { 1667 | "stack": [ 1668 | "0xff00000000000000000000000000000000000000000000000000000000000000" 1669 | ], 1670 | "success": true 1671 | } 1672 | }, 1673 | { 1674 | "name": "CALLDATASIZE", 1675 | "hint": "Size (in bytes) of calldata buffer", 1676 | "tx": { 1677 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1678 | }, 1679 | "code": { 1680 | "asm": "CALLDATASIZE", 1681 | "bin": "36" 1682 | }, 1683 | "expect": { 1684 | "stack": [ 1685 | "0x20" 1686 | ], 1687 | "success": true 1688 | } 1689 | }, 1690 | { 1691 | "name": "CALLDATASIZE (no data)", 1692 | "code": { 1693 | "asm": "CALLDATASIZE", 1694 | "bin": "36" 1695 | }, 1696 | "expect": { 1697 | "stack": [ 1698 | "0x0" 1699 | ], 1700 | "success": true 1701 | }, 1702 | "hint": "" 1703 | }, 1704 | { 1705 | "name": "CALLDATACOPY", 1706 | "hint": "Copy 32-byte chunk of calldata into memory. Do not forget to update MSIZE after CALLDATACOPY", 1707 | "tx": { 1708 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1709 | }, 1710 | "code": { 1711 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nCALLDATACOPY\nPUSH1 0\nMLOAD", 1712 | "bin": "60206000600037600051" 1713 | }, 1714 | "expect": { 1715 | "stack": [ 1716 | "0x102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1717 | ], 1718 | "success": true 1719 | } 1720 | }, 1721 | { 1722 | "name": "CALLDATACOPY (tail)", 1723 | "hint": "Overflow bytes filled with zeros", 1724 | "tx": { 1725 | "data": "000102030405060708090a0b0c0d0e0f00112233445566778899aabbccddeeff" 1726 | }, 1727 | "code": { 1728 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nCALLDATACOPY\nPUSH1 0\nMLOAD", 1729 | "bin": "6001601f600037600051" 1730 | }, 1731 | "expect": { 1732 | "stack": [ 1733 | "0xff00000000000000000000000000000000000000000000000000000000000000" 1734 | ], 1735 | "success": true 1736 | } 1737 | }, 1738 | { 1739 | "name": "CODESIZE (small)", 1740 | "hint": "Size of the bytecode running in the current context", 1741 | "code": { 1742 | "asm": "CODESIZE", 1743 | "bin": "38" 1744 | }, 1745 | "expect": { 1746 | "stack": [ 1747 | "0x1" 1748 | ], 1749 | "success": true 1750 | } 1751 | }, 1752 | { 1753 | "name": "CODESIZE", 1754 | "code": { 1755 | "asm": "PUSH20 0\nPOP\nCODESIZE", 1756 | "bin": "7300000000000000000000000000000000000000005038" 1757 | }, 1758 | "expect": { 1759 | "stack": [ 1760 | "0x17" 1761 | ], 1762 | "success": true 1763 | }, 1764 | "hint": "" 1765 | }, 1766 | { 1767 | "name": "CODECOPY", 1768 | "hint": "Copy your own code into memory. Implementing quines in EVM is really easy", 1769 | "code": { 1770 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nCODECOPY\nPUSH1 0\nMLOAD", 1771 | "bin": "60206000600039600051" 1772 | }, 1773 | "expect": { 1774 | "stack": [ 1775 | "0x6020600060003960005100000000000000000000000000000000000000000000" 1776 | ], 1777 | "success": true 1778 | } 1779 | }, 1780 | { 1781 | "name": "CODECOPY (tail)", 1782 | "hint": "Overflow bytes filled with zeros", 1783 | "code": { 1784 | "asm": "PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\nPOP\nPUSH1 2\nPUSH1 32\nPUSH1 0\nCODECOPY\nPUSH1 0\nMLOAD", 1785 | "bin": "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5060026020600039600051" 1786 | }, 1787 | "expect": { 1788 | "stack": [ 1789 | "0xff50000000000000000000000000000000000000000000000000000000000000" 1790 | ], 1791 | "success": true 1792 | } 1793 | }, 1794 | { 1795 | "name": "EXTCODESIZE (empty)", 1796 | "code": { 1797 | "asm": "PUSH20 0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d\nEXTCODESIZE", 1798 | "bin": "731e79b045dc29eae9fdc69673c9dcd7c53e5e159d3b" 1799 | }, 1800 | "expect": { 1801 | "stack": [ 1802 | "0x0" 1803 | ], 1804 | "success": true 1805 | }, 1806 | "hint": "" 1807 | }, 1808 | { 1809 | "name": "EXTCODESIZE", 1810 | "hint": "Read \"State\" section of the course learning materials", 1811 | "state": { 1812 | "0x1000000000000000000000000000000000000aaa": { 1813 | "code": { 1814 | "asm": "PUSH1 1", 1815 | "bin": "6001" 1816 | } 1817 | } 1818 | }, 1819 | "code": { 1820 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODESIZE", 1821 | "bin": "731000000000000000000000000000000000000aaa3b" 1822 | }, 1823 | "expect": { 1824 | "stack": [ 1825 | "0x2" 1826 | ], 1827 | "success": true 1828 | } 1829 | }, 1830 | { 1831 | "name": "EXTCODECOPY", 1832 | "state": { 1833 | "0x1000000000000000000000000000000000000aaa": { 1834 | "code": { 1835 | "asm": null, 1836 | "bin": "6001" 1837 | } 1838 | } 1839 | }, 1840 | "code": { 1841 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODECOPY\nPUSH1 0\nMLOAD", 1842 | "bin": "602060006000731000000000000000000000000000000000000aaa3c600051" 1843 | }, 1844 | "expect": { 1845 | "stack": [ 1846 | "0x6001000000000000000000000000000000000000000000000000000000000000" 1847 | ], 1848 | "success": true 1849 | }, 1850 | "hint": "" 1851 | }, 1852 | { 1853 | "name": "EXTCODEHASH", 1854 | "hint": "Use the same library you used for SHA3 opcode", 1855 | "state": { 1856 | "0x1000000000000000000000000000000000000aaa": { 1857 | "code": { 1858 | "asm": null, 1859 | "bin": "FFFFFFFF" 1860 | } 1861 | } 1862 | }, 1863 | "code": { 1864 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODEHASH", 1865 | "bin": "731000000000000000000000000000000000000aaa3f" 1866 | }, 1867 | "expect": { 1868 | "stack": [ 1869 | "0x29045a592007d0c246ef02c2223570da9522d0cf0f73282c79a1bc8f0bb2c238" 1870 | ], 1871 | "success": true 1872 | } 1873 | }, 1874 | { 1875 | "name": "EXTCODEHASH (empty)", 1876 | "code": { 1877 | "asm": "PUSH20 0x1000000000000000000000000000000000000aaa\nEXTCODEHASH", 1878 | "bin": "731000000000000000000000000000000000000aaa3f" 1879 | }, 1880 | "expect": { 1881 | "stack": [ 1882 | "0x0" 1883 | ], 1884 | "success": true 1885 | }, 1886 | "hint": "" 1887 | }, 1888 | { 1889 | "name": "SELFBALANCE", 1890 | "tx": { 1891 | "to": "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d" 1892 | }, 1893 | "state": { 1894 | "0x1e79b045dc29eae9fdc69673c9dcd7c53e5e159d": { 1895 | "balance": "0x200" 1896 | } 1897 | }, 1898 | "code": { 1899 | "asm": "SELFBALANCE", 1900 | "bin": "47" 1901 | }, 1902 | "expect": { 1903 | "stack": [ 1904 | "0x200" 1905 | ], 1906 | "success": true 1907 | }, 1908 | "hint": "" 1909 | }, 1910 | { 1911 | "name": "SSTORE", 1912 | "hint": "Read \"Storage\" section of the course learning materials", 1913 | "code": { 1914 | "asm": "PUSH1 1\nPUSH1 0\nSSTORE\nPUSH1 0\nSLOAD", 1915 | "bin": "6001600055600054" 1916 | }, 1917 | "expect": { 1918 | "stack": [ 1919 | "0x1" 1920 | ], 1921 | "success": true 1922 | } 1923 | }, 1924 | { 1925 | "name": "SSTORE (non-zero location)", 1926 | "code": { 1927 | "asm": "PUSH1 2\nPUSH4 0x98fe5c2c\nSSTORE\nPUSH4 0x98fe5c2c\nSLOAD", 1928 | "bin": "60026398fe5c2c556398fe5c2c54" 1929 | }, 1930 | "expect": { 1931 | "stack": [ 1932 | "0x2" 1933 | ], 1934 | "success": true 1935 | }, 1936 | "hint": "" 1937 | }, 1938 | { 1939 | "name": "SLOAD (empty)", 1940 | "hint": "All storage is initialized to zeros", 1941 | "code": { 1942 | "asm": "PUSH1 0xff\nSLOAD", 1943 | "bin": "60ff54" 1944 | }, 1945 | "expect": { 1946 | "stack": [ 1947 | "0x0" 1948 | ], 1949 | "success": true 1950 | } 1951 | }, 1952 | { 1953 | "name": "LOG0", 1954 | "hint": "Make evm function return array of logs, modify the testing code to assert that the logs match", 1955 | "tx": { 1956 | "to": "0x1000000000000000000000000000000000000001" 1957 | }, 1958 | "code": { 1959 | "asm": "PUSH1 0xaa\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nLOG0", 1960 | "bin": "60aa6000526001601fa0" 1961 | }, 1962 | "expect": { 1963 | "logs": [ 1964 | { 1965 | "address": "0x1000000000000000000000000000000000000001", 1966 | "data": "aa", 1967 | "topics": [] 1968 | } 1969 | ], 1970 | "success": true 1971 | } 1972 | }, 1973 | { 1974 | "name": "LOG1", 1975 | "hint": "Make evm function return array of logs, modify the testing code to assert that the logs match", 1976 | "tx": { 1977 | "to": "0x1000000000000000000000000000000000000001" 1978 | }, 1979 | "code": { 1980 | "asm": "PUSH1 0xbb\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH1 1\nPUSH1 31\nLOG1", 1981 | "bin": "60bb6000527f11111111111111111111111111111111111111111111111111111111111111116001601fa1" 1982 | }, 1983 | "expect": { 1984 | "logs": [ 1985 | { 1986 | "address": "0x1000000000000000000000000000000000000001", 1987 | "data": "bb", 1988 | "topics": [ 1989 | "0x1111111111111111111111111111111111111111111111111111111111111111" 1990 | ] 1991 | } 1992 | ], 1993 | "success": true 1994 | } 1995 | }, 1996 | { 1997 | "name": "LOG2", 1998 | "hint": "Use the same code to handle LOG1...LOG4 opcodes", 1999 | "tx": { 2000 | "to": "0x1000000000000000000000000000000000000001" 2001 | }, 2002 | "code": { 2003 | "asm": "PUSH1 0xcc\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH1 1\nPUSH1 31\nLOG2", 2004 | "bin": "60cc6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222226001601fa2" 2005 | }, 2006 | "expect": { 2007 | "logs": [ 2008 | { 2009 | "address": "0x1000000000000000000000000000000000000001", 2010 | "data": "cc", 2011 | "topics": [ 2012 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2013 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2014 | ] 2015 | } 2016 | ], 2017 | "success": true 2018 | } 2019 | }, 2020 | { 2021 | "name": "LOG3", 2022 | "hint": "N = OPCODE - LOG0, pop N items from the stack as topics", 2023 | "tx": { 2024 | "to": "0x1000000000000000000000000000000000000001" 2025 | }, 2026 | "code": { 2027 | "asm": "PUSH1 0xdd\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH32 0x3333333333333333333333333333333333333333333333333333333333333333\nPUSH1 1\nPUSH1 31\nLOG3", 2028 | "bin": "60dd6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222227f33333333333333333333333333333333333333333333333333333333333333336001601fa3" 2029 | }, 2030 | "expect": { 2031 | "logs": [ 2032 | { 2033 | "address": "0x1000000000000000000000000000000000000001", 2034 | "data": "dd", 2035 | "topics": [ 2036 | "0x3333333333333333333333333333333333333333333333333333333333333333", 2037 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2038 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2039 | ] 2040 | } 2041 | ], 2042 | "success": true 2043 | } 2044 | }, 2045 | { 2046 | "name": "LOG4", 2047 | "hint": "Refactoring code is always a good idea. Your code will become cleaner, and the tests will catch if something breaks", 2048 | "tx": { 2049 | "to": "0x1000000000000000000000000000000000000001" 2050 | }, 2051 | "code": { 2052 | "asm": "PUSH1 0xee\nPUSH1 0\nMSTORE\nPUSH32 0x1111111111111111111111111111111111111111111111111111111111111111\nPUSH32 0x2222222222222222222222222222222222222222222222222222222222222222\nPUSH32 0x3333333333333333333333333333333333333333333333333333333333333333\nPUSH32 0x4444444444444444444444444444444444444444444444444444444444444444\nPUSH1 1\nPUSH1 31\nLOG4", 2053 | "bin": "60ee6000527f11111111111111111111111111111111111111111111111111111111111111117f22222222222222222222222222222222222222222222222222222222222222227f33333333333333333333333333333333333333333333333333333333333333337f44444444444444444444444444444444444444444444444444444444444444446001601fa4" 2054 | }, 2055 | "expect": { 2056 | "logs": [ 2057 | { 2058 | "address": "0x1000000000000000000000000000000000000001", 2059 | "data": "ee", 2060 | "topics": [ 2061 | "0x4444444444444444444444444444444444444444444444444444444444444444", 2062 | "0x3333333333333333333333333333333333333333333333333333333333333333", 2063 | "0x2222222222222222222222222222222222222222222222222222222222222222", 2064 | "0x1111111111111111111111111111111111111111111111111111111111111111" 2065 | ] 2066 | } 2067 | ], 2068 | "success": true 2069 | } 2070 | }, 2071 | { 2072 | "name": "RETURN", 2073 | "hint": "Read \"Calls and Returns\" section of the course learning materials", 2074 | "code": { 2075 | "asm": "PUSH1 0xA2\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2076 | "bin": "60a26000526001601ff3" 2077 | }, 2078 | "expect": { 2079 | "success": true, 2080 | "return": "a2" 2081 | } 2082 | }, 2083 | { 2084 | "name": "REVERT", 2085 | "hint": "Note that this test expects `success` to be false", 2086 | "code": { 2087 | "asm": "PUSH1 0xF1\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nREVERT", 2088 | "bin": "60f16000526001601ffd" 2089 | }, 2090 | "expect": { 2091 | "success": false, 2092 | "return": "f1" 2093 | } 2094 | }, 2095 | { 2096 | "name": "CALL", 2097 | "hint": "Read \"Calls and Returns\" section of the course learning materials. Recursively call evm function from itself when handing this opcode", 2098 | "state": { 2099 | "0x1000000000000000000000000000000000000c42": { 2100 | "code": { 2101 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2102 | "bin": "60426000526001601ff3" 2103 | } 2104 | } 2105 | }, 2106 | "code": { 2107 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2108 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1600051" 2109 | }, 2110 | "expect": { 2111 | "stack": [ 2112 | "0x42", 2113 | "0x1" 2114 | ], 2115 | "success": true 2116 | } 2117 | }, 2118 | { 2119 | "name": "CALL (returns address)", 2120 | "hint": "In the inner context, the CALLER is the contract we are sending the initial transaction to", 2121 | "tx": { 2122 | "to": "0x1000000000000000000000000000000000000aaa" 2123 | }, 2124 | "state": { 2125 | "0x1000000000000000000000000000000000000c42": { 2126 | "code": { 2127 | "asm": "CALLER\nPUSH1 0\nMSTORE\nPUSH1 32\nPUSH1 0\nRETURN", 2128 | "bin": "3360005260206000f3" 2129 | } 2130 | } 2131 | }, 2132 | "code": { 2133 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2134 | "bin": "6014600c600060006000731000000000000000000000000000000000000c426000f1600051" 2135 | }, 2136 | "expect": { 2137 | "stack": [ 2138 | "0x1000000000000000000000000000000000000aaa", 2139 | "0x1" 2140 | ], 2141 | "success": true 2142 | } 2143 | }, 2144 | { 2145 | "name": "CALL (reverts)", 2146 | "hint": "Reverts can also return data", 2147 | "state": { 2148 | "0x1000000000000000000000000000000000000c42": { 2149 | "code": { 2150 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nREVERT", 2151 | "bin": "60426000526001601ffd" 2152 | } 2153 | } 2154 | }, 2155 | "code": { 2156 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPUSH1 0\nMLOAD", 2157 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1600051" 2158 | }, 2159 | "expect": { 2160 | "stack": [ 2161 | "0x42", 2162 | "0x0" 2163 | ], 2164 | "success": true 2165 | } 2166 | }, 2167 | { 2168 | "name": "RETURNDATASIZE (empty)", 2169 | "code": { 2170 | "asm": "RETURNDATASIZE", 2171 | "bin": "3d" 2172 | }, 2173 | "expect": { 2174 | "stack": [ 2175 | "0x0" 2176 | ], 2177 | "success": true 2178 | }, 2179 | "hint": "" 2180 | }, 2181 | { 2182 | "name": "RETURNDATASIZE", 2183 | "state": { 2184 | "0x1000000000000000000000000000000000000c42": { 2185 | "code": { 2186 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2187 | "bin": "60426000526001601ff3" 2188 | } 2189 | } 2190 | }, 2191 | "code": { 2192 | "asm": "PUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nCALL\nPOP\nRETURNDATASIZE", 2193 | "bin": "60006000600060006000731000000000000000000000000000000000000c426000f1503d" 2194 | }, 2195 | "expect": { 2196 | "stack": [ 2197 | "0x0" 2198 | ], 2199 | "success": true 2200 | }, 2201 | "hint": "" 2202 | }, 2203 | { 2204 | "name": "RETURNDATACOPY", 2205 | "state": { 2206 | "0x1000000000000000000000000000000000000c42": { 2207 | "code": { 2208 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2209 | "bin": "60426000526001601ff3" 2210 | } 2211 | } 2212 | }, 2213 | "code": { 2214 | "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", 2215 | "bin": "6001601f600060006000731000000000000000000000000000000000000c426000f1506001600060ff3e60ff51" 2216 | }, 2217 | "expect": { 2218 | "stack": [ 2219 | "0x4200000000000000000000000000000000000000000000000000000000000000" 2220 | ], 2221 | "success": true 2222 | }, 2223 | "hint": "" 2224 | }, 2225 | { 2226 | "name": "DELEGATECALL", 2227 | "hint": "Like CALL, but keep the transaction data (from, origin, address) and use the code from the other account", 2228 | "tx": { 2229 | "to": "0x1000000000000000000000000000000000000aaa" 2230 | }, 2231 | "state": { 2232 | "0xdddddddddddddddddddddddddddddddddddddddd": { 2233 | "code": { 2234 | "asm": "ADDRESS\nPUSH1 0\nSSTORE", 2235 | "bin": "30600055" 2236 | } 2237 | } 2238 | }, 2239 | "code": { 2240 | "asm": "PUSH1 0\nDUP1\nDUP1\nDUP1\nPUSH20 0xdddddddddddddddddddddddddddddddddddddddd\nGAS\nDELEGATECALL\nPUSH1 0\nSLOAD", 2241 | "bin": "600080808073dddddddddddddddddddddddddddddddddddddddd5af4600054" 2242 | }, 2243 | "expect": { 2244 | "stack": [ 2245 | "0x1000000000000000000000000000000000000aaa", 2246 | "0x1" 2247 | ], 2248 | "success": true 2249 | } 2250 | }, 2251 | { 2252 | "name": "STATICCALL", 2253 | "hint": "Like CALL, but disable state modifications", 2254 | "state": { 2255 | "0x1000000000000000000000000000000000000c42": { 2256 | "code": { 2257 | "asm": "PUSH1 0x42\nPUSH1 0\nMSTORE\nPUSH1 1\nPUSH1 31\nRETURN", 2258 | "bin": "60426000526001601ff3" 2259 | } 2260 | } 2261 | }, 2262 | "code": { 2263 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nSTATICCALL\nPUSH1 0\nMLOAD", 2264 | "bin": "6001601f60006000731000000000000000000000000000000000000c426000fa600051" 2265 | }, 2266 | "expect": { 2267 | "stack": [ 2268 | "0x42", 2269 | "0x1" 2270 | ], 2271 | "success": true 2272 | } 2273 | }, 2274 | { 2275 | "name": "STATICCALL (reverts on write)", 2276 | "hint": "Use a flag to tell the evm function whenever the context is writeable (CALL) or not (STATICCALL)", 2277 | "state": { 2278 | "0x1000000000000000000000000000000000000c42": { 2279 | "code": { 2280 | "asm": "PUSH1 0x42\nPUSH1 0\nSSTORE", 2281 | "bin": "6042600055" 2282 | } 2283 | } 2284 | }, 2285 | "code": { 2286 | "asm": "PUSH1 1\nPUSH1 31\nPUSH1 0\nPUSH1 0\nPUSH20 0x1000000000000000000000000000000000000c42\nPUSH1 0\nSTATICCALL", 2287 | "bin": "6001601f60006000731000000000000000000000000000000000000c426000fa" 2288 | }, 2289 | "expect": { 2290 | "stack": [ 2291 | "0x0" 2292 | ], 2293 | "success": true 2294 | } 2295 | }, 2296 | { 2297 | "name": "CREATE (empty)", 2298 | "hint": "Read \"Creating new contracts\" section of the course learning materials. This code creates a new empty account with balance 9", 2299 | "tx": { 2300 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2301 | }, 2302 | "code": { 2303 | "asm": "PUSH1 0\nPUSH1 0\nPUSH1 9\nCREATE\nBALANCE", 2304 | "bin": "600060006009f031" 2305 | }, 2306 | "expect": { 2307 | "stack": [ 2308 | "0x9" 2309 | ], 2310 | "success": true 2311 | } 2312 | }, 2313 | { 2314 | "name": "CREATE (with 4x FF)", 2315 | "hint": "Read \"Creating new contracts\" section of the course learning materials. CALL with the given code, store the returned bytes as new contracts bytecode", 2316 | "tx": { 2317 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2318 | }, 2319 | "code": { 2320 | "asm": "PUSH1 32\nPUSH1 0\nPUSH1 0\nPUSH13 0x63FFFFFFFF6000526004601CF3\nPUSH1 0\nMSTORE\nPUSH1 13\nPUSH1 19\nPUSH1 0\nCREATE\nEXTCODECOPY\nPUSH1 0\nMLOAD", 2321 | "bin": "6020600060006c63ffffffff6000526004601cf3600052600d60136000f03c600051" 2322 | }, 2323 | "expect": { 2324 | "stack": [ 2325 | "0xffffffff00000000000000000000000000000000000000000000000000000000" 2326 | ], 2327 | "success": true 2328 | } 2329 | }, 2330 | { 2331 | "name": "CREATE (reverts)", 2332 | "hint": "No address when constructor code reverts", 2333 | "tx": { 2334 | "to": "0x9bbfed6889322e016e0a02ee459d306fc19545d8" 2335 | }, 2336 | "code": { 2337 | "asm": "PUSH13 0x63FFFFFFFF6000526004601CFD\nPUSH1 0\nMSTORE\nPUSH1 13\nPUSH1 19\nPUSH1 0\nCREATE", 2338 | "bin": "6c63ffffffff6000526004601cfd600052600d60136000f0" 2339 | }, 2340 | "expect": { 2341 | "stack": [ 2342 | "0x0" 2343 | ], 2344 | "success": true 2345 | } 2346 | }, 2347 | { 2348 | "name": "SELFDESTRUCT", 2349 | "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", 2350 | "state": { 2351 | "0xdead00000000000000000000000000000000dead": { 2352 | "balance": "0x7", 2353 | "code": { 2354 | "asm": "PUSH20 0xa1c300000000000000000000000000000000a1c3\nSELFDESTRUCT", 2355 | "bin": "73a1c300000000000000000000000000000000a1c3ff" 2356 | } 2357 | } 2358 | }, 2359 | "code": { 2360 | "asm": "PUSH1 0\nDUP1\nDUP1\nDUP1\nDUP1\nPUSH20 0xdead00000000000000000000000000000000dead\nGAS\nCALL\nPOP\nPUSH20 0xa1c300000000000000000000000000000000a1c3\nBALANCE\nPUSH20 0xdead00000000000000000000000000000000dead\nEXTCODESIZE", 2361 | "bin": "60008080808073dead00000000000000000000000000000000dead5af15073a1c300000000000000000000000000000000a1c33173dead00000000000000000000000000000000dead3b" 2362 | }, 2363 | "expect": { 2364 | "stack": [ 2365 | "0x0", 2366 | "0x7" 2367 | ], 2368 | "success": true 2369 | } 2370 | } 2371 | ] --------------------------------------------------------------------------------