Skip to content

Commit

Permalink
Consensus block value calculation for produceBlockV3 API call. (#5873)
Browse files Browse the repository at this point in the history
* allow specifying get_proposer_reward block root at state.slot

* Add consensus_block_value calculation.

* Address review comments.

* Post-rebase adjustments.

* Use proper state to calculate consensus block value.

* Revert "allow specifying get_proposer_reward block root at state.slot"

This reverts commit 9fef9a8.

* Fix post-revert problems.
Return back to Gwei.

* Adding test which is not working.

* Do not use test suite if it does not have post-state.

* Add debug logging.

* Increase logging to track sources of balance changes.

* Fix sync committee rewards/penalties calculation.

* Revert "Increase logging to track sources of balance changes."

This reverts commit 32feb20.

* Adopt new vision to block rewards.

* Add block produce logging to VC.

* Remove rewards.nim.

* Eliminate toWei changes.

* Improve UInt256 shortLog.

* Fix conversion procedure.

* Address review comments.

* Fix test.

* Revert "Fix test."

This reverts commit 4948b2c.

---------

Co-authored-by: tersec <tersec@users.noreply.github.com>
Co-authored-by: Etan Kissling <etan@status.im>
  • Loading branch information
3 people authored Mar 11, 2024
1 parent 2d519ac commit f088e5f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 48 deletions.
4 changes: 2 additions & 2 deletions beacon_chain/rpc/rest_validator_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -672,8 +672,8 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
message = (await PayloadType.makeBeaconBlockForHeadAndSlot(
node, qrandao, proposer, qgraffiti, qhead, qslot)).valueOr:
return RestApiResponse.jsonError(Http500, error)
executionValue = Opt.some(UInt256(message.blockValue))
consensusValue = Opt.none(UInt256)
executionValue = Opt.some(UInt256(message.executionPayloadValue))
consensusValue = Opt.some(UInt256(message.consensusBlockValue))
headers = consensusFork.getMaybeBlindedHeaders(
isBlinded = false, executionValue, consensusValue)

Expand Down
35 changes: 29 additions & 6 deletions beacon_chain/spec/state_transition.nim
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import
beaconstate, eth2_merkleization, forks, helpers, signatures,
state_transition_block, state_transition_epoch, validator]

export results, extras
export results, extras, state_transition_block

