diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index b53ce1de1..fdd3d5c01 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -536,21 +536,26 @@ def is_valid_indexed_attestation(state: BeaconState, indexed_attestation: Indexe domain = get_domain(state, DOMAIN_BEACON_ATTESTER, attestation.data.target.epoch) aggregation_bits = attestation.aggregation_bits assert len(aggregation_bits) == len(indexed_attestation.committee) - for i, custody_bits in enumerate(attestation.custody_bits_blocks): - assert len(custody_bits) == len(indexed_attestation.committee) - for participant, abit, cbit in zip(indexed_attestation.committee, aggregation_bits, custody_bits): + + if len(attestation.custody_bits_blocks) == 0: + # fall back on phase0 behavior if there is no shard data. + for participant, abit in zip(indexed_attestation.committee, aggregation_bits): if abit: all_pubkeys.append(state.validators[participant].pubkey) - # Note: only 2N distinct message hashes - all_signing_roots.append(compute_signing_root( - AttestationCustodyBitWrapper(hash_tree_root(attestation.data), i, cbit), domain)) - else: - assert not cbit - # WARNING: this is BROKEN. If no custody_bits_blocks, - # a valid empty signature can pass validation, even though aggregate bits are set. - # Decide between: force at least 1 shard block (even if empty data), - # or fast-aggregate-verify with attestation data with empty shard data as message (alike to phase0) - return bls.AggregateVerify(zip(all_pubkeys, all_signing_roots), signature=attestation.signature) + signing_root = compute_signing_root(indexed_attestation.attestation.data, domain) + return bls.FastAggregateVerify(all_pubkeys, signing_root, signature=attestation.signature) + else: + for i, custody_bits in enumerate(attestation.custody_bits_blocks): + assert len(custody_bits) == len(indexed_attestation.committee) + for participant, abit, cbit in zip(indexed_attestation.committee, aggregation_bits, custody_bits): + if abit: + all_pubkeys.append(state.validators[participant].pubkey) + # Note: only 2N distinct message hashes + all_signing_roots.append(compute_signing_root( + AttestationCustodyBitWrapper(hash_tree_root(attestation.data), i, cbit), domain)) + else: + assert not cbit + return bls.AggregateVerify(zip(all_pubkeys, all_signing_roots), signature=attestation.signature) ``` diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 1cd9c07c4..b8733705a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -77,6 +77,7 @@ def sign_aggregate_attestation(spec, state, attestation_data, participants: List privkey ) ) + # TODO: we should try signing custody bits if spec.version == 'phase1' return bls.Aggregate(signatures) diff --git a/tests/core/pyspec/eth2spec/test/helpers/custody.py b/tests/core/pyspec/eth2spec/test/helpers/custody.py index e00d64a17..bcf2c199b 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/custody.py +++ b/tests/core/pyspec/eth2spec/test/helpers/custody.py @@ -1,6 +1,5 @@ from eth2spec.test.helpers.keys import privkeys from eth2spec.utils import bls -from eth2spec.utils.hash_function import hash from eth2spec.utils.ssz.ssz_typing import Bitlist, ByteVector, Bitvector from eth2spec.utils.ssz.ssz_impl import chunkify, pack, hash_tree_root from eth2spec.utils.merkle_minimal import get_merkle_tree, get_merkle_proof @@ -21,7 +20,7 @@ def get_valid_early_derived_secret_reveal(spec, state, epoch=None): signing_root = spec.compute_signing_root(spec.Epoch(epoch), domain) reveal = bls.Sign(privkeys[revealed_index], signing_root) # Generate the mask (any random 32 bytes that don't reveal the masker's secret will do) - mask = hash(reveal) + mask = spec.hash(reveal) # Generate masker's signature on the mask signing_root = spec.compute_signing_root(mask, domain) masker_signature = bls.Sign(privkeys[masker_index], signing_root) diff --git a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py index c7a144ec2..113bcf169 100644 --- a/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py +++ b/tests/core/pyspec/eth2spec/utils/ssz/ssz_impl.py @@ -2,7 +2,7 @@ from ..merkle_minimal import merkleize_chunks from ..hash_function import hash from .ssz_typing import ( SSZValue, SSZType, BasicValue, BasicType, Series, Elements, Bits, boolean, Container, List, ByteList, - Bitlist, Bitvector, uint, + Bitlist, Bitvector, uint, Bytes32 ) # SSZ Serialization @@ -140,7 +140,7 @@ def chunk_count(typ: SSZType) -> int: raise Exception(f"Type not supported: {typ}") -def hash_tree_root(obj: SSZValue): +def hash_tree_root(obj: SSZValue) -> Bytes32: if isinstance(obj, Series): if is_bottom_layer_kind(obj.type()): leaves = chunkify(pack(obj)) @@ -152,6 +152,6 @@ def hash_tree_root(obj: SSZValue): raise Exception(f"Type not supported: {type(obj)}") if isinstance(obj, (List, ByteList, Bitlist)): - return mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj)) + return Bytes32(mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj))) else: - return merkleize_chunks(leaves) + return Bytes32(merkleize_chunks(leaves))