Example #1
0
def serialize_full_transaction(transaction, block, transaction_index,
                               is_pending):
    if is_pending:
        block_number = None
        block_hash = None
        transaction_index = None
    else:
        block_number = block['number']
        block_hash = block['hash']

    serialized_transaction = pipe(
        transaction, partial(assoc, key='block_number', value=block_number),
        partial(assoc, key='block_hash', value=block_hash),
        partial(assoc, key='transaction_index', value=transaction_index),
        partial(assoc, key='type',
                value=extract_transaction_type(transaction)))

    if 'gas_price' in transaction:
        return serialized_transaction
    else:
        # TODO: Sometime in 2022 the inclusion of gas_price may be removed from dynamic fee
        #  transactions and we can get rid of this behavior.
        #  https://github.com/ethereum/execution-specs/pull/251
        gas_price = (transaction['max_fee_per_gas'] if is_pending else
                     calculate_effective_gas_price(transaction, block))
        return assoc(serialized_transaction, 'gas_price', gas_price)
Example #2
0
def map_abi_data(
    normalizers: Sequence[Callable[[TypeStr, Any], Tuple[TypeStr, Any]]],
    types: Sequence[TypeStr],
    data: Sequence[Any],
) -> Any:
    """
    This function will apply normalizers to your data, in the
    context of the relevant types. Each normalizer is in the format:

    def normalizer(datatype, data):
        # Conditionally modify data
        return (datatype, data)

    Where datatype is a valid ABI type string, like "uint".

    In case of an array, like "bool[2]", normalizer will receive `data`
    as an iterable of typed data, like `[("bool", True), ("bool", False)]`.

    Internals
    ---

    This is accomplished by:

    1. Decorating the data tree with types
    2. Recursively mapping each of the normalizers to the data
    3. Stripping the types back out of the tree
    """
    pipeline = itertools.chain(
        [abi_data_tree(types)],
        map(data_tree_map, normalizers),
        [partial(recursive_map, strip_abi_type)],
    )

    return pipe(data, *pipeline)
Example #3
0
def apply_module_to_formatters(
    formatters: Tuple[Callable[..., TReturn]],
    module: "Module",
    method_name: Union[RPCEndpoint, Callable[..., RPCEndpoint]],
) -> Iterable[Callable[..., TReturn]]:
    for f in formatters:
        yield partial(f, module, method_name)
def serialize_receipt(transaction, block, transaction_index, is_pending):
    if is_pending:
        block_number = None
        block_hash = None
        transaction_index = None
    else:
        block_number = block['number']
        block_hash = block['hash']

    return pipe(
        transaction,
        partial(assoc, key='block_number', value=block_number),
        partial(assoc, key='block_hash', value=block_hash),
        partial(assoc, key='transaction_index', value=transaction_index),
        partial(assoc, key='state_root', value=b'\x00'),
    )
Example #5
0
 def mine_blocks(self, num_blocks=1, coinbase=None):
     for _ in range(num_blocks):
         block_to_mine = dissoc(self.block, 'hash')
         block_hash = fake_rlp_hash(block_to_mine)
         mined_block = assoc(block_to_mine, 'hash', block_hash)
         assign_block_info = compose(
             partial(assoc, key='block_number',
                     value=mined_block['number']),
             partial(assoc, key='block_hash', value=mined_block['hash']),
         )
         mined_block['transactions'] = tuple(
             assign_block_info(transaction)
             for transaction in mined_block['transactions'])
         self.blocks.append(mined_block)
         self.block = make_block_from_parent(mined_block)
         yield block_hash
