Exemple #1
0
    def to_representation(self, obj):
        if not obj:
            return '0x'

        # We can get another types like `memoryview` from django models. `to_internal_value` is not used
        # when you provide an object instead of a json using `data`. Make sure everything is HexBytes.
        if hasattr(obj, 'hex'):
            obj = HexBytes(obj.hex())
        elif not isinstance(obj, HexBytes):
            obj = HexBytes(obj)
        return obj.hex()
Exemple #2
0
def transaction(request):
    if request.method == "GET":
        return render(request, 'transaction.html')
    else:
        dict_keystore = request.session["dict_keystore"]
        from_address = request.session['from_address']
        to_address = request.POST.get("to_address")
        if dict_keystore and from_address and to_address:
            to_address = Web3.toChecksumAddress(to_address)
            # assert Web3.isAddress(to_address),"invalid address"
            value = Web3.toWei(float(request.POST.get("value")), 'ether')
            #print("value",value)
            gas_limit = int(request.POST.get("gas"))
            print("gaslimit", gas_limit)
            gas_price = int(request.POST.get("gas_price"))
            print("gaslimit", gas_price)
            password = request.POST.get("password")
            #print(">>>>>>>>>pw",password)
            privatekey = HexBytes(
                account.Account.decrypt(
                    dict_keystore,
                    password))  ##convert the binary private key to hex type
            pk = privatekey.hex()
            #print(">>>>>>>>>pk",pk)
            txhash_b = send_transaction(from_address, to_address, value,
                                        gas_price, gas_limit, pk)
            txhash = HexBytes(txhash_b).hex()
            data = {}
            data["txn"] = txhash
            print(txhash)
            return render(request, 'outcome.html', context=data)

        else:
            return redirect(reverse("balance"))
 def get_order_by_hash(self,
                       order_hash: hexbytes.HexBytes) -> Optional[Order]:
     # remove 0x to keep backwards compatibility
     order_orm = self.session.query(OrderORM).get(
         remove_0x_prefix(HexStr(order_hash.hex())))
     if order_orm is None:
         return None
     return order_orm.to_order()
Exemple #4
0
    def send_same_trans(self, sender_addr, sender_priv, receive_addr):
        """
        Check network for sending identical transactions
        :param receive_addr: receive eth-address
        :param sender_priv: sender eth-private key
        :param sender_addr: sender eth-address
        :return: None
        """
        sender_addr = self.w3.toChecksumAddress(sender_addr)
        receive_addr = self.w3.toChecksumAddress(receive_addr)
        while True:
            try:
                params = dict(
                    nonce=self.w3.eth.getTransactionCount(sender_addr),
                    gasPrice=self.w3.eth.gasPrice,
                    gas=100000,
                    to=receive_addr,
                    value=hex(10000000000000000))
                params2 = params.copy()
                params2["value"] = hex(1)
                params2["gasPrice"] += 10000000000000000000
                signed_txn = self.w3.eth.account.signTransaction(
                    params, sender_priv)
                _signed_txn = self.w3.eth.account.signTransaction(
                    params2, sender_priv)
                trans1 = self.w3.eth.sendRawTransaction(
                    signed_txn.rawTransaction)
                trans2 = self.w3.eth.sendRawTransaction(
                    _signed_txn.rawTransaction)
                trans1_hash = HexBytes(trans1)
                trans2_hash = HexBytes(trans2)
                self.logger.info("\tSend same transactions:\n"
                                 "\t\t\t\t\t\t\t\t\t\t\t{0}\n"
                                 "\t\t\t\t\t\t\t\t\t\t\t{1}".format(
                                     trans1_hash.hex(), trans2_hash.hex()))
                return None
            except ValueError as e:
                self.logger.warning("\t{0}".format(e.args[0]["message"]))
                time.sleep(2)
                continue

            except Exception as e:
                raise e
Exemple #5
0
def sign_bytearray(w3, barray, account_adr):
    # Returns hex strings like '0x3532..'
    h = HexBytes(barray)
    h_hash = w3.sha3(hexstr = h.hex())
    sig = w3.eth.sign(account_adr, h_hash) # sig is HexBytes
    r = w3.toBytes(hexstr = HexBytes(sig[0 : 32]).hex())
    s = w3.toBytes(hexstr = HexBytes(sig[32 : 64]).hex())
    v = sig[64 : 65]
    v_int = int.from_bytes(v, byteorder='big')
    h_hash = w3.toBytes(hexstr = h_hash.hex())
    return h_hash, v_int, r, s
Exemple #6
0
    def wait_and_check_transaction(self, txhash):
        """Wait for a transaction to be mined, then check if it succeeded (blocking).

        :param txhash: Transaction hash to wait on and check
        :return: Receipt if transaction succeeded
        """
        txhash = HexBytes(txhash)
        receipt = self.wait_for_transaction(txhash)
        if not self.check_transaction(txhash):
            raise TransactionFailedError(
                'Transaction {0} failed, check network state'.format(
                    txhash.hex()))
        return receipt
Exemple #7
0
    def check_transaction(self, txhash):
        """Check that a transaction succeeded.

        :param txhash: Transaction hash to check
        :return: True if transaction succeeded, else False
        """
        txhash = HexBytes(txhash)
        tx = self.w3.eth.getTransaction(txhash)
        receipt = self.w3.eth.getTransactionReceipt(txhash)

        logger.info('Receipt for %s: %s', txhash.hex(), dict(receipt))
        return receipt is not None and receipt['gasUsed'] < tx[
            'gas'] and receipt['status'] == 1
Exemple #8
0
    def test_many_light_trans(self, accounts, time_live):
        """
        Checking the network for resistance to a large number of light
        transaction requests
        :param time_live: means how long will the thread run
        :param accounts: a list or a tuple that includes address and a key
                        from two accounts for transferring Ethereum
        """

        tries = 0
        success = 0
        start_time = time.time()
        address1 = self.w3.toChecksumAddress(accounts[0])
        key1 = accounts[1]
        address2 = self.w3.toChecksumAddress(accounts[2])
        nonce = self.get_current_nonce(address1)
        self.logger.info('Flow "{0}" with main account: {1}'.format(
            threading.current_thread().name, address1))
        while True:
            try:
                tries += 1
                trans_param = dict(nonce=nonce,
                                   gasPrice=self.w3.eth.gasPrice,
                                   gas=21000,
                                   to=address2,
                                   value=hex(10000000000000000))
                signed_txn = self.w3.eth.account.signTransaction(
                    trans_param, key1)

                trans = self.w3.eth.sendRawTransaction(
                    signed_txn.rawTransaction)

                trans_hash = HexBytes(trans)
                self.logger.info(
                    "\tSend transaction: {0} with nonce: {1}".format(
                        trans_hash.hex(), nonce))
                success += 1
            except ValueError as e:
                self.logger.warning("\t{0}".format(e.args[0]["message"]))
                continue

            except Exception as e:
                raise e
            finally:
                nonce += 1
                if time.time() - start_time >= time_live:
                    self.logger.info(
                        "Percent of successfully sent transactions: {0:.0%}".
                        format(success / tries))
                    break
Exemple #9
0
    def send_eth(self, account1, account2, funds):
        sender = self.w3.toChecksumAddress(account1[0])
        sender_key = account1[1]
        receiver = self.w3.toChecksumAddress(account2[0])
        trans_param = dict(nonce=self.w3.eth.getTransactionCount(sender),
                           gasPrice=self.w3.eth.gasPrice,
                           gas=21000,
                           to=receiver,
                           value=hex(funds))
        signed_txn = self.w3.eth.account.signTransaction(
            trans_param, sender_key)

        trans = self.w3.eth.sendRawTransaction(signed_txn.rawTransaction)

        trans_hash = HexBytes(trans)
        self.logger.info("\tSend transaction: {0}".format(trans_hash.hex()))
Exemple #10
0
 def order_cancelled(
     self,
     order_hash: hexbytes.HexBytes,
     cancelled_maker_token_amount: int,
     cancelled_taker_token_amount: int,
 ) -> None:
     order_orm = (
         self.session.query(OrderORM)
         # remove 0x to keep backwards compatibility
         .filter_by(msg_hash=remove_0x_prefix(order_hash.hex())).first()
     )
     if order_orm is not None:
         order_orm.cancelled_maker_token_amount += cancelled_maker_token_amount
         order_orm.cancelled_taker_token_amount += cancelled_taker_token_amount
         if order_orm.to_order().is_filled():
             self.session.delete(order_orm)
         self.session.commit()
