From 048a67d525cf6185d747353919897b95c0adf3de Mon Sep 17 00:00:00 2001 From: tersec Date: Fri, 18 Dec 2020 15:46:07 +0100 Subject: [PATCH] have each validator randomly pick a stability subnet, per spec (#2194) --- beacon_chain/nimbus_beacon_node.nim | 40 +++++++++++++++++++---------- beacon_chain/spec/datatypes.nim | 3 +-- beacon_chain/spec/network.nim | 25 +++++++++++------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/beacon_chain/nimbus_beacon_node.nim b/beacon_chain/nimbus_beacon_node.nim index 4e7de1062..58d87750b 100644 --- a/beacon_chain/nimbus_beacon_node.nim +++ b/beacon_chain/nimbus_beacon_node.nim @@ -359,11 +359,12 @@ proc installAttestationSubnetHandlers(node: BeaconNode, subnets: set[uint8]) await allFutures(attestationSubscriptions) -proc updateStabilitySubnetMetadata(node: BeaconNode, stabilitySubnet: uint64) = +proc updateStabilitySubnetMetadata( + node: BeaconNode, stabilitySubnets: set[uint8]) = # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#metadata node.network.metadata.seq_number += 1 for subnet in 0'u8 ..< ATTESTATION_SUBNET_COUNT: - node.network.metadata.attnets[subnet] = (subnet == stabilitySubnet) + node.network.metadata.attnets[subnet] = (subnet in stabilitySubnets) # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#phase-0-attestation-subnet-stability # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/p2p-interface.md#attestation-subnet-bitfield @@ -374,7 +375,11 @@ proc updateStabilitySubnetMetadata(node: BeaconNode, stabilitySubnet: uint64) = # be the correct one and the ENR will not increase in size. warn "Failed to update record on subnet cycle", error = res.error else: - debug "Stability subnet changed, updated ENR attnets", stabilitySubnet + debug "Stability subnets changed, updated ENR attnets", stabilitySubnets + +func getStabilitySubnets(stabilitySubnets: auto): set[uint8] = + for subnetInfo in stabilitySubnets: + result.incl subnetInfo.subnet proc cycleAttestationSubnets(node: BeaconNode, slot: Slot) {.async.} = static: doAssert RANDOM_SUBNETS_PER_VALIDATOR == 1 @@ -394,7 +399,8 @@ proc cycleAttestationSubnets(node: BeaconNode, slot: Slot) {.async.} = node.chainDag.headState.data.data, attachedValidators, node.attestationSubnets, slot.epoch) - let prevStabilitySubnet = node.attestationSubnets.stabilitySubnet + let prevStabilitySubnets = + getStabilitySubnets(node.attestationSubnets.stabilitySubnets) node.attestationSubnets = newAttestationSubnets debug "Attestation subnets", @@ -403,9 +409,7 @@ proc cycleAttestationSubnets(node: BeaconNode, slot: Slot) {.async.} = node.attestationSubnets.subscribedSubnets[1 - epochParity], upcoming_subnets = node.attestationSubnets.subscribedSubnets[epochParity], new_subnets = newSubnets, - stability_subnet = node.attestationSubnets.stabilitySubnet, - stability_subnet_expiration_epoch = - node.attestationSubnets.stabilitySubnetExpirationEpoch + stability_subnets = node.attestationSubnets.stabilitySubnets block: var unsubscriptions: seq[Future[void]] = @[] @@ -417,9 +421,11 @@ proc cycleAttestationSubnets(node: BeaconNode, slot: Slot) {.async.} = await node.installAttestationSubnetHandlers(newSubnets) - let stabilitySubnet = node.attestationSubnets.stabilitySubnet - if stabilitySubnet != prevStabilitySubnet: - node.updateStabilitySubnetMetadata(stabilitySubnet) + let stabilitySubnets = + getStabilitySubnets(node.attestationSubnets.stabilitySubnets) + + if stabilitySubnets != prevStabilitySubnets: + node.updateStabilitySubnetMetadata(stabilitySubnets) proc getAttestationSubnetHandlers(node: BeaconNode): Future[void] = var initialSubnets: set[uint8] @@ -432,11 +438,15 @@ proc getAttestationSubnetHandlers(node: BeaconNode): Future[void] = # - Restarting the node with a presistent netkey # - When going from synced -> syncing -> synced state let wallEpoch = node.beaconClock.now().slotOrZero().epoch - node.attestationSubnets.stabilitySubnet = rand(ATTESTATION_SUBNET_COUNT - 1).uint64 - node.attestationSubnets.stabilitySubnetExpirationEpoch = - wallEpoch + getStabilitySubnetLength() - node.updateStabilitySubnetMetadata(node.attestationSubnets.stabilitySubnet) + doAssert node.attestationSubnets.stabilitySubnets.len == 0 + for _ in 0 ..< node.attachedValidators.count: + node.attestationSubnets.stabilitySubnets.add ( + subnet: rand(ATTESTATION_SUBNET_COUNT - 1).uint8, + expiration: wallEpoch + getStabilitySubnetLength()) + + node.updateStabilitySubnetMetadata( + node.attestationSubnets.stabilitySubnets.getStabilitySubnets) # Sets the "current" and "future" attestation subnets. One of these gets # replaced by get_attestation_subnet_changes() immediately. @@ -465,6 +475,8 @@ proc removeMessageHandlers(node: BeaconNode): Future[void] = node.attestationSubnets.subscribedSubnets[1] = {} doAssert not node.getTopicSubscriptionEnabled() + node.attestationSubnets.stabilitySubnets.setLen(0) + var unsubscriptions = mapIt( [getBeaconBlocksTopic(node.forkDigest), getVoluntaryExitsTopic(node.forkDigest), diff --git a/beacon_chain/spec/datatypes.nim b/beacon_chain/spec/datatypes.nim index 766de946e..deada60c9 100644 --- a/beacon_chain/spec/datatypes.nim +++ b/beacon_chain/spec/datatypes.nim @@ -458,8 +458,7 @@ type AttestationSubnets* = object subscribedSubnets*: array[2, set[uint8]] - stabilitySubnet*: uint64 - stabilitySubnetExpirationEpoch*: Epoch + stabilitySubnets*: seq[tuple[subnet: uint8, expiration: Epoch]] # This matches the mutable state of the Solidity deposit contract # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/solidity_deposit_contract/deposit_contract.sol diff --git a/beacon_chain/spec/network.nim b/beacon_chain/spec/network.nim index 64d6404b3..d6ced036e 100644 --- a/beacon_chain/spec/network.nim +++ b/beacon_chain/spec/network.nim @@ -132,12 +132,20 @@ proc get_attestation_subnet_changes*( var attestationSubnets = prevAttestationSubnets # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#phase-0-attestation-subnet-stability - let prevStabilitySubnet = {attestationSubnets.stabilitySubnet.uint8} - if epoch >= attestationSubnets.stabilitySubnetExpirationEpoch: - attestationSubnets.stabilitySubnet = - rand(ATTESTATION_SUBNET_COUNT - 1).uint64 - attestationSubnets.stabilitySubnetExpirationEpoch = - epoch + getStabilitySubnetLength() + var + prevStabilitySubnets: set[uint8] = {} + stabilitySet: set[uint8] = {} + for i in 0 ..< attestationSubnets.stabilitySubnets.len: + static: doAssert ATTESTATION_SUBNET_COUNT <= high(uint8) + prevStabilitySubnets.incl attestationSubnets.stabilitySubnets[i].subnet + + if epoch >= attestationSubnets.stabilitySubnets[i].expiration: + attestationSubnets.stabilitySubnets[i].subnet = + rand(ATTESTATION_SUBNET_COUNT - 1).uint8 + attestationSubnets.stabilitySubnets[i].expiration = + epoch + getStabilitySubnetLength() + + stabilitySet.incl attestationSubnets.stabilitySubnets[i].subnet var nextEpochSubnets: set[uint8] for it in get_committee_assignments( @@ -148,16 +156,15 @@ proc get_attestation_subnet_changes*( let epochParity = epoch mod 2 - stabilitySet = {attestationSubnets.stabilitySubnet.uint8} currentEpochSubnets = attestationSubnets.subscribedSubnets[1 - epochParity] expiringSubnets = - (prevStabilitySubnet + + (prevStabilitySubnets + attestationSubnets.subscribedSubnets[epochParity]) - nextEpochSubnets - currentEpochSubnets - stabilitySet newSubnets = (nextEpochSubnets + stabilitySet) - - (currentEpochSubnets + prevStabilitySubnet) + (currentEpochSubnets + prevStabilitySubnets) doAssert newSubnets.len <= attachedValidators.len + 1