Example #1
0
def ecrecover(msg, signature, address=None):
    """
    Returns None on failure, returns the recovered address on success.
    If address is provided: Returns True if the recovered address matches it,
    otherwise False.
    """
    rawhash = sha3(msg)

    if len(signature) >= 65:
        v = safe_ord(signature[64]) + 27
        r = big_endian_to_int(signature[0:32])
        s = big_endian_to_int(signature[32:64])
    else:
        if address:
            return False
        else:
            return None

    pk = PublicKey(flags=ALL_FLAGS)
    pk.public_key = pk.ecdsa_recover(
        rawhash,
        pk.ecdsa_recoverable_deserialize(
            zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) +
            zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32), v - 27),
        raw=True)
    pub = pk.serialize(compressed=False)

    recaddr = data_encoder(sha3(pub[1:])[-20:])
    if address:
        if not address.startswith("0x"):
            recaddr = recaddr[2:]

        return recaddr == address

    return recaddr
Example #2
0
    def sign(self, payload):
        rawhash = keccak256(payload)

        v, r, s = ecsign(rawhash, self.key)
        signature = \
            zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \
            zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \
            bytearray_to_bytestr([v])

        return signature
Example #3
0
def personal_sign(private_key, message):

    if isinstance(private_key, str):
        private_key = data_decoder(private_key)

    rawhash = sha3("\x19Ethereum Signed Message:\n{}{}".format(len(message), message))

    v, r, s = ecsign(rawhash, private_key)
    signature = zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \
                zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \
                bytearray_to_bytestr([v])

    return data_encoder(signature)
Example #4
0
def sign_payload(private_key, payload):

    if isinstance(private_key, str):
        private_key = data_decoder(private_key)

    rawhash = sha3(payload)

    v, r, s = ecsign(rawhash, private_key)
    signature = zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32) + \
                zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32) + \
                bytearray_to_bytestr([v])

    return data_encoder(signature)
Example #5
0
def parseCode(code):
    code = code[2:] if code[:2] == '0x' else code

    try:
        codes = [c for c in decode_hex(code)]
    except ValueError as e:
        print(code)
        raise Exception("Did you forget to link any libraries?") from e

    instructions = collections.OrderedDict()
    pc = 0
    length = None
    while pc < len(codes):
        try:
            opcode = opcodes[codes[pc]]
        except KeyError:
            opcode = ['INVALID', 0, 0, 0]
        if opcode[0][:4] == 'PUSH':
            opcode = copy(opcode)
            length = codes[pc] - 0x5f
            pushData = codes[pc + 1:pc + length + 1]
            pushData = "0x" + encode_hex(bytearray_to_bytestr(pushData))
            if type(pushData) is not str:
                pushData = pushData.decode()
            opcode.append(pushData)

        instructions[pc] = opcode

        if length is not None:
            pc += length
            length = None
        pc += 1

    return instructions
Example #6
0
    def sender(self):

        if not self._sender:
            # Determine sender
            if self.v:
                if self.r >= N or self.s >= N or self.v < 27 or self.v > 28 \
                or self.r == 0 or self.s == 0:
                    raise InvalidTransaction("Invalid signature values!")
                log.debug('recovering sender')
                rlpdata = rlp.encode(self, UnsignedTransaction)
                rawhash = utils.sha3(rlpdata)

                pk = PublicKey(flags=ALL_FLAGS)
                try:
                    pk.public_key = pk.ecdsa_recover(
                        rawhash,
                        pk.ecdsa_recoverable_deserialize(
                            zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.r)), 32) + zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.s)), 32),
                            self.v - 27
                        ),
                        raw=True
                    )
                    pub = pk.serialize(compressed=False)
                except Exception:
                    raise InvalidTransaction("Invalid signature values (x^3+7 is non-residue)")

                if pub[1:] == b"\x00" * 32:
                    raise InvalidTransaction("Invalid signature (zero privkey cannot sign)")
                pub = encode_pubkey(pub, 'bin')
                self._sender = utils.sha3(pub[1:])[-20:]
                assert self.sender == self._sender
            else:
                self._sender = 0
        return self._sender
Example #7
0
    def sender(self):

        if not self._sender:
            # Determine sender
            if self.v:
                if self.r >= N or self.s >= N or self.v < 27 or self.v > 28 \
                or self.r == 0 or self.s == 0:
                    raise InvalidTransaction("Invalid signature values!")
                log.debug('recovering sender')
                rlpdata = rlp.encode(self, UnsignedTransaction)
                rawhash = utils.sha3(rlpdata)

                pk = PublicKey(flags=ALL_FLAGS)
                try:
                    pk.public_key = pk.ecdsa_recover(
                        rawhash,
                        pk.ecdsa_recoverable_deserialize(
                            zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.r)), 32) + zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.s)), 32),
                            self.v - 27
                        ),
                        raw=True
                    )
                    pub = pk.serialize(compressed=False)
                except Exception:
                    raise InvalidTransaction("Invalid signature values (x^3+7 is non-residue)")

                if pub[1:] == b"\x00" * 32:
                    raise InvalidTransaction("Invalid signature (zero privkey cannot sign)")
                pub = encode_pubkey(pub, 'bin')
                self._sender = utils.sha3(pub[1:])[-20:]
                assert self.sender == self._sender
            else:
                self._sender = 0
        return self._sender
Example #8
0
def parseCode(code):
    code = remove_0x_head(code)
    codes = [c for c in decode_hex(code)]

    instructions = collections.OrderedDict()
    pc = 0
    length = None
    while pc < len(codes):
        try:
            opcode = opcodes[codes[pc]]
        except KeyError:
            opcode = ['INVALID', 0, 0, 0]
        if opcode[0][:4] == 'PUSH':
            opcode = copy(opcode)
            length = codes[pc] - 0x5f
            pushData = codes[pc + 1:pc + length + 1]
            pushData = "0x" + encode_hex(
                bytearray_to_bytestr(pushData)).decode()
            opcode.append(pushData)

        instructions[pc] = opcode

        if length is not None:
            pc += length
            length = None
        pc += 1

    return instructions
Example #9
0
def test_ecrecover():
    s = tester.state()
    c = s.abi_contract(ecrecover_code)

    priv = utils.sha3('some big long brainwallet password')
    pub = bitcoin.privtopub(priv)

    msghash = utils.sha3('the quick brown fox jumps over the lazy dog')

    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(msghash, raw=True)
    )
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    V = utils.safe_ord(signature[64]) + 27
    R = big_endian_to_int(signature[0:32])
    S = big_endian_to_int(signature[32:64])

    assert bitcoin.ecdsa_raw_verify(msghash, (V, R, S), pub)

    addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:])
    assert utils.big_endian_to_int(utils.privtoaddr(priv)) == addr

    result = c.test_ecrecover(utils.big_endian_to_int(msghash), V, R, S)
    assert result == addr
Example #10
0
def sign(data: bytes, private_key_seed_ascii: str):
    priv = private_key_seed_ascii
    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(data, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    return signature, eth_privtoaddr(priv)
Example #11
0
def test_ecrecover():
    s = tester.state()
    c = s.abi_contract(ecrecover_code)

    priv = utils.sha3('some big long brainwallet password')
    pub = bitcoin.privtopub(priv)

    msghash = utils.sha3('the quick brown fox jumps over the lazy dog')

    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(msghash, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    V = utils.safe_ord(signature[64]) + 27
    R = big_endian_to_int(signature[0:32])
    S = big_endian_to_int(signature[32:64])

    assert bitcoin.ecdsa_raw_verify(msghash, (V, R, S), pub)

    addr = utils.big_endian_to_int(
        utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:])
    assert utils.big_endian_to_int(utils.privtoaddr(priv)) == addr

    result = c.test_ecrecover(utils.big_endian_to_int(msghash), V, R, S)
    assert result == addr
Example #12
0
    def sha3_(self, global_state):
        global keccak_function_manager

        state = global_state.mstate
        environment = global_state.environment
        op0, op1 = state.stack.pop(), state.stack.pop()

        try:
            index, length = util.get_concrete_int(op0), util.get_concrete_int(op1)
        # FIXME: broad exception catch
        except:
            # Can't access symbolic memory offsets
            if is_expr(op0):
                op0 = simplify(op0)
            state.stack.append(BitVec("KECCAC_mem[" + str(op0) + "]", 256))
            return [global_state]

        try:
            state.mem_extend(index, length)
            data = b''.join([util.get_concrete_int(i).to_bytes(1, byteorder='big')
                             for i in state.memory[index: index + length]])

        except AttributeError:
            argument = str(state.memory[index]).replace(" ", "_")

            result = BitVec("KECCAC[{}]".format(argument), 256)
            keccak_function_manager.add_keccak(result, state.memory[index])
            state.stack.append(result)
            return [global_state]

        keccak = utils.sha3(utils.bytearray_to_bytestr(data))
        logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccak)))

        state.stack.append(BitVecVal(util.concrete_int_from_bytes(keccak, 0), 256))
        return [global_state]
Example #13
0
def apply_message(state, msg=None, **kwargs):
    if msg is None:
        msg = vm.Message(**kwargs)
    else:
        assert not kwargs
    ext = VMExt(state, transactions.Transaction(0, 0, 21000, b'', 0, b''))
    result, gas_remained, data = apply_msg(ext, msg)
    return bytearray_to_bytestr(data) if result else None
def sign(data: str, private_key_seed_ascii: str):
    data = eth_message_hex(data)
    print("--eth_message_hex hash", data)
    priv = private_key_seed_ascii
    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(data, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    return signature, eth_privtoaddr(priv)
Example #15
0
def sign(data: bytes,
         private_key_seed_ascii: str,
         hash_function=bitcoin.bin_sha256):
    """Sign data using Ethereum private key.

    :param private_key_seed_ascii: Private key seed as ASCII string
    """

    msghash = hash_function(data)

    priv = utils.sha3(private_key_seed_ascii)
    pub = bitcoin.privtopub(priv)

    # Based on ethereum/tesrt_contracts.py test_ecrecover
    pk = PrivateKey(priv, raw=True)

    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(msghash, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])

    # Enforce non-tightly-packed arguments for signing
    # (0x00 left pad)
    # https://github.com/ethereum/web3.py/issues/466
    v = utils.safe_ord(signature[64]) + 27
    r_bytes = signature[0:32]
    r_bytes = pad_left(r_bytes, 32, b"\0")
    r = big_endian_to_int(r_bytes)
    s_bytes = signature[32:64]
    s_bytes = pad_left(s_bytes, 32, b"\0")
    s = big_endian_to_int(s_bytes)

    # Make sure we use bytes data and zero padding stays
    # good across different systems
    r_hex = binascii.hexlify(r_bytes).decode("ascii")
    s_hex = binascii.hexlify(s_bytes).decode("ascii")

    # Convert to Etheruem address format
    addr = utils.big_endian_to_int(
        utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:])

    # Return various bits about signing so it's easier to debug
    return {
        "signature": signature,
        "v": v,
        "r": r,
        "s": s,
        "r_bytes": r_bytes,
        "s_bytes": s_bytes,
        "r_hex": "0x" + r_hex,
        "s_hex": "0x" + s_hex,
        "address_bitcoin": addr,
        "address_ethereum": get_ethereum_address_from_private_key(priv),
        "public_key": pub,
        "hash": msghash,
        "payload":
        binascii.hexlify(bytes([v] + list(r_bytes) + list(s_bytes, )))
    }
Example #16
0
def sign_eth(rawhash, priv):
    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(rawhash, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    v = utils.safe_ord(signature[64]) + 27
    r = utils.big_endian_to_int(signature[0:32])
    s = utils.big_endian_to_int(signature[32:64])
    return (v, r, s)
def sign_eth(rawhash, priv):
    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(rawhash, raw=True)
    )
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    v = utils.safe_ord(signature[64]) + 27
    r = utils.big_endian_to_int(signature[0:32])
    s = utils.big_endian_to_int(signature[32:64])
    return (v, r, s)
Example #18
0
    def run(self, sender=None, to=None, code=None, gas=None):
        sender = normalize_address(sender) if sender else normalize_address(zpad('sender', 20))
        to = normalize_address(to) if to else normalize_address(zpad('receiver', 20))
        code = scan_bin(code) if code else ''
        gas = scan_int(gas) if gas else 10000000000000

        msg = vm.Message(sender, to, gas=gas)
        ext = VMExt(self.state, Transaction(0, 0, 21000, b'', 0, b''))

        result, gas_remained, data = _apply_msg(ext, msg, code)
        return bytearray_to_bytestr(data) if result else None
Example #19
0
def sign_payload(private_key, payload):

    if isinstance(private_key, str):
        private_key = data_decoder(private_key)

    rawhash = sha3(payload)

    pk = PrivateKey(private_key, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(rawhash, raw=True))
    signature = signature[0] + bytearray_to_bytestr([signature[1]])

    return data_encoder(signature)