Example #6
0
    def factory(cls, web3, class_name=None, **kwargs):

        kwargs['web3'] = web3

        normalizers = {
            'abi': normalize_abi,
            'address': partial(normalize_address, kwargs['web3'].ens),
            'bytecode': normalize_bytecode,
            'bytecode_runtime': normalize_bytecode,
        }

        contract = PropertyCheckingFactory(
            class_name or cls.__name__,
            (cls, ),
            kwargs,
            normalizers=normalizers,
        )
        contract.functions = ContractFunctions(contract.abi, contract.web3)
        contract.caller = ContractCaller(contract.abi, contract.web3,
                                         contract.address)
        contract.events = ContractEvents(contract.abi, contract.web3)
        contract.fallback = Contract.get_fallback_function(
            contract.abi, contract.web3)

        return contract
Example #7
0
    def __init__(self,
                 abi,
                 web3,
                 address,
                 transaction=None,
                 block_identifier='latest'):
        self.web3 = web3
        self.address = address
        self.abi = abi
        self._functions = None

        if self.abi:
            if transaction is None:
                transaction = {}

            self._functions = filter_by_type('function', self.abi)
            for func in self._functions:
                fn = ContractFunction.factory(func['name'],
                                              web3=self.web3,
                                              contract_abi=self.abi,
                                              address=self.address,
                                              function_identifier=func['name'])

                block_id = parse_block_identifier(self.web3, block_identifier)
                caller_method = partial(self.call_function,
                                        fn,
                                        transaction=transaction,
                                        block_identifier=block_id)

                setattr(self, func['name'], caller_method)
Example #8
0
    def create_log_filter(self,
                          from_block=None,
                          to_block=None,
                          address=None,
                          topics=None):
        self.validator.validate_inbound_filter_params(
            from_block=from_block,
            to_block=to_block,
            address=address,
            topics=topics,
        )
        (
            raw_from_block,
            raw_to_block,
            raw_address,
            raw_topics,
        ) = self.normalizer.normalize_inbound_filter_params(
            from_block=from_block,
            to_block=to_block,
            address=address,
            topics=topics,
        )

        raw_filter_id = next(self._filter_counter)
        raw_filter_params = {
            'from_block': raw_from_block,
            'to_block': raw_to_block,
            'addresses': raw_address,
            'topics': raw_topics,
        }
        filter_fn = partial(check_if_log_matches, **raw_filter_params)
        new_filter = Filter(
            filter_params=raw_filter_params,
            filter_fn=filter_fn,
        )
        self._log_filters[raw_filter_id] = new_filter

        if is_integer(raw_from_block):
            if is_integer(raw_to_block):
                upper_bound = raw_to_block + 1
            else:
                upper_bound = self.get_block_by_number('pending')['number']
            for block_number in range(raw_from_block, upper_bound):
                block = self.get_block_by_number(block_number)
                self._add_log_entries_to_filter(block, new_filter)

        filter_id = self.normalizer.normalize_outbound_filter_id(raw_filter_id)
        return filter_id
Example #9
0
def serialize_receipt(receipt, transaction, block, transaction_index, is_pending):
    if is_pending:
        block_number = None
        block_hash = None
        transaction_index = None
    else:
        block_number = block['number']
        block_hash = block['hash']

    return pipe(
        receipt,
        partial(assoc, key='block_number', value=block_number),
        partial(assoc, key='block_hash', value=block_hash),
        partial(assoc, key='effective_gas_price', value=(
            calculate_effective_gas_price(transaction, block)
        )),
        partial(assoc, key='from', value=to_bytes(transaction['from'])),
        partial(assoc, key='state_root', value=b'\x00'),
        partial(assoc, key='status', value=0),
        partial(assoc, key='to', value=transaction['to']),
        partial(assoc, key='transaction_index', value=transaction_index),
        partial(assoc, key='type', value=extract_transaction_type(transaction))
    )
Example #10
0
def validate_log_entry_type(value):
    if value not in {"pending", "mined"}:
        raise ValidationError(
            "Log entry type must be one of 'pending' or 'mined'")


