Beispiel #1
0
 def get_logs(self, block_number: BlockNumber) -> Tuple[DepositLog, ...]:
     block_hash = block_number.to_bytes(32, byteorder='big')
     if block_number == self.start_block_number:
         logs = (
             DepositLog(
                 block_hash=Hash32(block_hash),
                 pubkey=deposit.pubkey,
                 withdrawal_credentials=deposit.withdrawal_credentials,
                 signature=deposit.signature,
                 amount=deposit.amount,
             )
             for deposit in self.deposits
         )
         return tuple(logs)
     else:
         logs = (
             DepositLog(
                 block_hash=Hash32(block_hash),
                 pubkey=BLSPubkey(b'\x12' * 48),
                 withdrawal_credentials=Hash32(b'\x23' * 32),
                 signature=BLSSignature(b'\x34' * 96),
                 amount=Gwei(32 * GWEI_PER_ETH),
             )
             for _ in range(self.num_deposits_per_block)
         )
         return tuple(logs)
Beispiel #2
0
def create_mock_deposits_and_root(
    pubkeys: Sequence[BLSPubkey],
    keymap: Dict[BLSPubkey, int],
    config: Eth2Config,
    withdrawal_credentials: Sequence[Hash32] = None,
    leaves: Sequence[Hash32] = None,
) -> Tuple[Tuple[Deposit, ...], Hash32]:
    """
    Creates as many new deposits as there are keys in ``pubkeys``.

    Optionally provide corresponding ``withdrawal_credentials`` to include those.

    Optionally provide the prefix in the sequence of leaves leading up to the
    new deposits made by this function to get the correct updated root. If ``leaves`` is
    empty, this function simulates the genesis deposit tree calculation.
    """
    if not withdrawal_credentials:
        withdrawal_credentials = tuple(
            Hash32(b"\x22" * 32) for _ in range(len(pubkeys)))
    else:
        assert len(withdrawal_credentials) == len(pubkeys)
    if not leaves:
        leaves = tuple()

    deposit_datas = tuple()  # type: Tuple[DepositData, ...]
    deposit_data_leaves = cast(Tuple[Hash32, ...],
                               leaves)  # type: Tuple[Hash32, ...]
    for key, credentials in zip(pubkeys, withdrawal_credentials):
        privkey = keymap[key]
        deposit_data = create_mock_deposit_data(
            config=config,
            pubkey=key,
            privkey=privkey,
            withdrawal_credentials=credentials,
        )
        item = deposit_data.hash_tree_root
        deposit_data_leaves += (item, )
        deposit_datas += (deposit_data, )

    deposits: Tuple[Deposit, ...] = tuple()
    for index, data in enumerate(deposit_datas):
        length_mix_in = Hash32((index + 1).to_bytes(32, byteorder="little"))
        tree = calc_merkle_tree_from_leaves(deposit_data_leaves[:index + 1])

        deposit = Deposit(
            proof=(get_merkle_proof(tree, item_index=index) +
                   (length_mix_in, )),
            data=data,
        )
        deposits += (deposit, )

    if len(deposit_data_leaves) > 0:
        tree_root = get_root(tree)
        return deposits, hash_eth2(tree_root + length_mix_in)
    else:
        return tuple(), ZERO_HASH32
Beispiel #3
0
def hash_tree_root(value: Any, sedes: BaseSedes=None) -> Hash32:
    if sedes is None:
        sedes = infer_sedes(value)

    intermediate_tree_hash = sedes.intermediate_tree_hash(value)

    if len(intermediate_tree_hash) < 32:
        return Hash32(intermediate_tree_hash.ljust(32, b'\x00'))
    else:
        return Hash32(intermediate_tree_hash)
Beispiel #4
0
def _normalize_topics(
    raw_topics: List[Union[None, HexStr, List[HexStr]]],
) -> Iterable[Union[None, Hash32, Tuple[Hash32, ...]]]:
    for topic in raw_topics:
        if topic is None:
            yield None
        elif isinstance(topic, str):
            yield Hash32(decode_hex(topic))
        elif isinstance(topic, Sequence):
            yield tuple(Hash32(decode_hex(sub_topic)) for sub_topic in topic)
        else:
            raise TypeError(f"Unsupported topic: {topic!r}")