Example #20
0
def personal_sign(private_key, message):

    if isinstance(private_key, str):
        private_key = data_decoder(private_key)

    rawhash = sha3("\x19Ethereum Signed Message:\n{}{}".format(
        len(message), message))

    pk = PrivateKey(private_key, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(rawhash, raw=True))
    signature = signature[0] + bytearray_to_bytestr([signature[1] + 27])

    return data_encoder(signature)
Example #21
0
def sign(data: bytes,
         private_key_seed_ascii: str,
         hash_function=bitcoin.bin_sha256):
    """Sign data using Ethereum private key.

    :param private_key_seed_ascii: Private key seed as ASCII string
    """

    msghash = hash_function(data)

    priv = utils.sha3(private_key_seed_ascii)
    pub = bitcoin.privtopub(priv)

    # Based on ethereum/tesrt_contracts.py test_ecrecover
    pk = PrivateKey(priv, raw=True)

    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(msghash, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])

    v = utils.safe_ord(signature[64]) + 27
    r_bytes = signature[0:32]
    r = big_endian_to_int(r_bytes)
    s_bytes = signature[32:64]
    s = big_endian_to_int(s_bytes)

    #: XXX Ethereum wants its address in different format
    addr = utils.big_endian_to_int(
        utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:])

    return {
        "signature": signature,
        "v": v,
        "r": r,
        "s": s,
        "r_bytes": r_bytes,
        "s_bytes": s_bytes,
        "address_bitcoin": addr,
        "address_ethereum": eth_privtoaddr(priv),
        "public_key": pub,
        "hash": msghash,
        "payload": bytes([v] + list(r_bytes) + list(s_bytes, ))
    }
Example #22
0
def eth_sign_with_keyfile(message: bytes, raw: bool, keyfile: str, password: str):
    assert(isinstance(message, bytes))
    assert(isinstance(raw, bool))
    assert(isinstance(keyfile, str))
    assert(isinstance(password, str))

    if not raw:
        message = hexstring_to_bytes(Eth._recoveryMessageHash(data=message))

    key = eth_keyfile.decode_keyfile_json(eth_keyfile.load_keyfile(keyfile), bytes(password, 'utf-8'))
    pk = PrivateKey(key, raw=True)
    signature = pk.ecdsa_recoverable_serialize(
        pk.ecdsa_sign_recoverable(message, raw=True)
    )

    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex()

    return '0x' + signature_hex
Example #23
0
    def may_write_to(self, z3_index, z3_value, storage, constraint_list, consider_length):
        if consider_length:
            may_write_to, added_constraints = ConcretSlot.may_write_to(self, z3_index, z3_value, storage, constraint_list, consider_length)
            if may_write_to:
                return may_write_to, added_constraints

        slot_hash = utils.sha3(utils.bytearray_to_bytestr(util.concrete_int_to_bytes(self.slot_counter)))
        slot_hash = str(BitVecVal(util.concrete_int_from_bytes(slot_hash, 0), 256))

        z3_index_str = str(z3_index).replace("\n", "")
        if z3_index_str.startswith(slot_hash):
            if z3_index_str.endswith(slot_hash) or z3_index_str[len(slot_hash):].startswith(" +"):
                return True, []
        if z3_index_str in keccak_map:
            unresolved_index = keccak_map[str(z3_index).replace("\n" , "")]
            unresolved_index = str(unresolved_index).replace("\n", "")
            if unresolved_index.startswith(str(self.slot_counter)):
                if unresolved_index.endswith(str(self.slot_counter)) or unresolved_index[len(str(self.slot_counter)):].startswith(" +"):
                    return True, []
        return False, []
Example #24
0
def sign(data: bytes, private_key_seed_ascii: str, hash_function=bitcoin.bin_sha256):
    """Sign data using Ethereum private key.

    :param private_key_seed_ascii: Private key seed as ASCII string
    """

    msghash = hash_function(data)

    priv = utils.sha3(private_key_seed_ascii)
    pub = bitcoin.privtopub(priv)

    # Based on ethereum/tesrt_contracts.py test_ecrecover
    pk = PrivateKey(priv, raw=True)

    signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(msghash, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])

    v = utils.safe_ord(signature[64]) + 27
    r_bytes = signature[0:32]
    r = big_endian_to_int(r_bytes)
    s_bytes = signature[32:64]
    s = big_endian_to_int(s_bytes)

    #: XXX Ethereum wants its address in different format
    addr = utils.big_endian_to_int(utils.sha3(bitcoin.encode_pubkey(pub, 'bin')[1:])[12:])

    return {
        "signature": signature,
        "v": v,
        "r": r,
        "s": s,
        "r_bytes": r_bytes,
        "s_bytes": s_bytes,
        "address_bitcoin": addr,
        "address_ethereum": eth_privtoaddr(priv),
        "public_key": pub,
        "hash": msghash,
        "payload": bytes([v] + list(r_bytes)+ list(s_bytes,))
    }
Example #25
0
def eth_sign(message: bytes, web3: Web3):
    assert(isinstance(message, bytes))
    assert(isinstance(web3, Web3))

    # as `EthereumTesterProvider` does not support `eth_sign`, we implement it ourselves
    if str(web3.providers[0]) == 'EthereumTesterProvider':
        key = k0
        msg = hexstring_to_bytes(Eth._recoveryMessageHash(data=message))

        pk = PrivateKey(key, raw=True)
        signature = pk.ecdsa_recoverable_serialize(
            pk.ecdsa_sign_recoverable(msg, raw=True)
        )

        signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
        signature_hex = signature.hex()[0:128] + int_to_bytes(ord(bytes.fromhex(signature.hex()[128:130]))+27).hex()

        return '0x' + signature_hex

    return web3.manager.request_blocking(
        "eth_sign", [web3.eth.defaultAccount, encode_hex(message)],
    )
Example #26
0
    def sha3_(self, global_state):
        state = global_state.mstate
        environment = global_state.environment
        op0, op1 = state.stack.pop(), state.stack.pop()

        try:
            index, length = util.get_concrete_int(op0), util.get_concrete_int(
                op1)
        # FIXME: broad exception catch
        except:
            # Can't access symbolic memory offsets
            if is_expr(op0):
                op0 = simplify(op0)
            state.stack.append(BitVec("KECCAC_mem[" + str(op0) + "]", 256))
            return [global_state]

        try:
            data = b''

            for i in range(index, index + length):
                data += util.get_concrete_int(state.memory[i]).to_bytes(
                    1, byteorder='big')
                i += 1
            # FIXME: broad exception catch
        except:

            svar = str(state.memory[index])

            svar = svar.replace(" ", "_")

            state.stack.append(BitVec("keccac_" + svar, 256))
            return [global_state]

        keccac = utils.sha3(utils.bytearray_to_bytestr(data))
        logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccac)))

        state.stack.append(
            BitVecVal(util.concrete_int_from_bytes(keccac, 0), 256))
        return [global_state]
Example #27
0
    def sign(self, key):
        """Sign this transaction with a private key.

        A potentially already existing signature would be overridden.
        """
        if key in (0, '', b'\x00' * 32, '0' * 64):
            raise InvalidTransaction("Zero privkey cannot sign")
        rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction))

        if len(key) == 64:
            # we need a binary key
            key = encode_privkey(key, 'bin')

        pk = PrivateKey(key, raw=True)
        signature = pk.ecdsa_recoverable_serialize(
            pk.ecdsa_sign_recoverable(rawhash, raw=True))
        signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
        self.v = utils.safe_ord(signature[64]) + 27
        self.r = big_endian_to_int(signature[0:32])
        self.s = big_endian_to_int(signature[32:64])

        self.sender = utils.privtoaddr(key)
        return self
Example #28
0
    def sign(self, key):
        """Sign this transaction with a private key.

        A potentially already existing signature would be overridden.
        """
        if key in (0, '', b'\x00' * 32, '0' * 64):
            raise InvalidTransaction("Zero privkey cannot sign")
        rawhash = utils.sha3(rlp.encode(self, UnsignedTransaction))

        if len(key) == 64:
            # we need a binary key
            key = encode_privkey(key, 'bin')

        pk = PrivateKey(key, raw=True)
        signature = pk.ecdsa_recoverable_serialize(
            pk.ecdsa_sign_recoverable(rawhash, raw=True)
        )
        signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
        self.v = utils.safe_ord(signature[64]) + 27
        self.r = big_endian_to_int(signature[0:32])
        self.s = big_endian_to_int(signature[32:64])

        self.sender = utils.privtoaddr(key)
        return self