LOG_ENTRY_VALIDATORS = {
    "type": validate_log_entry_type,
    "log_index": validate_positive_integer,
    "transaction_index": if_not_null(validate_positive_integer),
    "transaction_hash": validate_32_byte_string,
    "block_hash": if_not_null(validate_32_byte_string),
    "block_number": if_not_null(validate_positive_integer),
    "address": validate_canonical_address,
    "data": validate_bytes,
    "topics": partial(validate_array, validator=validate_32_byte_string),
}
validate_log_entry = partial(validate_dict,
                             key_validators=LOG_ENTRY_VALIDATORS)


def validate_signature_v(value):
    validate_positive_integer(value)

    if value not in [0, 1, 27, 28] and value not in range(35, UINT256_MAX + 1):
        raise ValidationError(
            "The `v` portion of the signature must be 0, 1, 27, 28 or >= 35")


def validate_y_parity(value):
    validate_positive_integer(value)
Example #11
0
if TYPE_CHECKING:
    from web3 import Web3  # noqa: F401
    from web3.module import Module  # noqa: F401
    from web3.eth import Eth  # noqa: F401


def bytes_to_ascii(value: bytes) -> str:
    return codecs.decode(value, 'ascii')


to_ascii_if_bytes = apply_formatter_if(is_bytes, bytes_to_ascii)
to_integer_if_hex = apply_formatter_if(is_string, hex_to_integer)
to_hex_if_integer = apply_formatter_if(is_integer, integer_to_hex)

is_false = partial(operator.is_, False)

is_not_false = complement(is_false)
is_not_null = complement(is_null)


@curry
def to_hexbytes(num_bytes: int,
                val: Union[str, int, bytes],
                variable_length: bool = False) -> HexBytes:
    if isinstance(val, (str, int, bytes)):
        result = HexBytes(val)
    else:
        raise TypeError("Cannot convert %r to HexBytes" % val)

    extra_bytes = len(result) - num_bytes
Example #12
0
from eth_utils.toolz import partial

from eth.vm.forks.berlin import constants as berlin_constants
from eth.vm.forks.berlin.logic import (
    GAS_SCHEDULE_EIP2929,
    sstore_eip2929_generic,
)

SSTORE_CLEARS_SCHEDULE_EIP_3529 = (
    GAS_SCHEDULE_EIP2929.sstore_reset_gas +
    berlin_constants.ACCESS_LIST_STORAGE_KEY_COST_EIP_2930)

GAS_SCHEDULE_EIP3529 = GAS_SCHEDULE_EIP2929._replace(
    sstore_clears_schedule=SSTORE_CLEARS_SCHEDULE_EIP_3529)

sstore_eip3529 = partial(sstore_eip2929_generic, GAS_SCHEDULE_EIP3529)
Example #13
0
from eth_utils.toolz import (
    compose,
    identity,
    partial,
)

from .common import (
    normalize_if,
    normalize_dict,
    normalize_array,
)
from ..utils.encoding import int_to_32byte_big_endian

normalize_account = to_checksum_address
normalize_account_list = partial(normalize_array, normalizer=normalize_account)

to_empty_or_checksum_address = apply_one_of_formatters((
    (lambda addr: addr == b'', lambda addr: ''),
    (is_canonical_address, to_checksum_address),
))


def _normalize_outbound_access_list(access_list):
    return tuple([{
        'address':
        to_checksum_address(entry[0]),
        'storage_keys':
        tuple([encode_hex(int_to_32byte_big_endian(k)) for k in entry[1]])
    } for entry in access_list])
Example #14
0
))

TRANSACTION_NORMALIZERS = {
    'from': to_canonical_address,
    'to': to_empty_or_canonical_address,
    'gas': identity,
    'gas_price': identity,
    'nonce': identity,
    'value': identity,
    'data': decode_hex,
    'r': identity,
    's': identity,
    'v': identity,
}

normalize_transaction = partial(normalize_dict,
                                normalizers=TRANSACTION_NORMALIZERS)

