diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 699e4aabd..eb63d82e1 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -87,10 +87,11 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 OK: 4/4 Fail: 0/4 Skip: 0/4 ## Honest validator ```diff -+ General pubsub topics: OK ++ General pubsub topics OK + Mainnet attestation topics OK ++ is_aggregator OK ``` -OK: 2/2 Fail: 0/2 Skip: 0/2 +OK: 3/3 Fail: 0/3 Skip: 0/3 ## Interop ```diff + Interop genesis OK @@ -267,4 +268,4 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 OK: 1/1 Fail: 0/1 Skip: 0/1 ---TOTAL--- -OK: 144/153 Fail: 0/153 Skip: 9/153 +OK: 145/154 Fail: 0/154 Skip: 9/154 diff --git a/beacon_chain/attestation_aggregation.nim b/beacon_chain/attestation_aggregation.nim index 375f808bd..75fd3a6e6 100644 --- a/beacon_chain/attestation_aggregation.nim +++ b/beacon_chain/attestation_aggregation.nim @@ -252,10 +252,9 @@ proc validateAttestation*( # validator index. # Slightly modified to allow only newer attestations than were previously # seen (no point in propagating older votes) - if (pool.lastVotedEpoch.len > validator_index.int) and - pool.lastVotedEpoch[validator_index.int].isSome() and - (pool.lastVotedEpoch[validator_index.int].get() >= - attestation.data.target.epoch): + if (pool.nextAttestationEpoch.lenu64.ValidatorIndex > validator_index) and + pool.nextAttestationEpoch[validator_index].subnet > + attestation.data.target.epoch: return err((ValidationResult.Ignore, cstring( "Validator has already voted in epoch"))) @@ -285,9 +284,10 @@ proc validateAttestation*( "validateAttestation: attestation's target block not an ancestor of LMD vote block"))) # Only valid attestations go in the list - if pool.lastVotedEpoch.len <= validator_index.int: - pool.lastVotedEpoch.setLen(validator_index.int + 1) - pool.lastVotedEpoch[validator_index] = some(attestation.data.target.epoch) + if not (pool.nextAttestationEpoch.lenu64.ValidatorIndex > validator_index): + pool.nextAttestationEpoch.setLen(validator_index.int + 1) + pool.nextAttestationEpoch[validator_index].subnet = + attestation.data.target.epoch + 1 ok(attesting_indices) @@ -324,9 +324,15 @@ proc validateAggregate*( # [IGNORE] The aggregate is the first valid aggregate received for the # aggregator with index aggregate_and_proof.aggregator_index for the epoch # aggregate.data.target.epoch. - # - # This is [IGNORE] and already effectively checked by attestation pool upon - # attempting to resolve attestations. + # Slightly modified to allow only newer attestations than were previously + # seen (no point in propagating older votes) + if (pool.nextAttestationEpoch.lenu64 > + aggregate_and_proof.aggregator_index) and + pool.nextAttestationEpoch[ + aggregate_and_proof.aggregator_index].aggregate > + aggregate.data.target.epoch: + return err((ValidationResult.Ignore, cstring( + "Validator has already aggregated in epoch"))) # [REJECT] The attestation has participants -- that is, # len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1. @@ -418,4 +424,11 @@ proc validateAggregate*( # compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)) == # store.finalized_checkpoint.root + # Only valid aggregates go in the list + if pool.nextAttestationEpoch.lenu64 <= aggregate_and_proof.aggregator_index: + pool.nextAttestationEpoch.setLen( + aggregate_and_proof.aggregator_index.int + 1) + pool.nextAttestationEpoch[aggregate_and_proof.aggregator_index].aggregate = + aggregate.data.target.epoch + 1 + ok(attesting_indices) diff --git a/beacon_chain/beacon_node_types.nim b/beacon_chain/beacon_node_types.nim index 072fe5bb6..63cb04db5 100644 --- a/beacon_chain/beacon_node_types.nim +++ b/beacon_chain/beacon_node_types.nim @@ -71,7 +71,8 @@ type forkChoice*: ForkChoice - lastVotedEpoch*: seq[Option[Epoch]] # Sequence based on validator indices + nextAttestationEpoch*: seq[tuple[subnet: Epoch, aggregate: Epoch]] ## \ + ## sequence based on validator indices ExitPool* = object ## The exit pool tracks attester slashings, proposer slashings, and diff --git a/beacon_chain/block_pools/spec_cache.nim b/beacon_chain/block_pools/spec_cache.nim index d801f3a1d..443ff6328 100644 --- a/beacon_chain/block_pools/spec_cache.nim +++ b/beacon_chain/block_pools/spec_cache.nim @@ -102,15 +102,6 @@ func get_indexed_attestation*(epochRef: EpochRef, attestation: Attestation): Ind signature: attestation.signature ) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/validator.md#aggregation-selection -func is_aggregator*(epochRef: EpochRef, slot: Slot, index: CommitteeIndex, - slot_signature: ValidatorSig): bool = - let - committee_len = get_beacon_committee_len(epochRef, slot, index) - modulo = max(1'u64, committee_len div TARGET_AGGREGATORS_PER_COMMITTEE) - bytes_to_uint64(eth2digest( - slot_signature.toRaw()).data.toOpenArray(0, 7)) mod modulo == 0 - # https://github.com/ethereum/eth2.0-specs/blob/v1.0.0/specs/phase0/beacon-chain.md#is_valid_indexed_attestation proc is_valid_indexed_attestation*( fork: Fork, genesis_validators_root: Eth2Digest, diff --git a/beacon_chain/eth2_processor.nim b/beacon_chain/eth2_processor.nim index a7e2a2ebf..4bfd71f0d 100644 --- a/beacon_chain/eth2_processor.nim +++ b/beacon_chain/eth2_processor.nim @@ -357,7 +357,11 @@ proc aggregateValidator*( let v = self.attestationPool[].validateAggregate( signedAggregateAndProof, wallTime) if v.isErr: - debug "Dropping aggregate", err = v.error + debug "Dropping aggregate", + err = v.error, + aggregator_index = signedAggregateAndProof.message.aggregator_index, + selection_proof = signedAggregateAndProof.message.selection_proof, + wallSlot return v.error[0] beacon_aggregates_received.inc() @@ -371,7 +375,11 @@ proc aggregateValidator*( except CatchableError as exc: raiseAssert "We just checked that queue is not full! " & exc.msg - trace "Aggregate validated" + trace "Aggregate validated", + aggregator_index = signedAggregateAndProof.message.aggregator_index, + selection_proof = signedAggregateAndProof.message.selection_proof, + wallSlot + try: self.aggregatesQueue.addLastNoWait(AggregateEntry( v: signedAggregateAndProof.message.aggregate, diff --git a/beacon_chain/validator_duties.nim b/beacon_chain/validator_duties.nim index 65dc7d2ff..71afe6150 100644 --- a/beacon_chain/validator_duties.nim +++ b/beacon_chain/validator_duties.nim @@ -684,8 +684,7 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} = seconds(int64(SECONDS_PER_SLOT * 2) div 3), slot, "Waiting to aggregate attestations") - let aggregationHead = get_ancestor(head, slot) - await broadcastAggregatedAttestations(node, aggregationHead, slot) + await broadcastAggregatedAttestations(node, head, slot) if node.eth1Monitor != nil and (slot mod SLOTS_PER_EPOCH) == 0: let finalizedEpochRef = node.chainDag.getFinalizedEpochRef() diff --git a/tests/test_attestation_pool.nim b/tests/test_attestation_pool.nim index 612f7f227..8644b7df4 100644 --- a/tests/test_attestation_pool.nim +++ b/tests/test_attestation_pool.nim @@ -444,18 +444,18 @@ suiteReport "Attestation validation " & preset(): validateAttestation(pool[], attestation, beaconTime, subnet).error()[0] == ValidationResult.Ignore - pool[].lastVotedEpoch.setLen(0) # reset for test + pool[].nextAttestationEpoch.setLen(0) # reset for test check: # Wrong subnet validateAttestation(pool[], attestation, beaconTime, subnet + 1).isErr - pool[].lastVotedEpoch.setLen(0) # reset for test + pool[].nextAttestationEpoch.setLen(0) # reset for test check: # Too far in the future validateAttestation( pool[], attestation, beaconTime - 1.seconds, subnet + 1).isErr - pool[].lastVotedEpoch.setLen(0) # reset for test + pool[].nextAttestationEpoch.setLen(0) # reset for test check: # Too far in the past validateAttestation( diff --git a/tests/test_honest_validator.nim b/tests/test_honest_validator.nim index fba43d658..c3933a47d 100644 --- a/tests/test_honest_validator.nim +++ b/tests/test_honest_validator.nim @@ -2,12 +2,13 @@ import unittest, ./testutil, - ../beacon_chain/spec/[datatypes, network] + ../beacon_chain/spec/[crypto, datatypes, network], + ../beacon_chain/attestation_aggregation suiteReport "Honest validator": var forkDigest: ForkDigest - timedTest "General pubsub topics:": + timedTest "General pubsub topics": check: getBeaconBlocksTopic(forkDigest) == "/eth2/00000000/beacon_block/ssz" getVoluntaryExitsTopic(forkDigest) == "/eth2/00000000/voluntary_exit/ssz" @@ -57,3 +58,110 @@ suiteReport "Honest validator": "/eth2/00000000/beacon_attestation_62/ssz" getAttestationTopic(forkDigest, 63) == "/eth2/00000000/beacon_attestation_63/ssz" + + timedTest "is_aggregator": + check: + not is_aggregator(146, ValidatorSig.fromHex( + "aa176502f0a5e954e4c6b452d0e11a03513c19b6d189f125f07b6c5c120df011c31da4c4a9c4a52a5a48fcba5b14d7b316b986a146187966d2341388bbf1f86c42e90553ba009ba10edc6b5544a6e945ce6d2419197f66ab2b9df2b0a0c89987")[]) + + is_aggregator(147, ValidatorSig.fromHex( + "91a49ae4edfb4b1c9f7856f49c9b0f6d2278b0f714edd6654bf91678d08c0554b8c8bc375f88ffc227f679bf14287dd616e6d1df264599e56516a3f6ab4d91365cc6a40a7e72edeff37c456d4b2b80fa76283911471fe1e292bf64f54cafd55f")[]) + + # https://beaconcha.in/block/62082 + not is_aggregator(150, ValidatorSig.fromHex( + "97e7192cc0f5ec62dc6ea8b86d822b2020ea80c188a09088104781865375a5b4afb1de08ebd9e877458ae30c30f16844071633f1e3431f1fe6fb298bfd6f5947a229590c9bafca0dfcd111b4ceca135f4298e57ac9b6bd2ba96cfe97675910d8")[]) + + # https://beaconcha.in/block/62052 + not is_aggregator(149, ValidatorSig.fromHex( + "ad5c4e7354393125af9225c86035ebd8834d17ba4f9e895006b52117de452832b360eb5265aaec0ddd4ed41fd3a98a080b22de99cdf056c46f0df256449682f0e521c5010ff049044732848eac2f8d0b5cde94763c72b00d2a2361d71df18038")[]) + + # https://beaconcha.in/block/61804 + not is_aggregator(148, ValidatorSig.fromHex( + "0x8a0b276e0a5c9f7580a015b201e190381be48fad0ae1b228f6132028252463f2079137bfe65a64f5a78eef60c94390b10815d96564acd024d1ae78ea6b167651428ebea83f03337862f36503db55346a14e7e7b8f2ea17ba3651a8c0d1176f68")[]) + + # https://beaconcha.in/block/61804 + not is_aggregator(148, ValidatorSig.fromHex( + "0xaddda60eeba491f06454fb424128f127146e17dd5034051830e7198205bf43dcae481417fa437dbf3a822334c7f2e94902c356c417c23a01e2fa7c3e3bbffcad35e743687b7baf76147a2006ea43a348ad690b90cfb242ddffdf1f7f6c0e26f4")[]) + + # https://beaconcha.in/block/61804 + not is_aggregator(148, ValidatorSig.fromHex( + "0x918878d08ece660c6e9ce2d0f752bb2de016ff99dc7f67a29f672e00656654993a29450ed3a6c47b376a09429b75103d0a8c9fa40eb1c0c0d65eb1bf6635d3a83a6cc644987213ab375b0cb5d25d6f9b89871403dd420a93301c91813bf811d4")[]) + + # https://beaconcha.in/block/61804 + not is_aggregator(148, ValidatorSig.fromHex( + "0xabe02078312c72f0e0ccbae6bf0c90cf509bbfde36e8c04da102882fe80aaf7615237b8bc837cdc9b6a640e53dc1881c0e5905dd3d15052c9cbf7d7fdb02252b6a4e085a6dde593d9217c52324edfbde7999f06c862f4973eca47f05062f50ed")[]) + + # https://beaconcha.in/block/61804 + not is_aggregator(148, ValidatorSig.fromHex( + "0xac08ca70066c6ea0525aa54dd867f82b86945818cb9305aae30f3bee13275dcf13d6d0680a47e889482ff2bb9a9f3cdb0588746f9e30c04645eda6d01bbd0ce6326ceb695294cb338ebace5b130c5b8f2e4f8efa63d63d5bb255c21a39da9c12")[]) + + # https://beaconcha.in/block/61757 + not is_aggregator(148, ValidatorSig.fromHex( + "0xac08ca70066c6ea0525aa54dd867f82b86945818cb9305aae30f3bee13275dcf13d6d0680a47e889482ff2bb9a9f3cdb0588746f9e30c04645eda6d01bbd0ce6326ceb695294cb338ebace5b130c5b8f2e4f8efa63d63d5bb255c21a39da9c12")[]) + + # Extracted from Pyrmont and verified against Python executable spec + not is_aggregator(132, ValidatorSig.fromHex( + "0x9432e0532f2a265562e2f8be162c2eaa4156c9ff778a59e562188f82757e9292e6b7f84de75aa4eb4e87a1a8aaebde220befda18abb637157db8a8aaf085c4e5a63b4a506f80e88558396bb66c85492ca70ff9d3d357157857f4584f30cb7b5c")[]) + + not is_aggregator(133, ValidatorSig.fromHex( + "0x89d97295f2cee5cc556b316e142529832589a824537cb13f3d6f2145a23b6bf2981beed5715d8b8781310c48b280c1330e9f52feb4d22ac595aefbbe218b0e40c72ff2f4d75293c5355d4c57768384c248a8dbff20b9f4f7453b7d32747690c1")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0xafbec4bf82c7c1738259e99aa62568381886928dc4c77653748e6c6a4c46abc18789caee44e29cf55b01d9d29cb3ecb503636c261b9e58b2b36d80fc21668c8d7e2483b78387a5f63e11a1c69e795a73975eedc10ac876af4e5c4c280331d4a1")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0xb2724ec19b9c8477549a47fdd7082e539842c005b891788cd373d27fcd8d6b7317d9f9d197591d8fce3671c6e299b5e0145af4b2c6f54e966cd82bbd212ed7d7c1fa8911bbf1f5e9521733e8db1a931cbe76d2a34a1dec6720069027f0d4a10d")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0x878051cc81ef7070b365de3fbdb0a70f4feae610294417fc94cf4ab07e95952f798fd870f6dbc2721129bb1f5c3528f41320c8d07a513a90f3b5442a11927b16b979649c90fcb1f407b12ea28b4d9b4177e99a52aab828bf40e5e4908fc56c86")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0x8cf2996bee5c5031a019adc75bba3590799ec092dafc6a59b1e8807820ea785721f27ce717ea0f9e922c48a9e2ec27250d08eb847de446f109fd16325dd93eec43836e7ac1cb23b3dc4a73a711522c818c52e45cefafbda0cc686718f6b27c10")[]) + + not is_aggregator(133, ValidatorSig.fromHex( + "0x951b33198b2675561fad24a0cd45be054b59bc99070e804f273ecd0bd3c29a8d8d5369f2f458756a02103da59cf9fab508d0ba447be2fa333b957164e40768be3158be7924bb5059cabcccf428bd2e23d94ce9d32438338fdb5fb64bebcfd433")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0x853faf78a64e34e90f086c69f5d6cf147c67e4d06434b8da2758922a3b5af578aafc9d5d0ddb292ea9a4f370cff060ca0769a7b569f1a8df0fafea0ce181310aae6063aa96e020ec1021bd02adcc7e66f623aa25f696b2fb30d31ec0f750c6a1")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0xb28489f1e9923ce9ff9a86e8c0cbb26f3631130fc754d8ee978b73b7f07e4474b318c6f3590775b6beffb84f7c90dd3b073a4286238b28766d2274f9a781d01d35ceb42cfe92c8b983476226725a03f5e768ceb5fd265f2c91d2dd281e86f5e7")[]) + + not is_aggregator(133, ValidatorSig.fromHex( + "0xa9cf52565c529a84f98ab2ba244e79bb9f4f5366746fa9bd6e0e92b9e02743c1c63bb7deec70720fc0c6f21f1e5bea79113d75326e236259a890e1847c52230c1e68303f261246f7dcf273e21480fc1162e3a269a839dd468457be5215855d40")[]) + + not is_aggregator(132, ValidatorSig.fromHex( + "0xa2874f85c057f727e4dbcd3df609bc82f754d6adf21dfe66cc46b1f677bf342f71c4b467ffec8376df2ec06e104540b2053f68c0903dddb4b89348c6737f1aec815dc83243c163c6dd53c016f4d2820cc3402438b24b74736de294b5b71e8cba")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0xa564ea93c3476fdc454c43e0b56e046896608adf0a9d2ced9d39a4742d214f6779e46ebb815405cb7a675befed4cc08214d8f17a85a7fec089cdfe95c04707eb7b57045f3a3b65cd80d993e626806c8a69df92894c21e6a1241fdfc18ff2c64f")[]) + + is_aggregator(133, ValidatorSig.fromHex( + "0xa6e6a4a8f2beb878ba06ba58382497ca3c495c7d21579920ca9735f64d560edba9b84e3e379de3dc4c8529ae7209a344109ef5f2411dde16035b079860223c96f8830a2c394b966d2241e691dd3e6a941dcad261e10f2ed08e43fea59cadc26f")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x87a645d5eab8c687724f52f9aeefd4a639d70a4621e9254f698ce071a88650e10d7a17b969b0c56f31d383c2191954371230ae8a66cc523beb30e850daa686deb4ffe300aa3a3f5d24ff74e5b277a91cded3b0e56f7fe46c517bba9d75d41a8f")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x85c410b9469358b4b8b420f0828f4ddfcfc533a29cf582d814a114adc79360b55960cf6d702f18d79566646c6df2285f00c5f1435e3a7673c397410e6a416614fbcb4d7e0040efa8b1993c8ab6c049d783c19741b674d580de378719688ca726")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x8146a4b63874b62b047b03b22f1ef44611fc0f5a30b51841e3ea71c348fb30c14f95807046de2788a53f5ca9d9fc00320e6dee44f777ce709f4e30196dce6f2543cb563a4c46c731a35489e924b9352244a377e53be4efaae5aa18df511733c9")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x91e94bb799f2a5aa2cadd728d9543c871e4879761ff6a7b26d36941d5dfad4eb3ee77b3bf0b0772e8003a983f9ae8a2406749a622873d157efa72b15359c8df3c9688528bec129cb5211fc9649bfd69be0a9ea3722c98db53828d93091b25efc")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x979b098b4df783c2e94dd49ffc0400768b14c4a804b2e2947dbd55a65ae4c06d8cd87dd3fb81b7de795a47a7a5ef967b041b3bb1b1151a79bb15018115fe291fa2bebdb1fe6acd9f932ed8521f6959cb7415b175c82ac8378b5d47d050c8736b")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0xb111d056052dbeb60e1e9e8ebcdfc467be1b3795c3149f41fc8e4918b87e1b009dab79c5c5d7b69e83f4ddcef35f271309caa9f68a9f970166269015b5cd89ce3155c3c621bb2a9c1a2fba71c4ee915e7ef3a7db892602ee5466b853f3b62933")[]) + + is_aggregator(133, ValidatorSig.fromHex( + "0x93a284081cd6667c0944e7aaa0b71785beea9bd479b66153b9be1fd7e5c32ab4eda33139b251b655ba76ead27682ef8c008c7ab82b16f8d0d5b059725077dd58f8335ca3483223551c6eb2d13d639d74ba795dba50cf2751d99cd2d913429f9a")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0x997fdaba788cec16c7821d334583f591772c65c02a550ed428776ac7f00649cfe2006fa500cb7cc9e2479a0a9ed2a5f80a5bd3951ed5025a17e8e0f26a81c44a44c8644636268ef558949e9282f8f36417728d58dbd4e585a93e78dfde54d0c1")[]) + + is_aggregator(132, ValidatorSig.fromHex( + "0xa1e0546d5acaf84e5e108e9e23d5d2854c543142afaab5992c7544dd8934709c8c6252f9d23ce04207a1e9fca6716c660f950a9b27e1c591255f00ba2830ad7dba0d2595ae6b27106fadeff2059a6d70c32514db0d878b1dbc924058465e313d")[])