Example #29
0
    def _sym_exec(self, context, state, depth=0, constraints=[]):

        disassembly = context.module['disassembly']
        depth = depth

        start_addr = disassembly.instruction_list[state.pc]['address']

        if start_addr == 0:
            self.execution_state['current_func'] = "fallback"
            self.execution_state['current_func_addr'] = start_addr

        node = Node(context.module['name'], start_addr, constraints)

        logging.debug("DEBUG Node(type=" + str(type(node)) + "): " + str(node))

        logging.debug("- Entering block " + str(node.uid) + ", index = " +
                      str(state.pc) + ", address = " + str(start_addr) +
                      ", depth = " + str(depth))

        if start_addr in disassembly.addr_to_func:
            # Enter a new function

            function_name = disassembly.addr_to_func[start_addr]
            self.execution_state['current_func'] = function_name

            logging.info("- Entering function " + context.module['name'] +
                         ":" + function_name)

            node.instruction_list.append({
                'opcode':
                function_name,
                'address':
                disassembly.instruction_list[state.pc]['address']
            })

            state.pc += 1

        node.function_name = self.execution_state['current_func']

        halt = False

        instr = disassembly.instruction_list[state.pc]

        while not halt:

            try:
                instr = disassembly.instruction_list[state.pc]
            except IndexError:
                logging.debug("Invalid PC")
                return node

            # Save instruction and state

            node.instruction_list.append(instr)
            node.states[instr['address']] = state

            state = copy.deepcopy(state)
            self.total_states += 1
            state.pc += 1

            op = instr['opcode']

            logging.debug("[" + context.module['name'] + "] " +
                          helper.get_trace_line(instr, state))
            # slows down execution significantly.

            # stack ops

            if op.startswith("PUSH"):
                value = BitVecVal(int(instr['argument'][2:], 16), 256)
                state.stack.append(value)

            elif op.startswith('DUP'):
                dpth = int(op[3:])

                try:
                    state.stack.append(state.stack[-dpth])
                except:
                    halt = True
                    continue

            elif op.startswith('SWAP'):

                dpth = int(op[4:])

                try:
                    temp = state.stack[-dpth - 1]
                except IndexError:  # Stack underflow
                    halt = True
                    continue

                state.stack[-dpth - 1] = state.stack[-1]
                state.stack[-1] = temp

            elif op == 'POP':
                try:
                    state.stack.pop()
                except IndexError:  # Stack underflow
                    halt = True
                    continue

            # Bitwise ops

            elif op == 'AND':
                try:
                    state.stack.append(state.stack.pop() & state.stack.pop())
                except IndexError:  # Stack underflow
                    halt = True
                    continue

            elif op == 'OR':
                try:
                    op1, op2 = state.stack.pop(), state.stack.pop()
                except IndexError:  # Stack underflow
                    halt = True
                    continue

                if (type(op1) == BoolRef):
                    op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

                if (type(op2) == BoolRef):
                    op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

                state.stack.append(op1 | op2)

            elif op == 'XOR':
                state.stack.append(state.stack.pop() ^ state.stack.pop())

            elif op == 'NOT':
                state.stack.append(TT256M1 - state.stack.pop())

            elif op == 'BYTE':
                s0, s1 = state.stack.pop(), state.stack.pop()

                state.stack.append(BitVecVal(0, 256))

            # Arithmetics

            elif op == 'ADD':
                state.stack.append(
                    (helper.pop_bitvec(state) + helper.pop_bitvec(state)))

            elif op == 'SUB':
                state.stack.append(
                    (helper.pop_bitvec(state) - helper.pop_bitvec(state)))

            elif op == 'MUL':
                state.stack.append(
                    helper.pop_bitvec(state) * helper.pop_bitvec(state))

            elif op == 'DIV':
                s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state)

                state.stack.append(UDiv(s0, s1))

            elif op == 'MOD':
                s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append(0 if s1 == 0 else s0 % s1)

            elif op == 'SDIV':
                s0, s1 = helper.to_signed(
                    helper.pop_bitvec(state)), helper.to_signed(
                        helper.pop_bitvec(state))
                state.stack.append(0 if s1 == 0 else (
                    abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1)

            elif op == 'SMOD':
                s0, s1 = helper.to_signed(
                    helper.pop_bitvec(state)), helper.to_signed(
                        helper.pop_bitvec(state))
                state.stack.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                      (-1 if s0 < 0 else 1))
                                   & TT256M1)

            elif op == 'ADDMOD':
                s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(
                    state), helper.pop_bitvec(state)
                state.stack.append((s0 + s1) % s2 if s2 else 0)

            elif op == 'MULMOD':
                s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(
                    state), helper.pop_bitvec(state)
                state.stack.append((s0 * s1) % s2 if s2 else 0)

            elif op == 'EXP':
                # we only implement 2 ** x
                base, exponent = state.stack.pop(), state.stack.pop()

                if (type(base) != BitVecNumRef):
                    state.stack.append(
                        BitVec(str(base) + "_EXP_" + str(exponent), 256))
                elif (base.as_long() == 2):
                    if exponent == 0:
                        state.stack.append(BitVecVal(1, 256))
                    else:
                        state.stack.append(base << (exponent - 1))

                else:
                    state.stack.append(base)

            elif op == 'SIGNEXTEND':
                s0, s1 = state.stack.pop(), state.stack.pop()

                try:
                    s0 = get_concrete_int(s0)
                    s1 = get_concrete_int(s1)
                except:
                    halt = True
                    continue

                if s0 <= 31:
                    testbit = s0 * 8 + 7
                    if s1 & (1 << testbit):
                        state.stack.append(s1 | (TT256 - (1 << testbit)))
                    else:
                        state.stack.append(s1 & ((1 << testbit) - 1))
                else:
                    state.stack.append(s1)

            # Comparisons

            elif op == 'LT':

                exp = ULT(helper.pop_bitvec(state), helper.pop_bitvec(state))
                state.stack.append(exp)

            elif op == 'GT':

                exp = UGT(helper.pop_bitvec(state), helper.pop_bitvec(state))
                state.stack.append(exp)

            elif op == 'SLT':

                exp = helper.pop_bitvec(state) < helper.pop_bitvec(state)
                state.stack.append(exp)

            elif op == 'SGT':

                exp = helper.pop_bitvec(state) > helper.pop_bitvec(state)
                state.stack.append(exp)

            elif op == 'EQ':

                op1 = state.stack.pop()
                op2 = state.stack.pop()

                if (type(op1) == BoolRef):
                    op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

                if (type(op2) == BoolRef):
                    op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

                exp = op1 == op2

                state.stack.append(exp)

            elif op == 'ISZERO':

                val = state.stack.pop()

                if (type(val) == BoolRef):
                    exp = val == False
                else:
                    exp = val == 0

                state.stack.append(exp)

            # Call data

            elif op == 'CALLVALUE':
                state.stack.append(context.callvalue)

            elif op == 'CALLDATALOAD':
                # unpack 32 bytes from calldata into a word and put it on the stack

                op0 = state.stack.pop()

                try:
                    offset = helper.get_concrete_int(simplify(op0))
                except AttributeError:
                    logging.debug("CALLDATALOAD: Unsupported symbolic index")
                    state.stack.append(
                        BitVec(
                            "calldata_" + str(context.module['name']) + "_" +
                            str(op0), 256))
                    continue

                try:
                    b = context.calldata[offset]
                except IndexError:
                    logging.debug(
                        "Calldata not set, using symbolic variable instead")
                    state.stack.append(
                        BitVec(
                            "calldata_" + str(context.module['name']) + "_" +
                            str(op0), 256))
                    continue

                if type(b) == int:
                    # 32 byte concrete value

                    val = b''

                    try:
                        for i in range(offset, offset + 32):
                            val += context.calldata[i].to_bytes(
                                1, byteorder='big')

                        state.stack.append(
                            BitVecVal(int.from_bytes(val, byteorder='big'),
                                      256))

                    except:
                        state.stack.append(b)
                else:
                    # symbolic variable
                    state.stack.append(b)

            elif op == 'CALLDATASIZE':

                if context.calldata_type == CalldataType.SYMBOLIC:
                    state.stack.append(
                        BitVec("calldatasize_" + context.module['name'], 256))
                else:
                    state.stack.append(BitVecVal(len(context.calldata), 256))

            elif op == 'CALLDATACOPY':
                op0, op1, op2 = state.stack.pop(), state.stack.pop(
                ), state.stack.pop()

                try:
                    mstart = helper.get_concrete_int(op0)
                except:
                    logging.debug(
                        "Unsupported symbolic memory offset in CALLDATACOPY")
                    continue

                try:
                    dstart = helper.get_concrete_int(op1)
                except:
                    logging.debug(
                        "Unsupported symbolic calldata offset in CALLDATACOPY")
                    state.mem_extend(mstart, 1)
                    state.memory[mstart] = BitVec(
                        "calldata_" + str(context.module['name']) + "_cpy",
                        256)
                    continue

                try:
                    size = helper.get_concrete_int(op2)
                except:
                    logging.debug("Unsupported symbolic size in CALLDATACOPY")
                    state.mem_extend(mstart, 1)
                    state.memory[mstart] = BitVec(
                        "calldata_" + str(context.module['name']) + "_" +
                        str(dstart), 256)
                    continue

                if size > 0:

                    try:
                        state.mem_extend(mstart, size)
                    except:
                        logging.debug("Memory allocation error: mstart = " +
                                      str(mstart) + ", size = " + str(size))
                        state.mem_extend(mstart, 1)
                        state.memory[mstart] = BitVec(
                            "calldata_" + str(context.module['name']) + "_" +
                            str(dstart), 256)
                        continue

                    try:
                        i_data = context.calldata[dstart]

                        for i in range(mstart, mstart + size):
                            state.memory[i] = context.calldata[i_data]
                            i_data += 1
                    except:
                        logging.debug("Exception copying calldata to memory")

                        state.memory[mstart] = BitVec(
                            "calldata_" + str(context.module['name']) + "_" +
                            str(dstart), 256)

                        # continue

            # Control flow

            elif op == 'STOP':
                if self.last_call_address is not None:
                    self.pending_returns[self.last_call_address].append(
                        node.uid)

                halt = True
                continue

            # Environment

            elif op == 'ADDRESS':
                state.stack.append(context.address)

            elif op == 'BALANCE':
                addr = state.stack.pop()
                state.stack.append(BitVec("balance_at_" + str(addr), 256))

            elif op == 'ORIGIN':
                state.stack.append(context.origin)

            elif op == 'CALLER':
                state.stack.append(context.caller)

            elif op == 'CODESIZE':
                state.stack.append(len(disassembly.instruction_list))

            if op == 'SHA3':
                op0, op1 = state.stack.pop(), state.stack.pop()

                try:
                    index, length = helper.get_concrete_int(
                        op0), helper.get_concrete_int(op1)

                except:
                    # Can't access symbolic memory offsets
                    state.stack.append(
                        BitVec("KECCAC_mem_" + str(op0) + ")", 256))
                    continue

                try:
                    data = b''

                    for i in range(index, index + length):
                        data += helper.get_concrete_int(
                            state.memory[i]).to_bytes(1, byteorder='big')
                        i += 1

                except:

                    svar = str(state.memory[index])

                    svar = svar.replace(" ", "_")

                    state.stack.append(BitVec("keccac_" + svar, 256))
                    continue

                logging.debug("SHA3 Data: " + str(data))

                keccac = utils.sha3(utils.bytearray_to_bytestr(data))

                logging.debug("SHA3 Hash: " + str(binascii.hexlify(keccac)))

                state.stack.append(
                    BitVecVal(helper.concrete_int_from_bytes(keccac, 0), 256))

            elif op == 'GASPRICE':
                state.stack.append(BitVecVal(1, 256))

            elif op == 'CODECOPY':
                # Not implemented
                start, s1, size = state.stack.pop(), state.stack.pop(
                ), state.stack.pop()

            elif op == 'EXTCODESIZE':
                addr = state.stack.pop()
                state.stack.append(BitVec("extcodesize", 256))

            elif op == 'EXTCODECOPY':
                # Not implemented

                addr = state.stack.pop()
                start, s2, size = state.stack.pop(), state.stack.pop(
                ), state.stack.pop()

            elif op == 'BLOCKHASH':
                blocknumber = state.stack.pop()
                state.stack.append(
                    BitVec("blockhash_block_" + str(blocknumber), 256))

            elif op == 'COINBASE':
                state.stack.append(BitVec("coinbase", 256))

            elif op == 'TIMESTAMP':
                state.stack.append(BitVec("timestamp", 256))

            elif op == 'NUMBER':
                state.stack.append(BitVec("block_number", 256))

            elif op == 'DIFFICULTY':
                state.stack.append(BitVec("block_difficulty", 256))

            elif op == 'GASLIMIT':
                state.stack.append(BitVec("block_gaslimit", 256))

            elif op == 'MLOAD':

                op0 = state.stack.pop()

                logging.debug("MLOAD[" + str(op0) + "]")

                try:
                    offset = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("Can't MLOAD from symbolic index")
                    data = BitVec("mem_" + str(op0), 256)
                    continue

                try:
                    data = helper.concrete_int_from_bytes(state.memory, offset)
                except IndexError:  # Memory slot not allocated
                    data = BitVec("mem_" + str(offset), 256)
                except TypeError:  # Symbolic memory
                    data = state.memory[offset]

                logging.debug("Load from memory[" + str(offset) + "]: " +
                              str(data))

                state.stack.append(data)

            elif op == 'MSTORE':

                op0, value = state.stack.pop(), state.stack.pop()

                try:
                    mstart = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("MSTORE to symbolic index. Not supported")
                    continue

                try:
                    state.mem_extend(mstart, 32)
                except Exception:
                    logging.debug("Error extending memory, mstart = " +
                                  str(mstart) + ", size = 32")

                logging.debug("MSTORE to mem[" + str(mstart) + "]: " +
                              str(value))

                try:
                    # Attempt to concretize value
                    _bytes = helper.concrete_int_to_bytes(value)

                    i = 0

                    for b in _bytes:
                        state.memory[mstart + i] = _bytes[i]
                        i += 1

                except:
                    try:
                        state.memory[mstart] = value
                    except:
                        logging.debug("Invalid memory access")
                        continue

                # logging.debug("MEM: " + str(state.memory))

            elif op == 'MSTORE8':
                # Is this ever used?
                op0, value = state.stack.pop(), state.stack.pop()

                try:
                    offset = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("MSTORE to symbolic index. Not supported")
                    continue

                state.mem_extend(offset, 1)

                state.memory[offset] = value % 256

            elif op == 'SLOAD':
                index = state.stack.pop()
                logging.debug("Storage access at index " + str(index))

                if type(index) == BitVecRef:
                    # SLOAD from hash offset

                    # k = sha3.keccak_512()
                    # k.update(bytes(str(index), 'utf-8'))
                    # index = k.hexdigest()[:8]

                    index = str(index)

                try:
                    data = state.storage[index]
                except KeyError:
                    data = BitVec("storage_" + str(index), 256)
                    state.storage[index] = data

                state.stack.append(data)

            elif op == 'SSTORE':
                index, value = state.stack.pop(), state.stack.pop()

                logging.debug("Write to storage[" + str(index) + "] at node " +
                              str(start_addr))

                if type(index) == BitVecRef:
                    index = str(index)

                try:
                    state.storage[index] = value
                except KeyError:
                    logging.debug("Error writing to storage: Invalid index")
                    continue

            elif op == 'JUMP':

                try:
                    jump_addr = helper.get_concrete_int(state.stack.pop())
                except AttributeError:
                    logging.debug("Invalid jump argument (symbolic address)")
                    halt = True
                    continue
                except IndexError:  # Stack Underflow
                    halt = True
                    continue

                if (depth < self.max_depth):

                    i = helper.get_instruction_index(
                        disassembly.instruction_list, jump_addr)

                    if i is None:
                        logging.debug("JUMP to invalid address")
                        halt = True
                        continue

                    opcode = disassembly.instruction_list[i]['opcode']

                    if opcode == "JUMPDEST":

                        if (self.can_jump(jump_addr)):

                            new_state = copy.deepcopy(state)
                            new_state.pc = i

                            new_node = self._sym_exec(context,
                                                      new_state,
                                                      depth=depth + 1,
                                                      constraints=constraints)
                            self.nodes[new_node.uid] = new_node

                            self.edges.append(
                                Edge(node.uid, new_node.uid,
                                     JumpType.UNCONDITIONAL))
                            halt = True
                            continue
                        else:
                            logging.debug("JUMP target limit reached")
                            halt = True
                            continue
                    else:
                        logging.debug(
                            "Skipping JUMP to invalid destination (not JUMPDEST): "
                            + str(jump_addr))
                        halt = True
                        continue
                else:
                    logging.debug("Max depth reached, skipping JUMP")
                    halt = True
                    continue

            elif op == 'JUMPI':
                op0, condition = state.stack.pop(), state.stack.pop()

                try:
                    jump_addr = helper.get_concrete_int(op0)
                except:
                    logging.debug("Skipping JUMPI to invalid destination.")

                if (depth < self.max_depth):

                    i = helper.get_instruction_index(
                        disassembly.instruction_list, jump_addr)

                    if not i:
                        logging.debug("Invalid jump destination: " +
                                      str(jump_addr))

                    else:
                        instr = disassembly.instruction_list[i]

                        # Add new node for condition == True

                        if instr['opcode'] != "JUMPDEST":
                            logging.debug("Invalid jump destination: " +
                                          str(jump_addr))

                        else:

                            if (type(condition) == bool):
                                logging.debug("BOOL CONDITION TYPE")
                                # continue

                            elif (type(condition) == BoolRef):

                                if (self.can_jump(jump_addr)):

                                    new_state = copy.deepcopy(state)
                                    new_state.pc = i

                                    new_constraints = copy.deepcopy(
                                        constraints)
                                    new_constraints.append(condition)

                                    new_node = self._sym_exec(
                                        context,
                                        new_state,
                                        depth=depth + 1,
                                        constraints=new_constraints)
                                    self.nodes[new_node.uid] = new_node

                                    self.edges.append(
                                        Edge(node.uid, new_node.uid,
                                             JumpType.CONDITIONAL, condition))

                                else:
                                    logging.debug(
                                        "JUMP target limit reached (JUMPI)")

                            else:
                                logging.debug("Invalid condition: " +
                                              str(condition) + "(type " +
                                              str(type(condition)) + ")")
                                halt = True
                                continue

                        new_state = copy.deepcopy(state)

                        if (type(condition) == BoolRef):
                            negated = Not(condition)
                        else:
                            negated = condition == 0

                        new_constraints = copy.deepcopy(constraints)
                        new_constraints.append(negated)

                        new_node = self._sym_exec(context,
                                                  new_state,
                                                  depth=depth,
                                                  constraints=new_constraints)
                        self.nodes[new_node.uid] = new_node

                        self.edges.append(
                            Edge(node.uid, new_node.uid, JumpType.CONDITIONAL,
                                 negated))

                        halt = True
                        continue

                else:
                    logging.debug("Max depth reached, skipping JUMPI")

            elif op == 'PC':
                state.stack.append(state.pc - 1)

            elif op == 'MSIZE':
                state.stack.append(BitVec("msize", 256))

            elif op == 'GAS':
                state.stack.append(10000000)

            elif op.startswith('LOG'):
                dpth = int(op[3:])
                state.stack.pop(), state.stack.pop()
                [state.stack.pop() for x in range(dpth)]
                # Not supported

            elif op == 'CREATE':
                state.stack.pop(), state.stack.pop(), state.stack.pop()
                # Not supported
                state.stack.append(0)

            elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):

                if op in ('CALL', 'CALLCODE'):
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                        state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop()

                else:
                    gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                        state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop()

                try:
                    callee_address = hex(helper.get_concrete_int(to))

                except AttributeError:
                    # Not a concrete call address. Call target may be an address in storage.

                    m = re.search(r'storage_(\d+)', str(simplify(to)))

                    logging.debug("CALL to: " + str(simplify(to)))

                    if (m and self.dynamic_loader is not None):
                        idx = int(m.group(1))
                        logging.info(
                            "Dynamic contract address at storage index " +
                            str(idx))

                        # attempt to read the contract address from instance storage

                        callee_address = self.dynamic_loader.read_storage(
                            context.module['address'], idx)

                        # testrpc simply returns the address, geth response is more elaborate.

                        if not re.match(r"^0x[0-9a-f]{40}$", callee_address):

                            callee_address = "0x" + callee_address[26:]

                    else:
                        logging.info("Unable to resolve address from storage.")
                        ret = BitVec(
                            "retval_" + str(disassembly.instruction_list[
                                state.pc]['address']), 256)
                        state.stack.append(ret)
                        continue

                if (int(callee_address, 16) < 5):

                    logging.info("Native contract called: " + callee_address)

                    # Todo: Implement native contracts

                    ret = BitVec(
                        "retval_" +
                        str(disassembly.instruction_list[state.pc]['address']),
                        256)
                    state.stack.append(ret)
                    continue

                if not re.match(r"^0x[0-9a-f]{40}", callee_address):
                    logging.debug("Invalid address: " + str(callee_address))
                    ret = BitVec(
                        "retval_" +
                        str(disassembly.instruction_list[state.pc]['address']),
                        256)
                    state.stack.append(ret)

                    continue

                try:

                    module = self.modules[callee_address]

                except KeyError:
                    # We have a valid call address, but contract is not in the modules list

                    logging.info("Module with address " + callee_address +
                                 " not loaded.")

                    if self.dynamic_loader is not None:

                        logging.info("Attempting to load dependency")

                        module = self.dynamic_loader.dynld(
                            context.module['address'], callee_address)

                        if module is None:

                            logging.info(
                                "No code returned, not a contract account?")
                            ret = BitVec(
                                "retval_" + str(disassembly.instruction_list[
                                    state.pc]['address']), 256)
                            state.stack.append(ret)
                            continue

                        # New contract loaded successfully, add it to the modules list

                        self.modules[callee_address] = module
                        self.addr_visited[callee_address] = []

                        logging.info("Dependency loaded: " + module['address'])

                    else:
                        logging.info(
                            "Dynamic loader unavailable. Skipping call")
                        ret = BitVec(
                            "retval_" + str(disassembly.instruction_list[
                                state.pc]['address']), 256)
                        state.stack.append(ret)
                        continue

                logging.info("Executing " + op + " to: " + callee_address)

                try:
                    callee_module = self.modules[callee_address]
                except KeyError:
                    logging.info("Contract " + str(callee_address) +
                                 " not loaded.")
                    logging.info((str(self.modules)))

                    ret = BitVec(
                        "retval_" +
                        str(disassembly.instruction_list[state.pc]['address']),
                        256)
                    state.stack.append(ret)

                    continue

                # Attempt to write concrete calldata

                try:
                    calldata = state.memory[helper.get_concrete_int(
                        meminstart):helper.get_concrete_int(meminstart +
                                                            meminsz)]
                    calldata_type = CalldataType.CONCRETE
                    logging.debug("calldata: " + str(calldata))

                except AttributeError:

                    logging.info("Unsupported symbolic calldata offset")
                    calldata_type = CalldataType.SYMBOLIC
                    calldata = []

                self.last_call_address = disassembly.instruction_list[
                    state.pc]['address']
                self.pending_returns[self.last_call_address] = []

                callee_context = Context(callee_module,
                                         calldata=calldata,
                                         caller=context.address,
                                         origin=context.origin,
                                         calldata_type=calldata_type)

                if (op == 'CALL'):

                    new_node = self._sym_exec(callee_context,
                                              State(),
                                              depth=depth + 1,
                                              constraints=constraints)
                    self.nodes[new_node.uid] = new_node

                elif (op == 'CALLCODE'):

                    temp_module = context.module
                    temp_callvalue = context.callvalue
                    temp_caller = context.caller
                    temp_calldata = context.calldata

                    context.module = callee_module
                    context.callvalue = value
                    context.caller = context.address
                    context.calldata = calldata

                    new_node = self._sym_exec(context,
                                              State(),
                                              depth=depth + 1,
                                              constraints=constraints)
                    self.nodes[new_node.uid] = new_node

                    context.module = temp_module
                    context.callvalue = temp_callvalue
                    context.caller = temp_caller
                    context.calldata = temp_calldata

                elif (op == 'DELEGATECALL'):

                    temp_module = context.module
                    temp_calldata = context.calldata

                    context.module = callee_module
                    context.calldata = calldata

                    new_node = self._sym_exec(context,
                                              State(),
                                              depth=depth + 1,
                                              constraints=constraints)
                    self.nodes[new_node.uid] = new_node

                    context.module = temp_module
                    context.calldata = temp_calldata

                self.edges.append(Edge(node.uid, new_node.uid, JumpType.CALL))

                ret = BitVec(
                    "retval_" +
                    str(disassembly.instruction_list[state.pc]['address']),
                    256)
                state.stack.append(ret)

                new_state = copy.deepcopy(state)
                new_node = self._sym_exec(context,
                                          new_state,
                                          depth=depth + 1,
                                          constraints=constraints)

                self.nodes[new_node.uid] = new_node

                for ret_uid in self.pending_returns[self.last_call_address]:
                    self.edges.append(
                        Edge(ret_uid, new_node.uid, JumpType.RETURN))

                state.stack.append(BitVec("retval", 256))

                continue

            elif op == 'RETURN':
                offset, length = state.stack.pop(), state.stack.pop()

                try:
                    self.last_returned = state.memory[helper.get_concrete_int(
                        offset):helper.get_concrete_int(offset + length)]
                except AttributeError:
                    logging.debug(
                        "Return with symbolic length or offset. Not supported")

                if self.last_call_address is not None:
                    self.pending_returns[self.last_call_address].append(
                        node.uid)

                halt = True
                continue

            elif op == 'SUICIDE':
                halt = True
                continue

            elif op == 'REVERT':
                if self.last_call_address is not None:
                    self.pending_returns[self.last_call_address].append(
                        node.uid)

                halt = True
                continue

            elif op == 'INVALID':
                halt = True
                continue

        logging.debug("Returning from node " + str(node.uid))
        return node
