Skip to content

Commit 91e353c

Browse files
authored
Merge pull request #38 from feliam/dev-istanbul
Istanbul support
2 parents 59d84e3 + a0f89b8 commit 91e353c

File tree

2 files changed

+88
-11
lines changed

2 files changed

+88
-11
lines changed

pyevmasm/evmasm.py

Lines changed: 58 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def mnemonic(self):
145145
""" Alias for name """
146146
return self.name
147147

148-
148+
@staticmethod
149149
def _long_name(self, short_name, operand_size, pops):
150150
if short_name == "PUSH":
151151
return "PUSH{:d}".format(operand_size)
@@ -284,42 +284,62 @@ def writes_to_stack(self):
284284
@property
285285
def writes_to_memory(self):
286286
""" True if the instruction writes to memory """
287-
return self.semantics in {'MSTORE', 'MSTORE8', 'CALLDATACOPY', 'CODECOPY', 'EXTCODECOPY', 'RETURNDATACOPY', 'CALL', 'STATICCALL', 'DELEGATECALL', 'CALLCODE'}
287+
return self.semantics in {
288+
"MSTORE",
289+
"MSTORE8",
290+
"CALLDATACOPY",
291+
"CODECOPY",
292+
"EXTCODECOPY",
293+
"RETURNDATACOPY",
294+
"CALL",
295+
"STATICCALL",
296+
"DELEGATECALL",
297+
"CALLCODE",
298+
}
288299

289300
@property
290301
def reads_from_memory(self):
291302
""" True if the instruction reads from memory """
292-
return self.semantics in {'MLOAD', 'CREATE', 'CALL', 'STATICCALL', 'DELEGATECALL', 'CALLCODE', 'RETURN', 'REVERT'}
303+
return self.semantics in {
304+
"MLOAD",
305+
"CREATE",
306+
"CALL",
307+
"STATICCALL",
308+
"DELEGATECALL",
309+
"CALLCODE",
310+
"RETURN",
311+
"REVERT",
312+
}
293313

294314
@property
295315
def writes_to_storage(self):
296316
""" True if the instruction writes to the storage """
297-
return self.semantics == 'SSTORE'
317+
return self.semantics == "SSTORE"
298318

299319
@property
300320
def reads_from_storage(self):
301321
""" True if the instruction reads from the storage """
302-
return self.semantics == 'SLOAD'
322+
return self.semantics == "SLOAD"
303323

304324
@property
305325
def is_terminator(self):
306326
""" True if the instruction is a basic block terminator """
307-
return self.semantics in {'RETURN', 'STOP', 'INVALID', 'JUMP', 'JUMPI', 'SELFDESTRUCT', 'REVERT'}
327+
return self.semantics in {"RETURN", "STOP", "INVALID", "JUMP", "JUMPI", "SELFDESTRUCT", "REVERT"}
308328

309329
@property
310330
def is_endtx(self):
311331
""" True if the instruction is a transaction terminator """
312-
return self.semantics in {'RETURN', 'STOP', 'INVALID', 'SELFDESTRUCT', 'REVERT'}
332+
return self.semantics in {"RETURN", "STOP", "INVALID", "SELFDESTRUCT", "REVERT"}
313333

314334
@property
315335
def is_starttx(self):
316336
""" True if the instruction is a transaction initiator """
317-
return self.semantics in {'CREATE', 'CREATE2', 'CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'}
337+
return self.semantics in {"CREATE", "CREATE2", "CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"}
318338

319339
@property
320340
def is_branch(self):
321341
""" True if the instruction is a jump """
322-
return self.semantics in {'JUMP', 'JUMPI'}
342+
return self.semantics in {"JUMP", "JUMPI"}
323343

324344
@property
325345
def is_environmental(self):
@@ -340,7 +360,21 @@ def uses_block_info(self):
340360
def is_arithmetic(self):
341361
""" True if the instruction is an arithmetic operation """
342362
return self.semantics in {
343-
'ADD', 'MUL', 'SUB', 'DIV', 'SDIV', 'MOD', 'SMOD', 'ADDMOD', 'MULMOD', 'EXP', 'SIGNEXTEND', 'SHL', 'SHR', 'SAR'}
363+
"ADD",
364+
"MUL",
365+
"SUB",
366+
"DIV",
367+
"SDIV",
368+
"MOD",
369+
"SMOD",
370+
"ADDMOD",
371+
"MULMOD",
372+
"EXP",
373+
"SIGNEXTEND",
374+
"SHL",
375+
"SHR",
376+
"SAR",
377+
}
344378

345379

