VC: Add block scoring (#6303)
* Add scoring for blocks. * Update Alltests.
This commit is contained in:
parent
dc6951eee9
commit
1cdb32222b
|
@ -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
|
||||
|
|
|
@ -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 |
|
||||
|
|
|
@ -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("<n/a>")
|
||||
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,
|
||||
|
|
|
@ -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:
|
||||
"<perfect>"
|
||||
|
@ -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:
|
||||
"<missing>"
|
||||
evalue =
|
||||
if blck.executionValue.isSome():
|
||||
let value = blck.executionValue.get()
|
||||
score256 = score256 + value
|
||||
$value
|
||||
else:
|
||||
"<missing>"
|
||||
(score256, cvalue, evalue)
|
||||
|
||||
debug "Block score", blck = shortLog(blck), consensus_value = cv,
|
||||
execution_value = ev, score = shortScore(res)
|
||||
res
|
||||
|
|
|
@ -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},
|
||||
|
|
Loading…
Reference in New Issue