Example #30
0
def apply_transaction(state, tx):
    state.logs = []
    state.suicides = []
    state.refunds = 0
    validate_transaction(state, tx)

    intrinsic_gas = tx.intrinsic_gas_used
    if state.is_HOMESTEAD():
        assert tx.s * 2 < transactions.secpk1n
        if not tx.to or tx.to == CREATE_CONTRACT_ADDRESS:
            intrinsic_gas += opcodes.CREATE[3]
            if tx.startgas < intrinsic_gas:
                raise InsufficientStartGas(
                    rp(tx, 'startgas', tx.startgas, intrinsic_gas))

    log_tx.debug('TX NEW', txdict=tx.to_dict())

    # start transacting #################
    if tx.sender != null_address:
        state.increment_nonce(tx.sender)

    # buy startgas
    assert state.get_balance(tx.sender) >= tx.startgas * tx.gasprice
    state.delta_balance(tx.sender, -tx.startgas * tx.gasprice)

    message_data = vm.CallData([safe_ord(x) for x in tx.data], 0, len(tx.data))
    message = vm.Message(
        tx.sender,
        tx.to,
        tx.value,
        tx.startgas -
        intrinsic_gas,
        message_data,
        code_address=tx.to)

    # MESSAGE
    ext = VMExt(state, tx)

    if tx.to != b'':
        result, gas_remained, data = apply_msg(ext, message)
    else:  # CREATE
        result, gas_remained, data = create_contract(ext, message)

    assert gas_remained >= 0

    log_tx.debug("TX APPLIED", result=result, gas_remained=gas_remained,
                 data=data)

    gas_used = tx.startgas - gas_remained

    # Transaction failed
    if not result:
        log_tx.debug('TX FAILED', reason='out of gas',
                     startgas=tx.startgas, gas_remained=gas_remained)
        state.delta_balance(tx.sender, tx.gasprice * gas_remained)
        state.delta_balance(state.block_coinbase, tx.gasprice * gas_used)
        output = b''
        success = 0
    # Transaction success
    else:
        log_tx.debug('TX SUCCESS', data=data)
        state.refunds += len(set(state.suicides)) * opcodes.GSUICIDEREFUND
        if state.refunds > 0:
            log_tx.debug(
                'Refunding',
                gas_refunded=min(
                    state.refunds,
                    gas_used // 2))
            gas_remained += min(state.refunds, gas_used // 2)
            gas_used -= min(state.refunds, gas_used // 2)
            state.refunds = 0
        # sell remaining gas
        state.delta_balance(tx.sender, tx.gasprice * gas_remained)
        state.delta_balance(state.block_coinbase, tx.gasprice * gas_used)
        if tx.to:
            output = bytearray_to_bytestr(data)
        else:
            output = data
        success = 1

    state.gas_used += gas_used

    # Clear suicides
    suicides = state.suicides
    state.suicides = []
    for s in suicides:
        state.set_balance(s, 0)
        state.del_account(s)

    # Pre-Metropolis: commit state after every tx
    if not state.is_METROPOLIS() and not SKIP_MEDSTATES:
        state.commit()

    # Construct a receipt
    r = mk_receipt(state, success, state.logs)
    _logs = list(state.logs)
    state.logs = []
    state.add_receipt(r)
    state.set_param('bloom', state.bloom | r.bloom)
    state.set_param('txindex', state.txindex + 1)

    return success, output
Example #31
0
def vm_execute(ext, msg, code):
    # precompute trace flag
    # if we trace vm, we're in slow mode anyway
    trace_vm = log_vm_op.is_active('trace')

    compustate = Compustate(gas=msg.gas)
    stk = compustate.stack
    mem = compustate.memory

    if code in code_cache:
        processed_code = code_cache[code]
    else:
        processed_code = preprocess_code(code)
        code_cache[code] = processed_code
        # print(processed_code.keys(), code)

    codelen = len(code)

    s = time.time()
    steps = 0
    _prevop = None  # for trace only

    while compustate.pc in processed_code:
        ops, minstack, maxstack, totgas, nextpos = processed_code[
            compustate.pc]

        if len(compustate.stack) < minstack:
            return vm_exception('INSUFFICIENT STACK')
        if len(compustate.stack) > maxstack:
            return vm_exception('STACK SIZE LIMIT EXCEEDED')
        if totgas > compustate.gas:
            return vm_exception('OUT OF GAS %d %d' % (totgas, compustate.gas))
        jumped = False

        compustate.gas -= totgas
        compustate.pc = nextpos

        # Invalid operation; can only come at the end of a chunk
        if ops[-1][0] == 'INVALID':
            return vm_exception('INVALID OP', opcode=ops[-1][1])

        for op, opcode, pushval in ops:

            if trace_vm:
                """
                This diverges from normal logging, as we use the logging namespace
                only to decide which features get logged in 'eth.vm.op'
                i.e. tracing can not be activated by activating a sub
                like 'eth.vm.op.stack'
                """
                trace_data = {}
                trace_data['stack'] = list(
                    map(to_string, list(compustate.stack)))
                if _prevop in ('MLOAD', 'MSTORE', 'MSTORE8', 'SHA3', 'CALL',
                               'CALLCODE', 'CREATE', 'CALLDATACOPY',
                               'CODECOPY', 'EXTCODECOPY'):
                    if len(compustate.memory) < 1024:
                        trace_data['memory'] = \
                            ''.join([encode_hex(ascii_chr(x)) for x
                                     in compustate.memory])
                    else:
                        trace_data['sha3memory'] = \
                            encode_hex(utils.sha3(b''.join([ascii_chr(x) for
                                                            x in compustate.memory])))
                if _prevop in ('SSTORE', ) or steps == 0:
                    trace_data['storage'] = ext.log_storage(msg.to)
                trace_data['gas'] = to_string(compustate.gas + totgas)
                trace_data['inst'] = opcode
                trace_data['pc'] = to_string(compustate.pc - 1)
                if steps == 0:
                    trace_data['depth'] = msg.depth
                    trace_data['address'] = msg.to
                trace_data['steps'] = steps
                trace_data['depth'] = msg.depth
                if op[:4] == 'PUSH':
                    trace_data['pushvalue'] = pushval
                log_vm_op.trace('vm', op=op, **trace_data)
                steps += 1
                _prevop = op

            # Valid operations
            # Pushes first because they are very frequent
            if 0x60 <= opcode <= 0x7f:
                # compustate.pc += opcode - 0x5f # Move 1 byte forward for
                # 0x60, up to 32 bytes for 0x7f
                stk.append(pushval)
            elif opcode < 0x10:
                if op == 'STOP':
                    return peaceful_exit('STOP', compustate.gas, [])
                elif op == 'ADD':
                    stk.append((stk.pop() + stk.pop()) & TT256M1)
                elif op == 'SUB':
                    stk.append((stk.pop() - stk.pop()) & TT256M1)
                elif op == 'MUL':
                    stk.append((stk.pop() * stk.pop()) & TT256M1)
                elif op == 'DIV':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 // s1)
                elif op == 'MOD':
                    s0, s1 = stk.pop(), stk.pop()
                    stk.append(0 if s1 == 0 else s0 % s1)
                elif op == 'SDIV':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) *
                                                  (-1 if s0 * s1 < 0 else 1))
                               & TT256M1)
                elif op == 'SMOD':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) *
                                                  (-1 if s0 < 0 else 1))
                               & TT256M1)
                elif op == 'ADDMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 + s1) % s2 if s2 else 0)
                elif op == 'MULMOD':
                    s0, s1, s2 = stk.pop(), stk.pop(), stk.pop()
                    stk.append((s0 * s1) % s2 if s2 else 0)
                elif op == 'EXP':
                    base, exponent = stk.pop(), stk.pop()
                    # fee for exponent is dependent on its bytes
                    # calc n bytes to represent exponent
                    nbytes = len(utils.encode_int(exponent))
                    expfee = nbytes * opcodes.GEXPONENTBYTE
                    if ext.post_clearing_hardfork():
                        expfee += opcodes.EXP_SUPPLEMENTAL_GAS * nbytes
                    if compustate.gas < expfee:
                        compustate.gas = 0
                        return vm_exception('OOG EXPONENT')
                    compustate.gas -= expfee
                    stk.append(pow(base, exponent, TT256))
                elif op == 'SIGNEXTEND':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            stk.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            stk.append(s1 & ((1 << testbit) - 1))
                    else:
                        stk.append(s1)
            elif opcode < 0x20:
                if op == 'LT':
                    stk.append(1 if stk.pop() < stk.pop() else 0)
                elif op == 'GT':
                    stk.append(1 if stk.pop() > stk.pop() else 0)
                elif op == 'SLT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 < s1 else 0)
                elif op == 'SGT':
                    s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(
                        stk.pop())
                    stk.append(1 if s0 > s1 else 0)
                elif op == 'EQ':
                    stk.append(1 if stk.pop() == stk.pop() else 0)
                elif op == 'ISZERO':
                    stk.append(0 if stk.pop() else 1)
                elif op == 'AND':
                    stk.append(stk.pop() & stk.pop())
                elif op == 'OR':
                    stk.append(stk.pop() | stk.pop())
                elif op == 'XOR':
                    stk.append(stk.pop() ^ stk.pop())
                elif op == 'NOT':
                    stk.append(TT256M1 - stk.pop())
                elif op == 'BYTE':
                    s0, s1 = stk.pop(), stk.pop()
                    if s0 >= 32:
                        stk.append(0)
                    else:
                        stk.append((s1 // 256**(31 - s0)) % 256)
            elif opcode < 0x40:
                if op == 'SHA3':
                    s0, s1 = stk.pop(), stk.pop()
                    compustate.gas -= opcodes.GSHA3WORD * \
                        (utils.ceil32(s1) // 32)
                    if compustate.gas < 0:
                        return vm_exception('OOG PAYING FOR SHA3')
                    if not mem_extend(mem, compustate, op, s0, s1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    data = bytearray_to_bytestr(mem[s0:s0 + s1])
                    stk.append(utils.big_endian_to_int(utils.sha3(data)))
                elif op == 'ADDRESS':
                    stk.append(utils.coerce_to_int(msg.to))
                elif op == 'BALANCE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.BALANCE_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(ext.get_balance(addr))
                elif op == 'ORIGIN':
                    stk.append(utils.coerce_to_int(ext.tx_origin))
                elif op == 'CALLER':
                    stk.append(utils.coerce_to_int(msg.sender))
                elif op == 'CALLVALUE':
                    stk.append(msg.value)
                elif op == 'CALLDATALOAD':
                    stk.append(msg.data.extract32(stk.pop()))
                elif op == 'CALLDATASIZE':
                    stk.append(msg.data.size)
                elif op == 'CALLDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    msg.data.extract_copy(mem, mstart, dstart, size)
                elif op == 'CODESIZE':
                    stk.append(len(code))
                elif op == 'CODECOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if dstart + i < len(code):
                            mem[mstart + i] = utils.safe_ord(code[dstart + i])
                        else:
                            mem[mstart + i] = 0
                elif op == 'RETURNDATACOPY':
                    mstart, dstart, size = stk.pop(), stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, mstart, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    if dstart + size > len(compustate.last_returned):
                        return vm_exception('RETURNDATACOPY out of range')
                    mem[mstart:mstart + size] = compustate.last_returned
                elif op == 'RETURNDATASIZE':
                    stk.append(len(compustate.last_returned))
                elif op == 'GASPRICE':
                    stk.append(ext.tx_gasprice)
                elif op == 'EXTCODESIZE':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    stk.append(len(ext.get_code(addr) or b''))
                elif op == 'EXTCODECOPY':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.EXTCODELOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    addr = utils.coerce_addr_to_hex(stk.pop() % 2**160)
                    start, s2, size = stk.pop(), stk.pop(), stk.pop()
                    extcode = ext.get_code(addr) or b''
                    assert utils.is_string(extcode)
                    if not mem_extend(mem, compustate, op, start, size):
                        return vm_exception('OOG EXTENDING MEMORY')
                    if not data_copy(compustate, size):
                        return vm_exception('OOG COPY DATA')
                    for i in range(size):
                        if s2 + i < len(extcode):
                            mem[start + i] = utils.safe_ord(extcode[s2 + i])
                        else:
                            mem[start + i] = 0
            elif opcode < 0x50:
                if op == 'BLOCKHASH':
                    if ext.post_metropolis_hardfork() and False:
                        bh_addr = ext.blockhash_store
                        stk.append(ext.get_storage_data(bh_addr, stk.pop()))
                    else:
                        stk.append(
                            utils.big_endian_to_int(ext.block_hash(stk.pop())))
                elif op == 'COINBASE':
                    stk.append(utils.big_endian_to_int(ext.block_coinbase))
                elif op == 'TIMESTAMP':
                    stk.append(ext.block_timestamp)
                elif op == 'NUMBER':
                    stk.append(ext.block_number)
                elif op == 'DIFFICULTY':
                    stk.append(ext.block_difficulty)
                elif op == 'GASLIMIT':
                    stk.append(ext.block_gas_limit)
            elif opcode < 0x60:
                if op == 'POP':
                    stk.pop()
                elif op == 'MLOAD':
                    s0 = stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    stk.append(utils.bytes_to_int(mem[s0:s0 + 32]))
                elif op == 'MSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 32):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0:s0 + 32] = utils.encode_int32(s1)
                elif op == 'MSTORE8':
                    s0, s1 = stk.pop(), stk.pop()
                    if not mem_extend(mem, compustate, op, s0, 1):
                        return vm_exception('OOG EXTENDING MEMORY')
                    mem[s0] = s1 % 256
                elif op == 'SLOAD':
                    if ext.post_anti_dos_hardfork():
                        if not eat_gas(compustate,
                                       opcodes.SLOAD_SUPPLEMENTAL_GAS):
                            return vm_exception("OUT OF GAS")
                    stk.append(ext.get_storage_data(msg.to, stk.pop()))
                elif op == 'SSTORE':
                    s0, s1 = stk.pop(), stk.pop()
                    if msg.static:
                        return vm_exception(
                            'Cannot SSTORE inside a static context')
                    if ext.get_storage_data(msg.to, s0):
                        gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL
                        refund = 0 if s1 else opcodes.GSTORAGEREFUND
                    else:
                        gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD
                        refund = 0
                    if compustate.gas < gascost:
                        return vm_exception('OUT OF GAS')
                    compustate.gas -= gascost
                    # adds neg gascost as a refund if below zero
                    ext.add_refund(refund)
                    ext.set_storage_data(msg.to, s0, s1)
                elif op == 'JUMP':
                    compustate.pc = stk.pop()
                    opnew = code[
                        compustate.pc] if compustate.pc < codelen else 0
                    jumped = True
                    if opnew != JUMPDEST:
                        return vm_exception('BAD JUMPDEST')
                elif op == 'JUMPI':
                    s0, s1 = stk.pop(), stk.pop()
                    if s1:
                        compustate.pc = s0
                        opnew = code[
                            compustate.pc] if compustate.pc < codelen else 0
                        jumped = True
                        if opnew != JUMPDEST:
                            return vm_exception('BAD JUMPDEST')
                elif op == 'PC':
                    stk.append(compustate.pc - 1)
                elif op == 'MSIZE':
                    stk.append(len(mem))
                elif op == 'GAS':
                    stk.append(compustate.gas)  # AFTER subtracting cost 1
            elif op[:3] == 'DUP':
                # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for
                # 0x8f
                stk.append(stk[0x7f - opcode])
            elif op[:4] == 'SWAP':
                # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for
                # 0x9f
                temp = stk[0x8e - opcode]
                stk[0x8e - opcode] = stk[-1]
                stk[-1] = temp

            elif op[:3] == 'LOG':
                """
                0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
                a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments
                        MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
                b. Logs are kept track of during tx execution exactly the same way as suicides
                   (except as an ordered list, not a set).
                   Each log is in the form [address, [topic1, ... ], data] where:
                   * address is what the ADDRESS opcode would output
                   * data is mem[MEMSTART: MEMSTART + MEMSZ]
                   * topics are as provided by the opcode
                c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN].
                """
                depth = int(op[3:])
                mstart, msz = stk.pop(), stk.pop()
                topics = [stk.pop() for x in range(depth)]
                compustate.gas -= msz * opcodes.GLOGBYTE
                if msg.static:
                    return vm_exception('Cannot LOG inside a static context')
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                data = bytearray_to_bytestr(mem[mstart:mstart + msz])
                ext.log(msg.to, topics, data)
                log_log.trace('LOG',
                              to=msg.to,
                              topics=topics,
                              data=list(map(utils.safe_ord, data)))
                # print('LOG', msg.to, topics, list(map(ord, data)))

            elif op == 'CREATE':
                value, mstart, msz = stk.pop(), stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, mstart, msz):
                    return vm_exception('OOG EXTENDING MEMORY')
                if msg.static:
                    return vm_exception(
                        'Cannot CREATE inside a static context')
                if ext.get_balance(msg.to) >= value and msg.depth < MAX_DEPTH:
                    cd = CallData(mem, mstart, msz)
                    ingas = compustate.gas
                    if ext.post_anti_dos_hardfork():
                        ingas = all_but_1n(ingas,
                                           opcodes.CALL_CHILD_LIMIT_DENOM)
                    create_msg = Message(msg.to, b'', value, ingas, cd,
                                         msg.depth + 1)
                    o, gas, addr = ext.create(create_msg)
                    if o:
                        stk.append(utils.coerce_to_int(addr))
                    else:
                        stk.append(0)
                    compustate.gas = compustate.gas - ingas + gas
                else:
                    stk.append(0)
            elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):
                # Pull arguments from the stack
                if op in ('CALL', 'CALLCODE'):
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                else:
                    gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                        stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop()
                    value = 0
                # Static context prohibition
                if msg.static and value > 0:
                    return vm_exception(
                        'Cannot make a non-zero-value call inside a static context'
                    )
                # Expand memory
                if not mem_extend(mem, compustate, op, meminstart, meminsz) or \
                        not mem_extend(mem, compustate, op, memoutstart, memoutsz):
                    return vm_exception('OOG EXTENDING MEMORY')
                to = utils.int_to_addr(to)
                # Extra gas costs based on hard fork-dependent factors
                extra_gas = (not ext.account_exists(to)) * (op == 'CALL') * (value > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT + \
                    (value > 0) * opcodes.GCALLVALUETRANSFER + \
                    ext.post_anti_dos_hardfork() * opcodes.CALL_SUPPLEMENTAL_GAS
                # Compute child gas limit
                if ext.post_anti_dos_hardfork():
                    if compustate.gas < extra_gas:
                        return vm_exception('OUT OF GAS', needed=extra_gas)
                    gas = min(
                        gas,
                        all_but_1n(compustate.gas - extra_gas,
                                   opcodes.CALL_CHILD_LIMIT_DENOM))
                else:
                    if compustate.gas < gas + extra_gas:
                        return vm_exception('OUT OF GAS',
                                            needed=gas + extra_gas)
                submsg_gas = gas + opcodes.GSTIPEND * (value > 0)
                # Verify that there is sufficient balance and depth
                if ext.get_balance(msg.to) < value or msg.depth >= MAX_DEPTH:
                    compustate.gas -= (gas + extra_gas - submsg_gas)
                    stk.append(0)
                else:
                    # Subtract gas from parent
                    compustate.gas -= (gas + extra_gas)
                    assert compustate.gas >= 0
                    cd = CallData(mem, meminstart, meminsz)
                    # Generate the message
                    if op == 'CALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif ext.post_homestead_hardfork(
                    ) and op == 'DELEGATECALL':
                        call_msg = Message(msg.sender,
                                           msg.to,
                                           msg.value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           transfers_value=False,
                                           static=msg.static)
                    elif op == 'DELEGATECALL':
                        return vm_exception('OPCODE INACTIVE')
                    elif op == 'CALLCODE':
                        call_msg = Message(msg.to,
                                           msg.to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=msg.static)
                    elif op == 'STATICCALL':
                        call_msg = Message(msg.to,
                                           to,
                                           value,
                                           submsg_gas,
                                           cd,
                                           msg.depth + 1,
                                           code_address=to,
                                           static=True)
                    else:
                        raise Exception("Lolwut")
                    # Get result
                    result, gas, data = ext.msg(call_msg)
                    if result == 0:
                        stk.append(0)
                    else:
                        stk.append(1)
                    # Set output memory
                    for i in range(min(len(data), memoutsz)):
                        mem[memoutstart + i] = data[i]
                    compustate.gas += gas
                    compustate.last_returned = bytearray(data)
            elif op == 'RETURN':
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1])
            elif op == 'REVERT':
                if not ext.post_metropolis_hardfork():
                    return vm_exception('Opcode not yet enabled')
                s0, s1 = stk.pop(), stk.pop()
                if not mem_extend(mem, compustate, op, s0, s1):
                    return vm_exception('OOG EXTENDING MEMORY')
                return revert(compustate.gas, mem[s0:s0 + s1])
            elif op == 'SUICIDE':
                if msg.static:
                    return vm_exception(
                        'Cannot SUICIDE inside a static context')
                to = utils.encode_int(stk.pop())
                to = ((b'\x00' * (32 - len(to))) + to)[12:]
                xfer = ext.get_balance(msg.to)
                if ext.post_anti_dos_hardfork():
                    extra_gas = opcodes.SUICIDE_SUPPLEMENTAL_GAS + \
                        (not ext.account_exists(
                            to)) * (xfer > 0 or not ext.post_clearing_hardfork()) * opcodes.GCALLNEWACCOUNT
                    if not eat_gas(compustate, extra_gas):
                        return vm_exception("OUT OF GAS")
                ext.set_balance(to, ext.get_balance(to) + xfer)
                ext.set_balance(msg.to, 0)
                ext.add_suicide(msg.to)
                log_msg.debug('SUICIDING',
                              addr=utils.checksum_encode(msg.to),
                              to=utils.checksum_encode(to),
                              xferring=xfer)
                return 1, compustate.gas, []

            # assert utils.is_numeric(compustate.gas)
            # this is slow!
            # for a in stk:
            #     assert is_numeric(a), (op, stk)
            #     assert a >= 0 and a < 2**256, (a, op, stk)
        # if not jumped:
        #    assert compustate.pc == nextpos
        #    compustate.pc = nextpos
    if compustate.pc >= codelen:
        return peaceful_exit('CODE OUT OF RANGE', compustate.gas, [])
    return vm_exception('INVALID JUMP')
