From f8b5a1a9d602adf7be9841c4aca3b0e98b651502 Mon Sep 17 00:00:00 2001 From: Josselin Date: Sun, 7 Jun 2020 10:02:33 +0200 Subject: [PATCH] Add support for new C{value:1} syntax introduced in 0.6.2 (fix #485) Add support for salt parameter in new --- slither/core/expressions/call_expression.py | 19 +++++++++++------ slither/core/expressions/new_contract.py | 21 +++++++++++++++++++ slither/slithir/convert.py | 4 ++++ slither/slithir/operations/new_contract.py | 17 ++++++++++++--- slither/slithir/tmp_operations/tmp_call.py | 9 ++++++++ .../tmp_operations/tmp_new_contract.py | 18 ++++++++++++++++ slither/slithir/utils/ssa.py | 2 ++ .../expressions/expression_parsing.py | 8 ++++--- slither/visitors/expression/expression.py | 2 ++ .../visitors/slithir/expression_to_slithir.py | 10 +++++++++ 10 files changed, 98 insertions(+), 12 deletions(-) diff --git a/slither/core/expressions/call_expression.py b/slither/core/expressions/call_expression.py index bfdec76d91..955cac8b7e 100644 --- a/slither/core/expressions/call_expression.py +++ b/slither/core/expressions/call_expression.py @@ -14,6 +14,7 @@ def __init__(self, called, arguments, type_call): # And converted later to the correct info (convert.py) self._gas = None self._value = None + self._salt = None @property def call_value(self): @@ -31,6 +32,14 @@ def call_gas(self): def call_gas(self, gas): self._gas = gas + @property + def call_salt(self): + return self._salt + + @call_salt.setter + def call_salt(self, salt): + self._salt = salt + @property def called(self): return self._called @@ -48,10 +57,8 @@ def __str__(self): if self.call_gas or self.call_value: gas = f'gas: {self.call_gas}' if self.call_gas else '' value = f'value: {self.call_value}' if self.call_value else '' - if gas and value: - txt += '{' + f'{gas}, {value}' + '}' - elif gas: - txt += '{' + f'{gas}' + '}' - else: - txt += '{' + f'{value}' + '}' + salt = f'salt: {self.call_salt}' if self.call_salt else '' + if gas or value or salt: + options = [gas, value, salt] + txt += '{' + ','.join([o for o in options if o != '']) + '}' return txt + '(' + ','.join([str(a) for a in self._arguments]) + ')' diff --git a/slither/core/expressions/new_contract.py b/slither/core/expressions/new_contract.py index 2cde51a7b6..8bfc6de580 100644 --- a/slither/core/expressions/new_contract.py +++ b/slither/core/expressions/new_contract.py @@ -5,11 +5,32 @@ class NewContract(Expression): def __init__(self, contract_name): super(NewContract, self).__init__() self._contract_name = contract_name + self._gas = None + self._value = None + self._salt = None + @property def contract_name(self): return self._contract_name + @property + def call_value(self): + return self._value + + @call_value.setter + def call_value(self, v): + self._value = v + + @property + def call_salt(self): + return self._salt + + @call_salt.setter + def call_salt(self, salt): + self._salt = salt + + def __str__(self): return 'new ' + str(self._contract_name) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index e4a1df0531..c47739e95b 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -668,6 +668,10 @@ def extract_tmp_call(ins, contract): op = NewContract(Constant(ins.ori.contract_name), ins.lvalue) op.set_expression(ins.expression) op.call_id = ins.call_id + if ins.call_value: + op.call_value = ins.call_value + if ins.call_salt: + op.call_salt = ins.call_salt return op if isinstance(ins.ori, TmpNewArray): diff --git a/slither/slithir/operations/new_contract.py b/slither/slithir/operations/new_contract.py index 671f23fdbd..b1dbd57ede 100644 --- a/slither/slithir/operations/new_contract.py +++ b/slither/slithir/operations/new_contract.py @@ -14,6 +14,7 @@ def __init__(self, contract_name, lvalue): self._lvalue = lvalue self._callid = None # only used if gas/value != 0 self._call_value = None + self._call_salt = None @property def call_value(self): @@ -31,6 +32,14 @@ def call_id(self): def call_id(self, c): self._callid = c + @property + def call_salt(self): + return self._call_salt + + @call_salt.setter + def call_salt(self, s): + self._call_salt = s + @property def contract_name(self): return self._contract_name @@ -78,8 +87,10 @@ def can_send_eth(self): # endregion def __str__(self): - value = '' + options = '' if self.call_value: - value = 'value:{}'.format(self.call_value) + options = 'value:{} '.format(self.call_value) + if self.call_salt: + options += 'salt:{} '.format(self.call_salt) args = [str(a) for a in self.arguments] - return '{} = new {}({}) {}'.format(self.lvalue, self.contract_name, ','.join(args), value) + return '{} = new {}({}) {}'.format(self.lvalue, self.contract_name, ','.join(args), options) diff --git a/slither/slithir/tmp_operations/tmp_call.py b/slither/slithir/tmp_operations/tmp_call.py index f64c3718be..4d52a93c89 100644 --- a/slither/slithir/tmp_operations/tmp_call.py +++ b/slither/slithir/tmp_operations/tmp_call.py @@ -21,6 +21,7 @@ def __init__(self, called, nbr_arguments, result, type_call): self._callid = None self._gas = None self._value = None + self._salt = None @property def call_value(self): @@ -38,6 +39,14 @@ def call_gas(self): def call_gas(self, gas): self._gas = gas + @property + def call_salt(self): + return self._salt + + @call_salt.setter + def call_salt(self, salt): + self._salt = salt + @property def call_id(self): return self._callid diff --git a/slither/slithir/tmp_operations/tmp_new_contract.py b/slither/slithir/tmp_operations/tmp_new_contract.py index 35521d0685..f29d007d4b 100644 --- a/slither/slithir/tmp_operations/tmp_new_contract.py +++ b/slither/slithir/tmp_operations/tmp_new_contract.py @@ -6,11 +6,29 @@ def __init__(self, contract_name, lvalue): super(TmpNewContract, self).__init__() self._contract_name = contract_name self._lvalue = lvalue + self._call_value = None + self._call_salt = None @property def contract_name(self): return self._contract_name + @property + def call_value(self): + return self._call_value + + @call_value.setter + def call_value(self, v): + self._call_value = v + + @property + def call_salt(self): + return self._call_salt + + @call_salt.setter + def call_salt(self, s): + self._call_salt = s + @property def read(self): return [] diff --git a/slither/slithir/utils/ssa.py b/slither/slithir/utils/ssa.py index 044f71a9c2..e05069a597 100644 --- a/slither/slithir/utils/ssa.py +++ b/slither/slithir/utils/ssa.py @@ -613,6 +613,8 @@ def copy_ir(ir, *instances): lvalue = get_variable(ir, lambda x: x.lvalue, *instances) new_ir = NewContract(contract_name, lvalue) new_ir.arguments = get_arguments(ir, *instances) + new_ir.call_value = get_variable(ir, lambda x: x.call_value, *instances) + new_ir.call_salt = get_variable(ir, lambda x: x.call_salt, *instances) return new_ir elif isinstance(ir, NewStructure): structure = ir.structure diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index f51517f42b..5323a0f08d 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -267,9 +267,9 @@ def parse_call(expression, caller_context): call_gas = None call_value = None + call_salt = None if caller_context.is_compact_ast: called = parse_expression(expression['expression'], caller_context) - # If the next expression is a FunctionCallOptions # We can here the gas/value information # This is only available if the syntax is {gas: , value: } @@ -283,7 +283,8 @@ def parse_call(expression, caller_context): call_value = option if name == 'gas': call_gas = option - + if name == 'salt': + call_salt = option arguments = [] if expression['arguments']: arguments = [parse_expression(a, caller_context) for a in expression['arguments']] @@ -302,6 +303,7 @@ def parse_call(expression, caller_context): # Only available if the syntax {gas:, value:} was used call_expression.call_gas = call_gas call_expression.call_value = call_value + call_expression.call_salt = call_salt return call_expression @@ -419,7 +421,7 @@ def parse_expression(expression, caller_context): elif name == 'FunctionCallOptions': # call/gas info are handled in parse_call called = parse_expression(expression['expression'], caller_context) - assert isinstance(called, MemberAccess) + assert isinstance(called, (MemberAccess, NewContract)) return called elif name == 'TupleExpression': diff --git a/slither/visitors/expression/expression.py b/slither/visitors/expression/expression.py index 2666fac460..c0f1b35d40 100644 --- a/slither/visitors/expression/expression.py +++ b/slither/visitors/expression/expression.py @@ -111,6 +111,8 @@ def _visit_call_expression(self, expression): self._visit_expression(expression.call_value) if expression.call_gas: self._visit_expression(expression.call_gas) + if expression.call_salt: + self._visit_expression(expression.call_salt) def _visit_conditional_expression(self, expression): self._visit_expression(expression.if_expression) diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 08a87266d4..62a86b684f 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -160,6 +160,9 @@ def _post_call_expression(self, expression): if expression.call_value: call_value = get(expression.call_value) message_call.call_value = call_value + if expression.call_salt: + call_salt = get(expression.call_salt) + message_call.call_salt = call_salt self._result.append(message_call) set_val(expression, val) @@ -221,6 +224,13 @@ def _post_new_contract(self, expression): val = TemporaryVariable(self._node) operation = TmpNewContract(expression.contract_name, val) operation.set_expression(expression) + if expression.call_value: + call_value = get(expression.call_value) + operation.call_value = call_value + if expression.call_salt: + call_salt = get(expression.call_salt) + operation.call_salt = call_salt + self._result.append(operation) set_val(expression, val)