logScope:
topics = "state_transition"
Expand Down Expand Up @@ -382,7 +382,7 @@ func partialBeaconBlock*(

res

proc makeBeaconBlock*(
proc makeBeaconBlockWithRewards*(
cfg: RuntimeConfig,
state: var ForkedHashedBeaconState,
proposer_index: ValidatorIndex,
Expand All @@ -403,13 +403,15 @@ proc makeBeaconBlock*(
transactions_root: Opt[Eth2Digest],
execution_payload_root: Opt[Eth2Digest],
kzg_commitments: Opt[KzgCommitments]):
Result[ForkedBeaconBlock, cstring] =
Result[tuple[blck: ForkedBeaconBlock, rewards: BlockRewards], cstring] =
## Create a block for the given state. The latest block applied to it will
## be used for the parent_root value, and the slot will be take from
## state.slot meaning process_slots must be called up to the slot for which
## the block is to be created.

template makeBeaconBlock(kind: untyped): Result[ForkedBeaconBlock, cstring] =
template makeBeaconBlock(
kind: untyped
): Result[tuple[blck: ForkedBeaconBlock, rewards: BlockRewards], cstring] =
# To create a block, we'll first apply a partial block to the state, skipping
# some validations.

Expand Down Expand Up @@ -458,11 +460,10 @@ proc makeBeaconBlock*(
else:
static: raiseAssert "Unreachable"


state.`kind Data`.root = hash_tree_root(state.`kind Data`.data)
blck.`kind Data`.state_root = state.`kind Data`.root

ok(blck)
ok((blck: blck, rewards: res.get))

const payloadFork = typeof(executionPayload).kind
when payloadFork == ConsensusFork.Bellatrix:
Expand All @@ -482,6 +483,28 @@ proc makeBeaconBlock*(
else:
{.error: "Unsupported fork".}

proc makeBeaconBlock*(
cfg: RuntimeConfig, state: var ForkedHashedBeaconState,
proposer_index: ValidatorIndex, randao_reveal: ValidatorSig,
eth1_data: Eth1Data, graffiti: GraffitiBytes,
attestations: seq[Attestation], deposits: seq[Deposit],
validator_changes: BeaconBlockValidatorChanges,
sync_aggregate: SyncAggregate,
executionPayload: ForkyExecutionPayloadForSigning,
rollback: RollbackForkedHashedProc, cache: var StateCache,
verificationFlags: UpdateFlags,
transactions_root: Opt[Eth2Digest],
execution_payload_root: Opt[Eth2Digest],
kzg_commitments: Opt[KzgCommitments]):
Result[ForkedBeaconBlock, cstring] =
let blockAndRewards =
? makeBeaconBlockWithRewards(
cfg, state, proposer_index, randao_reveal, eth1_data, graffiti,
attestations, deposits, validator_changes, sync_aggregate,
executionPayload, rollback, cache, verificationFlags, transactions_root,
execution_payload_root, kzg_commitments)
ok(blockAndRewards.blck)

proc makeBeaconBlock*(
cfg: RuntimeConfig, state: var ForkedHashedBeaconState,
proposer_index: ValidatorIndex, randao_reveal: ValidatorSig,
Expand Down
31 changes: 31 additions & 0 deletions beacon_chain/validator_client/block_service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ type
blockRoot*: Eth2Digest
data*: ForkedBlindedBeaconBlock

func shortLog(v: Opt[UInt256]): auto =
if v.isNone(): "<not available>" else: toString(v.get, 10)

func shortLog(v: ForkedMaybeBlindedBeaconBlock): auto =
withForkyMaybeBlindedBlck(v):
when consensusFork < ConsensusFork.Deneb:
shortLog(forkyMaybeBlindedBlck)
else:
when isBlinded:
shortLog(forkyMaybeBlindedBlck)
else:
shortLog(forkyMaybeBlindedBlck.`block`)

proc proposeBlock(vc: ValidatorClientRef, slot: Slot,
proposerKey: ValidatorPubKey) {.async.}

Expand Down Expand Up @@ -237,6 +250,15 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot,
when isBlinded:
let
blockRoot = hash_tree_root(forkyMaybeBlindedBlck)

debug "Block produced",
block_type = "blinded",
block_root = shortLog(blockRoot),
blck = shortLog(maybeBlock),
execution_value = shortLog(maybeBlock.executionValue),
consensus_value = shortLog(maybeBlock.consensusValue)

let
signingRoot =
compute_block_signing_root(fork, genesisRoot, slot, blockRoot)
notSlashable = vc.attachedValidators[]
Expand Down Expand Up @@ -308,6 +330,15 @@ proc publishBlockV3(vc: ValidatorClientRef, currentSlot, slot: Slot,
else:
forkyMaybeBlindedBlck.`block`
)

debug "Block produced",
block_type = "non-blinded",
block_root = shortLog(blockRoot),
blck = shortLog(maybeBlock),
execution_value = shortLog(maybeBlock.executionValue),
consensus_value = shortLog(maybeBlock.consensusValue)

let
signingRoot =
compute_block_signing_root(fork, genesisRoot, slot, blockRoot)
notSlashable = vc.attachedValidators[]
Expand Down
103 changes: 63 additions & 40 deletions beacon_chain/validators/beacon_validators.nim
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,16 @@ declarePublicGauge(attached_validator_balance_total,
logScope: topics = "beacval"

type
EngineBid = tuple[
blck: ForkedBeaconBlock,
blockValue: Wei,
blobsBundleOpt: Opt[BlobsBundle]]
EngineBid* = object
blck*: ForkedBeaconBlock
executionPayloadValue*: Wei
consensusBlockValue*: UInt256
blobsBundleOpt*: Opt[BlobsBundle]

BuilderBid[SBBB] = tuple[
blindedBlckPart: SBBB, blockValue: UInt256]
BuilderBid[SBBB] = object
blindedBlckPart*: SBBB
executionPayloadValue*: UInt256
consensusBlockValue*: UInt256

ForkedBlockResult =
Result[EngineBid, string]
Expand All @@ -104,6 +107,11 @@ proc getValidator*(validators: auto,
Opt.some ValidatorAndIndex(index: ValidatorIndex(idx),
validator: validators[idx])

func blockConsensusValue(r: BlockRewards): UInt256 {.noinit.} =
# Returns value of `block-consensus-value` in Wei units.
u256(r.attestations + r.sync_aggregate +
r.proposer_slashings + r.attester_slashings) * u256(1000000000)

proc addValidatorsFromWeb3Signer(
node: BeaconNode, web3signerUrl: Web3SignerUrl, epoch: Epoch)
{.async: (raises: [CancelledError]).} =
Expand Down Expand Up @@ -494,7 +502,7 @@ proc makeBeaconBlockForHeadAndSlot*(
slot, validator_index
return err("Unable to get execution payload")

let blck = makeBeaconBlock(
let res = makeBeaconBlockWithRewards(
node.dag.cfg,
state[],
validator_index,
Expand Down Expand Up @@ -523,10 +531,16 @@ proc makeBeaconBlockForHeadAndSlot*(
var blobsBundleOpt = Opt.none(BlobsBundle)
when payload is deneb.ExecutionPayloadForSigning:
blobsBundleOpt = Opt.some(payload.blobsBundle)
return if blck.isOk:
ok((blck.get, payload.blockValue, blobsBundleOpt))

if res.isOk:
ok(EngineBid(
blck: res.get().blck,
executionPayloadValue: payload.blockValue,
consensusBlockValue: res.get().rewards.blockConsensusValue(),
blobsBundleOpt: blobsBundleOpt
))
else:
err(blck.error)
err(res.error)

proc makeBeaconBlockForHeadAndSlot*(
PayloadType: type ForkyExecutionPayloadForSigning, node: BeaconNode, randao_reveal: ValidatorSig,
Expand Down Expand Up @@ -578,11 +592,11 @@ proc getBlindedExecutionPayload[

when EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle:
template builderBid: untyped = blindedHeader.data.message
return ok((
return ok(BuilderBid[EPH](
blindedBlckPart: EPH(
execution_payload_header: builderBid.header,
blob_kzg_commitments: builderBid.blob_kzg_commitments),
blockValue: builderBid.value))
executionPayloadValue: builderBid.value))
else:
static: doAssert false

Expand Down Expand Up @@ -691,7 +705,7 @@ proc getBlindedBlockParts[
node: BeaconNode, payloadBuilderClient: RestClientRef, head: BlockRef,
pubkey: ValidatorPubKey, slot: Slot, randao: ValidatorSig,
validator_index: ValidatorIndex, graffiti: GraffitiBytes):
Future[Result[(EPH, UInt256, ForkedBeaconBlock), string]]
Future[Result[(EPH, UInt256, UInt256, ForkedBeaconBlock), string]]
{.async: (raises: [CancelledError]).} =
let
executionBlockHash = node.dag.loadExecutionBlockHash(head).valueOr:
Expand Down Expand Up @@ -776,7 +790,8 @@ proc getBlindedBlockParts[

return ok(
(executionPayloadHeader.get.blindedBlckPart,
executionPayloadHeader.get.blockValue,
executionPayloadHeader.get.executionPayloadValue,
forkedBlck.consensusBlockValue,
forkedBlck.blck))

proc getBuilderBid[SBBB: deneb_mev.SignedBlindedBeaconBlock](
Expand All @@ -801,15 +816,20 @@ proc getBuilderBid[SBBB: deneb_mev.SignedBlindedBeaconBlock](

# These, together, get combined into the blinded block for signing and
# proposal through the relay network.
let (executionPayloadHeader, bidValue, forkedBlck) = blindedBlockParts.get
let (executionPayloadHeader, bidValue, consensusValue, forkedBlck) =
blindedBlockParts.get

let unsignedBlindedBlock = getUnsignedBlindedBeaconBlock[SBBB](
node, slot, validator_index, forkedBlck, executionPayloadHeader)

if unsignedBlindedBlock.isErr:
return err unsignedBlindedBlock.error()

return ok (unsignedBlindedBlock.get, bidValue)
ok(BuilderBid[SBBB](
blindedBlckPart: unsignedBlindedBlock.get,
executionPayloadValue: bidValue,
consensusBlockValue: consensusValue
))

proc proposeBlockMEV(
node: BeaconNode, payloadBuilderClient: RestClientRef,
Expand Down Expand Up @@ -886,15 +906,20 @@ proc makeBlindedBeaconBlockForHeadAndSlot*[BBB: ForkyBlindedBeaconBlock](
# Don't try EL fallback -- VC specifically requested a blinded block
return err("Unable to create blinded block")

let (executionPayloadHeader, bidValue, forkedBlck) = blindedBlockParts.get
let (executionPayloadHeader, bidValue, consensusValue, forkedBlck) =
blindedBlockParts.get
withBlck(forkedBlck):
when consensusFork >= ConsensusFork.Capella:
when ((consensusFork == ConsensusFork.Deneb and
EPH is deneb_mev.BlindedExecutionPayloadAndBlobsBundle) or
(consensusFork == ConsensusFork.Capella and
EPH is capella.ExecutionPayloadHeader)):
return ok (constructPlainBlindedBlock[BBB](
forkyBlck, executionPayloadHeader), bidValue)
return ok(
BuilderBid[BBB](
blindedBlckPart:
constructPlainBlindedBlock[BBB](forkyBlck, executionPayloadHeader),
executionPayloadValue: bidValue,
consensusBlockValue: consensusValue))
else:
return err("makeBlindedBeaconBlockForHeadAndSlot: mismatched block/payload types")
else:
Expand Down Expand Up @@ -1013,8 +1038,8 @@ proc proposeBlockAux(
if collectedBids.builderBid.isSome():
collectedBids.engineBid.isNone() or builderBetterBid(
localBlockValueBoost,
collectedBids.builderBid.value().blockValue,
collectedBids.engineBid.value().blockValue)
collectedBids.builderBid.value().executionPayloadValue,
collectedBids.engineBid.value().executionPayloadValue)
else:
if not collectedBids.engineBid.isSome():
return head # errors logged in router
Expand All @@ -1035,14 +1060,14 @@ proc proposeBlockAux(
localBlockValueBoost,
useBuilderBlock,
builderBlockValue =
toString(collectedBids.builderBid.value().blockValue, 10),
toString(collectedBids.builderBid.value().executionPayloadValue, 10),
engineBlockValue =
toString(collectedBids.engineBid.value().blockValue, 10)
toString(collectedBids.engineBid.value().executionPayloadValue, 10)
elif payloadBuilderClient.isNil:
discard # builder API not configured for this block
else:
info "Did not receive expected builder bid; using engine block",
engineBlockValue = collectedBids.engineBid.value().blockValue
engineBlockValue = collectedBids.engineBid.value().executionPayloadValue
else:
# Similar three cases: builder bid expected and absent, builder bid
# expected and present, and builder bid not expected. However, only
Expand All @@ -1051,7 +1076,7 @@ proc proposeBlockAux(
if collectedBids.builderBid.isSome:
info "Did not receive expected engine bid; using builder block",
builderBlockValue =
collectedBids.builderBid.value().blockValue
collectedBids.builderBid.value().executionPayloadValue

if useBuilderBlock:
let
Expand Down Expand Up @@ -1993,25 +2018,23 @@ proc makeMaybeBlindedBeaconBlockForHeadAndSlotImpl[ResultType](
if collectedBids.builderBid.isSome():
collectedBids.engineBid.isNone() or builderBetterBid(
localBlockValueBoost,
collectedBids.builderBid.value().blockValue,
collectedBids.engineBid.value().blockValue)
collectedBids.builderBid.value().executionPayloadValue,
collectedBids.engineBid.value().executionPayloadValue)
else:
if not(collectedBids.engineBid.isSome):
return ResultType.err("Engine bid is not available")
false

engineBid = block:
if useBuilderBlock:
let
blindedBid = collectedBids.builderBid.value()
payloadValue = blindedBid.blockValue

let blindedBid = collectedBids.builderBid.value()
return ResultType.ok((
blck: consensusFork.MaybeBlindedBeaconBlock(
isBlinded: true,
blindedData: blindedBid.blindedBlckPart.message),
executionValue: Opt.some(payloadValue),
consensusValue: Opt.none(UInt256)))
blck:
consensusFork.MaybeBlindedBeaconBlock(
isBlinded: true,
blindedData: blindedBid.blindedBlckPart.message),
executionValue: Opt.some(blindedBid.executionPayloadValue),
consensusValue: Opt.some(blindedBid.consensusBlockValue)))

collectedBids.engineBid.value()

Expand All @@ -2027,15 +2050,15 @@ proc makeMaybeBlindedBeaconBlockForHeadAndSlotImpl[ResultType](
`block`: forkyBlck,
kzg_proofs: blobsBundle.proofs,
blobs: blobsBundle.blobs)),
executionValue: Opt.some(engineBid.blockValue),
consensusValue: Opt.none(UInt256)))
executionValue: Opt.some(engineBid.executionPayloadValue),
consensusValue: Opt.some(engineBid.consensusBlockValue)))
else:
ResultType.ok((
blck: consensusFork.MaybeBlindedBeaconBlock(
isBlinded: false,
data: forkyBlck),
executionValue: Opt.some(engineBid.blockValue),
consensusValue: Opt.none(UInt256)))
executionValue: Opt.some(engineBid.executionPayloadValue),
consensusValue: Opt.some(engineBid.consensusBlockValue)))

proc makeMaybeBlindedBeaconBlockForHeadAndSlot*(
node: BeaconNode, consensusFork: static ConsensusFork,
Expand Down

0 comments on commit f088e5f

Please sign in to comment.