Example #32
0
def create_contract(ext, msg):
    log_msg.debug('CONTRACT CREATION')

    code = msg.data.extract_all()

    if ext.tx_origin != msg.sender:
        ext.increment_nonce(msg.sender)

    if ext.post_constantinople_hardfork() and msg.sender == null_address:
        msg.to = utils.mk_contract_address(msg.sender, 0)
        # msg.to = sha3(msg.sender + code)[12:]
    else:
        nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1)
        msg.to = utils.mk_contract_address(msg.sender, nonce)

    if ext.post_metropolis_hardfork() and (
            ext.get_nonce(msg.to) or len(ext.get_code(msg.to))):
        log_msg.debug('CREATING CONTRACT ON TOP OF EXISTING CONTRACT')
        return 0, 0, b''

    b = ext.get_balance(msg.to)
    if b > 0:
        ext.set_balance(msg.to, b)
        ext.set_nonce(msg.to, 0)
        ext.set_code(msg.to, b'')
        # ext.reset_storage(msg.to)

    msg.is_create = True
    # assert not ext.get_code(msg.to)
    msg.data = vm.CallData([], 0, 0)
    snapshot = ext.snapshot()

    ext.set_nonce(msg.to, 1 if ext.post_spurious_dragon_hardfork() else 0)
    res, gas, dat = _apply_msg(ext, msg, code)

    log_msg.debug(
        'CONTRACT CREATION FINISHED',
        res=res,
        gas=gas,
        dat=dat if len(dat) < 2500 else (
            "data<%d>" %
            len(dat)))

    if res:
        if not len(dat):
            # ext.set_code(msg.to, b'')
            return 1, gas, msg.to
        gcost = len(dat) * opcodes.GCONTRACTBYTE
        if gas >= gcost and (
                len(dat) <= 24576 or not ext.post_spurious_dragon_hardfork()):
            gas -= gcost
        else:
            dat = []
            log_msg.debug(
                'CONTRACT CREATION FAILED',
                have=gas,
                want=gcost,
                block_number=ext.block_number)
            if ext.post_homestead_hardfork():
                ext.revert(snapshot)
                return 0, 0, b''
        ext.set_code(msg.to, bytearray_to_bytestr(dat))
        log_msg.debug('SETTING CODE', addr=encode_hex(msg.to), lendat=len(dat))
        return 1, gas, msg.to
    else:
        ext.revert(snapshot)
        return 0, gas, dat