Beispiel #5
0
 def parent_hash(self) -> Optional[Hash32]:
     if self._parent_hash is not None and self._detatched_parent_hash is not None:
         raise TypeError("Invalid: header has two parent hashes")
     elif self._detatched_parent_hash is not None:
         return Hash32(self._detatched_parent_hash)
     elif self._parent_hash is None:
         if self.block_number == 0:
             return GENESIS_PARENT_HASH
         else:
             return None
     else:
         return Hash32(self._parent_hash)
Beispiel #6
0
    def get_ancestor_hash(self, block_number: int) -> Hash32:
        ancestor_depth = self.block_number - block_number - 1
        is_ancestor_depth_out_of_range = (
            ancestor_depth >= MAX_PREV_HEADER_DEPTH or ancestor_depth < 0
            or block_number < 0)
        if is_ancestor_depth_out_of_range:
            return Hash32(b'')

        try:
            return nth(ancestor_depth, self.execution_context.prev_hashes)
        except StopIteration:
            # Ancestor with specified depth not present
            return Hash32(b'')
Beispiel #7
0
def extract_receipt(receipt_data: TxReceipt) -> Receipt:
    state_root: Hash32
    try:
        state_root = Hash32(to_bytes(hexstr=receipt_data["root"]))
    except KeyError:
        state_root = Hash32(receipt_data["status"].to_bytes(32, "big"))

    return Receipt(
        state_root=state_root,
        gas_used=receipt_data["gasUsed"],
        bloom=bytes(receipt_data["logsBloom"]),
        logs=extract_logs(receipt_data["logs"]),
    )
Beispiel #8
0
    def __str__(self) -> str:
        # TODO: use eth_utils.humanize_bytes once it is released
        if len(self.data) > 4:
            pretty_data = humanize_hash(Hash32(self.data))
        else:
            pretty_data = self.data.hex()

        if len(self.topics) == 0:  # type: ignore
            pretty_topics = "(anonymous)"
        else:
            pretty_topics = "|".join((
                humanize_hash(Hash32(topic.topic))
                for topic in self.topics  # type: ignore
            ))

        return f"Log[#{self.idx} A={humanize_hash(self.address)} D={pretty_data}/T={pretty_topics}]"  # type: ignore  # noqa: E501
Beispiel #9
0
def parse_byetherscan_uri(parsed: urllib.parse.ParseResult,
                          network_id: int) -> Checkpoint:

    try:
        network = Network(network_id)
    except ValueError:
        raise ValidationError(
            f"Can not resolve checkpoint through Etherscan API"
            f"for network {network_id}. Network not supported")

    try:
        etherscan_api_key = os.environ['TRINITY_ETHERSCAN_API_KEY']
    except KeyError:
        raise RuntimeError(
            "Etherscan API key missing. Assign your Etherscan API key "
            "to the TRINITY_ETHERSCAN_API_KEY environment variable.")

    etherscan_api = Etherscan(etherscan_api_key)

    latest_block_number = etherscan_api.get_latest_block(network)
    checkpoint_block_number = latest_block_number - BLOCKS_FROM_TIP
    checkpoint_block_response = etherscan_api.get_block_by_number(
        checkpoint_block_number, network)
    checkpoint_score = to_int(
        hexstr=checkpoint_block_response['totalDifficulty'])
    checkpoint_hash = checkpoint_block_response['hash']

    return Checkpoint(Hash32(decode_hex(checkpoint_hash)), checkpoint_score)
