Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load truffle json artifacts #1376

Merged
merged 18 commits into from
Mar 18, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,066 changes: 1,066 additions & 0 deletions examples/evm/minimal-json.py

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions examples/evm/minimal.py
Original file line number Diff line number Diff line change
@@ -7,20 +7,19 @@
#And now make the contract account to analyze
# cat | solc --bin
source_code = '''
pragma solidity ^0.4.13;
contract NoDistpatcher {
event Log(string);
function named_func(uint x) returns (uint) {
function named_func(uint x) public returns (uint) {
return 5 + x;
}
function() payable {
function() external payable {
if (msg.data[0] == 'A') {
Log("Got an A");
emit Log("Got an A");
}
else{
Log("Got something else");
emit Log("Got something else");
}
}
}
100 changes: 96 additions & 4 deletions manticore/ethereum/manticore.py
Original file line number Diff line number Diff line change
@@ -188,10 +188,17 @@ def _link(bytecode, libraries=None):
pos = 0
while pos < len(hex_contract):
if hex_contract[pos] == '_':
# __/tmp/tmp_9k7_l:Manticore______________
lib_placeholder = hex_contract[pos:pos + 40]
lib_name = lib_placeholder.split(':')[1].split('_')[0]
deps.setdefault(lib_name, []).append(pos)
# This is all very weak...
# Contract names starting/ending with _ ?
# Contract names longer than 40 bytes ?
if ':' in lib_placeholder:
# __/tmp/tmp_9k7_l:Manticore______________
lib_name = lib_placeholder.split(':')[1].strip('_')
deps.setdefault(lib_name, []).append(pos)
else:
lib_name = lib_placeholder.strip('_')
deps.setdefault(lib_name, []).append(pos)
pos += 40
else:
pos += 2
@@ -323,7 +330,7 @@ def _compile(source_code, contract_name, libraries=None, solc_bin=None, solc_rem
break

if name is None:
raise ValueError('Specified contract not found')
raise ValueError(f'Specified contract not found: {contract_name}')

name = name.split(':')[1]

@@ -572,6 +579,91 @@ def make_symbolic_arguments(self, types):
# FIXME this is more naive than reasonable.
return ABI.deserialize(types, self.make_symbolic_buffer(32, name='INITARGS', avoid_collisions=True))

def json_create_contract(self, jfile, owner=None, name=None, contract_name=None, balance=0, gas=None, network_id=None, args=()):
""" Creates a solidity contract based on a truffle json artifact.
https://github.com/trufflesuite/truffle/tree/develop/packages/truffle-contract-schema
:param jfile: truffle json artifact
:type jfile: str or IOBase
:param owner: owner account (will be default caller in any transactions)
:type owner: int or EVMAccount
:param contract_name: Name of the contract to analyze (optional if there is a single one in the source code)
:type contract_name: str
:param balance: balance to be transferred on creation
:type balance: int or BitVecVariable
:param gas: gas budget for each contract creation needed (may be more than one if several related contracts defined in the solidity source)
:type gas: int
:param network_id: Truffle network id to instantiate
:param tuple args: constructor arguments
:rtype: EVMAccount
"""

if isinstance(jfile, io.IOBase):
jfile = jfile.read()
elif isinstance(jfile, bytes):
jfile = str(jfile, 'utf-8')
elif not isinstance(jfile, str):
raise TypeError(f'source code bad type: {type(jfile).__name__}')

truffle = json.loads(jfile)
hashes = {}
for item in truffle['abi']:
item_type = item['type']
if item_type in ('function'):
signature = SolidityMetadata.function_signature_for_name_and_inputs(item['name'], item['inputs'])
hashes[signature] = sha3.keccak_256(signature.encode()).hexdigest()[:8]
if 'signature' in item:
if item['signature'] != f'0x{hashes[signature]}':
raise Exception(f"Something wrong with the sha3 of the method {signature} signature (a.k.a. the hash)")

if contract_name is None:
contract_name = truffle["contractName"]

if network_id is None:
if len(truffle['networks']) > 1:
raise Exception("Network id not specified")
if len(truffle['networks']) == 1:
network_id = list(truffle['networks'].keys())[0]
if network_id in truffle['networks']:
temp_dict = truffle['networks'][network_id]['links']
links = dict((k, int(v['address'], 0)) for k, v in temp_dict.items())
else:
links = ()

source_code = truffle["source"]
bytecode = self._link(truffle["bytecode"][2:], links)
runtime = self._link(truffle["deployedBytecode"][2:], links)
if "sourceMap" in truffle:
srcmap = truffle["sourceMap"].split(';')
else:
srcmap_runtime = []
if "deployedSourceMap" in truffle:
srcmap_runtime = truffle["deployedSourceMap"].split(';')
else:
srcmap_runtime = []
abi = truffle['abi']
md = SolidityMetadata(contract_name, source_code, bytecode, runtime, srcmap, srcmap_runtime, hashes, abi, b'')
constructor_types = md.get_constructor_arguments()
if constructor_types != '()':
if args is None:
args = self.make_symbolic_arguments(constructor_types)

constructor_data = ABI.serialize(constructor_types, *args)
else:
constructor_data = b''

contract_account = self.create_contract(owner=owner,
balance=balance,
init=md._init_bytecode + constructor_data,
gas=gas)

if contract_account is None:
raise EthereumError(f"Failed to build contract {contract_name}")
self.metadata[int(contract_account)] = md

if not self.count_running_states() or len(self.get_code(contract_account)) == 0:
return None
return contract_account

def solidity_create_contract(self, source_code, owner, name=None, contract_name=None, libraries=None,
balance=0, address=None, args=(), solc_bin=None, solc_remaps=[],
working_dir=None, gas=None):
43 changes: 22 additions & 21 deletions manticore/ethereum/solidity.py
Original file line number Diff line number Diff line change
@@ -88,31 +88,32 @@ def __build_source_map(self, bytecode, srcmap):
# https://solidity.readthedocs.io/en/develop/miscellaneous.html#source-mappings
new_srcmap = {}
bytecode = self._without_metadata(bytecode)
if self.source_code and srcmap:

asm_offset = 0
asm_pos = 0
md = dict(enumerate(srcmap[asm_pos].split(':')))
byte_offset = int(md.get(0, 0)) # is the byte-offset to the start of the range in the source file
source_len = int(md.get(1, 0)) # is the length of the source range in bytes
file_index = int(md.get(2, 0)) # is the source index over sourceList
jump_type = md.get(3, None) # this can be either i, o or - signifying whether a jump instruction goes into a function, returns from a function or is a regular jump as part of e.g. a loop
asm_offset = 0
asm_pos = 0
md = dict(enumerate(srcmap[asm_pos].split(':')))
byte_offset = int(md.get(0, 0)) # is the byte-offset to the start of the range in the source file
source_len = int(md.get(1, 0)) # is the length of the source range in bytes
file_index = int(md.get(2, 0)) # is the source index over sourceList
jump_type = md.get(3, None) # this can be either i, o or - signifying whether a jump instruction goes into a function, returns from a function or is a regular jump as part of e.g. a loop

pos_to_offset = {}
for i in EVMAsm.disassemble_all(bytecode):
pos_to_offset[asm_pos] = asm_offset
asm_pos += 1
asm_offset += i.size
pos_to_offset = {}
for i in EVMAsm.disassemble_all(bytecode):
pos_to_offset[asm_pos] = asm_offset
asm_pos += 1
asm_offset += i.size

for asm_pos, md in enumerate(srcmap):
if len(md):
d = {p: k for p, k in enumerate(md.split(':')) if k}
for asm_pos, md in enumerate(srcmap):
if len(md):
d = {p: k for p, k in enumerate(md.split(':')) if k}

byte_offset = int(d.get(0, byte_offset))
source_len = int(d.get(1, source_len))
file_index = int(d.get(2, file_index))
jump_type = d.get(3, jump_type)
byte_offset = int(d.get(0, byte_offset))
source_len = int(d.get(1, source_len))
file_index = int(d.get(2, file_index))
jump_type = d.get(3, jump_type)

new_srcmap[pos_to_offset[asm_pos]] = (byte_offset, source_len, file_index, jump_type)
new_srcmap[pos_to_offset[asm_pos]] = (byte_offset, source_len, file_index, jump_type)

return new_srcmap

@@ -270,7 +271,7 @@ def function_selectors(self) -> Iterable[bytes]:
"""
selectors = self._function_signatures_by_selector.keys()
if self._fallback_function_abi_item is None:
return selectors
return tuple(selectors)
return (*selectors, self.fallback_function_selector)

@property
1 change: 1 addition & 0 deletions manticore/platforms/evm.py
Original file line number Diff line number Diff line change
@@ -1742,6 +1742,7 @@ def CALLCODE(self, gas, address, value, in_offset, in_size, out_offset, out_size
def RETURN_gas(self, offset, size):
return self._get_memfee(offset, size)

@concretized_args(size='SAMPLED')
def RETURN(self, offset, size):
"""Halt execution returning output data"""
data = self.read_buffer(offset, size)
1,037 changes: 1,037 additions & 0 deletions tests/ethereum/data/MetaCoin.json

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion tests/ethereum/test_general.py
Original file line number Diff line number Diff line change
@@ -1369,6 +1369,20 @@ def test_overloaded_functions_and_events(self):
{'inputs': [], 'payable': False, 'stateMutability': 'nonpayable', 'type': 'constructor'})


class TruffleTests(unittest.TestCase):

def test_truffle_contract_schema(self):
filename = os.path.join(THIS_DIR, 'data/MetaCoin.json')
with open(filename, 'rb') as f:
truffle_json = f.read()
m = ManticoreEVM()
user_account = m.create_account(balance=1000, name='user_account')
contract_account = m.json_create_contract(truffle_json, owner=user_account, name='contract_account')
md: SolidityMetadata = m.get_metadata(contract_account)
self.assertEqual(md.runtime_bytecode, b'```@Rc\xff\xff\xff\xff`\xe0`\x02\n`\x005\x04\x16c{\xd7\x03\xe8\x81\x14a\x007W\x80c\x90\xb9\x8a\x11\x14a\x00eW\x80c\xf8\xb2\xcbO\x14a\x00\x98W[\xfe[4\x15a\x00?W\xfe[a\x00S`\x01`\xa0`\x02\n\x03`\x045\x16a\x00\xc6V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[4\x15a\x00mW\xfe[a\x00\x84`\x01`\xa0`\x02\n\x03`\x045\x16`$5a\x01MV[`@\x80Q\x91\x15\x15\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[4\x15a\x00\xa0W\xfe[a\x00S`\x01`\xa0`\x02\n\x03`\x045\x16a\x01\xe5V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[`\x00s{\xccc\xd4W\x90\xe2?n\x9b\xc3QN\x1a\xb5\xafd\x93\x02\xd0c\x96\xe4\xee=a\x00\xeb\x84a\x01\xe5V[`\x02`\x00`@Q` \x01R`@Q\x83c\xff\xff\xff\xff\x16`\xe0`\x02\n\x02\x81R`\x04\x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP` `@Q\x80\x83\x03\x81\x86\x80;\x15\x15a\x010W\xfe[a\x02\xc6Z\x03\xf4\x15\x15a\x01>W\xfe[PP`@QQ\x91PP[\x91\x90PV[`\x01`\xa0`\x02\n\x033\x16`\x00\x90\x81R` \x81\x90R`@\x81 T\x82\x90\x10\x15a\x01vWP`\x00a\x01\xdfV[`\x01`\xa0`\x02\n\x033\x81\x16`\x00\x81\x81R` \x81\x81R`@\x80\x83 \x80T\x88\x90\x03\x90U\x93\x87\x16\x80\x83R\x91\x84\x90 \x80T\x87\x01\x90U\x83Q\x86\x81R\x93Q\x91\x93\x7f\xdd\xf2R\xad\x1b\xe2\xc8\x9bi\xc2\xb0h\xfc7\x8d\xaa\x95+\xa7\xf1c\xc4\xa1\x16(\xf5ZM\xf5#\xb3\xef\x92\x90\x81\x90\x03\x90\x91\x01\x90\xa3P`\x01[\x92\x91PPV[`\x01`\xa0`\x02\n\x03\x81\x16`\x00\x90\x81R` \x81\x90R`@\x90 T[\x91\x90PV\x00')
self.assertEqual(md.init_bytecode, b"```@R4\x15a\x00\x0cW\xfe[[`\x01`\xa0`\x02\n\x032\x16`\x00\x90\x81R` \x81\x90R`@\x90 a\'\x10\x90U[[a\x020\x80a\x00;`\x009`\x00\xf3\x00```@Rc\xff\xff\xff\xff`\xe0`\x02\n`\x005\x04\x16c{\xd7\x03\xe8\x81\x14a\x007W\x80c\x90\xb9\x8a\x11\x14a\x00eW\x80c\xf8\xb2\xcbO\x14a\x00\x98W[\xfe[4\x15a\x00?W\xfe[a\x00S`\x01`\xa0`\x02\n\x03`\x045\x16a\x00\xc6V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[4\x15a\x00mW\xfe[a\x00\x84`\x01`\xa0`\x02\n\x03`\x045\x16`$5a\x01MV[`@\x80Q\x91\x15\x15\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[4\x15a\x00\xa0W\xfe[a\x00S`\x01`\xa0`\x02\n\x03`\x045\x16a\x01\xe5V[`@\x80Q\x91\x82RQ\x90\x81\x90\x03` \x01\x90\xf3[`\x00s{\xccc\xd4W\x90\xe2?n\x9b\xc3QN\x1a\xb5\xafd\x93\x02\xd0c\x96\xe4\xee=a\x00\xeb\x84a\x01\xe5V[`\x02`\x00`@Q` \x01R`@Q\x83c\xff\xff\xff\xff\x16`\xe0`\x02\n\x02\x81R`\x04\x01\x80\x83\x81R` \x01\x82\x81R` \x01\x92PPP` `@Q\x80\x83\x03\x81\x86\x80;\x15\x15a\x010W\xfe[a\x02\xc6Z\x03\xf4\x15\x15a\x01>W\xfe[PP`@QQ\x91PP[\x91\x90PV[`\x01`\xa0`\x02\n\x033\x16`\x00\x90\x81R` \x81\x90R`@\x81 T\x82\x90\x10\x15a\x01vWP`\x00a\x01\xdfV[`\x01`\xa0`\x02\n\x033\x81\x16`\x00\x81\x81R` \x81\x81R`@\x80\x83 \x80T\x88\x90\x03\x90U\x93\x87\x16\x80\x83R\x91\x84\x90 \x80T\x87\x01\x90U\x83Q\x86\x81R\x93Q\x91\x93\x7f\xdd\xf2R\xad\x1b\xe2\xc8\x9bi\xc2\xb0h\xfc7\x8d\xaa\x95+\xa7\xf1c\xc4\xa1\x16(\xf5ZM\xf5#\xb3\xef\x92\x90\x81\x90\x03\x90\x91\x01\x90\xa3P`\x01[\x92\x91PPV[`\x01`\xa0`\x02\n\x03\x81\x16`\x00\x90\x81R` \x81\x90R`@\x90 T[\x91\x90PV\x00")
self.assertSequenceEqual(md.function_selectors, (b'{\xd7\x03\xe8', b'\x90\xb9\x8a\x11', b'\xf8\xb2\xcbO'))

class EthSpecificTxIntructionTests(unittest.TestCase):

def test_jmpdest_check(self):
@@ -1481,7 +1495,6 @@ def test_delegatecall_env(self):
self.assertEqual( world.get_storage_data(0x111111111111111111111111111111111111111, 2), 0)
self.assertFalse(world.has_storage(0x333333333333333333333333333333333333333))


class EthPluginTests(unittest.TestCase):

def test_FilterFunctions_fallback_function_matching(self):