346380
def assemble_one(asmcode, pc=0, fork=DEFAULT_FORK):
@@ -656,7 +690,7 @@ def _name_to_opcode(self):
656690
if self.__name_to_opcode is None:
657691
self.__name_to_opcode = {}
658692
for opcode, (name, operand_size, pops, pushes, gas, description) in self._instruction_list.items():
659-
long_name = self._long_name(name, operand_size, pops)
693+
long_name = Instruction._long_name(name, operand_size, pops)
660694
self.__name_to_opcode[long_name] = opcode
661695
return self.__name_to_opcode
662696

@@ -928,6 +962,15 @@ def __repr__(self):
928962

929963
serenity_instruction_table = InstructionTable({}, previous_fork=constantinople_instruction_table)
930964

965+
istanbul_instruction_table = {
966+
0x31: ("BALANCE", 0, 1, 1, 700, "Get balance of the given account."),
967+
0x3F: ("EXTCODEHASH", 0, 1, 1, 700, "Get hash of code"),
968+
0x46: ("CHAINID", 0, 0, 1, 2, "Get current chainid."),
969+
0x47: ("SELFBALANCE", 0, 0, 1, 5, "Balance of the current address."),
970+
0x54: ("SLOAD", 0, 1, 1, 800, "Load word from storage."),
971+
}
972+
istanbul_instruction_table = InstructionTable(istanbul_instruction_table, previous_fork=serenity_instruction_table)
973+
931974
accepted_forks = (
932975
"frontier",
933976
"homestead",
@@ -937,7 +980,10 @@ def __repr__(self):
937980
"constantinople",
938981
"petersburg",
939982
"serenity",
983+
"istanbul",
940984
)
985+
986+
941987
instruction_tables = {
942988
"frontier": frontier_instruction_table,
943989
"homestead": homestead_instruction_table,
@@ -947,6 +993,7 @@ def __repr__(self):
947993
"constantinople": constantinople_instruction_table,
948994
"petersburg": constantinople_instruction_table, # constantinople table is intentional here: those two are aliases
949995
"serenity": serenity_instruction_table,
996+
"istanbul": istanbul_instruction_table,
950997
}
951998

952999

tests/test_EVMAssembler.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,40 @@ def test_constantinople_fork(self):
9494
insn = EVMAsm.disassemble_one(b"\xf5", fork="constantinople")
9595
self.assertTrue(insn.mnemonic == "CREATE2")
9696

97+
def test_istanbul_fork(self):
98+
insn = EVMAsm.disassemble_one(b'\x31', fork='istanbul')
99+
self.assertTrue(insn.mnemonic == 'BALANCE')
100+
self.assertTrue(insn.fee == 700)
101+
self.assertTrue(insn.pops == 1)
102+
self.assertTrue(insn.pushes == 1)
103+
insn = EVMAsm.disassemble_one(b'\x3f', fork='istanbul')
104+
self.assertTrue(insn.mnemonic == 'EXTCODEHASH')
105+
self.assertTrue(insn.fee == 700)
106+
self.assertTrue(insn.pops == 1)
107+
self.assertTrue(insn.pushes == 1)
108+
insn = EVMAsm.disassemble_one(b'\x46', fork='istanbul')
109+
self.assertTrue(insn.mnemonic == 'CHAINID')
110+
self.assertTrue(insn.fee == 2)
111+
self.assertTrue(insn.pops == 0)
112+
self.assertTrue(insn.pushes == 1)
113+
insn = EVMAsm.disassemble_one(b'\x47', fork='istanbul')
114+
self.assertTrue(insn.mnemonic == 'SELFBALANCE')
115+
self.assertTrue(insn.fee == 5)
116+
self.assertTrue(insn.pops == 0)
117+
self.assertTrue(insn.pushes == 1)
118+
insn = EVMAsm.disassemble_one(b'\x54', fork='istanbul')
119+
self.assertTrue(insn.mnemonic == 'SLOAD')
120+
self.assertTrue(insn.fee == 800)
121+
self.assertTrue(insn.pops == 1)
122+
self.assertTrue(insn.pushes == 1)
123+
124+
97125
def test_assemble_DUP1_regression(self):
98126
insn = EVMAsm.assemble_one("DUP1")
99127
self.assertEqual(insn.mnemonic, "DUP1")
100128
self.assertEqual(insn.opcode, 0x80)
101129

130+
102131
def test_assemble_LOGX_regression(self):
103132
inst_table = EVMAsm.instruction_tables[EVMAsm.DEFAULT_FORK]
104133
log0_opcode = 0xa0
@@ -111,6 +140,7 @@ def test_assemble_LOGX_regression(self):
111140
self.assertEqual(insn.mnemonic, asm)
112141
self.assertEqual(insn.opcode, opcode)
113142

143+
114144
def test_consistency_assembler_disassembler(self):
115145
"""
116146
Tests whether every opcode that can be disassembled, can also be

0 commit comments

Comments
 (0)