Beispiel #10
0
def get_shuffling(*, seed: Hash32, validators: Sequence['ValidatorRecord'],
                  epoch: EpochNumber, epoch_length: int,
                  target_committee_size: int,
                  shard_count: int) -> Tuple[Iterable[ValidatorIndex], ...]:
    """
    Shuffle ``validators`` into crosslink committees seeded by ``seed`` and ``epoch``.
    Return a list of ``committee_per_epoch`` committees where each
    committee is itself a list of validator indices.

    If ``get_shuffling(seed, validators, epoch)`` returns some value ``x`` for some
    ``epoch <= get_current_epoch(state) + ENTRY_EXIT_DELAY``, it should return the
    same value ``x`` for the same ``seed`` and ``epoch`` and possible future modifications
    of ``validators`` forever in phase 0, and until the ~1 year deletion delay in phase 2
    and in the future.
    """
    active_validator_indices = get_active_validator_indices(validators, epoch)

    committees_per_epoch = get_epoch_committee_count(
        len(active_validator_indices),
        shard_count,
        epoch_length,
        target_committee_size,
    )

    # Shuffle
    seed = bitwise_xor(seed, Hash32(epoch.to_bytes(32, byteorder="big")))
    shuffled_active_validator_indices = shuffle(active_validator_indices, seed)

    # Split the shuffled list into committees_per_epoch pieces
    return tuple(
        split(
            shuffled_active_validator_indices,
            committees_per_epoch,
        ))
Beispiel #11
0
def extract_receipt(receipt_data: TxReceipt) -> Receipt:
    return Receipt(
        state_root=Hash32(to_bytes(hexstr=receipt_data["root"])),
        gas_used=receipt_data["gasUsed"],
        bloom=bytes(receipt_data["logsBloom"]),
        logs=extract_logs(receipt_data["logs"]),
    )
Beispiel #12
0
    async def _handle_get_beacon_blocks(self, peer: BCCPeer, msg: GetBeaconBlocksMessage) -> None:
        if not peer.is_operational:
            return

        max_blocks = msg["max_blocks"]
        block_slot_or_root = msg["block_slot_or_root"]

        try:
            if isinstance(block_slot_or_root, int):
                start_block = self.db.get_canonical_block_by_slot(block_slot_or_root)
            elif isinstance(block_slot_or_root, bytes):
                start_block = self.db.get_block_by_root(Hash32(block_slot_or_root))
            else:
                raise TypeError(
                    f"Invariant: unexpected type for 'block_slot_or_root': "
                    f"{type(block_slot_or_root)}"
                )
        except BlockNotFound:
            start_block = None

        if start_block is not None:
            self.logger.debug2(
                "%s requested %d blocks starting with %s",
                peer,
                max_blocks,
                start_block,
            )
            blocks = self._get_blocks(start_block, max_blocks)
        else:
            self.logger.debug2("%s requested unknown block %s", block_slot_or_root)
            blocks = ()

        self.logger.debug2("Replying to %s with %d blocks", peer, len(blocks))
        peer.sub_proto.send_blocks(blocks)
def make_transfer_event(
    transaction_hash: Hash32 = Hash32(
        int_to_big_endian(12345).rjust(32, b"\x00")),
    from_="0x345DeAd084E056dc78a0832E70B40C14B6323458",
    to="0x1ADb0A4853bf1D564BbAD7565b5D50b33D20af60",
    value=1,
) -> AttributeDict:
    return AttributeDict({
        "event":
        TRANSFER_EVENT_NAME,
        "transactionHash":
        HexBytes(transaction_hash),
        "blockNumber":
        1,
        "transactionIndex":
        0,
        "logIndex":
        0,
        "args":
        AttributeDict({
            "from": from_,
            "to": to,
            "value": value
        }),
    })
Beispiel #14
0
 def _get_canonical_head(cls, db: DatabaseAPI) -> BlockHeaderAPI:
     try:
         canonical_head_hash = db[
             SchemaV1.make_canonical_head_hash_lookup_key()]
     except KeyError:
         raise CanonicalHeadNotFound("No canonical head set for this chain")
     return cls._get_block_header_by_hash(db, Hash32(canonical_head_hash))
