def parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, default_function): sub = ['seq', func_init_lll()] add_gas = func_init_lll().gas for _def in otherfuncs: sub.append( parse_function(_def, {**{'self': sigs}, **external_contracts}, origcode, global_ctx) ) sub[-1].total_gas += add_gas add_gas += 30 for sig in sig_utils.generate_default_arg_sigs(_def, external_contracts, global_ctx): sig.gas = sub[-1].total_gas sigs[sig.sig] = sig # Add fallback function if default_function: default_func = parse_function( default_function[0], {**{'self': sigs}, **external_contracts}, origcode, global_ctx, ) fallback = default_func else: fallback = LLLnode.from_list(['revert', 0, 0], typ=None, annotation='Default function') sub.append(['seq_unchecked', ['label', 'fallback'], fallback]) o.append(['return', 0, ['lll', sub, 0]]) return o, sub
def parse_other_functions( o, otherfuncs, sigs, external_interfaces, origcode, global_ctx, default_function ): sub = ["seq", func_init_lll()] add_gas = func_init_lll().gas for _def in otherfuncs: sub.append( parse_function(_def, {**{"self": sigs}, **external_interfaces}, origcode, global_ctx) ) sub[-1].total_gas += add_gas add_gas += 30 for sig in sig_utils.generate_default_arg_sigs(_def, external_interfaces, global_ctx): sig.gas = sub[-1].total_gas sigs[sig.sig] = sig # Add fallback function if default_function: default_func = parse_function( default_function[0], {**{"self": sigs}, **external_interfaces}, origcode, global_ctx, ) fallback = default_func else: fallback = LLLnode.from_list(["revert", 0, 0], typ=None, annotation="Default function") sub.append(["seq_unchecked", ["label", "fallback"], fallback]) o.append(["return", 0, ["lll", sub, 0]]) return o, sub
def parse_tree_to_lll(code, origcode, runtime_only=False, interface_codes=None): global_ctx = GlobalContext.get_global_context(code, interface_codes=interface_codes) _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: %s" % ( [name for name in _names_def if _names_def.count(name) > 1][0] ) ) _names_events = [_event.target.id for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException( "Duplicate event name: %s" % ( [name for name in _names_events if _names_events.count(name) > 1][0] ) ) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] sigs = {} external_contracts = {} # Create the main statement o = ['seq'] if global_ctx._events: sigs = parse_events(sigs, global_ctx) if global_ctx._contracts or global_ctx._interfaces: external_contracts = parse_external_contracts(external_contracts, global_ctx) # If there is an init func... if initfunc: o.append(INITIALIZER_LLL) o.append( parse_function( initfunc[0], {**{'self': sigs}, **external_contracts}, origcode, global_ctx, ) ) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions( o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only ) # Check if interface of contract is correct. check_valid_contract_interface(global_ctx, sigs) return LLLnode.from_list(o, typ=None)
def parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, default_function, runtime_only): sub = ['seq', FUNC_INIT_LLL] add_gas = FUNC_INIT_LLL.gas for _def in otherfuncs: sub.append( parse_function(_def, { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx)) sub[-1].total_gas += add_gas add_gas += 30 for sig in sig_utils.generate_default_arg_sigs(_def, external_contracts, global_ctx): sig.gas = sub[-1].total_gas sigs[sig.sig] = sig # Add fallback function if default_function: default_func = parse_function( default_function[0], { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx, ) sub.append(default_func) else: sub.append( LLLnode.from_list(['revert', 0, 0], typ=None, annotation='Default function')) if runtime_only: return sub else: o.append(['return', 0, ['lll', sub, 0]]) return o
def parse_tree_to_lll(source_code: str, global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]: _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: " f"{[name for name in _names_def if _names_def.count(name) > 1][0]}" ) _names_events = [_event.name for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException( f"""Duplicate event name: {[name for name in _names_events if _names_events.count(name) > 1][0]}""" ) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] sigs: dict = {} external_interfaces: dict = {} # Create the main statement o = ["seq"] if global_ctx._events: sigs = parse_events(sigs, global_ctx) if global_ctx._contracts or global_ctx._interfaces: external_interfaces = parse_external_interfaces(external_interfaces, global_ctx) # If there is an init func... if initfunc: o.append(init_func_init_lll()) o.append( parse_function( initfunc[0], {**{"self": sigs}, **external_interfaces}, source_code, global_ctx, ) ) # If there are regular functions... if otherfuncs or defaultfunc: o, runtime = parse_other_functions( o, otherfuncs, sigs, external_interfaces, source_code, global_ctx, defaultfunc ) else: runtime = o.copy() # Check if interface of contract is correct. check_valid_contract_interface(global_ctx, sigs) return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
def parse_tree_to_lll(global_ctx: GlobalContext) -> Tuple[LLLnode, LLLnode]: _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: " f"{[name for name in _names_def if _names_def.count(name) > 1][0]}" ) _names_events = [_event.name for _event in global_ctx._events] # Checks for duplicate event names if len(set(_names_events)) < len(_names_events): raise EventDeclarationException(f"""Duplicate event name: {[name for name in _names_events if _names_events.count(name) > 1][0]}""" ) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] # check if any functions in the contract are payable - if not, we do a single # ASSERT CALLVALUE ISZERO at the start of the bytecode rather than at the start # of each function is_contract_payable = next( (True for i in global_ctx._defs if FunctionSignature.from_definition( i, custom_structs=global_ctx._structs).mutability == "payable"), False, ) sigs: dict = {} external_interfaces: dict = {} # Create the main statement o = ["seq"] if global_ctx._contracts or global_ctx._interfaces: external_interfaces = parse_external_interfaces( external_interfaces, global_ctx) # If there is an init func... if initfunc: o.append(init_func_init_lll()) o.append( parse_function( initfunc[0], { **{ "self": sigs }, **external_interfaces }, global_ctx, False, )) # If there are regular functions... if otherfuncs or defaultfunc: o, runtime = parse_other_functions( o, otherfuncs, sigs, external_interfaces, global_ctx, defaultfunc, is_contract_payable, ) else: runtime = o.copy() if not is_contract_payable: # if no functions in the contract are payable, assert that callvalue is # zero at the beginning of the bytecode runtime.insert(1, ["assert", ["iszero", "callvalue"]]) return LLLnode.from_list(o, typ=None), LLLnode.from_list(runtime, typ=None)
def parse_other_functions(o, otherfuncs, sigs, external_interfaces, global_ctx, default_function): # check for payable/nonpayable external functions to optimize nonpayable assertions func_types = [i._metadata["type"] for i in global_ctx._defs] mutabilities = [ i.mutability for i in func_types if i.visibility == FunctionVisibility.EXTERNAL ] has_payable = next( (True for i in mutabilities if i == StateMutability.PAYABLE), False) has_nonpayable = next( (True for i in mutabilities if i != StateMutability.PAYABLE), False) is_default_payable = (default_function is not None and default_function._metadata["type"].mutability == StateMutability.PAYABLE) # when a contract has a payable default function and at least one nonpayable # external function, we must perform the nonpayable check on every function check_per_function = is_default_payable and has_nonpayable # generate LLL for regular functions payable_func_sub = ["seq"] external_func_sub = ["seq"] internal_func_sub = ["seq"] add_gas = func_init_lll().gas for func_node in otherfuncs: func_type = func_node._metadata["type"] func_lll = parse_function(func_node, { **{ "self": sigs }, **external_interfaces }, global_ctx, check_per_function) if func_type.visibility == FunctionVisibility.INTERNAL: internal_func_sub.append(func_lll) elif func_type.mutability == StateMutability.PAYABLE: add_gas += 30 payable_func_sub.append(func_lll) else: external_func_sub.append(func_lll) add_gas += 30 func_lll.total_gas += add_gas for sig in sig_utils.generate_default_arg_sigs(func_node, external_interfaces, global_ctx): sig.gas = func_lll.total_gas sigs[sig.sig] = sig # generate LLL for fallback function if default_function: fallback_lll = parse_function( default_function, { **{ "self": sigs }, **external_interfaces }, global_ctx, # include a nonpayble check here if the contract only has a default function check_per_function or not otherfuncs, ) else: fallback_lll = LLLnode.from_list(["revert", 0, 0], typ=None, annotation="Default function") if check_per_function: external_seq = ["seq", payable_func_sub, external_func_sub] else: # payable functions are placed prior to nonpayable functions # and seperated by a nonpayable assertion external_seq = ["seq"] if has_payable: external_seq.append(payable_func_sub) if has_nonpayable: external_seq.extend([["assert", ["iszero", "callvalue"]], external_func_sub]) # bytecode is organized by: external functions, fallback fn, internal functions # this way we save gas and reduce bytecode by not jumping over internal functions main_seq = [ "seq", func_init_lll(), ["with", "_func_sig", ["mload", 0], external_seq], ["seq_unchecked", ["label", "fallback"], fallback_lll], internal_func_sub, ] o.append(["return", 0, ["lll", main_seq, 0]]) return o, main_seq