diff --git a/AllTests-mainnet.md b/AllTests-mainnet.md index ebdb530e5..a27029e2e 100644 --- a/AllTests-mainnet.md +++ b/AllTests-mainnet.md @@ -905,12 +905,14 @@ OK: 1/1 Fail: 0/1 Skip: 0/1 + getAggregatedAttestationDataScore() test vectors OK + getAttestationDataScore() test vectors OK + getLiveness() response deserialization test OK ++ getProduceBlockResponseV3Score() default test OK ++ getProduceBlockResponseV3Score() test vectors OK + getSyncCommitteeContributionDataScore() test vectors OK + getSyncCommitteeMessageDataScore() test vectors OK + getUniqueVotes() test vectors OK + normalizeUri() test vectors OK ``` -OK: 12/12 Fail: 0/12 Skip: 0/12 +OK: 14/14 Fail: 0/14 Skip: 0/14 ## Validator change pool testing suite ```diff + addValidatorChangeMessage/getAttesterSlashingMessage OK @@ -1030,4 +1032,4 @@ OK: 2/2 Fail: 0/2 Skip: 0/2 OK: 9/9 Fail: 0/9 Skip: 0/9 ---TOTAL--- -OK: 687/692 Fail: 0/692 Skip: 5/692 +OK: 689/694 Fail: 0/694 Skip: 5/694 diff --git a/beacon_chain/spec/forks.nim b/beacon_chain/spec/forks.nim index 63ce8df4b..3c86b4926 100644 --- a/beacon_chain/spec/forks.nim +++ b/beacon_chain/spec/forks.nim @@ -1173,6 +1173,16 @@ template withForkyMaybeBlindedBlck*( template forkyMaybeBlindedBlck: untyped {.inject, used.} = b.phase0Data body +template shortLog*(x: ForkedMaybeBlindedBeaconBlock): auto = + withForkyMaybeBlindedBlck(x): + when consensusFork >= ConsensusFork.Deneb: + when isBlinded == true: + shortLog(forkyMaybeBlindedBlck) + else: + shortLog(forkyMaybeBlindedBlck.`block`) + else: + shortLog(forkyMaybeBlindedBlck) + template withStateAndBlck*( s: ForkedHashedBeaconState, b: ForkedBeaconBlock | ForkedSignedBeaconBlock | diff --git a/beacon_chain/validator_client/api.nim b/beacon_chain/validator_client/api.nim index 5762bf088..1e9b39316 100644 --- a/beacon_chain/validator_client/api.nim +++ b/beacon_chain/validator_client/api.nim @@ -39,14 +39,14 @@ type status*: ApiOperation data*: seq[ApiNodeResponse[T]] - ApiScore* = object + ApiScore*[T] = object index*: int - score*: Opt[float64] + score*: Opt[T] - BestNodeResponse*[T] = object + BestNodeResponse*[T, X] = object node*: BeaconNodeServerRef data*: ApiResponse[T] - score*: float64 + score*: X const ViableNodeStatus* = { @@ -56,7 +56,7 @@ const RestBeaconNodeStatus.Synced } -proc `$`*(s: ApiScore): string = +proc `$`*[T](s: ApiScore[T]): string = var res = Base10.toString(uint64(s.index)) res.add(": ") if s.score.isSome(): @@ -65,22 +65,27 @@ proc `$`*(s: ApiScore): string = res.add("") res -proc `$`*(ss: openArray[ApiScore]): string = +proc `$`*[T](ss: openArray[ApiScore[T]]): string = "[" & ss.mapIt($it).join(",") & "]" chronicles.formatIt(seq[ApiScore]): $it func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef, - score: float64): ApiScore = - ApiScore(index: node.index, score: Opt.some(score)) + score: float64): ApiScore[float64] = + ApiScore[float64](index: node.index, score: Opt.some(score)) -func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef): ApiScore = - ApiScore(index: node.index, score: Opt.none(float64)) +func init*(t: typedesc[ApiScore], node: BeaconNodeServerRef, + score: UInt256): ApiScore[UInt256] = + ApiScore[UInt256](index: node.index, score: Opt.some(score)) -func init*[T](t: typedesc[BestNodeResponse], node: BeaconNodeServerRef, - data: ApiResponse[T], score: float64): BestNodeResponse[T] = - BestNodeResponse[T](node: node, data: data, score: score) +func init*(tt: typedesc[ApiScore], + node: BeaconNodeServerRef, T: typedesc): ApiScore[T] = + ApiScore[T](index: node.index, score: Opt.none(T)) + +func init*[T, X](t: typedesc[BestNodeResponse], node: BeaconNodeServerRef, + data: ApiResponse[T], score: X): BestNodeResponse[T, X] = + BestNodeResponse[T, X](node: node, data: data, score: score) proc lazyWaiter(node: BeaconNodeServerRef, request: FutureBase, requestName: string, strategy: ApiStrategyKind) {.async.} = @@ -234,7 +239,7 @@ template firstSuccessParallel*( pendingNodes.del(index) let - node {.inject.} = beaconNode + node {.inject, used.} = beaconNode apiResponse {.inject.} = apiResponseOr[responseType](requestFut, timerFut, "Timeout exceeded while awaiting for the response") @@ -283,6 +288,7 @@ template bestSuccess*( vc: ValidatorClientRef, responseType: typedesc, handlerType: typedesc, + scoreType: typedesc, timeout: Duration, statuses: set[RestBeaconNodeStatus], roles: set[BeaconNodeRole], @@ -301,8 +307,8 @@ template bestSuccess*( var retRes: ApiResponse[handlerType] - scores: seq[ApiScore] - bestResponse: Opt[BestNodeResponse[handlerType]] + scores: seq[ApiScore[scoreType]] + bestResponse: Opt[BestNodeResponse[handlerType, scoreType]] block mainLoop: while true: @@ -395,7 +401,7 @@ template bestSuccess*( perfectScoreFound = true break else: - scores.add(ApiScore.init(node)) + scores.add(ApiScore.init(node, scoreType)) if perfectScoreFound: # lazyWait will cancel `pendingRequests` on timeout. @@ -1181,6 +1187,7 @@ proc getHeadBlockRoot*( let res = vc.bestSuccess( RestPlainResponse, GetBlockRootResponse, + float64, SlotDuration, ViableNodeStatus, {BeaconNodeRole.SyncCommitteeData}, @@ -1413,6 +1420,7 @@ proc produceAttestationData*( let res = vc.bestSuccess( RestPlainResponse, ProduceAttestationDataResponse, + float64, OneThirdDuration, ViableNodeStatus, {BeaconNodeRole.AttestationData}, @@ -1685,6 +1693,7 @@ proc getAggregatedAttestation*( let res = vc.bestSuccess( RestPlainResponse, GetAggregatedAttestationResponse, + float64, OneThirdDuration, ViableNodeStatus, {BeaconNodeRole.AggregatedData}, @@ -1818,6 +1827,7 @@ proc produceSyncCommitteeContribution*( let res = vc.bestSuccess( RestPlainResponse, ProduceSyncCommitteeContributionResponse, + float64, OneThirdDuration, ViableNodeStatus, {BeaconNodeRole.SyncCommitteeData}, @@ -2036,7 +2046,59 @@ proc produceBlockV3*( var failures: seq[ApiNodeFailure] case strategy - of ApiStrategyKind.First, ApiStrategyKind.Best: + of ApiStrategyKind.Best: + let res = vc.bestSuccess( + RestPlainResponse, + ProduceBlockResponseV3, + UInt256, + SlotDuration, + ViableNodeStatus, + {BeaconNodeRole.BlockProposalData}, + produceBlockV3Plain(it, slot, randao_reveal, graffiti, + builder_boost_factor), + getProduceBlockResponseV3Score(itresponse)): + if apiResponse.isErr(): + handleCommunicationError() + ApiResponse[ProduceBlockResponseV3].err(apiResponse.error) + else: + let response = apiResponse.get() + case response.status + of 200: + let + version = response.headers.getString("eth-consensus-version") + blinded = + response.headers.getString("eth-execution-payload-blinded") + executionValue = + response.headers.getString("eth-execution-payload-value") + consensusValue = + response.headers.getString("eth-consensus-block-value") + res = decodeBytes(ProduceBlockResponseV3, response.data, + response.contentType, version, blinded, + executionValue, consensusValue) + if res.isErr(): + handleUnexpectedData() + ApiResponse[ProduceBlockResponseV3].err($res.error) + else: + ApiResponse[ProduceBlockResponseV3].ok(res.get()) + of 400: + handle400() + ApiResponse[ProduceBlockResponseV3].err(ResponseInvalidError) + of 500: + handle500() + ApiResponse[ProduceBlockResponseV3].err(ResponseInternalError) + of 503: + handle503() + ApiResponse[ProduceBlockResponseV3].err( + ResponseNoSyncError) + else: + handleUnexpectedCode() + ApiResponse[ProduceBlockResponseV3].err( + ResponseUnexpectedError) + if res.isErr(): + raise (ref ValidatorApiError)(msg: res.error, data: failures) + return res.get() + + of ApiStrategyKind.First: let res = vc.firstSuccessParallel( RestPlainResponse, ProduceBlockResponseV3, diff --git a/beacon_chain/validator_client/scoring.nim b/beacon_chain/validator_client/scoring.nim index 2b401b25d..19448b94d 100644 --- a/beacon_chain/validator_client/scoring.nim +++ b/beacon_chain/validator_client/scoring.nim @@ -10,6 +10,7 @@ import std/strutils import ssz_serialization/[types, bitseqs] import stew/endians2 +import stint import nimcrypto/hash import "."/common @@ -24,6 +25,9 @@ const func perfectScore*(score: float64): bool = score == Inf +func perfectScore*(score: UInt256): bool = + score == high(UInt256) + proc shortScore*(score: float64): string = if score == Inf: "" @@ -32,6 +36,9 @@ proc shortScore*(score: float64): string = else: formatFloat(score, ffDecimal, 4) +proc shortScore*(score: UInt256): string = + $score + func getLexicographicScore(digest: Eth2Digest): float64 = # We calculate score on first 8 bytes of digest. let @@ -183,3 +190,28 @@ proc getUniqueVotes*(attestations: openArray[phase0.Attestation]): int = processVotes(attestation) res += count res + +proc getProduceBlockResponseV3Score*(blck: ProduceBlockResponseV3): UInt256 = + let (res, cv, ev) = + block: + var score256 = UInt256.zero + let + cvalue = + if blck.consensusValue.isSome(): + let value = blck.consensusValue.get() + score256 = score256 + value + $value + else: + "" + evalue = + if blck.executionValue.isSome(): + let value = blck.executionValue.get() + score256 = score256 + value + $value + else: + "" + (score256, cvalue, evalue) + + debug "Block score", blck = shortLog(blck), consensus_value = cv, + execution_value = ev, score = shortScore(res) + res diff --git a/tests/test_validator_client.nim b/tests/test_validator_client.nim index bbc5553ab..4caafee66 100644 --- a/tests/test_validator_client.nim +++ b/tests/test_validator_client.nim @@ -389,6 +389,7 @@ const ("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01", "0.9995"), ("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000101", "0.0005"), ] + ContributionDataVectors = [ ("0xffffffffffffffffffffffffffff7f7f", "0.9844"), ("0xffffffffffffffffffffffff7f7f7f7f", "0.9688"), @@ -446,6 +447,15 @@ const ([("0xff01", Slot(0), 0'u64), ("0xff01", Slot(0), 1'u64)], 16) ] + UInt256ScoreVectors = [ + ("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0"), + ("0x10", + "0x10", + "32") + ] + proc init(t: typedesc[Eth2Digest], data: string): Eth2Digest = let length = len(data) var dst = Eth2Digest() @@ -755,6 +765,25 @@ suite "Validator Client test suite": score == "0.0000" isLowestScoreAggregatedAttestation(adata.data) == true + test "getProduceBlockResponseV3Score() default test": + let + bdata1 = ProduceBlockResponseV3() + bdata2 = ProduceBlockResponseV3( + consensusValue: Opt.some(UInt256.zero) + ) + bdata3 = ProduceBlockResponseV3( + executionValue: Opt.some(UInt256.zero), + ) + bdata4 = ProduceBlockResponseV3( + consensusValue: Opt.some(UInt256.zero), + executionValue: Opt.some(UInt256.zero) + ) + check: + shortScore(getProduceBlockResponseV3Score(bdata1)) == "0" + shortScore(getProduceBlockResponseV3Score(bdata2)) == "0" + shortScore(getProduceBlockResponseV3Score(bdata3)) == "0" + shortScore(getProduceBlockResponseV3Score(bdata4)) == "0" + test "getSyncCommitteeContributionDataScore() test vectors": for vector in ContributionDataVectors: let @@ -773,11 +802,24 @@ suite "Validator Client test suite": check: score == vector[5] + test "getProduceBlockResponseV3Score() test vectors": + for vector in UInt256ScoreVectors: + let + value1 = strictParse(vector[0], UInt256, 16).get() + value2 = strictParse(vector[1], UInt256, 16).get() + bdata = ProduceBlockResponseV3( + executionValue: Opt.some(value1), + consensusValue: Opt.some(value2) + ) + check shortScore(getProduceBlockResponseV3Score(bdata)) == vector[2] + test "getUniqueVotes() test vectors": for vector in AttestationBitsVectors: let - a1 = phase0.Attestation.init(vector[0][0][0], vector[0][0][1], vector[0][0][2]) - a2 = phase0.Attestation.init(vector[0][1][0], vector[0][1][1], vector[0][1][2]) + a1 = phase0.Attestation.init(vector[0][0][0], vector[0][0][1], + vector[0][0][2]) + a2 = phase0.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": @@ -850,6 +892,7 @@ suite "Validator Client test suite": let response = vc.bestSuccess( RestPlainResponse, uint64, + float64, 100.milliseconds, AllBeaconNodeStatuses, {BeaconNodeRole.Duties},