Beispiel #15
0
def hash_eth2(data: Union[bytes, bytearray]) -> Hash32:
    """
    Return Keccak-256 hashed result.
    Note: it's a placeholder and we aim to migrate to a S[T/N]ARK-friendly hash function in
    a future Ethereum 2.0 deployment phase.
    """
    return Hash32(keccak(data))
Beispiel #16
0
def update_element_in_chunk(original_chunk: Hash32, index: int,
                            element: bytes) -> Hash32:
    """Replace part of a chunk with a given element.

    The chunk is interpreted as a concatenated sequence of equally sized elements. This function
    replaces the element given by its index in the chunk with the given data.

    If the length of the element is zero or not a divisor of the chunk size, a `ValueError` is
    raised. If the index is out of range, an `IndexError is raised.

    Example:
        >>> update_element_in_chunk(b"aabbcc", 1, b"xx")
        b'aaxxcc'
    """
    element_size = len(element)
    chunk_size = len(original_chunk)

    if element_size == 0:
        raise ValueError(f"Element size is zero")
    if chunk_size % element_size != 0:
        raise ValueError(
            f"Element size is not a divisor of chunk size: {element_size}")
    if not 0 <= index < chunk_size // element_size:
        raise IndexError(
            f"Index out of range for element size {element_size}: {index}")

    first_byte_index = index * element_size
    last_byte_index = first_byte_index + element_size

    prefix = original_chunk[:first_byte_index]
    suffix = original_chunk[last_byte_index:]
    return Hash32(prefix + element + suffix)
Beispiel #17
0
def bitwise_xor(a: Hash32, b: Hash32) -> Hash32:
    """
    Return the xor of hash ``a`` and hash ``b``
    """

    result = bytes(bit_a ^ bit_b for bit_a, bit_b in zip(a, b))
    return Hash32(result)
Beispiel #18
0
def _w3_get_block(w3: Web3, *args: Any, **kwargs: Any) -> Eth1Block:
    block_dict = w3.eth.getBlock(*args, **kwargs)
    return Eth1Block(
        block_hash=Hash32(block_dict["hash"]),
        number=BlockNumber(block_dict["number"]),
        timestamp=Timestamp(block_dict["timestamp"]),
    )
Beispiel #19
0
def hash_eth2(data: Union[bytes, bytearray]) -> Hash32:
    """
    Return SHA-256 hash of ``data``.
    Note: it's a placeholder and we aim to migrate to a S[T/N]ARK-friendly hash function in
    a future Ethereum 2.0 deployment phase.
    """
    return Hash32(sha256(data).digest())
Beispiel #20
0
def parse_byhash_uri(parsed: urllib.parse.ParseResult) -> Checkpoint:
    scheme, netloc, query = parsed.scheme, parsed.netloc, parsed.query

    try:
        parsed_query = urllib.parse.parse_qsl(query)
    except ValueError as e:
        raise ValidationError(str(e))

    query_dict = dict(parsed_query)

    # we allow any kind of separator for a nicer UX. e.g. instead of "11487662456884849810705"
    # one can use "114 876 624 568 848 498 107 05" or "1,487,662,456,884,849,810,705". This also
    # allows copying out a value from e.g etherscan.
    score = remove_non_digits(query_dict.get('score', ''))

    parts = PurePosixPath(parsed.path).parts

    if len(parts) != 3 or scheme != 'eth' or netloc != 'block' or not score:
        raise ValidationError('checkpoint string must be of the form'
                              '"eth://block/byhash/<hash>?score=<score>"')

    block_hash = parts[2]

    if not is_block_hash(block_hash):
        raise ValidationError(
            f'Block hash must be valid hex string, got: {block_hash}')

    if not score.isdigit():
        raise ValidationError(
            f'Score (total difficulty) must be an integer, got: {score}')

    return Checkpoint(Hash32(decode_hex(block_hash)), int(score))
