def run(self): # Set jump to main program body self.__opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) jump_to_prog_start_i = len(self.__opcodes.list) - 1 self.__opcodes.add('JUMP') for el in self.__ast.root.child_nodes: context = Context(self.__frame_service_atoms) if el.child_nodes[0].value == 'prog': context.is_prog = True # Jump from header to prog body self.__opcodes.add('JUMPDEST') self.__opcodes.list[jump_to_prog_start_i].extra_value = dec_to_hex(self.__opcodes.list[-1].id, 2 * self.__address_length) self.__opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) prog_atom_count = len(self.__opcodes.list) - 1 VirtualStackHelper().load_cur_atom_counter_addr(self.__opcodes) self.__opcodes.add('MSTORE') self.process_code_block(el.child_nodes[1], context, self.__opcodes) self.__opcodes.list[prog_atom_count].extra_value = \ dec_to_hex(context.id_counter - self.__frame_service_atoms, 2 * self.__address_length) else: self.declare_function(el, context, self.__opcodes) return self
def __break(self, body: AstNode, ctx: Context, opcodes: OpcodeList): """ INPUT: | EoS | OUTPUT: | EoS | """ assert len(body.child_nodes) == 1 opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) opcodes.add('JUMP', dec_to_hex(self.__current_while_id, 2 * self.__address_length)) pass
def calc_cur_frame_size(self, opcodes: OpcodeList): """ INPUT: | EoS | OUTPUT: | EoS | Size of current frame """ opcodes.add( 'PUSH', dec_to_hex(self.__frame_service_atoms * 0x20, 2 * self.__address_length)) self.load_cur_atom_counter(opcodes) opcodes.add('PUSH', dec_to_hex(32, 2 * self.__address_length)) opcodes.add('MUL') opcodes.add('ADD')
def load_cur_gap(self, opcodes: OpcodeList): """ INPUT: | EoS | OUTPUT: | EoS | Gap of current frame | """ opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) opcodes.add('MLOAD')
def store_new_gap(self, opcodes: OpcodeList): """ INPUT: | EoS | New gap OUTPUT: | EoS | """ opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) opcodes.add('MSTORE')
def load_cur_atom_counter_addr(self, opcodes: OpcodeList): """ INPUT: | EoS | OUTPUT: | EoS | Address of Current Atom counter """ self.load_cur_gap(opcodes) opcodes.add('PUSH', dec_to_hex(0x20, 2 * self.__address_length)) opcodes.add('ADD')
def __return(self, body: AstNode, ctx: Context, opcodes: OpcodeList): """ INPUT (1): | EoS | Some value OUTPUT (0): | EoS | """ assert len(body.child_nodes) == 2 if ctx.is_prog: opcodes.add('PUSH', dec_to_hex(0, 2 * self.address_length)) opcodes.add('MSTORE') opcodes.add('PUSH', dec_to_hex(32, 2 * self.address_length)) opcodes.add('PUSH', dec_to_hex(0, 2 * self.address_length)) opcodes.add('RETURN') else: VirtualStackHelper().load_back_address(opcodes) VirtualStackHelper().remove_frame(opcodes) opcodes.add('JUMP')
def load_back_address_addr(self, opcodes: OpcodeList): """ INPUT: | EoS | OUTPUT: | EoS | Address of Current Back address """ self.load_cur_gap(opcodes) opcodes.add('PUSH', dec_to_hex(0x40, 2 * self.__address_length)) opcodes.add('ADD')
def __not(self, body: AstNode, ctx: Context, opcodes: OpcodeList): """ INPUT (1): | EoS | value (bool) OUTPUT (1): | EoS | !value (bool) """ assert len(body.child_nodes) == 2 opcodes.add('PUSH', dec_to_hex(0, 2 * self.address_length)) opcodes.add('EQ')
def load_atom_address(self, opcodes: OpcodeList, atom_address: int): """ INPUT: | EoS | OUTPUT: | EoS | Address of Atom on provided address """ self.load_cur_gap(opcodes) opcodes.add('PUSH', dec_to_hex(atom_address, 2 * self.__address_length)) opcodes.add('ADD')
def __nonequal(self, body: AstNode, ctx: Context, opcodes: OpcodeList): """ INPUT (2): | EoS | Value 1 | Value 2 OUTPUT (1): | EoS | Does Value 1 differs from Value 2 (bool) """ assert len(body.child_nodes) == 3 opcodes.add('EQ') opcodes.add('PUSH', dec_to_hex(0, 2 * self.address_length)) opcodes.add('EQ')
def __read(self, body: AstNode, ctx: Context, opcodes: OpcodeList): """ INPUT (0): | EoS | OUTPUT (1): | EoS | Value from input """ assert len(body.child_nodes) == 2 opcodes.add('PUSH', dec_to_hex(0x20, 2 * self.address_length)) opcodes.add('MUL') opcodes.add('CALLDATALOAD')
def call(self, call_body: AstNode, ctx: Context, opcodes: OpcodeList): assert call_body.type == AstNodeType.List assert call_body.child_nodes[ 0].type == AstNodeType.Literal or call_body.child_nodes[ 0].type == AstNodeType.Atom # Prepare back address, part 1 opcodes.add('PUSH') back_address = len(opcodes.list) - 1 # Jump into the function opcodes.add( 'PUSH', dec_to_hex(self.__funcs[call_body.child_nodes[0].value], 2 * self.address_length)) opcodes.add('JUMP') # Prepare back address, part 2 opcodes.add('JUMPDEST') opcodes.list[back_address].extra_value = dec_to_hex( opcodes.list[-1].id, 2 * self.address_length)
def __cond(self, body: AstNode, ctx: Context, opcodes: OpcodeList, generator: Generator): """ INPUT (0): | EoS | OUTPUT (0): | EoS | """ assert len(body.child_nodes) == 3 or len(body.child_nodes) == 4 # Conditions check generator.process_call(body.child_nodes[1], ctx, opcodes) # JUMP TO TRUE opcodes.add('PUSH') jump_from_check_to_true = len(opcodes.list) - 1 opcodes.add('JUMPI') # JUMP TO ELSE opcodes.add('PUSH') jump_from_check_to_false = len(opcodes.list) - 1 opcodes.add('JUMP') # TRUE BLOCK opcodes.add('JUMPDEST') block_id = opcodes.list[-1].id opcodes.list[jump_from_check_to_true].extra_value = dec_to_hex(block_id, 2 * self.__address_length) generator.process_call(body.child_nodes[2], ctx, opcodes) opcodes.add('PUSH', ) jump_from_true_to_end = len(opcodes.list) - 1 opcodes.add('JUMP') # FALSE BLOCK opcodes.add('JUMPDEST') block_id = opcodes.list[-1].id opcodes.list[jump_from_check_to_false].extra_value = dec_to_hex(block_id, 2 * self.__address_length) if len(body.child_nodes) == 4: generator.process_call(body.child_nodes[3], ctx, opcodes) opcodes.add('PUSH') jump_from_false_to_end = len(opcodes.list) - 1 opcodes.add('JUMP') # END opcodes.add('JUMPDEST') block_id = opcodes.list[-1].id opcodes.list[jump_from_true_to_end].extra_value = dec_to_hex(block_id, 2 * self.__address_length) opcodes.list[jump_from_false_to_end].extra_value = dec_to_hex(block_id, 2 * self.__address_length)
def __init__(self, name: str, address_length: int, extra_value=None, instruction_set: dict = None): self.id = Opcode.__counter self.__instruction_set = instruction_set Opcode.__counter += 1 self.name = name if extra_value is not None: self.extra_value = extra_value elif name == 'PUSH': self.extra_value = dec_to_hex(0, 2 * address_length) else: self.extra_value = None if name == 'PUSH': Opcode.__counter += address_length
def declare_function(self, call_body: AstNode, ctx: Context, opcodes: OpcodeList): assert call_body.type == AstNodeType.List assert call_body.child_nodes[0].type == AstNodeType.Literal or call_body.child_nodes[0].type == AstNodeType.Atom assert call_body.child_nodes[0].value == 'func' """ INPUT: | EoS | Arg1 | ... | ArgN | Back address OUTPUT: | EoS | """ # Set entry point opcodes.add('JUMPDEST') Declared().add(call_body.child_nodes[1].value, opcodes.list[-1].id) # Make new stack frame # Back address gone VirtualStackHelper().add_frame(opcodes) # Set atom counter, part 1 opcodes.add('PUSH') func_atom_counter = len(opcodes.list) - 1 VirtualStackHelper().load_cur_atom_counter_addr(opcodes) opcodes.add('MSTORE') # Declare arguments in context # Args gone for arg_name in reversed(call_body.child_nodes[2].child_nodes): assert arg_name.type == AstNodeType.Atom address, is_new = ctx.get_atom_addr(arg_name.value) VirtualStackHelper().store_atom_value(opcodes, address) # Generate body self.process_call(call_body.child_nodes[3], ctx, opcodes) # Set atom counter, part 2 opcodes.list[func_atom_counter].extra_value = \ dec_to_hex(ctx.id_counter - self.__frame_service_atoms, 2 * self.__address_length) # Remove frame and leave function VirtualStackHelper().load_back_address(opcodes) VirtualStackHelper().remove_frame(opcodes) opcodes.add('JUMP')
def init_stack(self, opcodes: OpcodeList): """ NO SIDE EFFECTS """ # Set ZERO FRAME (prog frame) gap = 0x40 opcodes.add('PUSH', dec_to_hex(0x40, 2 * self.__address_length)) opcodes.add('PUSH', dec_to_hex(0, 2 * self.__address_length)) opcodes.add('MSTORE') # Init zero frame # Set start of previous frame and back address as 0x00 opcodes.add('PUSH', dec_to_hex(0x0, 2 * self.__address_length)) opcodes.add('DUP1') opcodes.add('PUSH', dec_to_hex(0x40, 2 * self.__address_length)) opcodes.add('MSTORE') opcodes.add('PUSH', dec_to_hex(0x40 + 0x40, 2 * self.__address_length)) opcodes.add('MSTORE') # Set counter of atoms as 0x00 opcodes.add('PUSH', dec_to_hex(0x0, 2 * self.__address_length)) opcodes.add('PUSH', dec_to_hex(0x40 + 0x20, 2 * self.__address_length)) opcodes.add('MSTORE')
def __while(self, body: AstNode, ctx: Context, opcodes: OpcodeList, generator: Generator): """ INPUT: | EoS | OUTPUT: | EoS | """ assert len(body.child_nodes) == 3 prev_while = self.__current_while_id self.__current_while_id = self.__while_count self.__while_count += 1 opcodes.add('JUMPDEST', dec_to_hex(self.__current_while_id, 2 * self.__address_length)) jumpdest_to_condition_check_id = dec_to_hex(opcodes.list[-1].id, 2 * self.__address_length) generator.process_call(body.child_nodes[1], ctx, opcodes) # if true: jump to while body opcodes.add('PUSH') jump_to_while_body = len(opcodes.list) - 1 opcodes.add('JUMPI') # else: jump to while end opcodes.add('PUSH') jump_to_while_end = len(opcodes.list) - 1 opcodes.add('JUMP') # while body opcodes.add('JUMPDEST') opcodes.list[jump_to_while_body].extra_value = dec_to_hex(opcodes.list[-1].id, 2 * self.__address_length) generator.process_call(body.child_nodes[2], ctx, opcodes) opcodes.add('PUSH', jumpdest_to_condition_check_id) opcodes.add('JUMP') # while end opcodes.add('JUMPDEST') opcodes.list[jump_to_while_end].extra_value = dec_to_hex(opcodes.list[-1].id, 2 * self.__address_length) for i in range(len(opcodes.list)): if opcodes.list[i].name == 'JUMP' and opcodes.list[i].extra_value == dec_to_hex(self.__current_while_id, 2 * self.__address_length): opcodes.list[i - 1].extra_value = dec_to_hex(opcodes.list[-1].id, 2 * self.__address_length) opcodes.list[i].extra_value = None BuiltIns.current_while = prev_while
def process_literal(self, call_body: AstNode, opcodes: OpcodeList): assert call_body.type == AstNodeType.Literal value = dec_to_hex(call_body.value, 2 * self.__address_length) opcodes.add('PUSH', value)