Exemple #11
0
def registered():
    """
    Calling a contract function and interact with it using the data from the input
    provided previously.
    """
    call_contract_function = ASSETREGISTER.functions.setRegistration(
        request.form['some_string'],
        w3.eth.accounts[int(request.form['ethereum_address'])]).transact(
        )  # create the transaction
    transaction_info = w3.eth.getTransaction(call_contract_function)
    return render_template(
        'registered.html',
        # pass these variables to the html template
        reg_ethaddress=w3.eth.accounts[int(request.form['ethereum_address'])],
        reg_serial=request.form['some_string'],
        reg_accountnumber=request.form['ethereum_address'],
        reg_receipt=w3.eth.getTransactionReceipt(call_contract_function),
        reg_txhash=HexBytes.hex(transaction_info['hash']),
        reg_txdata=HexBytes(transaction_info['input']),
        contractaddress=ASSETREGISTER.address)
def biggest_tx(transaction_hashes):
    tx_values = []

    #query_times = []

    for tx_hash in transaction_hashes:

        #start = time.time()
        tx = web3.eth.getTransaction(tx_hash)
        #end = time.time()
        tx_values.append(tx["value"])
        #query_times.append(end-start)

    highest_value = max(tx_values)
    index_of_largest = tx_values.index(max(tx_values))
    largest_tx_hash = HexBytes.hex(transaction_hashes[index_of_largest])

    #average_q_time = statistics.mean(query_times)
    #print(average_q_time)

    return highest_value, largest_tx_hash
Exemple #13
0
    def test_json_encoder(self):
        base_address = 'b58d5491d17ebf46e9db7f18cea7c556ae80d53B'
        ipfs_hash_string = 'Qme4GBhwNJharbu83iNEsd5WnUhQYM1rBAgCgsSuFMdjcS'
        ipfs_hash_bytes = ipfs_hash_string.encode()  # b'...'

        json = {'ipfs_hash': ipfs_hash_bytes}
        # Simulate string encoding and convert back to dict
        encoded_json = loads(dumps(json, cls=JsonBytesEncoder))
        self.assertEqual(ipfs_hash_bytes.decode(), encoded_json['ipfs_hash'])

        json = {'ipfs_hash': ipfs_hash_string}
        # Simulate string encoding and convert back to dict
        encoded_json = loads(dumps(json, cls=JsonBytesEncoder))
        self.assertEqual(ipfs_hash_string, encoded_json['ipfs_hash'])

        hex_bytes_address = HexBytes(base_address)
        json = {'address': hex_bytes_address}
        encoded_json = loads(dumps(json, cls=JsonBytesEncoder))
        self.assertEqual(hex_bytes_address.hex(), encoded_json['address'])

        bytes_address = bytes.fromhex(base_address)
        json = {'address': bytes_address}
        encoded_json = loads(dumps(json, cls=JsonBytesEncoder))
        self.assertEqual('0x' + bytes_address.hex(), encoded_json['address'])
def split_hex_32bytes(hex_obj: HexBytes) -> list:
    hex_str = str(hex_obj.hex())
    return [hex_str[i:i + 64] for i in range(0, len(hex_str), 64)]
    def test_safe_multisig_tx_serializer(self):
        safe = get_eth_address_with_key()[0]
        to = None
        value = int(10e18)
        tx_data = None
        operation = 0
        safe_tx_gas = 1
        data_gas = 1
        gas_price = 1
        gas_token = None
        refund_receiver = None
        nonce = 0

        data = {
            "safe": safe,
            "to": to,
            "value": value,  # 1 ether
            "data": tx_data,
            "operation": operation,
            "safe_tx_gas": safe_tx_gas,
            "data_gas": data_gas,
            "gas_price": gas_price,
            "gas_token": gas_token,
            "nonce": nonce,
            "signatures": [
                {
                    'r': 5,
                    's': 7,
                    'v': 27
                },
                {
                    'r': 17,
                    's': 29,
                    'v': 28
                }]}
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertFalse(serializer.is_valid())  # Less signatures than threshold

        # Signatures must be sorted!
        accounts = [Account.create() for _ in range(2)]
        accounts.sort(key=lambda x: x.address.lower())

        safe = get_eth_address_with_key()[0]
        data['safe'] = safe

        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertFalse(serializer.is_valid())  # To and data cannot both be null

        tx_data = HexBytes('0xabcd')
        data['data'] = tx_data.hex()
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertFalse(serializer.is_valid())  # Operation is not create, but no to provided

        # Now we fix the signatures
        to = accounts[-1].address
        data['to'] = to
        multisig_tx_hash = SafeTx(
            None,
            safe,
            to,
            value,
            tx_data,
            operation,
            safe_tx_gas,
            data_gas,
            gas_price,
            gas_token,
            refund_receiver,
            safe_nonce=nonce
        ).safe_tx_hash

        signatures = [account.signHash(multisig_tx_hash) for account in accounts]
        data['signatures'] = [{'v': s.v, 'r': s.r, 's': s.s} for s in signatures]
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertTrue(serializer.is_valid())

        data = {
            "safe": safe,
            "to": to,
            "value": value,  # 1 ether
            "data": tx_data,
            "operation": operation,
            "safe_tx_gas": safe_tx_gas,
            "data_gas": data_gas,
            "gas_price": gas_price,
            "gas_token": gas_token,
            "nonce": nonce,
            "refund_receiver": accounts[0].address,  # Refund receiver must be empty or relay service sender
            "signatures": [
                {
                    'r': 5,
                    's': 7,
                    'v': 27
                }]
        }
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertFalse(serializer.is_valid())

        data['refund_receiver'] = Account.from_key(settings.SAFE_TX_SENDER_PRIVATE_KEY).address
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertTrue(serializer.is_valid())

        data['refund_receiver'] = NULL_ADDRESS
        serializer = SafeRelayMultisigTxSerializer(data=data)
        self.assertTrue(serializer.is_valid())