Example #33
0
def createOrder(orderHash, key=tester.k0):
    key = normalize_key(key)
    v, r, s = ecsign(sha3("\x19Ethereum Signed Message:\n32" + orderHash), key)
    return v, zpad(bytearray_to_bytestr(int_to_32bytearray(r)), 32), zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32)
Example #34
0
def createOrder(orderHash, key=tester.k0):
    key = normalize_key(key)
    v, r, s = ecsign(sha3("\x19Ethereum Signed Message:\n32" + orderHash), key)
    return v, zpad(bytearray_to_bytestr(int_to_32bytearray(r)),
                   32), zpad(bytearray_to_bytestr(int_to_32bytearray(s)), 32)
Example #35
0
def sign(data: bytes, private_key_seed_ascii: str):
    priv = private_key_seed_ascii
    pk = PrivateKey(priv, raw=True)
    signature = pk.ecdsa_recoverable_serialize(pk.ecdsa_sign_recoverable(data, raw=True))
    signature = signature[0] + utils.bytearray_to_bytestr([signature[1]])
    return signature, eth_privtoaddr(priv)
Example #36
0
    def _sym_exec(self, gblState, depth=0, constraints=[]):

        environment = gblState.environment
        disassembly = environment.code
        state = gblState.mstate
        depth = depth

        start_addr = disassembly.instruction_list[state.pc]['address']

        if start_addr == 0:
            self.current_func = "fallback"
            self.current_func_addr = start_addr

        node = Node(environment.active_account.contract_name, start_addr, constraints)

        logging.debug("- Entering node " + str(node.uid) + ", index = " + str(state.pc) + ", address = " + str(start_addr) + ", depth = " + str(depth))

        if start_addr in disassembly.addr_to_func:
            # Enter a new function

            function_name = disassembly.addr_to_func[start_addr]
            self.current_func = function_name
            node.flags |= NodeFlags.FUNC_ENTRY

            logging.info("- Entering function " + environment.active_account.contract_name + ":" + function_name)

        node.function_name = self.current_func

        halt = False

        while not halt:

            try:
                instr = disassembly.instruction_list[state.pc]
            except IndexError:
                logging.debug("Invalid PC")
                return node

            # Save state before modifying anything

            node.states.append(gblState)
            gblState = self.copy_global_state(gblState)

            state = gblState.mstate

            self.total_states += 1

            # Point program counter to next instruction

            state.pc += 1
            op = instr['opcode']

            # logging.debug("[" + environment.active_account.contract_name + "] " + helper.get_trace_line(instr, state))
            # slows down execution significantly.

            # Stack ops

            if op.startswith("PUSH"):
                value = BitVecVal(int(instr['argument'][2:], 16), 256)
                state.stack.append(value)

            elif op.startswith('DUP'):
                dpth = int(op[3:])

                try:
                    state.stack.append(state.stack[-dpth])
                except:
                    halt = True

            elif op.startswith('SWAP'):

                dpth = int(op[4:])

                try:
                    temp = state.stack[-dpth - 1]

                    state.stack[-dpth - 1] = state.stack[-1]
                    state.stack[-1] = temp
                except IndexError:  # Stack underflow
                    halt = True

            elif op == 'POP':
                try:
                    state.stack.pop()
                except IndexError:  # Stack underflow
                    halt = True

            # Bitwise ops

            elif op == 'AND':
                try:
                    op1, op2 = state.stack.pop(), state.stack.pop()
                    if (type(op1) == BoolRef):
                        op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

                    if (type(op2) == BoolRef):
                        op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

                    state.stack.append(op1 & op2)
                except IndexError:  # Stack underflow
                    halt = True

            elif op == 'OR':
                try:
                    op1, op2 = state.stack.pop(), state.stack.pop()

                    if (type(op1) == BoolRef):
                        op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

                    if (type(op2) == BoolRef):
                        op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

                    state.stack.append(op1 | op2)
                except IndexError:  # Stack underflow
                    halt = True

            elif op == 'XOR':
                state.stack.append(state.stack.pop() ^ state.stack.pop())

            elif op == 'NOT':
                state.stack.append(TT256M1 - state.stack.pop())

            elif op == 'BYTE':
                s0, s1 = state.stack.pop(), state.stack.pop()

                state.stack.append(BitVecVal(0, 256))

            # Arithmetics

            elif op == 'ADD':
                state.stack.append((helper.pop_bitvec(state) + helper.pop_bitvec(state)))

            elif op == 'SUB':
                state.stack.append((helper.pop_bitvec(state) - helper.pop_bitvec(state)))

            elif op == 'MUL':
                state.stack.append(helper.pop_bitvec(state) * helper.pop_bitvec(state))

            elif op == 'DIV':
                state.stack.append(UDiv(helper.pop_bitvec(state), helper.pop_bitvec(state)))

            elif op == 'MOD':
                s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append(0 if s1 == 0 else URem(s0, s1))

            elif op == 'SDIV':
                s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append(s0 / s1)

            elif op == 'SMOD':
                s0, s1 = helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append(0 if s1 == 0 else s0 % s1)

            elif op == 'ADDMOD':
                s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append((s0 + s1) % s2 if s2 else 0)

            elif op == 'MULMOD':
                s0, s1, s2 = helper.pop_bitvec(state), helper.pop_bitvec(state), helper.pop_bitvec(state)
                state.stack.append((s0 * s1) % s2 if s2 else 0)

            elif op == 'EXP':
                # we only implement 2 ** x
                base, exponent = helper.pop_bitvec(state), helper.pop_bitvec(state)

                if (type(base) != BitVecNumRef) or (type(exponent) != BitVecNumRef):
                    state.stack.append(BitVec(str(base) + "_EXP_" + str(exponent), 256))
                elif (base.as_long() == 2):
                    if exponent.as_long() == 0:
                        state.stack.append(BitVecVal(1, 256))
                    else:
                        state.stack.append(base << (exponent - 1))

                else:
                    state.stack.append(base)

            elif op == 'SIGNEXTEND':
                s0, s1 = state.stack.pop(), state.stack.pop()

                try:
                    s0 = helper.get_concrete_int(s0)
                    s1 = helper.get_concrete_int(s1)

                    if s0 <= 31:
                        testbit = s0 * 8 + 7
                        if s1 & (1 << testbit):
                            state.stack.append(s1 | (TT256 - (1 << testbit)))
                        else:
                            state.stack.append(s1 & ((1 << testbit) - 1))
                    else:
                        state.stack.append(s1)    
                except:
                    halt = True
                    continue

            # Comparisons

            elif op == 'LT':

                exp = ULT(helper.pop_bitvec(state), helper.pop_bitvec(state))
                state.stack.append(exp)

            elif op == 'GT':

                exp = UGT(helper.pop_bitvec(state), helper.pop_bitvec(state))
                state.stack.append(exp)

            elif op == 'SLT':

                exp = helper.pop_bitvec(state) < helper.pop_bitvec(state)
                state.stack.append(exp)

            elif op == 'SGT':

                exp = helper.pop_bitvec(state) > helper.pop_bitvec(state)
                state.stack.append(exp)

            elif op == 'EQ':

                op1 = state.stack.pop()
                op2 = state.stack.pop()

                if(type(op1) == BoolRef):
                    op1 = If(op1, BitVecVal(1, 256), BitVecVal(0, 256))

                if(type(op2) == BoolRef):
                    op2 = If(op2, BitVecVal(1, 256), BitVecVal(0, 256))

                exp = op1 == op2

                state.stack.append(exp)

            elif op == 'ISZERO':

                val = state.stack.pop()

                if (type(val) == BoolRef):
                    exp = val == False
                else:
                    exp = val == 0

                state.stack.append(exp)

            # Call data

            elif op == 'CALLVALUE':
                state.stack.append(environment.callvalue)

            elif op == 'CALLDATALOAD':
                # unpack 32 bytes from calldata into a word and put it on the stack

                op0 = state.stack.pop()

                try:
                    offset = helper.get_concrete_int(simplify(op0))
                    b = environment.calldata[offset]

                except AttributeError:
                    logging.debug("CALLDATALOAD: Unsupported symbolic index")
                    state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256))
                    continue
                except IndexError:
                    logging.debug("Calldata not set, using symbolic variable instead")
                    state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256))
                    continue

                if type(b) == int:

                    val = b''

                    try:
                        for i in range(offset, offset + 32):
                            val += environment.calldata[i].to_bytes(1, byteorder='big')

                        logging.debug("Final value: " + str(int.from_bytes(val, byteorder='big')))
                        state.stack.append(BitVecVal(int.from_bytes(val, byteorder='big'), 256))

                    except:
                        state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256))
                else:
                    # symbolic variable
                    state.stack.append(BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(op0), 256))

            elif op == 'CALLDATASIZE':

                if environment.calldata_type == CalldataType.SYMBOLIC:
                    state.stack.append(BitVec("calldatasize_" + environment.active_account.contract_name, 256))
                else:
                    state.stack.append(BitVecVal(len(environment.calldata), 256))

            elif op == 'CALLDATACOPY':
                op0, op1, op2 = state.stack.pop(), state.stack.pop(), state.stack.pop()

                try:
                    mstart = helper.get_concrete_int(op0)
                except:
                    logging.debug("Unsupported symbolic memory offset in CALLDATACOPY")
                    continue

                try:
                    dstart = helper.get_concrete_int(op1)
                except:
                    logging.debug("Unsupported symbolic calldata offset in CALLDATACOPY")
                    state.mem_extend(mstart, 1)
                    state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_cpy", 256)
                    continue

                try:
                    size = helper.get_concrete_int(op2)
                except:
                    logging.debug("Unsupported symbolic size in CALLDATACOPY")
                    state.mem_extend(mstart, 1)
                    state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256)
                    continue

                if size > 0:

                    try:
                        state.mem_extend(mstart, size)
                    except:
                        logging.debug("Memory allocation error: mstart = " + str(mstart) + ", size = " + str(size))
                        state.mem_extend(mstart, 1)
                        state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256)
                        continue

                    try:
                        i_data = environment.calldata[dstart]

                        for i in range(mstart, mstart + size):
                            state.memory[i] = environment.calldata[i_data]
                            i_data += 1
                    except:
                        logging.debug("Exception copying calldata to memory")

                        state.memory[mstart] = BitVec("calldata_" + str(environment.active_account.contract_name) + "_" + str(dstart), 256)

            elif op == 'STOP':
                if len(self.call_stack):
                    self.pending_returns[self.call_stack[-1]].append(node.uid)

                halt = True
                continue

            # Environment

            elif op == 'ADDRESS':
                state.stack.append(environment.sender)

            elif op == 'BALANCE':
                addr = state.stack.pop()
                state.stack.append(BitVec("balance_at_" + str(addr), 256))

            elif op == 'ORIGIN':
                state.stack.append(environment.origin)

            elif op == 'CALLER':
                state.stack.append(environment.sender)

            elif op == 'CODESIZE':
                state.stack.append(len(disassembly.instruction_list))

            if op == 'SHA3':
                op0, op1 = state.stack.pop(), state.stack.pop()

                try:
                    index, length = helper.get_concrete_int(op0), helper.get_concrete_int(op1)

                except:
                    # Can't access symbolic memory offsets
                    state.stack.append(BitVec("KECCAC_mem_" + str(op0) + ")", 256))
                    continue

                try:
                    data = b''

                    for i in range(index, index + length):
                        data += helper.get_concrete_int(state.memory[i]).to_bytes(1, byteorder='big')
                        i += 1
                except:

                    svar = str(state.memory[index])

                    svar = svar.replace(" ", "_")

                    state.stack.append(BitVec("keccac_" + svar, 256))
                    continue

                keccac = utils.sha3(utils.bytearray_to_bytestr(data))
                logging.debug("Computed SHA3 Hash: " + str(binascii.hexlify(keccac)))

                state.stack.append(BitVecVal(helper.concrete_int_from_bytes(keccac, 0), 256))

            elif op == 'GASPRICE':
                state.stack.append(BitVec("gasprice", 256))

            elif op == 'CODECOPY':
                # Not implemented
                start, s1, size = state.stack.pop(), state.stack.pop(), state.stack.pop()

            elif op == 'EXTCODESIZE':
                addr = state.stack.pop()
                state.stack.append(BitVec("extcodesize", 256))

            elif op == 'EXTCODECOPY':
                # Not implemented

                addr = state.stack.pop()
                start, s2, size = state.stack.pop(), state.stack.pop(), state.stack.pop()

            elif op == 'BLOCKHASH':
                blocknumber = state.stack.pop()
                state.stack.append(BitVec("blockhash_block_" + str(blocknumber), 256))

            elif op == 'COINBASE':
                state.stack.append(BitVec("coinbase", 256))

            elif op == 'TIMESTAMP':
                state.stack.append(BitVec("timestamp", 256))

            elif op == 'NUMBER':
                state.stack.append(BitVec("block_number", 256))

            elif op == 'DIFFICULTY':
                state.stack.append(BitVec("block_difficulty", 256))

            elif op == 'GASLIMIT':
                state.stack.append(BitVec("block_gaslimit", 256))

            elif op == 'MLOAD':

                op0 = state.stack.pop()

                logging.debug("MLOAD[" + str(op0) + "]")

                try:
                    offset = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("Can't MLOAD from symbolic index")
                    data = BitVec("mem_" + str(op0), 256)
                    continue

                try:
                    data = helper.concrete_int_from_bytes(state.memory, offset)
                except IndexError:  # Memory slot not allocated
                    data = BitVec("mem_" + str(offset), 256)
                except TypeError:  # Symbolic memory
                    data = state.memory[offset]

                logging.debug("Load from memory[" + str(offset) + "]: " + str(data))

                state.stack.append(data)

            elif op == 'MSTORE':

                op0, value = state.stack.pop(), state.stack.pop()

                try:
                    mstart = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("MSTORE to symbolic index. Not supported")
                    continue

                try:
                    state.mem_extend(mstart, 32)
                except Exception:
                    logging.debug("Error extending memory, mstart = " + str(mstart) + ", size = 32")

                logging.debug("MSTORE to mem[" + str(mstart) + "]: " + str(value))

                try:
                    # Attempt to concretize value
                    _bytes = helper.concrete_int_to_bytes(value)

                    i = 0

                    for b in _bytes:
                        state.memory[mstart + i] = _bytes[i]
                        i += 1

                except:
                    try:
                        state.memory[mstart] = value
                    except:
                        logging.debug("Invalid memory access")
                        continue

            elif op == 'MSTORE8':
                op0, value = state.stack.pop(), state.stack.pop()

                try:
                    offset = helper.get_concrete_int(op0)
                except AttributeError:
                    logging.debug("MSTORE to symbolic index. Not supported")
                    continue

                state.mem_extend(offset, 1)

                state.memory[offset] = value % 256

            elif op == 'SLOAD':
                index = state.stack.pop()
                logging.debug("Storage access at index " + str(index))

                try:
                    index = helper.get_concrete_int(index)
                except AttributeError:
                    index = str(index)

                try:
                    data = gblState.accounts[gblState.environment.sender].storage[index]
                except KeyError:
                    data = BitVec("storage_" + str(index), 256)
                    gblState.environment.active_account.storage[index] = data

                state.stack.append(data)

            elif op == 'SSTORE':
                index, value = state.stack.pop(), state.stack.pop()

                logging.debug("Write to storage[" + str(index) + "] at node " + str(start_addr))

                try:
                    index = helper.get_concrete_int(index)
                except AttributeError:
                    index = str(index)

                try:
                    # Create a fresh copy of the account object before modifying storage

                    for k in gblState.accounts:
                        if gblState.accounts[k] == gblState.environment.active_account:
                            gblState.accounts[k] = copy.deepcopy(gblState.accounts[k])
                            gblState.environment.active_account = gblState.accounts[k]
                            break

                    gblState.environment.active_account.storage[index] = value
                except KeyError:
                    logging.debug("Error writing to storage: Invalid index")
                    continue

            elif op == 'JUMP':

                try:
                    jump_addr = helper.get_concrete_int(state.stack.pop())
                except AttributeError:
                    logging.debug("Invalid jump argument (symbolic address)")
                    halt = True
                    continue
                except IndexError:  # Stack Underflow
                    halt = True
                    continue

                if (depth < self.max_depth):

                    i = helper.get_instruction_index(disassembly.instruction_list, jump_addr)

                    if i is None:
                        logging.debug("JUMP to invalid address")
                        halt = True
                        continue

                    opcode = disassembly.instruction_list[i]['opcode']

                    if opcode == "JUMPDEST":

                        new_gblState = self.copy_global_state(gblState)
                        new_gblState.mstate.pc = i

                        new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints)
                        self.nodes[new_node.uid] = new_node

                        self.edges.append(Edge(node.uid, new_node.uid, JumpType.UNCONDITIONAL))
                        halt = True
                        continue

                    else:
                        logging.debug("Skipping JUMP to invalid destination (not JUMPDEST): " + str(jump_addr))
                        halt = True
                        # continue
                else:
                    logging.debug("Max depth reached, skipping JUMP")
                    halt = True
                    # continue

            elif op == 'JUMPI':
                op0, condition = state.stack.pop(), state.stack.pop()

                try:
                    jump_addr = helper.get_concrete_int(op0)
                except:
                    logging.debug("Skipping JUMPI to invalid destination.")

                if (depth < self.max_depth):

                    i = helper.get_instruction_index(disassembly.instruction_list, jump_addr)

                    if not i:
                        logging.debug("Invalid jump destination: " + str(jump_addr))

                    else:
                        instr = disassembly.instruction_list[i]

                        if instr['opcode'] != "JUMPDEST":
                            logging.debug("Invalid jump destination: " + str(jump_addr))
                            halt = True
                            continue

                        elif (type(condition) == BoolRef):

                            if not is_false(simplify(condition)):

                                # Create new node for condition == True

                                new_gblState = self.copy_global_state(gblState)
                                new_gblState.mstate.pc = i

                                new_constraints = copy.deepcopy(constraints)
                                new_constraints.append(condition)

                                new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=new_constraints)
                                self.nodes[new_node.uid] = new_node
                                self.edges.append(Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, condition))

                            else:
                                logging.debug("Pruned unreachable states.")

                        else:
                            logging.debug("Invalid condition: " + str(condition) + "(type " + str(type(condition)) + ")")
                            halt = True
                            continue

                        new_gblState = self.copy_global_state(gblState)

                        if (type(condition) == BoolRef):
                            negated = Not(condition)
                        else:
                            negated = condition == 0

                        if not is_false(simplify(negated)):

                            new_constraints = copy.deepcopy(constraints)
                            new_constraints.append(negated)

                            new_node = self._sym_exec(new_gblState, depth=depth, constraints=new_constraints)
                            self.nodes[new_node.uid] = new_node
                            self.edges.append(Edge(node.uid, new_node.uid, JumpType.CONDITIONAL, negated))

                        halt = True
                        # continue

                else:
                    logging.debug("Max depth reached, skipping JUMPI")

            elif op == 'PC':
                state.stack.append(state.pc - 1)

            elif op == 'MSIZE':
                state.stack.append(BitVec("msize", 256))

            elif op == 'GAS':
                state.stack.append(BitVec("gas", 256))

            elif op.startswith('LOG'):
                dpth = int(op[3:])
                state.stack.pop(), state.stack.pop()
                [state.stack.pop() for x in range(dpth)]
                # Not supported

            elif op == 'CREATE':
                state.stack.pop(), state.stack.pop(), state.stack.pop()
                # Not supported
                state.stack.append(0)

            elif op in ('CALL', 'CALLCODE', 'DELEGATECALL', 'STATICCALL'):

                if op in ('CALL', 'CALLCODE'):
                    gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \
                        state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop()

                else:
                    gas, to, meminstart, meminsz, memoutstart, memoutsz = \
                        state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop(), state.stack.pop()

                try:
                    callee_address = hex(helper.get_concrete_int(to))

                except AttributeError:
                    # Not a concrete call address. Call target may be an address in storage.

                    m = re.search(r'storage_(\d+)', str(simplify(to)))

                    logging.debug("CALL to: " + str(simplify(to)))

                    if (m and self.dynamic_loader is not None):
                        idx = int(m.group(1))
                        logging.info("Dynamic contract address at storage index " + str(idx))

                        # attempt to read the contract address from instance storage

                        try:
                            callee_address = self.dynamic_loader.read_storage(environment.active_account.address, idx)
                        except:
                            logging.debug("Error accessing contract storage.")
                            ret = BitVec("retval_" + str(instr['address']), 256)
                            state.stack.append(ret)
                            continue

                        # testrpc simply returns the address, geth response is more elaborate.

                        if not re.match(r"^0x[0-9a-f]{40}$", callee_address):

                            callee_address = "0x" + callee_address[26:]

                    else:
                        ret = BitVec("retval_" + str(instr['address']), 256)
                        state.stack.append(ret)
                        continue

                if not re.match(r"^0x[0-9a-f]{40}", callee_address):
                        logging.debug("Invalid address: " + str(callee_address))
                        ret = BitVec("retval_" + str(instr['address']), 256)
                        state.stack.append(ret)
                        continue

                if (int(callee_address, 16) < 5):

                    logging.info("Native contract called: " + callee_address)

                    # Todo: Implement native contracts

                    ret = BitVec("retval_" + str(instr['address']), 256)
                    state.stack.append(ret)
                    continue

                try:

                    callee_account = self.accounts[callee_address]

                except KeyError:
                    # We have a valid call address, but contract is not in the modules list

                    logging.info("Module with address " + callee_address + " not loaded.")

                    if self.dynamic_loader is not None:

                        logging.info("Attempting to load dependency")

                        try:
                            code = self.dynamic_loader.dynld(environment.active_account.address, callee_address)
                        except Exception as e:
                            logging.info("Unable to execute dynamic loader.")

                        if code is None:

                            logging.info("No code returned, not a contract account?")
                            ret = BitVec("retval_" + str(instr['address']), 256)
                            state.stack.append(ret)
                            continue

                        # New contract bytecode loaded successfully, create a new contract account

                        self.accounts[callee_address] = Account(callee_address, code, callee_address)

                        logging.info("Dependency loaded: " + callee_address)

                    else:
                        logging.info("Dynamic loader unavailable. Skipping call")
                        ret = BitVec("retval_" + str(instr['address']), 256)
                        state.stack.append(ret)
                        continue

                logging.info("Executing " + op + " to: " + callee_address)

                try:
                    callee_account = self.accounts[callee_address]
                except KeyError:
                    logging.info("Contract " + str(callee_address) + " not loaded.")
                    logging.info((str(self.accounts)))

                    ret = BitVec("retval_" + str(instr['address']), 256)
                    state.stack.append(ret)
                    continue

                try:
                    # TODO: This only allows for either fully concrete or fully symbolic calldata.
                    # Improve management of memory and callata to support a mix between both types.

                    calldata = state.memory[helper.get_concrete_int(meminstart):helper.get_concrete_int(meminstart + meminsz)]

                    if (len(calldata) < 32):
                        calldata += [0] * (32 - len(calldata))

                    calldata_type = CalldataType.CONCRETE
                    logging.debug("Calldata: " + str(calldata))

                except AttributeError:

                    logging.info("Unsupported symbolic calldata offset")
                    calldata_type = CalldataType.SYMBOLIC
                    calldata = []

                self.call_stack.append(instr['address'])
                self.pending_returns[instr['address']] = []

                if (op == 'CALL'):

                    callee_environment = Environment(callee_account, BitVecVal(int(environment.active_account.address, 16), 256), calldata, environment.gasprice, value, environment.origin, calldata_type=calldata_type)
                    new_gblState = GlobalState(gblState.accounts, callee_environment, MachineState(gas))

                    new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints)

                    self.nodes[new_node.uid] = new_node

                elif (op == 'CALLCODE'):

                    temp_callvalue = environment.callvalue
                    temp_caller = environment.caller
                    temp_calldata = environment.calldata

                    environment.callvalue = value
                    environment.caller = environment.address
                    environment.calldata = calldata

                    new_gblState = GlobalState(gblState.accounts, environment, MachineState(gas))

                    new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints)
                    self.nodes[new_node.uid] = new_node

                    environment.callvalue = temp_callvalue
                    environment.caller = temp_caller
                    environment.calldata = temp_calldata

                elif (op == 'DELEGATECALL'):
                    temp_code = environment.code
                    temp_calldata = environment.calldata

                    environment.code = callee_account.code
                    environment.calldata = calldata

                    new_gblState = GlobalState(gblState.accounts, environment, MachineState(gas))

                    new_node = self._sym_exec(new_gblState, depth=depth + 1, constraints=constraints)
                    self.nodes[new_node.uid] = new_node

                    environment.code = temp_code
                    environment.calldata = temp_calldata

                self.edges.append(Edge(node.uid, new_node.uid, JumpType.CALL))

                '''
                There may be multiple possible returns from the callee contract. Currently, we don't create separate nodes on the CFG
                for each of them. Instead, a single "return node" is created and a separate edge is added for each return path.
                The return value is always symbolic.
                '''

                ret = BitVec("retval_" + str(disassembly.instruction_list[state.pc]['address']), 256)
                state.stack.append(ret)

                return_address = self.call_stack.pop()

                new_gblState = self.copy_global_state(gblState)
                new_node = self._sym_exec(gblState, depth=depth + 1, constraints=constraints)
                new_node.flags |= NodeFlags.CALL_RETURN

                self.nodes[new_node.uid] = new_node

                for ret_uid in self.pending_returns[return_address]:
                    self.edges.append(Edge(ret_uid, new_node.uid, JumpType.RETURN))

                state.stack.append(BitVec("retval", 256))

                halt = True

            elif op == 'RETURN':
                offset, length = state.stack.pop(), state.stack.pop()

                try:
                    self.last_returned = state.memory[helper.get_concrete_int(offset):helper.get_concrete_int(offset + length)]
                except AttributeError:
                    logging.debug("Return with symbolic length or offset. Not supported")

                if len(self.call_stack):
                    self.pending_returns[self.call_stack[-1]].append(node.uid)

                halt = True

            elif op == 'SUICIDE':
                halt = True

            elif op == 'REVERT':
                if len(self.call_stack):
                    self.pending_returns[self.call_stack[-1]].append(node.uid)

                halt = True

            elif op == 'ASSERT_FAIL' or op == 'INVALID':
                if len(self.call_stack):
                    self.pending_returns[self.call_stack[-1]].append(node.uid)

                halt = True

        logging.debug("Returning from node " + str(node.uid))
        return node
