Example #1
0
def encode_intended_validator(validator_address: Union[Address, str],
                              primitive: bytes = None,
                              *,
                              hexstr: str = None,
                              text: str = None) -> SignableMessage:
    """
    Encode a message using the "intended validator" approach (ie~ version 0)
    defined in EIP-191_.

    Supply the message as exactly one of these three arguments:
    bytes as a primitive, a hex string, or a unicode string.

    .. WARNING:: Note that this code has not gone through an external audit.
        Also, watch for updates to the format, as the EIP is still in DRAFT.

    :param validator_address: which on-chain contract is capable of validating this message,
        provided as a checksummed address or in native bytes.
    :param primitive: the binary message to be signed
    :type primitive: bytes or int
    :param str hexstr: the message encoded as hex
    :param str text: the message as a series of unicode characters (a normal Py3 str)
    :returns: The EIP-191 encoded message, ready for signing

    .. _EIP-191: https://eips.ethereum.org/EIPS/eip-191
    """
    if not is_valid_address(validator_address):
        raise ValidationError(
            f"Cannot encode message with 'Validator Address': {validator_address}. "
            "It must be a checksum address, or an address converted to bytes.")
    message_bytes = to_bytes(primitive, hexstr=hexstr, text=text)
    return SignableMessage(
        HexBytes(b'\x00'),  # version 0, as defined in EIP-191
        to_canonical_address(validator_address),
        message_bytes,
    )
Example #2
0
def state_definition_to_dict(state_definition: GeneralState) -> AccountState:
    """Convert a state definition to the canonical dict form.

    State can either be defined in the canonical form, or as a list of sub states that are then
    merged to one. Sub states can either be given as dictionaries themselves, or as tuples where
    the last element is the value and all others the keys for this value in the nested state
    dictionary. Example:

    ```
        [
            ("0xaabb", "balance", 3),
            ("0xaabb", "storage", {
                4: 5,
            }),
            "0xbbcc", {
                "balance": 6,
                "nonce": 7
            }
        ]
    ```
    """
    if isinstance(state_definition, Mapping):
        state_dict = state_definition
    elif isinstance(state_definition, Iterable):
        state_dicts = [
            assoc_in(
                {},
                state_item[:-1],
                state_item[-1]
            ) if not isinstance(state_item, Mapping) else state_item
            for state_item
            in state_definition
        ]
        if not is_cleanly_mergable(*state_dicts):
            raise ValidationError("Some state item is defined multiple times")
        state_dict = deep_merge(*state_dicts)
    else:
        assert TypeError("State definition must either be a mapping or a sequence")

    seen_keys = set(concat(d.keys() for d in state_dict.values()))
    bad_keys = seen_keys - {"balance", "nonce", "storage", "code"}
    if bad_keys:
        raise ValidationError(
            f"State definition contains the following invalid account fields: {', '.join(bad_keys)}"
        )

    return state_dict
Example #3
0
def _hash_eip191_message(signable_message: SignableMessage) -> Hash32:
    version = signable_message.version
    if len(version) != 1:
        raise ValidationError(
            "The supplied message version is {version!r}. "
            "The EIP-191 signable message standard only supports one-byte versions."
        )

    joined = b'\x19' + version + signable_message.header + signable_message.body
    return Hash32(keccak(joined))
Example #4
0
    def field_type(self, field: str) -> str:
        """
        Looks up ``field`` via type annotations, returning the underlying ABI
        type (e.g. ``"uint256"``) or :class:`EIP712Type`. Raises ``KeyError``
        if the field doesn't exist.
        """
        typ = self.__annotations__[field]

        if isinstance(typ, str):
            if not is_encodable_type(typ):
                raise ValidationError(
                    f"'{field}: {typ}' is not a valid ABI type")

            return typ

        elif issubclass(typ, EIP712Type):
            return typ.type

        else:
            raise ValidationError(
                f"'{field}' type annotation must either be a subclass of "
                f"`EIP712Type` or valid ABI Type string, not {typ.__name__}")
Example #5
0
def _hash_eip191_message(signable_message: SignableMessage) -> Hash32:
    """
    Hash the given ``signable_message`` according to the EIP-191 Signed Data Standard.
    """
    version = signable_message.version
    if len(version) != 1:
        raise ValidationError(
            "The supplied message version is {version!r}. "
            "The EIP-191 signable message standard only supports one-byte versions."
        )

    joined = b"\x19" + version + signable_message.header + signable_message.body
    return Hash32(keccak(joined))
Example #6
0
def normalize_signed_transaction(
        transaction: Dict[str, Any]) -> Dict[str, Any]:
    normalized_universal_transaction = {
        'data': robust_decode_hex(transaction['data']),
        'gasLimit': to_int(transaction['gasLimit']),
        'nonce': to_int(transaction['nonce']),
        'r': to_int(transaction['r']),
        's': to_int(transaction['s']),
        'v': to_int(transaction['v']),
        'to': decode_hex(transaction['to']),
        'value': to_int(transaction['value']),
    }
    if 'type' in transaction:
        type_id = to_int(transaction['type'])
        if type_id == 1:
            custom_fields = {
                'type': type_id,
                'gasPrice': to_int(transaction['gasPrice']),
                'chainId': to_int(transaction['chainId']),
            }
        elif type_id == 2:
            custom_fields = {
                'type':
                type_id,
                'chainId':
                to_int(transaction['chainId']),
                'maxFeePerGas':
                to_int(transaction['maxFeePerGas']),
                'maxPriorityFeePerGas':
                to_int(transaction['maxPriorityFeePerGas']),
            }
        else:
            raise ValidationError(
                f"Did not recognize transaction type {type_id}")
    else:
        custom_fields = {
            'gasPrice': to_int(transaction['gasPrice']),
        }

    return merge(normalized_universal_transaction, custom_fields)
Example #7
0
 def __post_init__(self):
     # At least one of the header fields must be in the EIP712 message header
     if len(self.domain) == 0:
         raise ValidationError(
             f"EIP712 Message definition '{self.type}' must define "
             f"at least one of {EIP712_DOMAIN_FIELDS}")