Exemple #16
0
class SafeTx:
    tx: TxParams  # If executed, `tx` is set
    tx_hash: bytes  # If executed, `tx_hash` is set

    def __init__(
        self,
        ethereum_client: EthereumClient,
        safe_address: str,
        to: Optional[str],
        value: int,
        data: bytes,
        operation: int,
        safe_tx_gas: int,
        base_gas: int,
        gas_price: int,
        gas_token: Optional[str],
        refund_receiver: Optional[str],
        signatures: Optional[bytes] = None,
        safe_nonce: Optional[int] = None,
        safe_version: str = None,
        chain_id: Optional[int] = None,
    ):
        """
        :param ethereum_client:
        :param safe_address:
        :param to:
        :param value:
        :param data:
        :param operation:
        :param safe_tx_gas:
        :param base_gas:
        :param gas_price:
        :param gas_token:
        :param refund_receiver:
        :param signatures:
        :param safe_nonce: Current nonce of the Safe. If not provided, it will be retrieved from network
        :param safe_version: Safe version 1.0.0 renamed `baseGas` to `dataGas`. Safe version 1.3.0 added `chainId` to
        the `domainSeparator`. If not provided, it will be retrieved from network
        :param chain_id: Ethereum network chain_id is used in hash calculation for Safes >= 1.3.0. If not provided,
        it will be retrieved from the provided ethereum_client
        """

        self.ethereum_client = ethereum_client
        self.safe_address = safe_address
        self.to = to or NULL_ADDRESS
        self.value = value
        self.data = HexBytes(data) if data else b""
        self.operation = operation
        self.safe_tx_gas = safe_tx_gas
        self.base_gas = base_gas
        self.gas_price = gas_price
        self.gas_token = gas_token or NULL_ADDRESS
        self.refund_receiver = refund_receiver or NULL_ADDRESS
        self.signatures = signatures or b""
        self._safe_nonce = safe_nonce
        self._safe_version = safe_version
        self._chain_id = chain_id

    def __str__(self):
        return (
            f"SafeTx - safe={self.safe_address} - to={self.to} - value={self.value} - data={self.data.hex()} - "
            f"operation={self.operation} - safe-tx-gas={self.safe_tx_gas} - base-gas={self.base_gas} - "
            f"gas-price={self.gas_price} - gas-token={self.gas_token} - refund-receiver={self.refund_receiver} - "
            f"signers = {self.signers}")

    @property
    def w3(self):
        return self.ethereum_client.w3

    @cached_property
    def contract(self):
        return get_safe_contract(self.w3, address=self.safe_address)

    @cached_property
    def chain_id(self) -> int:
        if self._chain_id is not None:
            return self._chain_id
        else:
            return self.w3.eth.chain_id

    @cached_property
    def safe_nonce(self) -> str:
        if self._safe_nonce is not None:
            return self._safe_nonce
        else:
            return self.contract.functions.nonce().call()

    @cached_property
    def safe_version(self) -> str:
        if self._safe_version is not None:
            return self._safe_version
        else:
            return self.contract.functions.VERSION().call()

    @property
    def _eip712_payload(self) -> StructTuple:
        data = self.data.hex() if self.data else ""
        safe_version = Version(self.safe_version)
        cls = EIP712SafeTx if safe_version >= Version(
            "1.0.0") else EIP712LegacySafeTx
        message = cls(
            to=self.to,
            value=self.value,
            data=data,
            operation=self.operation,
            safeTxGas=self.safe_tx_gas,
            baseGas=self.base_gas,
            dataGas=self.base_gas,
            gasPrice=self.gas_price,
            gasToken=self.gas_token,
            refundReceiver=self.refund_receiver,
            nonce=self.safe_nonce,
        )
        domain = make_domain(
            verifyingContract=self.safe_address,
            chainId=self.chain_id
            if safe_version >= Version("1.3.0") else None,
        )
        return StructTuple(message, domain)

    @property
    def eip712_structured_data(self) -> Dict:
        message, domain = self._eip712_payload
        return message.to_message(domain)

    @property
    def safe_tx_hash(self) -> HexBytes:
        message, domain = self._eip712_payload
        signable_bytes = message.signable_bytes(domain)
        return HexBytes(Web3.keccak(signable_bytes))

    @property
    def signers(self) -> List[str]:
        if not self.signatures:
            return []
        else:
            return [
                safe_signature.owner
                for safe_signature in SafeSignature.parse_signature(
                    self.signatures, self.safe_tx_hash)
            ]

    @property
    def sorted_signers(self):
        return sorted(self.signers, key=lambda x: int(x, 16))

    @property
    def w3_tx(self):
        """
        :return: Web3 contract tx prepared for `call`, `transact` or `buildTransaction`
        """
        return self.contract.functions.execTransaction(
            self.to,
            self.value,
            self.data,
            self.operation,
            self.safe_tx_gas,
            self.base_gas,
            self.gas_price,
            self.gas_token,
            self.refund_receiver,
            self.signatures,
        )

    def _raise_safe_vm_exception(self, message: str) -> NoReturn:
        error_with_exception: Dict[str, Type[InvalidMultisigTx]] = {
            # https://github.com/gnosis/safe-contracts/blob/v1.3.0/docs/error_codes.md
            "GS000": CouldNotFinishInitialization,
            "GS001": ThresholdNeedsToBeDefined,
            "Could not pay gas costs with ether": CouldNotPayGasWithEther,
            "GS011": CouldNotPayGasWithEther,
            "Could not pay gas costs with token": CouldNotPayGasWithToken,
            "GS012": CouldNotPayGasWithToken,
            "GS013": SafeTransactionFailedWhenGasPriceAndSafeTxGasEmpty,
            "Hash has not been approved": HashHasNotBeenApproved,
            "Hash not approved": HashHasNotBeenApproved,
            "GS025": HashHasNotBeenApproved,
            "Invalid contract signature location: data not complete":
            InvalidContractSignatureLocation,
            "GS023": InvalidContractSignatureLocation,
            "Invalid contract signature location: inside static part":
            InvalidContractSignatureLocation,
            "GS021": InvalidContractSignatureLocation,
            "Invalid contract signature location: length not present":
            InvalidContractSignatureLocation,
            "GS022": InvalidContractSignatureLocation,
            "Invalid contract signature provided":
            InvalidContractSignatureLocation,
            "GS024": InvalidContractSignatureLocation,
            "Invalid owner provided": InvalidOwnerProvided,
            "Invalid owner address provided": InvalidOwnerProvided,
            "GS026": InvalidOwnerProvided,
            "Invalid signatures provided": InvalidSignaturesProvided,
            "Not enough gas to execute safe transaction":
            NotEnoughSafeTransactionGas,
            "GS010": NotEnoughSafeTransactionGas,
            "Only owners can approve a hash": OnlyOwnersCanApproveAHash,
            "GS030": OnlyOwnersCanApproveAHash,
            "GS031": MethodCanOnlyBeCalledFromThisContract,
            "Signature not provided by owner": SignatureNotProvidedByOwner,
            "Signatures data too short": SignaturesDataTooShort,
            "GS020": SignaturesDataTooShort,
            # ModuleManager
            "GS100": ModuleManagerException,
            "Invalid module address provided": ModuleManagerException,
            "GS101": ModuleManagerException,
            "GS102": ModuleManagerException,
            "Invalid prevModule, module pair provided": ModuleManagerException,
            "GS103": ModuleManagerException,
            "Method can only be called from an enabled module":
            ModuleManagerException,
            "GS104": ModuleManagerException,
            "Module has already been added": ModuleManagerException,
            # OwnerManager
            "Address is already an owner": OwnerManagerException,
            "GS200": OwnerManagerException,  # Owners have already been setup
            "GS201":
            OwnerManagerException,  # Threshold cannot exceed owner count
            "GS202": OwnerManagerException,  # Invalid owner address provided
            "GS203": OwnerManagerException,  # Invalid ower address provided
            "GS204": OwnerManagerException,  # Address is already an owner
            "GS205":
            OwnerManagerException,  # Invalid prevOwner, owner pair provided
            "Invalid prevOwner, owner pair provided": OwnerManagerException,
            "New owner count needs to be larger than new threshold":
            OwnerManagerException,
            "Threshold cannot exceed owner count": OwnerManagerException,
            "Threshold needs to be greater than 0": OwnerManagerException,
        }

        for reason, custom_exception in error_with_exception.items():
            if reason in message:
                raise custom_exception(message)
        raise InvalidMultisigTx(message)

    def call(
        self,
        tx_sender_address: Optional[str] = None,
        tx_gas: Optional[int] = None,
        block_identifier: Optional[BlockIdentifier] = "latest",
    ) -> int:
        """
        :param tx_sender_address:
        :param tx_gas: Force a gas limit
        :param block_identifier:
        :return: `1` if everything ok
        """
        parameters: Dict[str, Any] = {
            "from":
            tx_sender_address if tx_sender_address else self.safe_address
        }

        if tx_gas:
            parameters["gas"] = tx_gas
        try:
            success = self.w3_tx.call(parameters,
                                      block_identifier=block_identifier)

            if not success:
                raise InvalidInternalTx(
                    "Success bit is %d, should be equal to 1" % success)
            return success
        except (ContractLogicError, BadFunctionCallOutput, ValueError) as exc:
            # e.g. web3.exceptions.ContractLogicError: execution reverted: Invalid owner provided
            return self._raise_safe_vm_exception(str(exc))
        except ValueError as exc:  # Parity
            """
            Parity throws a ValueError, e.g.
            {'code': -32015,
             'message': 'VM execution error.',
             'data': 'Reverted 0x08c379a0000000000000000000000000000000000000000000000000000000000000020000000000000000
                      000000000000000000000000000000000000000000000001b496e76616c6964207369676e6174757265732070726f7669
                      6465640000000000'
            }
            """
            error_dict = exc.args[0]
            data = error_dict.get("data")
            if data and isinstance(data, str) and "Reverted " in data:
                # Parity
                result = HexBytes(data.replace("Reverted ", ""))
                return self._raise_safe_vm_exception(str(result))
            else:
                raise exc

    def recommended_gas(self) -> Wei:
        """
        :return: Recommended gas to use on the ethereum_tx
        """
        return Wei(self.base_gas + self.safe_tx_gas + 75000)

    def execute(
        self,
        tx_sender_private_key: str,
        tx_gas: Optional[int] = None,
        tx_gas_price: Optional[int] = None,
        tx_nonce: Optional[int] = None,
        block_identifier: Optional[BlockIdentifier] = "latest",
        eip1559_speed: Optional[TxSpeed] = None,
    ) -> Tuple[HexBytes, TxParams]:
        """
        Send multisig tx to the Safe

        :param tx_sender_private_key: Sender private key
        :param tx_gas: Gas for the external tx. If not, `(safe_tx_gas + base_gas) * 2` will be used
        :param tx_gas_price: Gas price of the external tx. If not, `gas_price` will be used
        :param tx_nonce: Force nonce for `tx_sender`
        :param block_identifier: `latest` or `pending`
        :param eip1559_speed: If provided, use EIP1559 transaction
        :return: Tuple(tx_hash, tx)
        :raises: InvalidMultisigTx: If user tx cannot go through the Safe
        """

        sender_account = Account.from_key(tx_sender_private_key)
        if eip1559_speed and self.ethereum_client.is_eip1559_supported():
            tx_parameters = self.ethereum_client.set_eip1559_fees(
                {
                    "from": sender_account.address,
                },
                tx_speed=eip1559_speed,
            )
        else:
            tx_parameters = {
                "from": sender_account.address,
                "gasPrice": tx_gas_price or self.w3.eth.gas_price,
            }

        if tx_gas:
            tx_parameters["gas"] = tx_gas
        if tx_nonce is not None:
            tx_parameters["nonce"] = tx_nonce

        self.tx = self.w3_tx.buildTransaction(tx_parameters)
        self.tx["gas"] = Wei(
            tx_gas or (max(self.tx["gas"] + 75000, self.recommended_gas())))

        self.tx_hash = self.ethereum_client.send_unsigned_transaction(
            self.tx,
            private_key=sender_account.key,
            retry=False if tx_nonce is not None else True,
            block_identifier=block_identifier,
        )

        # Set signatures empty after executing the tx. `Nonce` is increased even if it fails,
        # so signatures are not valid anymore
        self.signatures = b""
        return self.tx_hash, self.tx

    def sign(self, private_key: str) -> bytes:
        """
        {bytes32 r}{bytes32 s}{uint8 v}
        :param private_key:
        :return: Signature
        """
        account = Account.from_key(private_key)
        signature_dict = account.signHash(self.safe_tx_hash)
        signature = signature_to_bytes(signature_dict["v"],
                                       signature_dict["r"],
                                       signature_dict["s"])

        # Insert signature sorted
        if account.address not in self.signers:
            new_owners = self.signers + [account.address]
            new_owner_pos = sorted(new_owners, key=lambda x: int(x, 16)).index(
                account.address)
            self.signatures = (self.signatures[:65 * new_owner_pos] +
                               signature +
                               self.signatures[65 * new_owner_pos:])
        return signature

    def unsign(self, address: str) -> bool:
        for pos, signer in enumerate(self.signers):
            if signer == address:
                self.signatures = self.signatures.replace(
                    self.signatures[pos * 65:pos * 65 + 65], b"")
                return True
        return False
    def test_decode_multisend(self):
        # Change Safe contract master copy and set fallback manager multisend transaction
        safe_contract_address = "0x5B9ea52Aaa931D4EEf74C8aEaf0Fe759434FeD74"
        value = "0"
        operation = MultiSendOperation.CALL.value
        data = HexBytes(
            "0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000"
            "00000000000000000000000000000000000000000000f2005b9ea52aaa931d4eef74c8aeaf0fe759434fed740000"
            "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
            "000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e9422"
            "7e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000"
            "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f0"
            "8a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf440000000000000000000000"
            "000000")
        change_master_copy_data = HexBytes(
            "0x7de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f"
        )
        change_fallback_manager_data = HexBytes(
            "0xf08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cd"
            "bdf44")
        tx_decoder = get_tx_decoder()
        expected = [
            {
                "operation": operation,
                "to": safe_contract_address,
                "value": value,
                "data": change_master_copy_data.hex(),
                "data_decoded": {
                    "method":
                    "changeMasterCopy",
                    "parameters": [{
                        "name":
                        "_masterCopy",
                        "type":
                        "address",
                        "value":
                        "0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F",
                    }],
                },
            },
            {
                "operation": operation,
                "to": safe_contract_address,
                "value": value,
                "data": change_fallback_manager_data.hex(),
                "data_decoded": {
                    "method":
                    "setFallbackHandler",
                    "parameters": [{
                        "name":
                        "handler",
                        "type":
                        "address",
                        "value":
                        "0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44",
                    }],
                },
            },
        ]
        # Get just the multisend object
        self.assertEqual(tx_decoder.decode_multisend_data(data), expected)

        # Now decode all the data
        expected = (
            "multiSend",
            [{
                "name":
                "transactions",
                "type":
                "bytes",
                "value":
                "0x005b9ea52aaa931d4eef74c8aeaf0fe759434fed74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf44",
                "value_decoded": [
                    {
                        "operation": operation,
                        "to": safe_contract_address,
                        "value": value,
                        "data": change_master_copy_data.hex(),
                        "data_decoded": {
                            "method":
                            "changeMasterCopy",
                            "parameters": [{
                                "name":
                                "_masterCopy",
                                "type":
                                "address",
                                "value":
                                "0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F",
                            }],
                        },
                    },
                    {
                        "operation": operation,
                        "to": safe_contract_address,
                        "value": value,
                        "data": change_fallback_manager_data.hex(),
                        "data_decoded": {
                            "method":
                            "setFallbackHandler",
                            "parameters": [{
                                "name":
                                "handler",
                                "type":
                                "address",
                                "value":
                                "0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44",
                            }],
                        },
                    },
                ],
            }],
        )
        self.assertEqual(tx_decoder.decode_transaction_with_types(data),
                         expected)

        # Safe tx decoder cannot decode it. It would be problematic for the internal tx indexer
        safe_tx_decoder = get_safe_tx_decoder()
        with self.assertRaises(CannotDecode):
            safe_tx_decoder.decode_transaction_with_types(data)