Beispiel #21
0
    async def _get_block_numbers_for_query(
        self,
        query: BlockHeadersQuery,
    ) -> Tuple[BlockNumber, ...]:
        """
        Generate the block numbers for a given `HeaderRequest`.
        """
        if isinstance(query.block_number_or_hash, bytes):
            header = await self.db.coro_get_block_header_by_hash(
                Hash32(query.block_number_or_hash))
            start_number = header.block_number
        elif isinstance(query.block_number_or_hash, int):
            start_number = query.block_number_or_hash
        else:
            actual_type = type(query.block_number_or_hash)
            raise TypeError(
                f"Invariant: unexpected type for 'block_number_or_hash': {actual_type}"
            )

        return sequence_builder(
            start_number=start_number,
            max_length=query.max_headers,
            skip=query.skip,
            reverse=query.reverse,
        )
Beispiel #22
0
def hash_eth2(data: bytes) -> Hash32:
    """
    Return SHA-256 hashed result.
    Note: it's a placeholder and we aim to migrate to a S[T/N]ARK-friendly hash function in
    a future Ethereum 2.0 deployment phase.
    """
    return Hash32(hashlib.sha256(data).digest())
Beispiel #23
0
 def _get_canonical_head(cls, db: BaseDB) -> BaseBeaconBlock:
     try:
         canonical_head_hash = db[
             SchemaV1.make_canonical_head_hash_lookup_key()]
     except KeyError:
         raise CanonicalHeadNotFound("No canonical head set for this chain")
     return cls._get_block_by_hash(db, Hash32(canonical_head_hash))
 def get_block(self,
               arg: web3.types.BlockIdentifier) -> Optional[Eth1Block]:
     # If `arg` is block number
     if isinstance(arg, int):
         block_time = self._get_block_time(BlockNumber(arg))
         return Eth1Block(
             block_hash=Hash32(int(arg).to_bytes(32, byteorder="big")),
             number=BlockNumber(arg),
             timestamp=Timestamp(block_time),
         )
     # If `arg` is block hash
     elif isinstance(arg, bytes):
         block_hash = Hash32(arg)
         # Why are we interpreting the block hash as a block number here?
         block_number = int.from_bytes(block_hash, byteorder="big")
         latest_block_number = self._get_latest_block_number()
         # Block that's way in the future is presumed to be fake eth1 block in genesis state.
         # Return the block at `start_block_number` in this case.
         # The magic number `100` here stands for distance that's way in the future.
         if block_number > latest_block_number + 100:
             return Eth1Block(
                 block_hash=Hash32(
                     self.start_block_number.to_bytes(32, byteorder="big")),
                 number=BlockNumber(self.start_block_number),
                 timestamp=Timestamp(self.start_block_timestamp),
             )
         block_time = self._get_block_time(BlockNumber(block_number))
         return Eth1Block(
             block_hash=block_hash,
             number=BlockNumber(block_number),
             timestamp=Timestamp(block_time),
         )
     elif arg == "latest":
         latest_block_number = self._get_latest_block_number()
         block_time = self._get_block_time(latest_block_number)
         return Eth1Block(
             block_hash=Hash32(
                 latest_block_number.to_bytes(32, byteorder="big")),
             number=BlockNumber(latest_block_number),
             timestamp=block_time,
         )
     elif isinstance(arg, str):
         raise NotImplementedError(f"get_block({arg!r}) is not implemented")
     else:
         raise TypeError(
             f"Argument {arg!r} to get_block was an unexpected type: {type(arg)}"
         )
Beispiel #25
0
 def parent_hash(self) -> Optional[Hash32]:
     if self._parent_hash is None:
         if self.block_number == 0:
             return GENESIS_PARENT_HASH
         else:
             return None
     else:
         return Hash32(self._parent_hash)
    def apply_proper_event(self, event: AttributeDict) -> None:
        event_name = event.event

        if event_name == TRANSFER_EVENT_NAME:
            transfer_hash = compute_transfer_hash(event)
            self.transfer_hashes.add(transfer_hash)
            self.transfer_events[transfer_hash] = event
        elif event_name == CONFIRMATION_EVENT_NAME:
            transfer_hash = Hash32(bytes(event.args.transferHash))
            assert len(transfer_hash) == 32
            self.confirmation_hashes.add(transfer_hash)
        elif event_name == COMPLETION_EVENT_NAME:
            transfer_hash = Hash32(bytes(event.args.transferHash))
            assert len(transfer_hash) == 32
            self.completion_hashes.add(transfer_hash)
        else:
            raise ValueError(f"Got unknown event {event}")