Example #37
0
def send_msg_transfer_value(mainchain_state, shard_state, shard_id, tx):
    urs_addr = get_urs_contract(shard_id)['addr']
    log_rctx.debug("Begin: urs.balance={}, tx.to.balance={}".format(
        shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to)))

    receipt_id = tx.r
    # we should deduct the startgas of this message in advance, because
    # the message may be possibly a contract, not only a normal value transfer.
    value = tx.value - tx.gasprice * tx.startgas
    log_rctx.debug(
        "value={}, tx.value={}, tx.gasprice={}, tx.startgas={}".format(
            value, tx.value, tx.gasprice, tx.startgas))
    if value <= 0:
        return False, None

    # start transactioning
    if not send_msg_add_used_receipt(shard_state, shard_id, receipt_id):
        return False, None

    receipt_sender_hex = call_valmgr(mainchain_state, 'get_receipts__sender',
                                     [receipt_id])
    receipt_data = call_valmgr(mainchain_state, 'get_receipts__data',
                               [receipt_id])
    msg_data = (b'00' *
                12) + utils.parse_as_bin(receipt_sender_hex) + receipt_data
    msg = vm.Message(urs_addr, tx.to, value,
                     tx.startgas - tx.intrinsic_gas_used, msg_data)
    env_tx = Transaction(0, tx.gasprice, tx.startgas, b'', 0, b'')
    env_tx._sender = urs_addr
    ext = VMExt(shard_state, env_tx)
    log_rctx.debug(
        "before apply_msg: urs_addr.balance={}, tx.to.balance={}".format(
            shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to)))
    # even if `transfer_value` in `apply_msg` fails, no error occurs.
    # it seems no raise in apply_msg
    result, gas_remained, data = apply_msg(ext, msg)
    log_rctx.debug(
        "after apply_msg:  urs_addr.balance={}, tx.to.balance={}".format(
            shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to)))

    assert gas_remained >= 0

    gas_used = tx.startgas - gas_remained

    # Transaction failed
    if not result:
        log_rctx.debug('TX FAILED',
                       reason='out of gas',
                       startgas=tx.startgas,
                       gas_remained=gas_remained)
        shard_state.gas_used += tx.startgas
        shard_state.delta_balance(tx.to, tx.gasprice * gas_remained)
        shard_state.delta_balance(shard_state.block_coinbase,
                                  tx.gasprice * gas_used)
        output = b''
        success = 0
    # Transaction success
    else:
        log_rctx.debug('TX SUCCESS', data=data)
        shard_state.refunds += len(set(
            shard_state.suicides)) * opcodes.GSUICIDEREFUND
        if shard_state.refunds > 0:
            log_rctx.debug('Refunding',
                           gas_refunded=min(shard_state.refunds,
                                            gas_used // 2))
            gas_remained += min(shard_state.refunds, gas_used // 2)
            gas_used -= min(shard_state.refunds, gas_used // 2)
            shard_state.refunds = 0
        # sell remaining gas
        shard_state.delta_balance(tx.to, tx.gasprice * gas_remained)
        log_rctx.debug("gas_remained={}, gasprice={}".format(
            gas_remained, tx.gasprice))
        log_rctx.debug("End: urs.balance={}, tx.to.balance={}".format(
            shard_state.get_balance(urs_addr), shard_state.get_balance(tx.to)))
        shard_state.delta_balance(shard_state.block_coinbase,
                                  tx.gasprice * gas_used)
        shard_state.gas_used += gas_used
        if tx.to:
            output = utils.bytearray_to_bytestr(data)
        else:
            output = data
        success = 1

    # Clear suicides
    suicides = shard_state.suicides
    shard_state.suicides = []
    for s in suicides:
        shard_state.set_balance(s, 0)
        shard_state.del_account(s)

    # Pre-Metropolis: commit state after every tx
    if not shard_state.is_METROPOLIS() and not SKIP_MEDSTATES:
        shard_state.commit()

    # Construct a receipt
    r = mk_receipt(shard_state, success, shard_state.logs)
    _logs = list(shard_state.logs)
    shard_state.logs = []
    shard_state.add_receipt(r)
    shard_state.set_param('bloom', shard_state.bloom | r.bloom)
    shard_state.set_param('txindex', shard_state.txindex + 1)

    return success, output