class SignedTransaction:
    raw_transaction: str
    signature: HexBytes

    def __init__(self, raw_transaction: str, signature: Union[bytes, str,
                                                              int]):
        self.raw_transaction = raw_transaction
        self.signature = HexBytes(signature)

    @property
    def payload(self) -> Dict[str, str]:
        return {
            'raw_transaction': self.raw_transaction,
            'signature': self.signature.hex()
        }

    def ecrecover(self) -> ChecksumAddress:
        public_key = self.__recover()
        self.__validate(public_key)
        return public_key.to_checksum_address()

    def __recover(self) -> PublicKey:
        message_hash = Transaction.hash(self.raw_transaction)
        return PublicKey.recover_from_msg_hash(message_hash,
                                               self.__load_signature())

    def __load_signature(self) -> Signature:
        try:
            return Signature(signature_bytes=self.signature)
        except (TypeError, ValidationError, BadSignature):
            raise exceptions.InvalidSignatureError(
                f'{self.signature} is not a valid signature')

    def __validate(self, public_key: PublicKey):
        transaction_address = json.loads(self.raw_transaction)['from']
        recovered_address = public_key.to_checksum_address()
        if transaction_address != recovered_address:
            raise exceptions.WrongSignatureError(
                f'{recovered_address} did not match expected {transaction_address}'
            )

    def transaction(self) -> Transaction:
        loaded = self.__load_validated_transaction()
        transaction = self.__import_transaction(loaded)
        return transaction(**loaded['data'])

    def __load_validated_transaction(self) -> Dict[str, Any]:
        loaded = json.loads(self.raw_transaction)
        jsonschema.validate(loaded, TRANSACTION_SCHEMA)
        return loaded

    def __import_transaction(
            self, loaded_transaction: Dict[str, Any]) -> Type[Transaction]:
        module_name, class_name = self.__get_transaction_module_name(
            loaded_transaction)
        module = self.__import_transaction_module(module_name)
        return self.__import_transaction_class(module, class_name)

    @staticmethod
    def __get_transaction_module_name(
            loaded_transaction: Dict[str, Any]) -> Tuple[str, str]:
        parts = loaded_transaction['name'].rsplit(':', 1)
        # Assuming this was checked by the schema first
        return parts[0], parts[1]

    @staticmethod
    def __import_transaction_module(module_name: str) -> ModuleType:
        mod_spec = importlib.util.find_spec(module_name)
        if not mod_spec:
            raise exceptions.UnsupportedTransactionError(
                f'Missing {module_name}')

        return importlib.import_module(mod_spec.name)

    @staticmethod
    def __import_transaction_class(module: ModuleType,
                                   class_name: str) -> Type[Transaction]:
        if not hasattr(module, class_name):
            raise exceptions.UnsupportedTransactionError(
                f'{module.__name__} has no class {class_name}')

        transaction = getattr(module, class_name)
        if not issubclass(transaction, Transaction):
            raise exceptions.UnsupportedTransactionError(
                f'{module.__name__}:{class_name} is not a Transaction')

        return transaction