Beispiel #27
0
 def _get_finalized_head(cls,
                         db: BaseDB,
                         block_class: Type[BaseBeaconBlock]) -> BaseBeaconBlock:
     try:
         finalized_head_root = db[SchemaV1.make_finalized_head_root_lookup_key()]
     except KeyError:
         raise CanonicalHeadNotFound("No finalized head set for this chain")
     return cls._get_block_by_root(db, Hash32(finalized_head_root), block_class)
Beispiel #28
0
async def retrieve_header(w3: Web3, block_number: int) -> BlockHeader:
    logger.debug('Retrieving header #%d', block_number)
    fancy_header = await trio.to_thread.run_sync(w3.eth.getBlock, block_number)
    header = BlockHeader(
        difficulty=fancy_header['difficulty'],
        block_number=fancy_header['number'],
        gas_limit=fancy_header['gasLimit'],
        timestamp=fancy_header['timestamp'],
        coinbase=to_canonical_address(fancy_header['miner']),
        parent_hash=Hash32(fancy_header['parentHash']),
        uncles_hash=Hash32(fancy_header['sha3Uncles']),
        state_root=Hash32(fancy_header['stateRoot']),
        transaction_root=Hash32(fancy_header['transactionsRoot']),
        receipt_root=Hash32(fancy_header['receiptsRoot']),  # type: ignore
        bloom=big_endian_to_int(bytes(fancy_header['logsBloom'])),
        gas_used=fancy_header['gasUsed'],
        extra_data=bytes(fancy_header['extraData']),
        mix_hash=Hash32(fancy_header['mixHash']),
        nonce=bytes(fancy_header['nonce']),
    )
    if header.hash != Hash32(fancy_header['hash']):
        raise ValueError(
            f"Reconstructed header hash does not match expected: "
            f"expected={encode_hex(fancy_header['hash'])}  actual={header.hex_hash}"
        )
    return header
Beispiel #29
0
async def retrieve_header(w3: Web3, block_number: int) -> BlockHeader:
    logger.debug("Retrieving header #%d", block_number)
    w3_header = await trio.to_thread.run_sync(w3.eth.getBlock, block_number)
    header = BlockHeader(
        difficulty=w3_header["difficulty"],
        block_number=w3_header["number"],
        gas_limit=w3_header["gasLimit"],
        timestamp=w3_header["timestamp"],
        coinbase=to_canonical_address(w3_header["miner"]),
        parent_hash=Hash32(w3_header["parentHash"]),
        uncles_hash=Hash32(w3_header["sha3Uncles"]),
        state_root=Hash32(w3_header["stateRoot"]),
        transaction_root=Hash32(w3_header["transactionsRoot"]),
        receipt_root=Hash32(w3_header["receiptsRoot"]),
        bloom=big_endian_to_int(bytes(w3_header["logsBloom"])),
        gas_used=w3_header["gasUsed"],
        extra_data=bytes(w3_header["extraData"]),
        mix_hash=Hash32(w3_header["mixHash"]),
        nonce=bytes(w3_header["nonce"]),
    )
    if header.hash != Hash32(w3_header["hash"]):
        raise ValueError(
            f"Reconstructed header hash does not match expected: "
            f"expected={encode_hex(w3_header['hash'])}  actual={header.hex_hash}"
        )
    return header
Beispiel #30
0
 def get_block(self, arg: Union[Hash32, int, str]) -> Optional[Eth1Block]:
     block_dict = self.w3.eth.getBlock(arg)
     if block_dict is None:
         raise BlockNotFound
     return Eth1Block(
         block_hash=Hash32(block_dict["hash"]),
         number=BlockNumber(block_dict["number"]),
         timestamp=Timestamp(block_dict["timestamp"]),
     )