From 11f2b272a0a29aa401b9d8a725ee32b8f753d613 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Tue, 29 Aug 2023 15:37:45 +0300 Subject: [PATCH 01/10] Initial commit. --- beacon_chain/spec/eth2_apis/rest_types.nim | 24 ++++ beacon_chain/validator_client/api.nim | 134 +++++++++++++++++- beacon_chain/validator_client/scoring.nim | 105 ++++++++++++++ .../sync_committee_service.nim | 2 +- 4 files changed, 261 insertions(+), 4 deletions(-) diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 46acb0b3e7..b4cf1e0c94 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -940,3 +940,27 @@ func toValidatorIndex*(value: RestValidatorIndex): Result[ValidatorIndex, err(ValidatorIndexError.TooHighValue) else: doAssert(false, "ValidatorIndex type size is incorrect") + +template withBlck*(x: ProduceBlockResponseV2, + body: untyped): untyped = + case x.kind + of ConsensusFork.Phase0: + const consensusFork {.inject, used.} = ConsensusFork.Phase0 + template blck: untyped {.inject.} = x.phase0Data + body + of ConsensusFork.Altair: + const consensusFork {.inject, used.} = ConsensusFork.Altair + template blck: untyped {.inject.} = x.altairData + body + of ConsensusFork.Bellatrix: + const consensusFork {.inject, used.} = ConsensusFork.Bellatrix + template blck: untyped {.inject.} = x.bellatrixData + body + of ConsensusFork.Capella: + const consensusFork {.inject, used.} = ConsensusFork.Capella + template blck: untyped {.inject.} = x.capellaData + body + of ConsensusFork.Deneb: + const consensusFork {.inject, used.} = ConsensusFork.Deneb + template blck: untyped {.inject.} = x.denebData.blck + body diff --git a/beacon_chain/validator_client/api.nim b/beacon_chain/validator_client/api.nim index 359f5de013..f3055ea9e5 100644 --- a/beacon_chain/validator_client/api.nim +++ b/beacon_chain/validator_client/api.nim @@ -1140,7 +1140,7 @@ proc getHeadBlockRoot*( let blockIdent = BlockIdent.init(BlockIdentType.Head) case strategy - of ApiStrategyKind.First, ApiStrategyKind.Best: + of ApiStrategyKind.First: let res = vc.firstSuccessParallel(RestPlainResponse, GetBlockRootResponse, SlotDuration, @@ -1184,6 +1184,52 @@ proc getHeadBlockRoot*( raise (ref ValidatorApiError)(msg: res.error, data: failures) return res.get() + of ApiStrategyKind.Best: + let res = vc.bestSuccess( + RestPlainResponse, + GetBlockRootResponse, + SlotDuration, + ViableNodeStatus, + {BeaconNodeRole.SyncCommitteeData}, + getBlockRootPlain(it, blockIdent), + getSyncCommitteeMessageDataScore(vc, itresponse)): + if apiResponse.isErr(): + handleCommunicationError() + ApiResponse[GetBlockRootResponse].err(apiResponse.error) + else: + let response = apiResponse.get() + case response.status + of 200: + let res = decodeBytes(GetBlockRootResponse, response.data, + response.contentType) + if res.isErr(): + handleUnexpectedData() + ApiResponse[GetBlockRootResponse].err($res.error) + else: + let data = res.get() + if data.execution_optimistic.get(false): + handleOptimistic() + failures.add(failure) + ApiResponse[GetBlockRootResponse].err(ResponseECNotInSyncError) + else: + ApiResponse[GetBlockRootResponse].ok(data) + of 400: + handle400() + ApiResponse[GetBlockRootResponse].err(ResponseInvalidError) + of 404: + handle404() + ApiResponse[GetBlockRootResponse].err(ResponseNotFoundError) + of 500: + handle500() + ApiResponse[GetBlockRootResponse].err(ResponseInternalError) + else: + handleUnexpectedCode() + ApiResponse[GetBlockRootResponse].err(ResponseUnexpectedError) + + if res.isErr(): + raise (ref ValidatorApiError)(msg: res.error, data: failures) + return res.get() + of ApiStrategyKind.Priority: vc.firstSuccessSequential(RestPlainResponse, #RestResponse[GetBlockRootResponse], SlotDuration, @@ -1603,7 +1649,7 @@ proc getAggregatedAttestation*( var failures: seq[ApiNodeFailure] case strategy - of ApiStrategyKind.First, ApiStrategyKind.Best: + of ApiStrategyKind.First: let res = vc.firstSuccessParallel( RestPlainResponse, GetAggregatedAttestationResponse, @@ -1642,6 +1688,46 @@ proc getAggregatedAttestation*( raise (ref ValidatorApiError)(msg: res.error, data: failures) return res.get().data + of ApiStrategyKind.Best: + let res = vc.bestSuccess( + RestPlainResponse, + GetAggregatedAttestationResponse, + OneThirdDuration, + ViableNodeStatus, + {BeaconNodeRole.AggregatedData}, + getAggregatedAttestationPlain(it, root, slot), + getAggregatedAttestationDataScore(itresponse)): + if apiResponse.isErr(): + handleCommunicationError() + ApiResponse[GetAggregatedAttestationResponse].err(apiResponse.error) + else: + let response = apiResponse.get() + case response.status: + of 200: + let res = decodeBytes(GetAggregatedAttestationResponse, response.data, + response.contentType) + if res.isErr(): + handleUnexpectedData() + ApiResponse[GetAggregatedAttestationResponse].err($res.error) + else: + ApiResponse[GetAggregatedAttestationResponse].ok(res.get()) + of 400: + handle400() + ApiResponse[GetAggregatedAttestationResponse].err( + ResponseInvalidError) + of 500: + handle500() + ApiResponse[GetAggregatedAttestationResponse].err( + ResponseInternalError) + else: + handleUnexpectedCode() + ApiResponse[GetAggregatedAttestationResponse].err( + ResponseUnexpectedError) + + if res.isErr(): + raise (ref ValidatorApiError)(msg: res.error, data: failures) + return res.get().data + of ApiStrategyKind.Priority: vc.firstSuccessSequential( RestPlainResponse, @@ -1687,7 +1773,7 @@ proc produceSyncCommitteeContribution*( var failures: seq[ApiNodeFailure] case strategy - of ApiStrategyKind.First, ApiStrategyKind.Best: + of ApiStrategyKind.First: let res = vc.firstSuccessParallel( RestPlainResponse, ProduceSyncCommitteeContributionResponse, @@ -1728,6 +1814,48 @@ proc produceSyncCommitteeContribution*( raise (ref ValidatorApiError)(msg: res.error, data: failures) return res.get().data + of ApiStrategyKind.Best: + let res = vc.bestSuccess( + RestPlainResponse, + ProduceSyncCommitteeContributionResponse, + OneThirdDuration, + ViableNodeStatus, + {BeaconNodeRole.SyncCommitteeData}, + produceSyncCommitteeContributionPlain(it, slot, subcommitteeIndex, root), + getSyncCommitteeContributionDataScore(itresponse)): + if apiResponse.isErr(): + handleCommunicationError() + ApiResponse[ProduceSyncCommitteeContributionResponse].err( + apiResponse.error) + else: + let response = apiResponse.get() + case response.status: + of 200: + let res = decodeBytes(ProduceSyncCommitteeContributionResponse, + response.data, response.contentType) + if res.isErr(): + handleUnexpectedData() + ApiResponse[ProduceSyncCommitteeContributionResponse].err( + $res.error) + else: + ApiResponse[ProduceSyncCommitteeContributionResponse].ok(res.get()) + of 400: + handle400() + ApiResponse[ProduceSyncCommitteeContributionResponse].err( + ResponseInvalidError) + of 500: + handle500() + ApiResponse[ProduceSyncCommitteeContributionResponse].err( + ResponseInternalError) + else: + handleUnexpectedCode() + ApiResponse[ProduceSyncCommitteeContributionResponse].err( + ResponseUnexpectedError) + + if res.isErr(): + raise (ref ValidatorApiError)(msg: res.error, data: failures) + return res.get().data + of ApiStrategyKind.Priority: vc.firstSuccessSequential( RestPlainResponse, diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index cb9c0e1395..4c66ef2aad 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -6,16 +6,29 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import std/strutils +import ssz_serialization/[types, bitseqs] +import nimcrypto/hash import "."/common {.push raises: [].} +const + DefaultCommitteeTable = + default(Table[CommitteeIndex, CommitteeValidatorsBits]) + DefaultCommitteeBits = + default(CommitteeValidatorsBits) + func perfectScore*(score: float64): bool = score == Inf proc shortScore*(score: float64): string = if score == Inf: "" else: formatFloat(score, ffDecimal, 4) +func getLexicographicScore(digest: Eth2Digest): float64 = + # We calculate score on first 8 bytes of digest. + let value = uint64.fromBytesBE(digest.data.toOpenArray(0, sizeof(uint64) - 1)) + float64(value) + proc getAttestationDataScore*(rootsSeen: Table[Eth2Digest, Slot], adata: ProduceAttestationDataResponse): float64 = let @@ -47,3 +60,95 @@ proc getAttestationDataScore*(rootsSeen: Table[Eth2Digest, Slot], proc getAttestationDataScore*(vc: ValidatorClientRef, adata: ProduceAttestationDataResponse): float64 = getAttestationDataScore(vc.rootsSeen, adata) + +proc getAggregatedAttestationDataScore*( + adata: GetAggregatedAttestationResponse + ): float64 = + let + length = len(adata.data.aggregation_bits) + ones = countOnes(adata.data.aggregation_bits) + res = if length == ones: Inf else: float64(ones) / float64(length) + + debug "Aggregated attestation score", attestation_data = shortLog(adata.data), + block_slot = adata.data.data.slot, score = shortScore(res) + res + +proc getSyncCommitteeContributionDataScore*( + cdata: ProduceSyncCommitteeContributionResponse + ): float64 = + let + length = len(cdata.data.aggregation_bits) + ones = countOnes(cdata.data.aggregation_bits) + res = if length == ones: Inf else: float64(ones) / float64(length) + + debug "Sync committee contribution score", + contribution_data = shortLog(cdata.data), block_slot = cdata.data.slot, + score = shortScore(res) + res + +proc getSyncCommitteeMessageDataScore*( + rootsSeen: Table[Eth2Digest, Slot], + currentSlot: Slot, + cdata: GetBlockRootResponse + ): float64 = + let + slot = rootsSeen.getOrDefault(cdata.data.root, FAR_FUTURE_SLOT) + res = + if cdata.execution_optimistic.get(true): + # Responses from the nodes which are optimistically synced only are + # not suitable, score it with minimal possible score. + -Inf + else: + if slot != FAR_FUTURE_SLOT: + if slot == currentSlot: + # Perfect score + Inf + else: + float64(1) / float64(1) + float64(currentSlot) - float64(slot) + else: + # Block monitoring is disabled or we missed a block. + getLexicographicScore(cdata.data.root) + + debug "Sync committee message score", + head_block_root = shortLog(cdata.data.root), slot = slot, + current_slot = currentSlot, score = shortScore(res) + res + +proc getSyncCommitteeMessageDataScore*( + vc: ValidatorClientRef, + cdata: GetBlockRootResponse + ): float64 = + getSyncCommitteeMessageDataScore( + vc.rootsSeen, vc.beaconClock.now().slotOrZero(), cdata) + +proc processVotes(bits: var CommitteeValidatorsBits, + attestation: Attestation): uint64 = + var res = 0'u64 + for index in 0 ..< len(attestation.aggregation_bits): + if attestation.aggregation_bits[index]: + if not(bits[index]): + inc(res) + bits[index] = true + res + +proc getUniqueVotes*(attestations: openArray[Attestation]): uint64 = + var + res = 0'u64 + attested: Table[Slot, Table[CommitteeIndex, CommitteeValidatorsBits]] + for attestation in attestations: + let + data = attestation.data + count = + attested.mgetOrPut(data.slot, DefaultCommitteeTable). + mgetOrPut(CommitteeIndex(data.index), DefaultCommitteeBits). + processVotes(attestation) + res += count + res + +proc getAttestationVotes*(response: ProduceBlockResponseV2): int = + withBlck(response): + let count = getUniqueVotes(distinctBase(blck.body.attestations)) + +proc getBlockProposalScore*(bdata: ProduceBlockResponseV2): float64 = + let data = getAttestationVotes(bdata) + float64(data) diff --git a/beacon_chain/validator_client/sync_committee_service.nim b/beacon_chain/validator_client/sync_committee_service.nim index b83f1752ca..2bd38882d2 100644 --- a/beacon_chain/validator_client/sync_committee_service.nim +++ b/beacon_chain/validator_client/sync_committee_service.nim @@ -350,7 +350,7 @@ proc publishSyncMessagesAndContributions(service: SyncCommitteeServiceRef, let beaconBlockRoot = block: try: - let res = await vc.getHeadBlockRoot(ApiStrategyKind.First) + let res = await vc.getHeadBlockRoot(ApiStrategyKind.Best) if res.execution_optimistic.isNone(): ## The `execution_optimistic` is missing from the response, we assume ## that the BN is unaware optimistic sync, so we consider the BN From 118ccbfdb18319af77bd227a4199000d3744be8b Mon Sep 17 00:00:00 2001 From: cheatfate Date: Tue, 19 Sep 2023 22:46:07 +0300 Subject: [PATCH 02/10] Fix issues and tests. --- beacon_chain/validator_client/scoring.nim | 16 ++- tests/test_validator_client.nim | 135 +++++++++++++++++++++- 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index 4c66ef2aad..ca7724c84b 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -7,6 +7,7 @@ import std/strutils import ssz_serialization/[types, bitseqs] +import stew/endians2 import nimcrypto/hash import "."/common @@ -22,12 +23,19 @@ func perfectScore*(score: float64): bool = score == Inf proc shortScore*(score: float64): string = - if score == Inf: "" else: formatFloat(score, ffDecimal, 4) + if score == Inf: + "" + elif score == -Inf: + "" + else: + formatFloat(score, ffDecimal, 4) func getLexicographicScore(digest: Eth2Digest): float64 = # We calculate score on first 8 bytes of digest. - let value = uint64.fromBytesBE(digest.data.toOpenArray(0, sizeof(uint64) - 1)) - float64(value) + let + dvalue = uint64.fromBytesBE(digest.data.toOpenArray(0, sizeof(uint64) - 1)) + value = float64(dvalue) / float64(high(uint64)) + value proc getAttestationDataScore*(rootsSeen: Table[Eth2Digest, Slot], adata: ProduceAttestationDataResponse): float64 = @@ -104,7 +112,7 @@ proc getSyncCommitteeMessageDataScore*( # Perfect score Inf else: - float64(1) / float64(1) + float64(currentSlot) - float64(slot) + float64(1) / (float64(1) + float64(currentSlot) - float64(slot)) else: # Block monitoring is disabled or we missed a block. getLexicographicScore(cdata.data.root) diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim index 229725329b..304f006156 100644 --- a/tests/test_validator_client.nim +++ b/tests/test_validator_client.nim @@ -11,7 +11,8 @@ import std/strutils import httputils import chronos/unittest2/asynctests -import ../beacon_chain/validator_client/[api, common, scoring, fallback_service] +import ../beacon_chain/spec/eth2_apis/eth2_rest_serialization, + ../beacon_chain/validator_client/[api, common, scoring, fallback_service] const HostNames = [ @@ -309,6 +310,12 @@ type target: uint64 ] + AttestationBitsObject* = object + data: CommitteeValidatorsBits + + SyncCommitteeBitsObject* = object + data: SyncCommitteeAggregationBits + const AttestationDataVectors = [ # Attestation score with block monitoring enabled (perfect). @@ -368,6 +375,66 @@ const ("00000000", 0'u64), "375197.0000"), ] + AggregatedDataVectors = [ + ("0xff00000001", "0.2500"), + ("0xffff000001", "0.5000"), + ("0xffffff0001", "0.7500"), + ("0xffffffff01", ""), + ("0xfffffff701", "0.9688"), + ("0xffffefdf01", "0.9375") + ] + + ContributionDataVectors = [ + ("0xffffffffffffffffffffffffffff7f7f", "0.9844"), + ("0xffffffffffffffffffffffff7f7f7f7f", "0.9688"), + ("0xffffffffffffffffffff7f7f7f7f7f7f", "0.9531"), + ("0xffffffffffffffff7f7f7f7f7f7f7f7f", "0.9375"), + ("0xffffffffffff7f7f7f7f7f7f7f7f7f7f", "0.9219"), + ("0xffffffff7f7f7f7f7f7f7f7f7f7f7f7f", "0.9062"), + ("0xffff7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "0.8906"), + ("0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", "0.8750"), + ("0xffffffffffffffffffffffffffffffff", "") + ] + + SyncMessageDataVectors = [ + # Sync committee messages score with block monitoring enabled (perfect) + (6002798'u64, "22242212", "22242212", 6002798'u64, Opt.some(false), + ""), + (6002811'u64, "26ec78d6", "26ec78d6", 6002811'u64, Opt.some(false), + ""), + (6002836'u64, "42354ded", "42354ded", 6002836'u64, Opt.some(false), + ""), + (6002859'u64, "97d8ac69", "97d8ac69", 6002859'u64, Opt.some(false), + ""), + # Sync committee messages score when beacon node is optimistically synced + (6002798'u64, "22242212", "22242212", 6002798'u64, Opt.some(true), + ""), + (6002811'u64, "26ec78d6", "26ec78d6", 6002811'u64, Opt.some(true), + ""), + (6002836'u64, "42354ded", "42354ded", 6002836'u64, Opt.some(true), + ""), + (6002859'u64, "97d8ac69", "97d8ac69", 6002859'u64, Opt.some(true), + ""), + # Sync committee messages score with block monitoring enabled (not perfect) + (6002797'u64, "22242212", "22242212", 6002798'u64, Opt.some(false), + "0.5000"), + (6002809'u64, "26ec78d6", "26ec78d6", 6002811'u64, Opt.some(false), + "0.3333"), + (6002826'u64, "42354ded", "42354ded", 6002836'u64, Opt.some(false), + "0.0909"), + (6002819'u64, "97d8ac69", "97d8ac69", 6002859'u64, Opt.some(false), + "0.0244"), + # Sync committee messages score with block monitoring disabled + (6002797'u64, "00000000", "22242212", 6002798'u64, Opt.some(false), + "0.1334"), + (6002809'u64, "00000000", "26ec78d6", 6002811'u64, Opt.some(false), + "0.1520"), + (6002826'u64, "00000000", "42354ded", 6002836'u64, Opt.some(false), + "0.2586"), + (6002819'u64, "00000000", "97d8ac69", 6002859'u64, Opt.some(false), + "0.5931"), + ] + proc init(t: typedesc[Eth2Digest], data: string): Eth2Digest = let length = len(data) var dst = Eth2Digest() @@ -378,8 +445,8 @@ proc init(t: typedesc[Eth2Digest], data: string): Eth2Digest = discard dst -proc init*(t: typedesc[ProduceAttestationDataResponse], - ad: AttestationDataTuple): ProduceAttestationDataResponse = +proc init(t: typedesc[ProduceAttestationDataResponse], + ad: AttestationDataTuple): ProduceAttestationDataResponse = ProduceAttestationDataResponse(data: AttestationData( slot: Slot(ad.slot), index: ad.index, beacon_block_root: Eth2Digest.init(ad.beacon_block_root), @@ -387,6 +454,42 @@ proc init*(t: typedesc[ProduceAttestationDataResponse], target: Checkpoint(epoch: Epoch(ad.target)) )) +proc init(t: typedesc[GetAggregatedAttestationResponse], + bits: string): GetAggregatedAttestationResponse = + + let + jdata = "{\"data\":\"" & bits & "\"}" + bits = + try: + RestJson.decode(jdata, AttestationBitsObject) + except SerializationError as exc: + raiseAssert "Serialization error from [" & $exc.name & "]: " & $exc.msg + GetAggregatedAttestationResponse(data: Attestation( + aggregation_bits: bits.data + )) + +proc init(t: typedesc[ProduceSyncCommitteeContributionResponse], + bits: string): ProduceSyncCommitteeContributionResponse = + let + jdata = "{\"data\":\"" & bits & "\"}" + bits = + try: + RestJson.decode(jdata, SyncCommitteeBitsObject) + except SerializationError as exc: + raiseAssert "Serialization error from [" & $exc.name & "]: " & $exc.msg + ProduceSyncCommitteeContributionResponse(data: SyncCommitteeContribution( + aggregation_bits: bits.data + )) + +proc init(t: typedesc[GetBlockRootResponse], + optimistic: Opt[bool], root: Eth2Digest): GetBlockRootResponse = + let optopt = + if optimistic.isNone(): + none[bool]() + else: + some(optimistic.get()) + GetBlockRootResponse(data: RestRoot(root: root), execution_optimistic: optopt) + proc createRootsSeen( root: tuple[root: string, slot: uint64]): Table[Eth2Digest, Slot] = var res: Table[Eth2Digest, Slot] @@ -623,6 +726,31 @@ suite "Validator Client test suite": score = shortScore(roots.getAttestationDataScore(adata)) check score == vector[2] + test "getAggregatedAttestationDataScore() test vectors": + for vector in AggregatedDataVectors: + let + adata = GetAggregatedAttestationResponse.init(vector[0]) + score = shortScore(getAggregatedAttestationDataScore(adata)) + check score == vector[1] + + test "getSyncCommitteeContributionDataScore() test vectors": + for vector in ContributionDataVectors: + let + adata = ProduceSyncCommitteeContributionResponse.init(vector[0]) + score = shortScore(getSyncCommitteeContributionDataScore(adata)) + check score == vector[1] + + test "getSyncCommitteeMessageDataScore() test vectors": + for vector in SyncMessageDataVectors: + let + roots = createRootsSeen((vector[1], vector[0])) + rdata = GetBlockRootResponse.init(vector[4], Eth2Digest.init(vector[2])) + currentSlot = Slot(vector[3]) + score = shortScore(getSyncCommitteeMessageDataScore(roots, currentSlot, + rdata)) + check: + score == vector[5] + asyncTest "firstSuccessParallel() API timeout test": let uri = parseUri("http://127.0.0.1/") @@ -708,7 +836,6 @@ suite "Validator Client test suite": response.isErr() gotCancellation == true - test "getLiveness() response deserialization test": proc generateLivenessResponse(T: typedesc[string], start, count, modv: int): string = From da20a96f6a0e259c3be3012c6b01485a088c2071 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Wed, 20 Sep 2023 00:19:12 +0300 Subject: [PATCH 03/10] Fix test compilation issue. --- tests/test_signing_node.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index e1174d9e30..723695cab9 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -96,7 +96,7 @@ func init(T: type ForkedBeaconBlock, contents: ProduceBlockResponseV2): T = of ConsensusFork.Capella: return ForkedBeaconBlock.init(contents.capellaData) of ConsensusFork.Deneb: - return ForkedBeaconBlock.init(contents.denebData.block) + return ForkedBeaconBlock.init(contents.denebData.blck) proc getBlock(fork: ConsensusFork, feeRecipient = SigningExpectedFeeRecipient): ForkedBeaconBlock = From d03c2d0ba7e9950c59d9b170087a2861159213ef Mon Sep 17 00:00:00 2001 From: cheatfate Date: Wed, 20 Sep 2023 00:49:30 +0300 Subject: [PATCH 04/10] Update AllTests. --- AllTests-mainnet.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 32516bfb9f..7a651d8b9b 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -48,7 +48,6 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + sanity check Deneb blocks [Preset: mainnet] OK + sanity check Deneb states [Preset: mainnet] OK + sanity check Deneb states, reusing buffers [Preset: mainnet] OK -+ sanity check blobs [Preset: mainnet] OK + sanity check genesis roundtrip [Preset: mainnet] OK + sanity check phase 0 blocks [Preset: mainnet] OK + sanity check phase 0 getState rollback [Preset: mainnet] OK @@ -56,7 +55,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + sanity check phase 0 states, reusing buffers [Preset: mainnet] OK + sanity check state diff roundtrip [Preset: mainnet] OK ``` -OK: 25/25 Fail: 0/25 Skip: 0/25 +OK: 24/24 Fail: 0/24 Skip: 0/24 ## Beacon state [Preset: mainnet] ```diff + Smoke test initialize_beacon_state_from_eth1 [Preset: mainnet] OK @@ -262,14 +261,13 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 ## Honest validator ```diff + General pubsub topics OK -+ Index shuffling and unshuffling invert OK + Liveness failsafe conditions OK + Mainnet attestation topics OK + Stability subnets OK + isNearSyncCommitteePeriod OK + is_aggregator OK ``` -OK: 7/7 Fail: 0/7 Skip: 0/7 +OK: 6/6 Fail: 0/6 Skip: 0/6 ## ImportKeystores requests [Beacon Node] [Preset: mainnet] ```diff + ImportKeystores/ListKeystores/DeleteKeystores [Beacon Node] [Preset: mainnet] OK @@ -358,6 +356,7 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 ```diff + Aggregate and proof signatures OK + Attestation signatures OK ++ Blob sidecar signatures OK + Deposit signatures OK + Slot signatures OK + Sync committee message signatures OK @@ -365,7 +364,7 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + Sync committee signed contribution and proof signatures OK + Voluntary exit signatures OK ``` -OK: 8/8 Fail: 0/8 Skip: 0/8 +OK: 9/9 Fail: 0/9 Skip: 0/9 ## Network metadata ```diff + goerli OK @@ -428,15 +427,6 @@ OK: 12/12 Fail: 0/12 Skip: 0/12 + prune states OK ``` OK: 1/1 Fail: 0/1 Skip: 0/1 -## REST JSON encoding and decoding -```diff -+ Blob OK -+ DenebSignedBlockContents decoding OK -+ KzgCommitment OK -+ KzgProof OK -+ RestPublishedSignedBlockContents decoding OK -``` -OK: 5/5 Fail: 0/5 Skip: 0/5 ## Remove keystore testing suite ```diff + Many remotes OK @@ -590,20 +580,21 @@ OK: 7/7 Fail: 0/7 Skip: 0/7 OK: 24/24 Fail: 0/24 Skip: 0/24 ## Type helpers ```diff -+ BeaconBlock OK ++ BeaconBlockType OK ``` OK: 1/1 Fail: 0/1 Skip: 0/1 ## Validator Client test suite ```diff -+ /eth/v1/validator/beacon_committee_selections serialization/deserialization test OK -+ /eth/v1/validator/sync_committee_selections serialization/deserialization test OK + bestSuccess() API timeout test OK + firstSuccessParallel() API timeout test OK ++ getAggregatedAttestationDataScore() test vectors OK + getAttestationDataScore() test vectors OK + getLiveness() response deserialization test OK ++ getSyncCommitteeContributionDataScore() test vectors OK ++ getSyncCommitteeMessageDataScore() test vectors OK + normalizeUri() test vectors OK ``` -OK: 7/7 Fail: 0/7 Skip: 0/7 +OK: 8/8 Fail: 0/8 Skip: 0/8 ## Validator change pool testing suite ```diff + addValidatorChangeMessage/getAttesterSlashingMessage OK @@ -716,4 +707,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 405/410 Fail: 0/410 Skip: 5/410 +OK: 400/405 Fail: 0/405 Skip: 5/405 From f47e1bcfa847e6c997fe53c4363799c48227518c Mon Sep 17 00:00:00 2001 From: cheatfate Date: Wed, 20 Sep 2023 10:57:21 +0300 Subject: [PATCH 05/10] Change the most poor score name from to . Split sync committee message score in range, so lexicographic scores will not intersect with normal one. Lexicographic scores should be below to normal scores. --- beacon_chain/validator_client/scoring.nim | 10 +++++++--- tests/test_validator_client.nim | 16 ++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index ca7724c84b..033023d8cf 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -26,7 +26,7 @@ proc shortScore*(score: float64): string = if score == Inf: "" elif score == -Inf: - "" + "" else: formatFloat(score, ffDecimal, 4) @@ -108,13 +108,17 @@ proc getSyncCommitteeMessageDataScore*( -Inf else: if slot != FAR_FUTURE_SLOT: + # When `slot` has been found score value will be in range of + # `(1, 2]` or `Inf`. if slot == currentSlot: # Perfect score Inf else: - float64(1) / (float64(1) + float64(currentSlot) - float64(slot)) + float64(1) + + float64(1) / (float64(1) + float64(currentSlot) - float64(slot)) else: - # Block monitoring is disabled or we missed a block. + # Block monitoring is disabled or we missed a block, in this case + # score value will be in range of `(0, 1]` getLexicographicScore(cdata.data.root) debug "Sync committee message score", diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim index 304f006156..356949ccf9 100644 --- a/tests/test_validator_client.nim +++ b/tests/test_validator_client.nim @@ -408,22 +408,22 @@ const ""), # Sync committee messages score when beacon node is optimistically synced (6002798'u64, "22242212", "22242212", 6002798'u64, Opt.some(true), - ""), + ""), (6002811'u64, "26ec78d6", "26ec78d6", 6002811'u64, Opt.some(true), - ""), + ""), (6002836'u64, "42354ded", "42354ded", 6002836'u64, Opt.some(true), - ""), + ""), (6002859'u64, "97d8ac69", "97d8ac69", 6002859'u64, Opt.some(true), - ""), + ""), # Sync committee messages score with block monitoring enabled (not perfect) (6002797'u64, "22242212", "22242212", 6002798'u64, Opt.some(false), - "0.5000"), + "1.5000"), (6002809'u64, "26ec78d6", "26ec78d6", 6002811'u64, Opt.some(false), - "0.3333"), + "1.3333"), (6002826'u64, "42354ded", "42354ded", 6002836'u64, Opt.some(false), - "0.0909"), + "1.0909"), (6002819'u64, "97d8ac69", "97d8ac69", 6002859'u64, Opt.some(false), - "0.0244"), + "1.0244"), # Sync committee messages score with block monitoring disabled (6002797'u64, "00000000", "22242212", 6002798'u64, Opt.some(false), "0.1334"), From 06048134221f0d0f33f3e1834e514a9ee0a33bde Mon Sep 17 00:00:00 2001 From: cheatfate Date: Mon, 6 Nov 2023 14:22:14 +0200 Subject: [PATCH 06/10] Address review comments. Fix aggregated attestation scoring to use MAX_VALIDATORS_PER_COMMITTEE. Fix sync committee contributions to use SYNC_SUBCOMMITTEE_SIZE. Add getUniqueVotes test vectors. --- beacon_chain/validator_client/scoring.nim | 46 ++++++++++------------- tests/test_validator_client.nim | 46 +++++++++++++++-------- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index 033023d8cf..e80e199a36 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -13,11 +13,13 @@ import "."/common {.push raises: [].} +type + CommitteeBitsArray = BitArray[int(MAX_VALIDATORS_PER_COMMITTEE)] + CommitteeTable = Table[CommitteeIndex, CommitteeBitsArray] + const - DefaultCommitteeTable = - default(Table[CommitteeIndex, CommitteeValidatorsBits]) - DefaultCommitteeBits = - default(CommitteeValidatorsBits) + DefaultCommitteeTable = default(CommitteeTable) + DefaultCommitteeBitsArray = default(CommitteeBitsArray) func perfectScore*(score: float64): bool = score == Inf @@ -73,7 +75,7 @@ proc getAggregatedAttestationDataScore*( adata: GetAggregatedAttestationResponse ): float64 = let - length = len(adata.data.aggregation_bits) + length = int(MAX_VALIDATORS_PER_COMMITTEE) ones = countOnes(adata.data.aggregation_bits) res = if length == ones: Inf else: float64(ones) / float64(length) @@ -85,7 +87,7 @@ proc getSyncCommitteeContributionDataScore*( cdata: ProduceSyncCommitteeContributionResponse ): float64 = let - length = len(cdata.data.aggregation_bits) + length = int(SYNC_SUBCOMMITTEE_SIZE) ones = countOnes(cdata.data.aggregation_bits) res = if length == ones: Inf else: float64(ones) / float64(length) @@ -133,9 +135,10 @@ proc getSyncCommitteeMessageDataScore*( getSyncCommitteeMessageDataScore( vc.rootsSeen, vc.beaconClock.now().slotOrZero(), cdata) -proc processVotes(bits: var CommitteeValidatorsBits, - attestation: Attestation): uint64 = - var res = 0'u64 +proc processVotes(bits: var CommitteeBitsArray, + attestation: Attestation): int = + doAssert(len(attestation.aggregation_bits) <= len(bits)) + var res = 0 for index in 0 ..< len(attestation.aggregation_bits): if attestation.aggregation_bits[index]: if not(bits[index]): @@ -143,24 +146,15 @@ proc processVotes(bits: var CommitteeValidatorsBits, bits[index] = true res -proc getUniqueVotes*(attestations: openArray[Attestation]): uint64 = +proc getUniqueVotes*(attestations: openArray[Attestation]): int = var - res = 0'u64 - attested: Table[Slot, Table[CommitteeIndex, CommitteeValidatorsBits]] + res = 0 + attested: Table[Slot, CommitteeTable] for attestation in attestations: - let - data = attestation.data - count = - attested.mgetOrPut(data.slot, DefaultCommitteeTable). - mgetOrPut(CommitteeIndex(data.index), DefaultCommitteeBits). - processVotes(attestation) + let count = + attested.mgetOrPut(attestation.data.slot, DefaultCommitteeTable). + mgetOrPut(CommitteeIndex(attestation.data.index), + DefaultCommitteeBitsArray). + processVotes(attestation) res += count res - -proc getAttestationVotes*(response: ProduceBlockResponseV2): int = - withBlck(response): - let count = getUniqueVotes(distinctBase(blck.body.attestations)) - -proc getBlockProposalScore*(bdata: ProduceBlockResponseV2): float64 = - let data = getAttestationVotes(bdata) - float64(data) diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim index 356949ccf9..3c86c88812 100644 --- a/tests/test_validator_client.nim +++ b/tests/test_validator_client.nim @@ -310,10 +310,10 @@ type target: uint64 ] - AttestationBitsObject* = object + AttestationBitsObject = object data: CommitteeValidatorsBits - SyncCommitteeBitsObject* = object + SyncCommitteeBitsObject = object data: SyncCommitteeAggregationBits const @@ -376,14 +376,13 @@ const ] AggregatedDataVectors = [ - ("0xff00000001", "0.2500"), - ("0xffff000001", "0.5000"), - ("0xffffff0001", "0.7500"), - ("0xffffffff01", ""), - ("0xfffffff701", "0.9688"), - ("0xffffefdf01", "0.9375") + ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01", ""), + ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0.2500"), + ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0.5000"), + ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0.7500"), + ("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01", "0.9995"), + ("0x0101", "0.0005"), ] - ContributionDataVectors = [ ("0xffffffffffffffffffffffffffff7f7f", "0.9844"), ("0xffffffffffffffffffffffff7f7f7f7f", "0.9688"), @@ -435,6 +434,12 @@ const "0.5931"), ] + AttestationBitsVectors = [ + ([("0xff01", Slot(0), 0'u64), ("0xff01", Slot(0), 0'u64)], 8), + ([("0xff01", Slot(0), 0'u64), ("0xff01", Slot(1), 0'u64)], 16), + ([("0xff01", Slot(0), 0'u64), ("0xff01", Slot(0), 1'u64)], 16) + ] + proc init(t: typedesc[Eth2Digest], data: string): Eth2Digest = let length = len(data) var dst = Eth2Digest() @@ -454,9 +459,8 @@ proc init(t: typedesc[ProduceAttestationDataResponse], target: Checkpoint(epoch: Epoch(ad.target)) )) -proc init(t: typedesc[GetAggregatedAttestationResponse], - bits: string): GetAggregatedAttestationResponse = - +proc init(t: typedesc[Attestation], bits: string, + slot: Slot = GENESIS_SLOT, index: uint64 = 0'u64): Attestation = let jdata = "{\"data\":\"" & bits & "\"}" bits = @@ -464,9 +468,12 @@ proc init(t: typedesc[GetAggregatedAttestationResponse], RestJson.decode(jdata, AttestationBitsObject) except SerializationError as exc: raiseAssert "Serialization error from [" & $exc.name & "]: " & $exc.msg - GetAggregatedAttestationResponse(data: Attestation( - aggregation_bits: bits.data - )) + Attestation(aggregation_bits: bits.data, + data: AttestationData(slot: slot, index: index)) + +proc init(t: typedesc[GetAggregatedAttestationResponse], + bits: string): GetAggregatedAttestationResponse = + GetAggregatedAttestationResponse(data: Attestation.init(bits)) proc init(t: typedesc[ProduceSyncCommitteeContributionResponse], bits: string): ProduceSyncCommitteeContributionResponse = @@ -751,6 +758,15 @@ suite "Validator Client test suite": check: score == vector[5] + test "getUniqueVotes() test vectors": + var data = CommitteeValidatorsBits.init(16) + + for vector in AttestationBitsVectors: + let + a1 = Attestation.init(vector[0][0][0], vector[0][0][1], vector[0][0][2]) + a2 = Attestation.init(vector[0][1][0], vector[0][1][1], vector[0][1][2]) + check getUniqueVotes([a1, a2]) == vector[1] + asyncTest "firstSuccessParallel() API timeout test": let uri = parseUri("http://127.0.0.1/") From fe2337dc968d2cd07f91dec4d91e856784d5d960 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Tue, 7 Nov 2023 13:14:32 +0200 Subject: [PATCH 07/10] Post-rebase fixes. --- AllTests-mainnet.md | 25 ++++++++++++++++++------- tests/test_signing_node.nim | 2 +- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 7a651d8b9b..880de92b42 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -48,6 +48,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + sanity check Deneb blocks [Preset: mainnet] OK + sanity check Deneb states [Preset: mainnet] OK + sanity check Deneb states, reusing buffers [Preset: mainnet] OK ++ sanity check blobs [Preset: mainnet] OK + sanity check genesis roundtrip [Preset: mainnet] OK + sanity check phase 0 blocks [Preset: mainnet] OK + sanity check phase 0 getState rollback [Preset: mainnet] OK @@ -55,7 +56,7 @@ OK: 3/3 Fail: 0/3 Skip: 0/3 + sanity check phase 0 states, reusing buffers [Preset: mainnet] OK + sanity check state diff roundtrip [Preset: mainnet] OK ``` -OK: 24/24 Fail: 0/24 Skip: 0/24 +OK: 25/25 Fail: 0/25 Skip: 0/25 ## Beacon state [Preset: mainnet] ```diff + Smoke test initialize_beacon_state_from_eth1 [Preset: mainnet] OK @@ -261,13 +262,14 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 ## Honest validator ```diff + General pubsub topics OK ++ Index shuffling and unshuffling invert OK + Liveness failsafe conditions OK + Mainnet attestation topics OK + Stability subnets OK + isNearSyncCommitteePeriod OK + is_aggregator OK ``` -OK: 6/6 Fail: 0/6 Skip: 0/6 +OK: 7/7 Fail: 0/7 Skip: 0/7 ## ImportKeystores requests [Beacon Node] [Preset: mainnet] ```diff + ImportKeystores/ListKeystores/DeleteKeystores [Beacon Node] [Preset: mainnet] OK @@ -356,7 +358,6 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 ```diff + Aggregate and proof signatures OK + Attestation signatures OK -+ Blob sidecar signatures OK + Deposit signatures OK + Slot signatures OK + Sync committee message signatures OK @@ -364,7 +365,7 @@ OK: 4/4 Fail: 0/4 Skip: 0/4 + Sync committee signed contribution and proof signatures OK + Voluntary exit signatures OK ``` -OK: 9/9 Fail: 0/9 Skip: 0/9 +OK: 8/8 Fail: 0/8 Skip: 0/8 ## Network metadata ```diff + goerli OK @@ -427,6 +428,15 @@ OK: 12/12 Fail: 0/12 Skip: 0/12 + prune states OK ``` OK: 1/1 Fail: 0/1 Skip: 0/1 +## REST JSON encoding and decoding +```diff ++ Blob OK ++ DenebSignedBlockContents decoding OK ++ KzgCommitment OK ++ KzgProof OK ++ RestPublishedSignedBlockContents decoding OK +``` +OK: 5/5 Fail: 0/5 Skip: 0/5 ## Remove keystore testing suite ```diff + Many remotes OK @@ -580,7 +590,7 @@ OK: 7/7 Fail: 0/7 Skip: 0/7 OK: 24/24 Fail: 0/24 Skip: 0/24 ## Type helpers ```diff -+ BeaconBlockType OK ++ BeaconBlock OK ``` OK: 1/1 Fail: 0/1 Skip: 0/1 ## Validator Client test suite @@ -592,9 +602,10 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 + getLiveness() response deserialization test OK + getSyncCommitteeContributionDataScore() test vectors OK + getSyncCommitteeMessageDataScore() test vectors OK ++ getUniqueVotes() test vectors OK + normalizeUri() test vectors OK ``` -OK: 8/8 Fail: 0/8 Skip: 0/8 +OK: 9/9 Fail: 0/9 Skip: 0/9 ## Validator change pool testing suite ```diff + addValidatorChangeMessage/getAttesterSlashingMessage OK @@ -707,4 +718,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 400/405 Fail: 0/405 Skip: 5/405 +OK: 407/412 Fail: 0/412 Skip: 5/412 diff --git a/tests/test_signing_node.nim b/tests/test_signing_node.nim index 723695cab9..370a87fe1e 100644 --- a/tests/test_signing_node.nim +++ b/tests/test_signing_node.nim @@ -96,7 +96,7 @@ func init(T: type ForkedBeaconBlock, contents: ProduceBlockResponseV2): T = of ConsensusFork.Capella: return ForkedBeaconBlock.init(contents.capellaData) of ConsensusFork.Deneb: - return ForkedBeaconBlock.init(contents.denebData.blck) + return ForkedBeaconBlock.init(contents.denebData.`block`) proc getBlock(fork: ConsensusFork, feeRecipient = SigningExpectedFeeRecipient): ForkedBeaconBlock = From 01d5e8826072007404c95f71df21a6d50c6a63b3 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Mon, 13 Nov 2023 13:36:56 +0200 Subject: [PATCH 08/10] Address review comments. --- beacon_chain/validator_client/scoring.nim | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index e80e199a36..2f98d057fa 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -75,25 +75,24 @@ proc getAggregatedAttestationDataScore*( adata: GetAggregatedAttestationResponse ): float64 = let - length = int(MAX_VALIDATORS_PER_COMMITTEE) ones = countOnes(adata.data.aggregation_bits) - res = if length == ones: Inf else: float64(ones) / float64(length) + res = float64(ones) / float64(MAX_VALIDATORS_PER_COMMITTEE) debug "Aggregated attestation score", attestation_data = shortLog(adata.data), - block_slot = adata.data.data.slot, score = shortScore(res) + block_slot = adata.data.data.slot, ones_count = ones, + score = shortScore(res) res proc getSyncCommitteeContributionDataScore*( cdata: ProduceSyncCommitteeContributionResponse ): float64 = let - length = int(SYNC_SUBCOMMITTEE_SIZE) ones = countOnes(cdata.data.aggregation_bits) - res = if length == ones: Inf else: float64(ones) / float64(length) + res = float64(ones) / float64(SYNC_SUBCOMMITTEE_SIZE) debug "Sync committee contribution score", contribution_data = shortLog(cdata.data), block_slot = cdata.data.slot, - score = shortScore(res) + ones_count = ones, score = shortScore(res) res proc getSyncCommitteeMessageDataScore*( From 2d061686489c398f64a21387ee840641b950a660 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Mon, 13 Nov 2023 17:05:38 +0200 Subject: [PATCH 09/10] Return back score calculation based on actual bits length. --- beacon_chain/validator_client/scoring.nim | 36 +++++++++++++++++++---- tests/test_validator_client.nim | 2 +- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index 2f98d057fa..2075041482 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -74,25 +74,51 @@ proc getAttestationDataScore*(vc: ValidatorClientRef, proc getAggregatedAttestationDataScore*( adata: GetAggregatedAttestationResponse ): float64 = + # This procedure returns score value in range [0.0000, 1.0000) and `Inf`. + # It returns perfect score when all the bits was set to `1`, but this could + # provide wrong expectation for some edge cases (when different attestations + # has different committee sizes), but currently this is the only viable way + # to return perfect score. + const MaxLength = int(MAX_VALIDATORS_PER_COMMITTEE) + doAssert(len(adata.data.aggregation_bits) <= MaxLength) let + size = len(adata.data.aggregation_bits) ones = countOnes(adata.data.aggregation_bits) - res = float64(ones) / float64(MAX_VALIDATORS_PER_COMMITTEE) + res = + if ones == size: + # We consider score perfect, when all bits was set to 1. + Inf + else: + float64(ones) / float64(size) debug "Aggregated attestation score", attestation_data = shortLog(adata.data), - block_slot = adata.data.data.slot, ones_count = ones, - score = shortScore(res) + block_slot = adata.data.data.slot, committee_size = size, + ones_count = ones, score = shortScore(res) res proc getSyncCommitteeContributionDataScore*( cdata: ProduceSyncCommitteeContributionResponse ): float64 = + # This procedure returns score value in range [0.0000, 1.0000) and `Inf`. + # It returns perfect score when all the bits was set to `1`, but this could + # provide wrong expectation for some edge cases (when different contributions + # has different committee sizes), but currently this is the only viable way + # to return perfect score. + const MaxLength = int(SYNC_SUBCOMMITTEE_SIZE) + doAssert(len(cdata.data.aggregation_bits) <= MaxLength) let + size = len(cdata.data.aggregation_bits) ones = countOnes(cdata.data.aggregation_bits) - res = float64(ones) / float64(SYNC_SUBCOMMITTEE_SIZE) + res = + if ones == size: + # We consider score perfect, when all bits was set to 1. + Inf + else: + float64(ones) / float64(size) debug "Sync committee contribution score", contribution_data = shortLog(cdata.data), block_slot = cdata.data.slot, - ones_count = ones, score = shortScore(res) + committee_size = size, ones_count = ones, score = shortScore(res) res proc getSyncCommitteeMessageDataScore*( diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim index 3c86c88812..ba4c948ffd 100644 --- a/tests/test_validator_client.nim +++ b/tests/test_validator_client.nim @@ -381,7 +381,7 @@ const ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0.5000"), ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "0.7500"), ("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01", "0.9995"), - ("0x0101", "0.0005"), + ("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101", "0.0005"), ] ContributionDataVectors = [ ("0xffffffffffffffffffffffffffff7f7f", "0.9844"), From 8a004c202ae983f56b4ce24cad4f60351021a0c5 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Mon, 13 Nov 2023 18:37:00 +0200 Subject: [PATCH 10/10] AllTests modification. --- AllTests-mainnet.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index 880de92b42..573ef8cb2e 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -595,6 +595,8 @@ OK: 24/24 Fail: 0/24 Skip: 0/24 OK: 1/1 Fail: 0/1 Skip: 0/1 ## Validator Client test suite ```diff ++ /eth/v1/validator/beacon_committee_selections serialization/deserialization test OK ++ /eth/v1/validator/sync_committee_selections serialization/deserialization test OK + bestSuccess() API timeout test OK + firstSuccessParallel() API timeout test OK + getAggregatedAttestationDataScore() test vectors OK @@ -605,7 +607,7 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 + getUniqueVotes() test vectors OK + normalizeUri() test vectors OK ``` -OK: 9/9 Fail: 0/9 Skip: 0/9 +OK: 11/11 Fail: 0/11 Skip: 0/11 ## Validator change pool testing suite ```diff + addValidatorChangeMessage/getAttesterSlashingMessage OK @@ -718,4 +720,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 407/412 Fail: 0/412 Skip: 5/412 +OK: 409/414 Fail: 0/414 Skip: 5/414