Exemple #19
0
def _decode(inputs: List, topics: List, data: str) -> List:
    indexed_count = len([i for i in inputs if i["indexed"]])

    if indexed_count and not topics:
        # special case - if the ABI has indexed values but the log does not,
        # we should still be able to decode the data
        unindexed_types = inputs

    else:
        if indexed_count < len(topics):
            raise EventError(
                "Event log does not contain enough topics for the given ABI - this"
                " is usually because an event argument is not marked as indexed"
            )
        if indexed_count > len(topics):
            raise EventError(
                "Event log contains more topics than expected for the given ABI - this is"
                " usually because an event argument is incorrectly marked as indexed"
            )
        unindexed_types = [i for i in inputs if not i["indexed"]]

    # decode the unindexed event data
    try:
        unindexed_types = _params(unindexed_types)
    except (KeyError, TypeError):
        raise ABIError("Invalid ABI")

    if unindexed_types and data == "0x":
        length = len(unindexed_types) * 32
        data = f"0x{bytes(length).hex()}"

    try:
        decoded = list(decode_abi(unindexed_types, HexBytes(data)))[::-1]
    except InsufficientDataBytes:
        raise EventError("Event data has insufficient length")
    except NonEmptyPaddingBytes:
        raise EventError("Malformed data field in event log")
    except OverflowError:
        raise EventError("Cannot decode event due to overflow error")

    # decode the indexed event data and create the returned dict
    topics = topics[::-1]
    result = []
    for i in inputs:
        result.append({"name": i["name"], "type": i["type"]})

        if "components" in i:
            result[-1]["components"] = i["components"]

        if topics and i["indexed"]:
            encoded = HexBytes(topics.pop())
            try:
                value = decode_single(i["type"], encoded)
            except (InsufficientDataBytes, NoEntriesFound, OverflowError):
                # an array or other data type that uses multiple slots
                result[-1].update({"value": encoded.hex(), "decoded": False})
                continue
        else:
            value = decoded.pop()

        if isinstance(value, bytes):
            # converting to `HexBytes` first ensures the leading `0x`
            value = HexBytes(value).hex()
        result[-1].update({"value": value, "decoded": True})

    return result
Exemple #20
0
class SafeTx:
    tx: TxParams  # If executed, `tx` is set
    tx_hash: bytes  # If executed, `tx_hash` is set

    def __init__(self,
                 ethereum_client: EthereumClient,
                 safe_address: str,
                 to: str,
                 value: int,
                 data: bytes,
                 operation: int,
                 safe_tx_gas: int,
                 base_gas: int,
                 gas_price: int,
                 gas_token: str,
                 refund_receiver: str,
                 signatures: bytes = b'',
                 safe_nonce: Optional[int] = None,
                 safe_version: str = '1.0.0'):

        assert isinstance(signatures, bytes), "Signatures must be bytes"
        self.ethereum_client = ethereum_client
        self.safe_address = safe_address
        self.to = to or NULL_ADDRESS
        self.value = value
        self.data = HexBytes(data) if data else b''
        self.operation = operation
        self.safe_tx_gas = safe_tx_gas
        self.base_gas = base_gas
        self.gas_price = gas_price
        self.gas_token = gas_token or NULL_ADDRESS
        self.refund_receiver = refund_receiver or NULL_ADDRESS
        self.safe_nonce = safe_nonce
        self.signatures = signatures
        self.safe_version = safe_version

    @property
    def w3(self):
        return self.ethereum_client.w3

    @property
    def safe_tx_hash(self) -> HexBytes:
        if self.safe_nonce is None:
            raise ValueError('`safe_nonce` must be set to calculate hash')
        data = self.data.hex() if self.data else ''
        base_gas_name = 'baseGas' if Version(
            self.safe_version) >= Version('1.0.0') else 'dataGas'

        structured_data = {
            'types': {
                'EIP712Domain': [
                    {
                        'name': 'verifyingContract',
                        'type': 'address'
                    },
                ],
                'SafeTx': [{
                    'name': 'to',
                    'type': 'address'
                }, {
                    'name': 'value',
                    'type': 'uint256'
                }, {
                    'name': 'data',
                    'type': 'bytes'
                }, {
                    'name': 'operation',
                    'type': 'uint8'
                }, {
                    'name': 'safeTxGas',
                    'type': 'uint256'
                }, {
                    'name': base_gas_name,
                    'type': 'uint256'
                }, {
                    'name': 'gasPrice',
                    'type': 'uint256'
                }, {
                    'name': 'gasToken',
                    'type': 'address'
                }, {
                    'name': 'refundReceiver',
                    'type': 'address'
                }, {
                    'name': 'nonce',
                    'type': 'uint256'
                }]
            },
            'primaryType': 'SafeTx',
            'domain': {
                'verifyingContract': self.safe_address,
            },
            'message': {
                'to': self.to,
                'value': self.value,
                'data': data,
                'operation': self.operation,
                'safeTxGas': self.safe_tx_gas,
                base_gas_name: self.base_gas,
                'gasPrice': self.gas_price,
                'gasToken': self.gas_token,
                'refundReceiver': self.refund_receiver,
                'nonce': self.safe_nonce,
            },
        }

        return HexBytes(encode_typed_data(structured_data))

    @property
    def signers(self) -> List[str]:
        return list([
            safe_signature.owner
            for safe_signature in SafeSignature.parse_signature(
                self.signatures, self.safe_tx_hash)
        ])

    @property
    def sorted_signers(self):
        return sorted(self.signers, key=lambda x: x.lower())

    @property
    def w3_tx(self):
        """
        :return: Web3 contract tx prepared for `call`, `transact` or `buildTransaction`
        """
        safe_contract = get_safe_contract(self.w3, address=self.safe_address)
        return safe_contract.functions.execTransaction(
            self.to, self.value, self.data, self.operation, self.safe_tx_gas,
            self.base_gas, self.gas_price, self.gas_token,
            self.refund_receiver, self.signatures)

    def _raise_safe_vm_exception(self, message: str) -> NoReturn:
        error_with_exception: Dict[str, Type[InvalidMultisigTx]] = {
            'Could not pay gas costs with ether': CouldNotPayGasWithEther,
            'Could not pay gas costs with token': CouldNotPayGasWithToken,
            'Hash has not been approved': HashHasNotBeenApproved,
            'Hash not approved': HashHasNotBeenApproved,
            'Invalid contract signature location: data not complete':
            InvalidContractSignatureLocation,
            'Invalid contract signature location: inside static part':
            InvalidContractSignatureLocation,
            'Invalid contract signature location: length not present':
            InvalidContractSignatureLocation,
            'Invalid contract signature provided':
            InvalidContractSignatureLocation,
            'Invalid owner provided': InvalidOwnerProvided,
            'Invalid owner address provided': InvalidOwnerProvided,
            'Invalid signatures provided': InvalidSignaturesProvided,
            'Not enough gas to execute safe transaction':
            NotEnoughSafeTransactionGas,
            'Only owners can approve a hash': OnlyOwnersCanApproveAHash,
            'Signature not provided by owner': SignatureNotProvidedByOwner,
            'Signatures data too short': SignaturesDataTooShort,
            # OwnerManager
            'Address is already an owner': OwnerManagerException,
            'Invalid prevOwner, owner pair provided': OwnerManagerException,
            'New owner count needs to be larger than new threshold':
            OwnerManagerException,
            'Threshold cannot exceed owner count': OwnerManagerException,
            'Threshold needs to be greater than 0': OwnerManagerException,
            # ModuleManager
            'Invalid module address provided': ModuleManagerException,
            'Invalid prevModule, module pair provided': ModuleManagerException,
            'Method can only be called from an enabled module':
            ModuleManagerException,
            'Module has already been added': ModuleManagerException,
        }

        for reason, custom_exception in error_with_exception.items():
            if reason in message:
                raise custom_exception(message)
        raise InvalidMultisigTx(message)

    def call(self,
             tx_sender_address: Optional[str] = None,
             tx_gas: Optional[int] = None,
             block_identifier: Optional[BlockIdentifier] = 'latest') -> int:
        """
        :param tx_sender_address:
        :param tx_gas: Force a gas limit
        :param block_identifier:
        :return: `1` if everything ok
        """
        parameters: Dict[str, Any] = {
            'from':
            tx_sender_address if tx_sender_address else self.safe_address
        }

        if tx_gas:
            parameters['gas'] = tx_gas
        try:
            success = self.w3_tx.call(parameters,
                                      block_identifier=block_identifier)

            if not success:
                raise InvalidInternalTx(
                    'Success bit is %d, should be equal to 1' % success)
            return success
        except BadFunctionCallOutput as exc:  # Geth
            return self._raise_safe_vm_exception(str(exc))
        except ValueError as exc:  # Parity
            """
            Parity throws a ValueError, e.g.
            {'code': -32015,
             'message': 'VM execution error.',
             'data': 'Reverted 0x08c379a0000000000000000000000000000000000000000000000000000000000000020000000000000000
                      000000000000000000000000000000000000000000000001b496e76616c6964207369676e6174757265732070726f7669
                      6465640000000000'
            }
            """
            error_dict = exc.args[0]
            data = error_dict.get('data')
            if data and isinstance(data, str) and 'Reverted ' in data:
                # Parity
                result = HexBytes(data.replace('Reverted ', ''))
                return self._raise_safe_vm_exception(str(result))
            else:
                raise exc

    def execute(
        self,
        tx_sender_private_key: str,
        tx_gas: Optional[int] = None,
        tx_gas_price: Optional[int] = None,
        tx_nonce: Optional[int] = None,
        block_identifier: Optional[BlockIdentifier] = 'latest'
    ) -> Tuple[HexBytes, TxParams]:
        """
        Send multisig tx to the Safe
        :param tx_sender_private_key: Sender private key
        :param tx_gas: Gas for the external tx. If not, `(safe_tx_gas + base_gas) * 2` will be used
        :param tx_gas_price: Gas price of the external tx. If not, `gas_price` will be used
        :param tx_nonce: Force nonce for `tx_sender`
        :param block_identifier: `latest` or `pending`
        :return: Tuple(tx_hash, tx)
        :raises: InvalidMultisigTx: If user tx cannot go through the Safe
        """

        sender_account = Account.from_key(tx_sender_private_key)
        tx_gas_price = tx_gas_price or self.gas_price or self.w3.eth.gasPrice

        tx_parameters = {
            'from': sender_account.address,
            'gasPrice': tx_gas_price,
        }

        if tx_gas:
            tx_parameters['gas'] = tx_gas
        if tx_nonce is not None:
            tx_parameters['nonce'] = tx_nonce

        self.tx = self.w3_tx.buildTransaction(tx_parameters)
        self.tx['gas'] = Wei(
            tx_gas
            or (max(self.tx['gas'], self.base_gas + self.safe_tx_gas) + 25000))

        self.tx_hash = self.ethereum_client.send_unsigned_transaction(
            self.tx,
            private_key=sender_account.key,
            retry=True,
            block_identifier=block_identifier)

        # Set signatures empty after executing the tx. `Nonce` is increased even if it fails,
        # so signatures are not valid anymore
        self.signatures = b''
        return self.tx_hash, self.tx

    def sign(self, private_key: str) -> bytes:
        """
        {bytes32 r}{bytes32 s}{uint8 v}
        :param private_key:
        :return:
        """
        account = Account.from_key(private_key)
        signature_dict = account.signHash(self.safe_tx_hash)
        signature = signature_to_bytes(
            (signature_dict['v'], signature_dict['r'], signature_dict['s']))

        # Insert signature sorted
        if account.address not in self.signers:
            new_owners = self.signers + [account.address]
            new_owner_pos = sorted(new_owners, key=lambda x: x.lower()).index(
                account.address)
            self.signatures = (self.signatures[:65 * new_owner_pos] +
                               signature +
                               self.signatures[65 * new_owner_pos:])
        return signature

    def unsign(self, address: str) -> bool:
        for pos, signer in enumerate(self.signers):
            if signer == address:
                self.signatures = self.signatures.replace(
                    self.signatures[pos * 65:pos * 65 + 65], b'')
                return True
        return False
