From dc1a565b3ffe8c63a4980d364890627355ebc3f2 Mon Sep 17 00:00:00 2001 From: tersec Date: Thu, 18 Jun 2020 13:10:25 +0000 Subject: [PATCH] support v0.12.1 attestation topics in beacon node/inspector subscribing (#1187) * support v0.12.1 attestation topics in beacon node and inspector subscribing * bump is_valid_merkle_branch() spec ref --- beacon_chain/beacon_node.nim | 20 ++- beacon_chain/block_pools/candidate_chains.nim | 4 +- beacon_chain/inspector.nim | 8 +- beacon_chain/spec/beaconstate.nim | 2 +- beacon_chain/spec/helpers.nim | 13 +- beacon_chain/spec/network.nim | 50 +++++-- beacon_chain/spec/presets/mainnet.nim | 2 +- tests/test_honest_validator.nim | 128 ++++++++++++------ 8 files changed, 159 insertions(+), 68 deletions(-) diff --git a/beacon_chain/beacon_node.nim b/beacon_chain/beacon_node.nim index 3c4a2ad02..a477c6171 100644 --- a/beacon_chain/beacon_node.nim +++ b/beacon_chain/beacon_node.nim @@ -742,12 +742,20 @@ proc installAttestationHandlers(node: BeaconNode) = for it in 0'u64 ..< ATTESTATION_SUBNET_COUNT.uint64: closureScope: let ci = it - attestationSubscriptions.add(node.network.subscribe( - getMainnetAttestationTopic(node.forkDigest, ci), attestationHandler, - # This proc needs to be within closureScope; don't lift out of loop. - proc(attestation: Attestation): bool = - attestationValidator(attestation, ci) - )) + when ETH2_SPEC == "v0.12.1": + attestationSubscriptions.add(node.network.subscribe( + getAttestationTopic(node.forkDigest, ci), attestationHandler, + # This proc needs to be within closureScope; don't lift out of loop. + proc(attestation: Attestation): bool = + attestationValidator(attestation, ci) + )) + else: + attestationSubscriptions.add(node.network.subscribe( + getMainnetAttestationTopic(node.forkDigest, ci), attestationHandler, + # This proc needs to be within closureScope; don't lift out of loop. + proc(attestation: Attestation): bool = + attestationValidator(attestation, ci) + )) when ETH2_SPEC == "v0.11.3": # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#interop-3 diff --git a/beacon_chain/block_pools/candidate_chains.nim b/beacon_chain/block_pools/candidate_chains.nim index bb22ef600..1a5df91ed 100644 --- a/beacon_chain/block_pools/candidate_chains.nim +++ b/beacon_chain/block_pools/candidate_chains.nim @@ -53,7 +53,7 @@ func parent*(bs: BlockSlot): BlockSlot = slot: bs.slot - 1 ) -func populateEpochCache*(state: BeaconState, epoch: Epoch): EpochRef = +func populateEpochCache(state: BeaconState, epoch: Epoch): EpochRef = result = (EpochRef)( epoch: state.slot.compute_epoch_at_slot, shuffled_active_validator_indices: @@ -148,7 +148,7 @@ func getEpochInfo*(blck: BlockRef, state: BeaconState): EpochRef = if matching_epochinfo.len == 0: let cache = populateEpochCache(state, state_epoch) blck.epochsInfo.add(cache) - trace "candidate_chains.skipAndUpdateState(): back-filling parent.epochInfo", + trace "candidate_chains.getEpochInfo: back-filling parent.epochInfo", state_slot = state.slot cache elif matching_epochinfo.len == 1: diff --git a/beacon_chain/inspector.nim b/beacon_chain/inspector.nim index 6e676bfbf..dda016887 100644 --- a/beacon_chain/inspector.nim +++ b/beacon_chain/inspector.nim @@ -207,8 +207,12 @@ func getTopics(forkDigest: ForkDigest, var topics = newSeq[string](ATTESTATION_SUBNET_COUNT * 2) var offset = 0 for i in 0'u64 ..< ATTESTATION_SUBNET_COUNT.uint64: - topics[offset] = getMainnetAttestationTopic(forkDigest, i) - topics[offset + 1] = getMainnetAttestationTopic(forkDigest, i) & "_snappy" + when ETH2_SPEC == "v0.12.1": + topics[offset] = getAttestationTopic(forkDigest, i) + topics[offset + 1] = getAttestationTopic(forkDigest, i) & "_snappy" + else: + topics[offset] = getMainnetAttestationTopic(forkDigest, i) + topics[offset + 1] = getMainnetAttestationTopic(forkDigest, i) & "_snappy" offset += 2 topics diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 7287850e5..45a3e8216 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -14,7 +14,7 @@ import ./crypto, ./datatypes, ./digest, ./helpers, ./signatures, ./validator, ../../nbench/bench_lab -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#is_valid_merkle_branch +# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#is_valid_merkle_branch func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openarray[Eth2Digest], depth: uint64, index: uint64, root: Eth2Digest): bool {.nbench.}= ## Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ## ``branch``. diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index 879fdd46e..6d9416df6 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -64,7 +64,13 @@ func get_active_validator_indices*(state: BeaconState, epoch: Epoch): if is_active_validator(val, epoch): result.add idx.ValidatorIndex -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/beacon-chain.md#get_committee_count_at_slot +# https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/beacon-chain.md#get_committee_count_at_slot +func get_committee_count_at_slot*(num_active_validators: auto): + uint64 = + clamp( + num_active_validators div SLOTS_PER_EPOCH div TARGET_COMMITTEE_SIZE, + 1, MAX_COMMITTEES_PER_SLOT).uint64 + func get_committee_count_at_slot*(state: BeaconState, slot: Slot): uint64 = # Return the number of committees at ``slot``. @@ -74,10 +80,7 @@ func get_committee_count_at_slot*(state: BeaconState, slot: Slot): uint64 = # CommitteeIndex return type here. let epoch = compute_epoch_at_slot(slot) let active_validator_indices = get_active_validator_indices(state, epoch) - let committees_per_slot = clamp( - len(active_validator_indices) div SLOTS_PER_EPOCH div TARGET_COMMITTEE_SIZE, - 1, MAX_COMMITTEES_PER_SLOT).uint64 - result = committees_per_slot + result = get_committee_count_at_slot(len(active_validator_indices)) # Otherwise, get_beacon_committee(...) cannot access some committees. doAssert (SLOTS_PER_EPOCH * MAX_COMMITTEES_PER_SLOT).uint64 >= result diff --git a/beacon_chain/spec/network.nim b/beacon_chain/spec/network.nim index 0c237fb36..3f1e7426c 100644 --- a/beacon_chain/spec/network.nim +++ b/beacon_chain/spec/network.nim @@ -12,14 +12,17 @@ import datatypes const + # https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/p2p-interface.md#topics-and-messages topicBeaconBlocksSuffix* = "beacon_block/ssz" - topicMainnetAttestationsSuffix* = "_beacon_attestation/ssz" topicVoluntaryExitsSuffix* = "voluntary_exit/ssz" topicProposerSlashingsSuffix* = "proposer_slashing/ssz" topicAttesterSlashingsSuffix* = "attester_slashing/ssz" topicAggregateAndProofsSuffix* = "beacon_aggregate_and_proof/ssz" - # https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#configuration + # https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/p2p-interface.md#topics-and-messages + topicMainnetAttestationsSuffix* = "_beacon_attestation/ssz" + + # https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#misc ATTESTATION_SUBNET_COUNT* = 64 defaultEth2TcpPort* = 9000 @@ -30,35 +33,30 @@ const when ETH2_SPEC == "v0.11.3": const topicInteropAttestationSuffix* = "beacon_attestation/ssz" -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#topics-and-messages func getBeaconBlocksTopic*(forkDigest: ForkDigest): string = try: &"/eth2/{$forkDigest}/{topicBeaconBlocksSuffix}" except ValueError as e: raiseAssert e.msg -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#topics-and-messages func getVoluntaryExitsTopic*(forkDigest: ForkDigest): string = try: &"/eth2/{$forkDigest}/{topicVoluntaryExitsSuffix}" except ValueError as e: raiseAssert e.msg -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#topics-and-messages func getProposerSlashingsTopic*(forkDigest: ForkDigest): string = try: &"/eth2/{$forkDigest}/{topicProposerSlashingsSuffix}" except ValueError as e: raiseAssert e.msg -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#topics-and-messages func getAttesterSlashingsTopic*(forkDigest: ForkDigest): string = try: &"/eth2/{$forkDigest}/{topicAttesterSlashingsSuffix}" except ValueError as e: raiseAssert e.msg -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#topics-and-messages func getAggregateAndProofsTopic*(forkDigest: ForkDigest): string = try: &"/eth2/{$forkDigest}/{topicAggregateAndProofsSuffix}" @@ -72,10 +70,44 @@ when ETH2_SPEC == "v0.11.3": except ValueError as e: raiseAssert e.msg -# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/p2p-interface.md#mainnet-3 +# https://github.com/ethereum/eth2.0-specs/blob/v0.11.3/specs/phase0/p2p-interface.md#mainnet-3 func getMainnetAttestationTopic*(forkDigest: ForkDigest, committeeIndex: uint64): string = + let topicIndex = committeeIndex mod ATTESTATION_SUBNET_COUNT try: - let topicIndex = committeeIndex mod ATTESTATION_SUBNET_COUNT &"/eth2/{$forkDigest}/committee_index{topicIndex}{topicMainnetAttestationsSuffix}" except ValueError as e: raiseAssert e.msg + +when ETH2_SPEC == "v0.12.1": + import helpers + + # https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#broadcast-attestation + func compute_subnet_for_attestation*( + num_active_validators: uint64, attestation: Attestation): uint64 = + # Compute the correct subnet for an attestation for Phase 0. + # Note, this mimics expected Phase 1 behavior where attestations will be + # mapped to their shard subnet. + # + # The spec version has params (state: BeaconState, attestation: Attestation), + # but it's only to call get_committee_count_at_slot(), which needs only epoch + # and the number of active validators. + let + slots_since_epoch_start = attestation.data.slot mod SLOTS_PER_EPOCH + committees_since_epoch_start = + get_committee_count_at_slot(num_active_validators) * slots_since_epoch_start + + (committees_since_epoch_start + attestation.data.index) mod ATTESTATION_SUBNET_COUNT + + # https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/validator.md#broadcast-attestation + func getAttestationTopic*(forkDigest: ForkDigest, subnetIndex: uint64): + string = + # This is for subscribing or broadcasting manually to a known index. + try: + &"/eth2/{$forkDigest}/beacon_attestation_{subnetIndex}/ssz" + except ValueError as e: + raiseAssert e.msg + + func getAttestationTopic*(forkDigest: ForkDigest, attestation: Attestation, num_active_validators: uint64): string = + getAttestationTopic( + forkDigest, + compute_subnet_for_attestation(num_active_validators, attestation)) diff --git a/beacon_chain/spec/presets/mainnet.nim b/beacon_chain/spec/presets/mainnet.nim index e32626834..cb1e7168f 100644 --- a/beacon_chain/spec/presets/mainnet.nim +++ b/beacon_chain/spec/presets/mainnet.nim @@ -24,7 +24,7 @@ const MAX_COMMITTEES_PER_SLOT* {.intdefine.} = 64 - TARGET_COMMITTEE_SIZE* = 2^7 ##\ + TARGET_COMMITTEE_SIZE* = 128 ##\ ## Number of validators in the committee attesting to one shard ## Per spec: ## For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds diff --git a/tests/test_honest_validator.nim b/tests/test_honest_validator.nim index 1ecb9b502..1ae8b90b5 100644 --- a/tests/test_honest_validator.nim +++ b/tests/test_honest_validator.nim @@ -19,45 +19,89 @@ suiteReport "Honest validator": true getAggregateAndProofsTopic(forkDigest) == "/eth2/00000000/beacon_aggregate_and_proof/ssz" - timedTest "Mainnet attestation topics": - check: - getMainnetAttestationTopic(forkDigest, 0) == - "/eth2/00000000/committee_index0_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 9) == - "/eth2/00000000/committee_index9_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 10) == - "/eth2/00000000/committee_index10_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 11) == - "/eth2/00000000/committee_index11_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 14) == - "/eth2/00000000/committee_index14_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 22) == - "/eth2/00000000/committee_index22_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 34) == - "/eth2/00000000/committee_index34_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 46) == - "/eth2/00000000/committee_index46_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 60) == - "/eth2/00000000/committee_index60_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 63) == - "/eth2/00000000/committee_index63_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 200) == - "/eth2/00000000/committee_index8_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 400) == - "/eth2/00000000/committee_index16_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 469) == - "/eth2/00000000/committee_index21_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 550) == - "/eth2/00000000/committee_index38_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 600) == - "/eth2/00000000/committee_index24_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 613) == - "/eth2/00000000/committee_index37_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 733) == - "/eth2/00000000/committee_index29_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 775) == - "/eth2/00000000/committee_index7_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 888) == - "/eth2/00000000/committee_index56_beacon_attestation/ssz" - getMainnetAttestationTopic(forkDigest, 995) == - "/eth2/00000000/committee_index35_beacon_attestation/ssz" + when ETH2_SPEC == "v0.11.3": + timedTest "Mainnet attestation topics": + check: + getMainnetAttestationTopic(forkDigest, 0) == + "/eth2/00000000/committee_index0_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 9) == + "/eth2/00000000/committee_index9_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 10) == + "/eth2/00000000/committee_index10_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 11) == + "/eth2/00000000/committee_index11_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 14) == + "/eth2/00000000/committee_index14_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 22) == + "/eth2/00000000/committee_index22_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 34) == + "/eth2/00000000/committee_index34_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 46) == + "/eth2/00000000/committee_index46_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 60) == + "/eth2/00000000/committee_index60_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 63) == + "/eth2/00000000/committee_index63_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 200) == + "/eth2/00000000/committee_index8_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 400) == + "/eth2/00000000/committee_index16_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 469) == + "/eth2/00000000/committee_index21_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 550) == + "/eth2/00000000/committee_index38_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 600) == + "/eth2/00000000/committee_index24_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 613) == + "/eth2/00000000/committee_index37_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 733) == + "/eth2/00000000/committee_index29_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 775) == + "/eth2/00000000/committee_index7_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 888) == + "/eth2/00000000/committee_index56_beacon_attestation/ssz" + getMainnetAttestationTopic(forkDigest, 995) == + "/eth2/00000000/committee_index35_beacon_attestation/ssz" + else: + timedTest "Mainnet attestation topics": + check: + getAttestationTopic(forkDigest, 0) == + "/eth2/00000000/beacon_attestation_0/ssz" + getAttestationTopic(forkDigest, 5) == + "/eth2/00000000/beacon_attestation_5/ssz" + getAttestationTopic(forkDigest, 7) == + "/eth2/00000000/beacon_attestation_7/ssz" + getAttestationTopic(forkDigest, 9) == + "/eth2/00000000/beacon_attestation_9/ssz" + getAttestationTopic(forkDigest, 13) == + "/eth2/00000000/beacon_attestation_13/ssz" + getAttestationTopic(forkDigest, 19) == + "/eth2/00000000/beacon_attestation_19/ssz" + getAttestationTopic(forkDigest, 20) == + "/eth2/00000000/beacon_attestation_20/ssz" + getAttestationTopic(forkDigest, 22) == + "/eth2/00000000/beacon_attestation_22/ssz" + getAttestationTopic(forkDigest, 25) == + "/eth2/00000000/beacon_attestation_25/ssz" + getAttestationTopic(forkDigest, 27) == + "/eth2/00000000/beacon_attestation_27/ssz" + getAttestationTopic(forkDigest, 31) == + "/eth2/00000000/beacon_attestation_31/ssz" + getAttestationTopic(forkDigest, 39) == + "/eth2/00000000/beacon_attestation_39/ssz" + getAttestationTopic(forkDigest, 45) == + "/eth2/00000000/beacon_attestation_45/ssz" + getAttestationTopic(forkDigest, 47) == + "/eth2/00000000/beacon_attestation_47/ssz" + getAttestationTopic(forkDigest, 48) == + "/eth2/00000000/beacon_attestation_48/ssz" + getAttestationTopic(forkDigest, 50) == + "/eth2/00000000/beacon_attestation_50/ssz" + getAttestationTopic(forkDigest, 53) == + "/eth2/00000000/beacon_attestation_53/ssz" + getAttestationTopic(forkDigest, 54) == + "/eth2/00000000/beacon_attestation_54/ssz" + getAttestationTopic(forkDigest, 62) == + "/eth2/00000000/beacon_attestation_62/ssz" + getAttestationTopic(forkDigest, 63) == + "/eth2/00000000/beacon_attestation_63/ssz"