LOG_ENTRY_NORMALIZERS = {
    'type':
    identity,
    'log_index':
    identity,
    'transaction_index':
    identity,
    'transaction_hash':
    decode_hex,
    'block_hash':
    partial(normalize_if, conditional_fn=is_string, normalizer=decode_hex),
    'block_number':
    identity,
    'address':
Example #15
0
TRANSACTION_RESULT_KEY_MAPPING = {
    'access_list': 'accessList',
    'block_hash': 'blockHash',
    'block_number': 'blockNumber',
    'gas_price': 'gasPrice',
    'max_fee_per_gas': 'maxFeePerGas',
    'max_priority_fee_per_gas': 'maxPriorityFeePerGas',
    'transaction_hash': 'transactionHash',
    'transaction_index': 'transactionIndex',
}
transaction_result_remapper = apply_key_map(TRANSACTION_RESULT_KEY_MAPPING)


TRANSACTION_RESULT_FORMATTERS = {
    'to': apply_formatter_if(partial(operator.eq, ''), static_return(None)),
    'access_list': apply_formatter_to_array(
        apply_key_map({'storage_keys': 'storageKeys'}),
    ),
}
transaction_result_formatter = apply_formatters_to_dict(TRANSACTION_RESULT_FORMATTERS)


LOG_RESULT_KEY_MAPPING = {
    'log_index': 'logIndex',
    'transaction_index': 'transactionIndex',
    'transaction_hash': 'transactionHash',
    'block_hash': 'blockHash',
    'block_number': 'blockNumber',
}
log_result_remapper = apply_key_map(LOG_RESULT_KEY_MAPPING)
import pytest

from eth_utils.toolz import (
    partial, )

from web3._utils.blocks import (
    select_method_for_block_identifier, )

selector_fn = partial(
    select_method_for_block_identifier,
    if_hash='test_hash',
    if_number='test_number',
    if_predefined='test_predefined',
)


@pytest.mark.parametrize(
    'input,expected',
    (
        ('latest', 'test_predefined'),
        ('pending', 'test_predefined'),
        ('earliest', 'test_predefined'),
        (-1, ValueError),
        (0, 'test_number'),
        (1, 'test_number'),
        (4000000, 'test_number'),
        ('0x0', 'test_number'),
        ('0x00', 'test_number'),
        ('0x1', 'test_number'),
        ('0x01', 'test_number'),
        (hex(4000000), 'test_number'),
Example #17
0
def validate_log_entry_type(value):
    if value not in {"pending", "mined"}:
        raise ValidationError(
            "Log entry type must be one of 'pending' or 'mined'")


LOG_ENTRY_VALIDATORS = {
    "type": validate_log_entry_type,
    "log_index": validate_positive_integer,
    "transaction_index": if_not_null(validate_positive_integer),
    "transaction_hash": validate_32_byte_string,
    "block_hash": if_not_null(validate_32_byte_string),
    "block_number": if_not_null(validate_positive_integer),
    "address": validate_canonical_address,
    "data": validate_bytes,
    "topics": partial(validate_array, validator=validate_32_byte_string),
}

validate_log_entry = partial(validate_dict,
                             key_validators=LOG_ENTRY_VALIDATORS)


def validate_signature_v(value):
    validate_positive_integer(value)

    if value not in [0, 1, 27, 28] and value not in range(35, UINT256_MAX + 1):
        raise ValidationError(
            "The `v` portion of the signature must be 0, 1, 27, 28 or >= 35")


TRANSACTION_VALIDATORS = {
Example #18
0
}

filter_params_remapper = apply_key_map(FILTER_PARAMS_MAPPINGS)

FILTER_PARAMS_FORMATTERS = {
    'fromBlock': to_integer_if_hex,
    'toBlock': to_integer_if_hex,
}

filter_params_formatter = apply_formatters_to_dict(FILTER_PARAMS_FORMATTERS)

filter_params_transformer = compose(filter_params_remapper,
                                    filter_params_formatter)

TRANSACTION_FORMATTERS = {
    'to': apply_formatter_if(partial(operator.eq, ''),
                             static_return(None)),  # type: ignore
}

transaction_formatter = apply_formatters_to_dict(TRANSACTION_FORMATTERS)

RECEIPT_FORMATTERS = {
    'logs': apply_formatter_to_array(log_key_remapper),
}

receipt_formatter = apply_formatters_to_dict(RECEIPT_FORMATTERS)

transaction_params_transformer = compose(transaction_params_remapper,
                                         transaction_params_formatter)

ethereum_tester_middleware = construct_formatting_middleware(
Example #19
0
def validate_block_number(value):
    error_message = (
        "Block number must be a positive integer or one of the strings "
        "'latest', 'earliest', or 'pending'.  Got: {}".format(value)
    )
    if is_string(value):
        validate_text(value)
        if value not in BLOCK_NUMBER_META_VALUES:
            raise ValidationError(error_message)
    elif not is_integer(value) or is_boolean(value):
        raise ValidationError(error_message)
    elif value < 0:
        raise ValidationError(error_message)


validate_block_hash = partial(validate_32_byte_hex_value, name="Block hash")
validate_transaction_hash = partial(validate_32_byte_hex_value, name="Transaction hash")
validate_filter_id = partial(validate_positive_integer)


def validate_account(value):
    if not is_text(value):
        raise ValidationError("Address must be 20 bytes encoded as hexidecimal")
    elif not is_hex_address(value):
        raise ValidationError("Address must be 20 bytes encoded as hexidecimal")
    elif is_checksum_formatted_address(value) and not is_checksum_address(value):
        raise ValidationError("Address does not validate EIP55 checksum")


def is_valid_topic_array(value):
    if not is_list_like(value):
Example #20
0
    def get_logs(self, from_block=None, to_block=None, address=None, topics=None):
        self.validator.validate_inbound_filter_params(
            from_block=from_block,
            to_block=to_block,
            address=address,
            topics=topics,
        )
        (
            raw_from_block,
            raw_to_block,
            raw_address,
            raw_topics,
        ) = self.normalizer.normalize_inbound_filter_params(
            from_block=from_block,
            to_block=to_block,
            address=address,
            topics=topics,
        )

        # Setup the filter object
        raw_filter_params = {
            'from_block': raw_from_block,
            'to_block': raw_to_block,
            'addresses': raw_address,
            'topics': raw_topics,
        }
        filter_fn = partial(
            check_if_log_matches,
            **raw_filter_params,
        )
        log_filter = Filter(
            filter_params=raw_filter_params,
            filter_fn=filter_fn,
        )

        # Set from/to block defaults
        if raw_from_block is None:
            raw_from_block = 'latest'
        if raw_to_block is None:
            raw_to_block = 'latest'

        # Determine lower bound for block range.
        if isinstance(raw_from_block, int):
            lower_bound = raw_from_block
        else:
            lower_bound = self.get_block_by_number(raw_from_block)['number']

        # Determine upper bound for block range.
        if isinstance(raw_to_block, int):
            upper_bound = raw_to_block
        else:
            upper_bound = self.get_block_by_number(raw_to_block)['number']

        # Enumerate the blocks in the block range to find all log entries which match.
        for block_number in range(lower_bound, upper_bound + 1):
            block = self.get_block_by_number(block_number)
            for transaction_hash in block['transactions']:
                receipt = self.get_transaction_receipt(transaction_hash)
                for log_entry in receipt['logs']:
                    raw_log_entry = self.normalizer.normalize_inbound_log_entry(log_entry)
                    log_filter.add(raw_log_entry)

        # Return the matching log entries
        for item in log_filter.get_all():
            yield self.normalizer.normalize_outbound_log_entry(item)