Exemple #21
0
def random_32bytes():
    value = HexBytes(os.urandom(32))
    return value.hex()
def query_block():
    textbox.delete('1.0',END)
    # Connecting to the database file
    #conn = sqlite3.connect(sqlite_file)
    #c = conn.cursor()

    n = int(user_entry.get())

    #Block
    block = web3.eth.getBlock(n)

    #This converts the time from seconds since unix epoch to a date-time timestamp
    formatted_time = str(time.strftime('%d-%m-%Y %H:%M:%S', time.localtime(block['timestamp'])))

    all_txs_hash = block["transactions"]


    if not all_txs_hash: #if the block contained no transactions
        highest_value_tx_in_block = 0
        hash_of_containing_transaction = '-'
    else:
        #calls function biggest_tx and passed an array of the tx hashes in the block being queried
        highest_value_tx_in_block, hash_of_containing_transaction = biggest_tx(all_txs_hash)

    eth_price = es.get_eth_price()
    eth_price_float = float(eth_price['ethusd'])
    dollar_value = str("{:,.2f}".format((eth_price_float*(float(web3.fromWei(highest_value_tx_in_block, 'ether'))))))
    
 

    blocknum=str(block["number"])
    timestampyo=str(formatted_time)
    sizebytes=str(block["size"])
    txcount=str(web3.eth.getBlockTransactionCount(n))
    nonce=str(block["nonce"])
    miner=str(block["miner"])
    gasused=str("{:,}".format(block["gasUsed"]))
    gaslimit=str("{:,}".format(block["gasLimit"]))
    difficulty=str("{:,.2f}".format(block["difficulty"]*0.000000000001))
    combined=''

    gasused_float = float(block["gasUsed"])
    gaslimit_float = float(block["gasLimit"])

    gasused_percent_of_limit = (100*(gasused_float/gaslimit_float))

    textbox.configure(font='courier 13')# = Text(root, font='courier 18', background='gray90')


    #inserting the data and making the titles bold
    textbox.insert('1.0', 'BLOCK #:............................. '+blocknum+'\n\n')
    textbox.tag_add("BOLD","1.0","1.36")

    textbox.insert('3.0', 'TIMESTAMP:........................... '+timestampyo+'\n\n')
    textbox.tag_add("BOLD","3.0","3.36")
    
    textbox.insert('5.0', 'SIZE (BYTES):........................ '+sizebytes+'\n\n')
    textbox.tag_add("BOLD","5.0","5.36")
    
    textbox.insert('7.0', 'TX COUNT:............................ '+txcount+'\n\n')
    textbox.tag_add("BOLD","7.0","7.36")
    
    textbox.insert('9.0', 'MINER:............................... '+miner+'\n\n')
    textbox.tag_add("BOLD","9.0","9.36")
    
    textbox.insert('11.0', 'GAS LIMIT:........................... '+gaslimit+'\n\n')
    textbox.tag_add("BOLD","11.0","11.36")
        
    textbox.insert('13.0', 'GAS USED:............................ '+gasused+'  ('+str("{:.2f}".format(gasused_percent_of_limit))+'%)'+'\n\n')
    textbox.tag_add("BOLD","13.0","13.36")
    
    textbox.insert('15.0', 'DIFFICULTY (TH):..................... '+difficulty+'\n\n')
    textbox.tag_add("BOLD","15.0","15.36")

    textbox.insert('17.0', '------------------------'+'\n\n')
    textbox.tag_add("BOLD","17.0","17.37")

    textbox.insert('18.0', 'LARGEST TRANSACTION WITHIN BLOCK:\n\n')
    textbox.tag_add("BOLD","18.0","18.36")

    textbox.insert('19.0', 'HASH:.................................'+str(hash_of_containing_transaction)+'\n\n')
    textbox.tag_add("BOLD","19.0","19.37")

    textbox.insert('20.0', 'VALUE:................................'+str((web3.fromWei(highest_value_tx_in_block, 'ether')))+' ETH'+ ' ($' +str(dollar_value)+')'+'\n\n')
    textbox.tag_add("BOLD","20.0","20.37")

    textbox.insert('21.0', '------------------------'+'\n\n')
    textbox.tag_add("BOLD","21.0","21.37")

    textbox.insert('23.0', 'All Transactions in block:'+'\n\n')
    textbox.tag_add("BOLD","23.0","23.37")

    for tx in all_txs_hash:
        formatted = HexBytes.hex(tx)
        textbox.insert('25.0', formatted+'\n\n')
        
    
    textbox.tag_config("BOLD", font=bold_font_bigger)
Exemple #23
0
            if receipt._subcalls == None:
                receipt._subcalls = []
            receipt._subcalls.append(
                {"from": step["address"], "to": EthAddress(address), "op": step["op"]}
            )
            if step["op"] in ("CALL", "CALLCODE"):
                receipt._subcalls[-1]["value"] = int(step["stack"][-3], 16)
            if calldata and last_map[trace[i]["depth"]].get("function"):
                fn = last_map[trace[i]["depth"]]["function"]
                receipt._subcalls[-1]["function"] = fn._input_sig
                try:
                    zip_ = zip(fn.abi["inputs"], fn.decode_input(calldata))
                    inputs = {i[0]["name"]: i[1] for i in zip_}  # type: ignore
                    receipt._subcalls[-1]["inputs"] = inputs
                except Exception:
                    receipt._subcalls[-1]["calldata"] = calldata.hex()
            elif calldata:
                receipt._subcalls[-1]["calldata"] = calldata.hex()

        # update trace from last_map
        last = last_map[trace[i]["depth"]]
        trace[i].update(
            address=last["address"],
            fn=last["internal_calls"][-1],
            jumpDepth=last["jumpDepth"],
            source=False,
        )

        opcode = trace[i]["op"]
        if opcode == "CALL" and int(trace[i]["stack"][-3], 16):
            receipt._add_internal_xfer(
Exemple #24
0
 def delete_order_by_hash(self, order_hash: hexbytes.HexBytes) -> None:
     self.session.query(OrderORM).filter_by(
         # remove 0x to keep backwards compatibility
         msg_hash=remove_0x_prefix(order_hash.hex())
     ).delete(synchronize_session=False)
     self.session.commit()
def parse_string(hex_obj: HexBytes) -> str:
    hex_str = str(hex_obj.hex())
    # chunk = web3.toInt(hexstr=hex_str[:64])
    len = web3.toInt(hexstr=hex_str[64:128])
    data = hex_str[128:128 + len * 2]
    return web3.toText(hexstr=data)
Exemple #26
0
    def _expand_trace(self) -> None:
        """Adds the following attributes to each step of the stack trace:

        address: The address executing this contract.
        contractName: The name of the contract.
        fn: The name of the function.
        jumpDepth: Number of jumps made since entering this contract. The
                   initial value is 0.
        source: {
            filename: path to the source file for this step
            offset: Start and end offset associated source code
        }
        """
        if self._raw_trace is None:
            self._get_trace()
        if self._trace is not None:
            # in case `_get_trace` also expanded the trace, do not repeat
            return

        self._trace = trace = self._raw_trace
        self._new_contracts = []
        self._internal_transfers = []
        self._subcalls = []
        if self.contract_address or not trace:
            coverage._add_transaction(self.coverage_hash, {})
            return

        if trace[0]["depth"] == 1:
            self._trace_origin = "geth"
            self._call_cost = self.gas_used - trace[0]["gas"] + trace[-1]["gas"]
            for t in trace:
                t["depth"] = t["depth"] - 1
        else:
            self._trace_origin = "ganache"
            if trace[0]["gasCost"] >= 21000:
                # in ganache <6.10.0, gas costs are shifted by one step - we can
                # identify this when the first step has a gas cost >= 21000
                self._call_cost = trace[0]["gasCost"]
                for i in range(len(trace) - 1):
                    trace[i]["gasCost"] = trace[i + 1]["gasCost"]
                trace[-1]["gasCost"] = 0
            else:
                self._call_cost = self.gas_used - trace[0]["gas"] + trace[-1][
                    "gas"]

        # last_map gives a quick reference of previous values at each depth
        last_map = {
            0: _get_last_map(self.receiver, self.input[:10])
        }  # type: ignore
        coverage_eval: Dict = {last_map[0]["name"]: {}}

        for i in range(len(trace)):
            # if depth has increased, tx has called into a different contract
            if trace[i]["depth"] > trace[i - 1]["depth"]:
                step = trace[i - 1]
                if step["op"] in ("CREATE", "CREATE2"):
                    # creating a new contract
                    out = next(x for x in trace[i:]
                               if x["depth"] == step["depth"])
                    address = out["stack"][-1][-40:]
                    sig = f"<{step['op']}>"
                    calldata = None
                    self._new_contracts.append(EthAddress(address))
                    if int(step["stack"][-1], 16):
                        self._add_internal_xfer(step["address"], address,
                                                step["stack"][-1])
                else:
                    # calling an existing contract
                    stack_idx = -4 if step["op"] in ("CALL",
                                                     "CALLCODE") else -3
                    offset = int(step["stack"][stack_idx], 16)
                    length = int(step["stack"][stack_idx - 1], 16)
                    calldata = HexBytes("".join(
                        step["memory"]))[offset:offset + length]
                    sig = calldata[:4].hex()
                    address = step["stack"][-2][-40:]

                last_map[trace[i]["depth"]] = _get_last_map(address, sig)
                coverage_eval.setdefault(last_map[trace[i]["depth"]]["name"],
                                         {})

                self._subcalls.append({
                    "from": step["address"],
                    "to": EthAddress(address),
                    "op": step["op"]
                })
                if step["op"] in ("CALL", "CALLCODE"):
                    self._subcalls[-1]["value"] = int(step["stack"][-3], 16)
                if calldata and last_map[trace[i]["depth"]].get("function"):
                    fn = last_map[trace[i]["depth"]]["function"]
                    zip_ = zip(fn.abi["inputs"], fn.decode_input(calldata))
                    self._subcalls[-1].update(
                        inputs={i[0]["name"]: i[1]
                                for i in zip_},  # type:ignore
                        function=fn._input_sig,
                    )
                elif calldata:
                    self._subcalls[-1]["calldata"] = calldata.hex()

            # update trace from last_map
            last = last_map[trace[i]["depth"]]
            trace[i].update(
                address=last["address"],
                contractName=last["name"],
                fn=last["internal_calls"][-1],
                jumpDepth=last["jumpDepth"],
                source=False,
            )

            opcode = trace[i]["op"]
            if opcode == "CALL" and int(trace[i]["stack"][-3], 16):
                self._add_internal_xfer(last["address"],
                                        trace[i]["stack"][-2][-40:],
                                        trace[i]["stack"][-3])

            try:
                pc = last["pc_map"][trace[i]["pc"]]
            except (KeyError, TypeError):
                # we don't have enough information about this contract
                continue

            if trace[i]["depth"] and opcode in ("RETURN", "REVERT", "INVALID",
                                                "SELFDESTRUCT"):
                subcall: dict = next(
                    i for i in self._subcalls[::-1]
                    if i["to"] == last["address"]  # type: ignore
                )

                if opcode == "RETURN":
                    returndata = _get_memory(trace[i], -1)
                    if returndata:
                        fn = last["function"]
                        try:
                            return_values = fn.decode_output(returndata)
                            if len(fn.abi["outputs"]) == 1:
                                return_values = (return_values, )
                            subcall["return_value"] = return_values
                        except Exception:
                            subcall["returndata"] = returndata.hex()
                    else:
                        subcall["return_value"] = None
                elif opcode == "SELFDESTRUCT":
                    subcall["selfdestruct"] = True
                else:
                    if opcode == "REVERT":
                        data = _get_memory(trace[i], -1)[4:]
                        if data:
                            subcall["revert_msg"] = decode_abi(["string"],
                                                               data)[0]
                    if "revert_msg" not in subcall and "dev" in pc:
                        subcall["revert_msg"] = pc["dev"]

            if "path" not in pc:
                continue
            trace[i]["source"] = {
                "filename": last["path_map"][pc["path"]],
                "offset": pc["offset"]
            }

            if "fn" not in pc:
                continue

            # calculate coverage
            if last["coverage"]:
                if pc["path"] not in coverage_eval[last["name"]]:
                    coverage_eval[last["name"]][pc["path"]] = [
                        set(), set(), set()
                    ]
                if "statement" in pc:
                    coverage_eval[last["name"]][pc["path"]][0].add(
                        pc["statement"])
                if "branch" in pc:
                    if pc["op"] != "JUMPI":
                        last["active_branches"].add(pc["branch"])
                    elif "active_branches" not in last or pc["branch"] in last[
                            "active_branches"]:
                        # false, true
                        key = 1 if trace[i +
                                         1]["pc"] == trace[i]["pc"] + 1 else 2
                        coverage_eval[last["name"]][pc["path"]][key].add(
                            pc["branch"])
                        if "active_branches" in last:
                            last["active_branches"].remove(pc["branch"])

            # ignore jumps with no function - they are compiler optimizations
            if "jump" in pc:
                # jump 'i' is calling into an internal function
                if pc["jump"] == "i":
                    try:
                        fn = last["pc_map"][trace[i + 1]["pc"]]["fn"]
                    except (KeyError, IndexError):
                        continue
                    if fn != last["internal_calls"][-1]:
                        last["internal_calls"].append(fn)
                        last["jumpDepth"] += 1
                # jump 'o' is returning from an internal function
                elif last["jumpDepth"] > 0:
                    del last["internal_calls"][-1]
                    last["jumpDepth"] -= 1
        coverage._add_transaction(
            self.coverage_hash,
            dict((k, v) for k, v in coverage_eval.items() if v))
    def test_decode_multisend(self):
        # Change Safe contract master copy and set fallback manager multisend transaction
        safe_contract_address = '0x5B9ea52Aaa931D4EEf74C8aEaf0Fe759434FeD74'
        value = 0
        operation = MultiSendOperation.CALL.value
        data = HexBytes(
            '0x8d80ff0a0000000000000000000000000000000000000000000000000000000000000020000000000000000000'
            '00000000000000000000000000000000000000000000f2005b9ea52aaa931d4eef74c8aeaf0fe759434fed740000'
            '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
            '000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e9422'
            '7e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000'
            '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f0'
            '8a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf440000000000000000000000'
            '000000')
        change_master_copy_data = HexBytes(
            '0x7de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f'
        )
        change_fallback_manager_data = HexBytes(
            '0xf08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cd'
            'bdf44')
        tx_decoder = get_tx_decoder()
        expected = [{
            'operation': operation,
            'to': safe_contract_address,
            'value': value,
            'data': change_master_copy_data.hex(),
            'data_decoded': {
                'method':
                'changeMasterCopy',
                'parameters': [{
                    'name':
                    '_masterCopy',
                    'type':
                    'address',
                    'value':
                    '0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F'
                }]
            }
        }, {
            'operation': operation,
            'to': safe_contract_address,
            'value': value,
            'data': change_fallback_manager_data.hex(),
            'data_decoded': {
                'method':
                'setFallbackHandler',
                'parameters': [{
                    'name':
                    'handler',
                    'type':
                    'address',
                    'value':
                    '0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44'
                }]
            }
        }]
        # Get just the multisend object
        self.assertEqual(tx_decoder.get_data_decoded_for_multisend(data),
                         expected)

        # Now decode all the data
        expected = ('multiSend', [{
            'name':
            'transactions',
            'type':
            'bytes',
            'value':
            '0x005b9ea52aaa931d4eef74c8aeaf0fe759434fed74000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000247de7edef00000000000000000000000034cfac646f301356faa8b21e94227e3583fe3f5f005b9ea52aaa931d4eef74c8aeaf0fe759434fed7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f08a0323000000000000000000000000d5d82b6addc9027b22dca772aa68d5d74cdbdf44',
            'value_decoded': [{
                'operation': operation,
                'to': safe_contract_address,
                'value': value,
                'data': change_master_copy_data.hex(),
                'data_decoded': {
                    'method':
                    'changeMasterCopy',
                    'parameters': [{
                        'name':
                        '_masterCopy',
                        'type':
                        'address',
                        'value':
                        '0x34CfAC646f301356fAa8B21e94227e3583Fe3F5F'
                    }]
                }
            }, {
                'operation': operation,
                'to': safe_contract_address,
                'value': value,
                'data': change_fallback_manager_data.hex(),
                'data_decoded': {
                    'method':
                    'setFallbackHandler',
                    'parameters': [{
                        'name':
                        'handler',
                        'type':
                        'address',
                        'value':
                        '0xd5D82B6aDDc9027B22dCA772Aa68D5d74cdBdF44'
                    }]
                }
            }]
        }])
        self.assertEqual(tx_decoder.decode_transaction_with_types(data),
                         expected)

        # Safe tx decoder cannot decode it. It would be problematic for the internal tx indexer
        safe_tx_decoder = get_safe_tx_decoder()
        with self.assertRaises(CannotDecode):
            safe_tx_decoder.decode_transaction_with_types(data)
Exemple #28
0
    def _process_decoded_element(
        self, decoded_element: EventData
    ) -> Optional[InternalTx]:
        safe_address = decoded_element["address"]
        event_name = decoded_element["event"]
        # As log
        log_index = decoded_element["logIndex"]
        trace_address = str(log_index)
        args = dict(decoded_element["args"])
        ethereum_tx_hash = decoded_element["transactionHash"]
        ethereum_block = EthereumBlock.objects.values("number", "timestamp").get(
            txs=ethereum_tx_hash
        )

        internal_tx = InternalTx(
            ethereum_tx_id=ethereum_tx_hash,
            timestamp=ethereum_block["timestamp"],
            block_number=ethereum_block["number"],
            _from=safe_address,
            gas=50000,
            data=b"",
            to=NULL_ADDRESS,  # It should be Master copy address but we cannot detect it
            value=0,
            gas_used=50000,
            contract_address=None,
            code=None,
            output=None,
            refund_address=None,
            tx_type=InternalTxType.CALL.value,
            call_type=EthereumTxCallType.DELEGATE_CALL.value,
            trace_address=trace_address,
            error=None,
        )
        child_internal_tx = None  # For Ether transfers
        internal_tx_decoded = InternalTxDecoded(
            internal_tx=internal_tx,
            function_name="",
            arguments=args,
        )
        if event_name == "ProxyCreation":
            # Should be the 2nd event to be indexed, after `SafeSetup`
            safe_address = args.pop("proxy")

            if self._is_setup_indexed(safe_address):
                internal_tx = None
            else:
                new_trace_address = f"{trace_address},0"
                to = args.pop("singleton")

                # Try to update InternalTx created by SafeSetup (if Safe was created using the ProxyFactory) with
                # the master copy used. Without tracing it cannot be detected otherwise
                InternalTx.objects.filter(
                    contract_address=safe_address, decoded_tx__function_name="setup"
                ).update(to=to, contract_address=None, trace_address=new_trace_address)
                # Add creation internal tx. _from is the address of the proxy instead of the safe_address
                internal_tx.contract_address = safe_address
                internal_tx.tx_type = InternalTxType.CREATE.value
                internal_tx.call_type = None
                internal_tx_decoded = None
        elif event_name == "SafeSetup":
            # Should be the 1st event to be indexed, unless custom `to` and `data` are set
            if self._is_setup_indexed(safe_address):
                internal_tx = None
            else:
                # Usually ProxyCreation is called before SafeSetup, but it can be the opposite if someone
                # creates a Safe and configure it in the next transaction. Remove it if that's the case
                InternalTx.objects.filter(contract_address=safe_address).delete()
                internal_tx.contract_address = safe_address
                internal_tx_decoded.function_name = "setup"
                args["payment"] = 0
                args["paymentReceiver"] = NULL_ADDRESS
                args["_threshold"] = args.pop("threshold")
                args["_owners"] = args.pop("owners")
        elif event_name == "SafeMultiSigTransaction":
            internal_tx_decoded.function_name = "execTransaction"
            data = HexBytes(args["data"])
            args["data"] = data.hex()
            args["signatures"] = HexBytes(args["signatures"]).hex()
            args["nonce"], args["sender"], args["threshold"] = decode_abi(
                ["uint256", "address", "uint256"],
                internal_tx_decoded.arguments.pop("additionalInfo"),
            )
            if args["value"] and not data:  # Simulate ether transfer
                child_internal_tx = InternalTx(
                    ethereum_tx_id=ethereum_tx_hash,
                    timestamp=ethereum_block["timestamp"],
                    block_number=ethereum_block["number"],
                    _from=safe_address,
                    gas=23000,
                    data=b"",
                    to=args["to"],
                    value=args["value"],
                    gas_used=23000,
                    contract_address=None,
                    code=None,
                    output=None,
                    refund_address=None,
                    tx_type=InternalTxType.CALL.value,
                    call_type=EthereumTxCallType.CALL.value,
                    trace_address=f"{trace_address},0",
                    error=None,
                )
        elif event_name == "SafeModuleTransaction":
            internal_tx_decoded.function_name = "execTransactionFromModule"
            args["data"] = HexBytes(args["data"]).hex()
        elif event_name == "ApproveHash":
            internal_tx_decoded.function_name = "approveHash"
            args["hashToApprove"] = args.pop("approvedHash").hex()
        elif event_name == "EnabledModule":
            internal_tx_decoded.function_name = "enableModule"
        elif event_name == "DisabledModule":
            internal_tx_decoded.function_name = "disableModule"
        elif event_name == "AddedOwner":
            internal_tx_decoded.function_name = "addOwnerWithThreshold"
            args["_threshold"] = None
        elif event_name == "RemovedOwner":
            internal_tx_decoded.function_name = "removeOwner"
            args["_threshold"] = None
        elif event_name == "ChangedThreshold":
            internal_tx_decoded.function_name = "changeThreshold"
            args["_threshold"] = args.pop("threshold")
        elif event_name == "ChangedFallbackHandler":
            internal_tx_decoded.function_name = "setFallbackHandler"
        elif event_name == "ChangedGuard":
            internal_tx_decoded.function_name = "setGuard"
        elif event_name == "SafeReceived":  # Received ether
            internal_tx.call_type = EthereumTxCallType.CALL.value
            internal_tx._from = args["sender"]
            internal_tx.to = safe_address
            internal_tx.value = args["value"]
            internal_tx_decoded = None
        elif event_name == "ChangedMasterCopy":
            internal_tx_decoded.function_name = "changeMasterCopy"
            internal_tx.arguments = {
                "_masterCopy": args.get("singleton") or args.get("masterCopy")
            }
        else:
            # 'SignMsg', 'ExecutionFailure', 'ExecutionSuccess',
            # 'ExecutionFromModuleSuccess', 'ExecutionFromModuleFailure'
            internal_tx_decoded = None

        if internal_tx:
            with transaction.atomic():
                try:
                    internal_tx.save()
                    if child_internal_tx:
                        child_internal_tx.save()
                    if internal_tx_decoded:
                        internal_tx_decoded.save()
                except IntegrityError as exc:
                    logger.info(
                        "Ignoring already processed event %s for Safe %s on tx-hash=%s: %s",
                        event_name,
                        safe_address,
                        decoded_element["transactionHash"].hex(),
                        exc,
                    )
                    return None

        return internal_tx