Skip to content

Commit

Permalink
initial Electra support skeleton (#5946)
Browse files Browse the repository at this point in the history
  • Loading branch information
tersec committed Feb 24, 2024
1 parent feec45b commit d09bf3b
Show file tree
Hide file tree
Showing 44 changed files with 794 additions and 160 deletions.
32 changes: 22 additions & 10 deletions beacon_chain/beacon_chain_db.nim
Expand Up @@ -508,7 +508,8 @@ proc new*(T: type BeaconChainDB,
kvStore db.openKvStore("altair_blocks").expectDb(),
kvStore db.openKvStore("bellatrix_blocks").expectDb(),
kvStore db.openKvStore("capella_blocks").expectDb(),
kvStore db.openKvStore("deneb_blocks").expectDb()]
kvStore db.openKvStore("deneb_blocks").expectDb(),
kvStore db.openKvStore("electra_blocks").expectDb()]

stateRoots = kvStore db.openKvStore("state_roots", true).expectDb()

Expand All @@ -517,7 +518,8 @@ proc new*(T: type BeaconChainDB,
kvStore db.openKvStore("altair_state_no_validators").expectDb(),
kvStore db.openKvStore("bellatrix_state_no_validators").expectDb(),
kvStore db.openKvStore("capella_state_no_validator_pubkeys").expectDb(),
kvStore db.openKvStore("deneb_state_no_validator_pubkeys").expectDb()]
kvStore db.openKvStore("deneb_state_no_validator_pubkeys").expectDb(),
kvStore db.openKvStore("electra_state_no_validator_pubkeys").expectDb()]

stateDiffs = kvStore db.openKvStore("state_diffs").expectDb()
summaries = kvStore db.openKvStore("beacon_block_summaries", true).expectDb()
Expand Down Expand Up @@ -789,7 +791,8 @@ proc putBlock*(
proc putBlock*(
db: BeaconChainDB,
value: bellatrix.TrustedSignedBeaconBlock |
capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock) =
capella.TrustedSignedBeaconBlock | deneb.TrustedSignedBeaconBlock |
electra.TrustedSignedBeaconBlock) =
db.withManyWrites:
db.blocks[type(value).kind].putSZSSZ(value.root.data, value)
db.putBeaconBlockSummary(value.root, value.message.toBeaconBlockSummary())
Expand Down Expand Up @@ -839,6 +842,10 @@ template toBeaconStateNoImmutableValidators(state: deneb.BeaconState):
DenebBeaconStateNoImmutableValidators =
isomorphicCast[DenebBeaconStateNoImmutableValidators](state)

template toBeaconStateNoImmutableValidators(state: electra.BeaconState):
ElectraBeaconStateNoImmutableValidators =
isomorphicCast[ElectraBeaconStateNoImmutableValidators](state)

proc putState*(
db: BeaconChainDB, key: Eth2Digest,
value: phase0.BeaconState | altair.BeaconState) =
Expand All @@ -848,7 +855,8 @@ proc putState*(

proc putState*(
db: BeaconChainDB, key: Eth2Digest,
value: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState) =
value: bellatrix.BeaconState | capella.BeaconState | deneb.BeaconState |
electra.BeaconState) =
db.updateImmutableValidators(value.validators.asSeq())
db.statesNoVal[type(value).kind].putSZSSZ(
key.data, toBeaconStateNoImmutableValidators(value))
Expand Down Expand Up @@ -976,7 +984,7 @@ proc getBlock*(

proc getBlock*[
X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock |
deneb.TrustedSignedBeaconBlock](
deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock](
db: BeaconChainDB, key: Eth2Digest,
T: type X): Opt[T] =
# We only store blocks that we trust in the database
Expand Down Expand Up @@ -1031,7 +1039,7 @@ proc getBlockSSZ*(

proc getBlockSSZ*[
X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock |
deneb.TrustedSignedBeaconBlock](
deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock](
db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool =
let dataPtr = addr data # Short-lived
var success = true
Expand Down Expand Up @@ -1080,7 +1088,7 @@ proc getBlockSZ*(

proc getBlockSZ*[
X: bellatrix.TrustedSignedBeaconBlock | capella.TrustedSignedBeaconBlock |
deneb.TrustedSignedBeaconBlock](
deneb.TrustedSignedBeaconBlock | electra.TrustedSignedBeaconBlock](
db: BeaconChainDB, key: Eth2Digest, data: var seq[byte], T: type X): bool =
let dataPtr = addr data # Short-lived
func decode(data: openArray[byte]) =
Expand Down Expand Up @@ -1178,7 +1186,8 @@ proc getStateOnlyMutableValidators(
proc getStateOnlyMutableValidators(
immutableValidators: openArray[ImmutableValidatorData2],
store: KvStoreRef, key: openArray[byte],
output: var (capella.BeaconState | deneb.BeaconState),
output: var (capella.BeaconState | deneb.BeaconState |
electra.BeaconState),
rollback: RollbackProc): bool =
## Load state into `output` - BeaconState is large so we want to avoid
## re-allocating it if possible
Expand Down Expand Up @@ -1263,7 +1272,8 @@ proc getState*(
proc getState*(
db: BeaconChainDB, key: Eth2Digest,
output: var (altair.BeaconState | bellatrix.BeaconState |
capella.BeaconState | deneb.BeaconState),
capella.BeaconState | deneb.BeaconState |
electra.BeaconState),
rollback: RollbackProc): bool =
## Load state into `output` - BeaconState is large so we want to avoid
## re-allocating it if possible
Expand Down Expand Up @@ -1483,7 +1493,7 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest):

# Backwards compat for reading old databases, or those that for whatever
# reason lost a summary along the way..
static: doAssert ConsensusFork.high == ConsensusFork.Deneb
static: doAssert ConsensusFork.high == ConsensusFork.Electra
while true:
if db.v0.backend.getSnappySSZ(
subkey(BeaconBlockSummary, res.root), res.summary) == GetResult.found:
Expand All @@ -1498,6 +1508,8 @@ iterator getAncestorSummaries*(db: BeaconChainDB, root: Eth2Digest):
res.summary = blck.get().message.toBeaconBlockSummary()
elif (let blck = db.getBlock(res.root, deneb.TrustedSignedBeaconBlock); blck.isSome()):
res.summary = blck.get().message.toBeaconBlockSummary()
elif (let blck = db.getBlock(res.root, electra.TrustedSignedBeaconBlock); blck.isSome()):
res.summary = blck.get().message.toBeaconBlockSummary()
else:
break

Expand Down
2 changes: 2 additions & 0 deletions beacon_chain/beacon_node_light_client.nim
Expand Up @@ -77,6 +77,8 @@ proc initLightClient*(

case node.dag.cfg.consensusForkAtEpoch(
forkyBlck.message.slot.epoch)
of ConsensusFork.Electra:
debugRaiseAssert "initLightClient"
of ConsensusFork.Deneb:
callForkchoiceUpdated(PayloadAttributesV3)
of ConsensusFork.Capella:
Expand Down
3 changes: 2 additions & 1 deletion beacon_chain/consensus_object_pools/attestation_pool.nim
Expand Up @@ -483,7 +483,8 @@ func init(
func init(
T: type AttestationCache,
state: altair.HashedBeaconState | bellatrix.HashedBeaconState |
capella.HashedBeaconState | deneb.HashedBeaconState,
capella.HashedBeaconState | deneb.HashedBeaconState |
electra.HashedBeaconState,
cache: var StateCache): T =
# Load attestations that are scheduled for being given rewards for
let
Expand Down
8 changes: 5 additions & 3 deletions beacon_chain/consensus_object_pools/blob_quarantine.nim
Expand Up @@ -69,16 +69,18 @@ func hasBlob*(

func popBlobs*(
quarantine: var BlobQuarantine, digest: Eth2Digest,
blck: deneb.SignedBeaconBlock): seq[ref BlobSidecar] =
blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock):
seq[ref BlobSidecar] =
var r: seq[ref BlobSidecar] = @[]
for idx, kzg_commitment in blck.message.body.blob_kzg_commitments:
var b: ref BlobSidecar
if quarantine.blobs.pop((digest, BlobIndex idx, kzg_commitment), b):
r.add(b)
r

func hasBlobs*(quarantine: BlobQuarantine, blck: deneb.SignedBeaconBlock):
bool =
func hasBlobs*(
quarantine: BlobQuarantine,
blck: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool =
for idx, kzg_commitment in blck.message.body.blob_kzg_commitments:
if (blck.root, BlobIndex idx, kzg_commitment) notin quarantine.blobs:
return false
Expand Down
3 changes: 2 additions & 1 deletion beacon_chain/consensus_object_pools/block_dag.nim
Expand Up @@ -73,7 +73,8 @@ func init*(
T: type BlockRef, root: Eth2Digest, executionValid: bool,
blck: bellatrix.SomeBeaconBlock | bellatrix.TrustedBeaconBlock |
capella.SomeBeaconBlock | capella.TrustedBeaconBlock |
deneb.SomeBeaconBlock | deneb.TrustedBeaconBlock): BlockRef =
deneb.SomeBeaconBlock | deneb.TrustedBeaconBlock |
electra.SomeBeaconBlock | electra.TrustedBeaconBlock): BlockRef =
BlockRef.init(
root, Opt.some Eth2Digest(blck.body.execution_payload.block_hash),
executionValid =
Expand Down
7 changes: 5 additions & 2 deletions beacon_chain/consensus_object_pools/block_pools_types.nim
Expand Up @@ -293,10 +293,11 @@ type
OnBellatrixBlockAdded* = OnBlockAdded[bellatrix.TrustedSignedBeaconBlock]
OnCapellaBlockAdded* = OnBlockAdded[capella.TrustedSignedBeaconBlock]
OnDenebBlockAdded* = OnBlockAdded[deneb.TrustedSignedBeaconBlock]
OnElectraBlockAdded* = OnBlockAdded[electra.TrustedSignedBeaconBlock]

OnForkyBlockAdded* =
OnPhase0BlockAdded | OnAltairBlockAdded | OnBellatrixBlockAdded |
OnCapellaBlockAdded | OnDenebBlockAdded
OnCapellaBlockAdded | OnDenebBlockAdded | OnElectraBlockAdded

HeadChangeInfoObject* = object
slot*: Slot
Expand Down Expand Up @@ -328,7 +329,9 @@ type
optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool]

template OnBlockAddedCallback*(kind: static ConsensusFork): auto =
when kind == ConsensusFork.Deneb:
when kind == ConsensusFork.Electra:
typedesc[OnElectraBlockAdded]
elif kind == ConsensusFork.Deneb:
typedesc[OnDenebBlockAdded]
elif kind == ConsensusFork.Capella:
typedesc[OnCapellaBlockAdded]
Expand Down
9 changes: 5 additions & 4 deletions beacon_chain/consensus_object_pools/block_quarantine.nim
Expand Up @@ -294,8 +294,7 @@ iterator pop*(quarantine: var Quarantine, root: Eth2Digest):

proc addBlobless*(
quarantine: var Quarantine, finalizedSlot: Slot,
signedBlock: deneb.SignedBeaconBlock): bool =

signedBlock: deneb.SignedBeaconBlock | electra.SignedBeaconBlock): bool =
if not isViable(finalizedSlot, signedBlock.message.slot):
quarantine.addUnviable(signedBlock.root)
return false
Expand All @@ -306,8 +305,10 @@ proc addBlobless*(
return true

debug "block quarantine: Adding blobless", blck = shortLog(signedBlock)
quarantine.blobless[signedBlock.root] = signedBlock
quarantine.missing.del(signedBlock.root)
debugRaiseAssert "addBlobless; needs consideration how to handle deneb and electra"
when not (signedBlock is electra.SignedBeaconBlock):
quarantine.blobless[signedBlock.root] = signedBlock
quarantine.missing.del(signedBlock.root)
true

func popBlobless*(quarantine: var Quarantine, root: Eth2Digest):
Expand Down
24 changes: 18 additions & 6 deletions beacon_chain/consensus_object_pools/blockchain_dag.nim
Expand Up @@ -268,8 +268,11 @@ proc getForkedBlock*(db: BeaconChainDB, root: Eth2Digest):
Opt[ForkedTrustedSignedBeaconBlock] =
# When we only have a digest, we don't know which fork it's from so we try
# them one by one - this should be used sparingly
static: doAssert high(ConsensusFork) == ConsensusFork.Deneb
if (let blck = db.getBlock(root, deneb.TrustedSignedBeaconBlock);
static: doAssert high(ConsensusFork) == ConsensusFork.Electra
if (let blck = db.getBlock(root, electra.TrustedSignedBeaconBlock);
blck.isSome()):
ok(ForkedTrustedSignedBeaconBlock.init(blck.get()))
elif (let blck = db.getBlock(root, deneb.TrustedSignedBeaconBlock);
blck.isSome()):
ok(ForkedTrustedSignedBeaconBlock.init(blck.get()))
elif (let blck = db.getBlock(root, capella.TrustedSignedBeaconBlock);
Expand Down Expand Up @@ -1002,6 +1005,12 @@ proc applyBlock(
state_transition(
dag.cfg, state, data, cache, info,
dag.updateFlags + {slotProcessed}, noRollback)
of ConsensusFork.Electra:
let data = getBlock(dag, bid, electra.TrustedSignedBeaconBlock).valueOr:
return err("Block load failed")
state_transition(
dag.cfg, state, data, cache, info,
dag.updateFlags + {slotProcessed}, noRollback)

proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,
validatorMonitor: ref ValidatorMonitor, updateFlags: UpdateFlags,
Expand Down Expand Up @@ -1155,11 +1164,12 @@ proc init*(T: type ChainDAGRef, cfg: RuntimeConfig, db: BeaconChainDB,

let
configFork = case dag.headState.kind
of ConsensusFork.Phase0: genesisFork(cfg)
of ConsensusFork.Altair: altairFork(cfg)
of ConsensusFork.Phase0: genesisFork(cfg)
of ConsensusFork.Altair: altairFork(cfg)
of ConsensusFork.Bellatrix: bellatrixFork(cfg)
of ConsensusFork.Capella: capellaFork(cfg)
of ConsensusFork.Deneb: denebFork(cfg)
of ConsensusFork.Capella: capellaFork(cfg)
of ConsensusFork.Deneb: denebFork(cfg)
of ConsensusFork.Electra: electraFork(cfg)
stateFork = getStateField(dag.headState, fork)

# Here, we check only the `current_version` field because the spec
Expand Down Expand Up @@ -2398,6 +2408,8 @@ proc updateHead*(
of ConsensusFork.Deneb:
if dag.vanityLogs.onUpgradeToDeneb != nil:
dag.vanityLogs.onUpgradeToDeneb()
of ConsensusFork.Electra:
debugRaiseAssert "updateHead"

if dag.vanityLogs.onKnownBlsToExecutionChange != nil and
checkBlsToExecutionChanges(
Expand Down
11 changes: 10 additions & 1 deletion beacon_chain/consensus_object_pools/consensus_manager.nim
Expand Up @@ -379,7 +379,16 @@ proc runProposalForkchoiceUpdated*(
payloadAttributes = some fcPayloadAttributes)
debug "Fork-choice updated for proposal", status

static: doAssert high(ConsensusFork) == ConsensusFork.Deneb
static: doAssert high(ConsensusFork) == ConsensusFork.Electra
when consensusFork >= ConsensusFork.Electra:
debugRaiseAssert "runProposalForkchoiceUpdated, probably will be a new payload attributes type here"
callForkchoiceUpdated(PayloadAttributesV3(
timestamp: Quantity timestamp,
prevRandao: FixedBytes[32] randomData,
suggestedFeeRecipient: feeRecipient,
withdrawals:
toEngineWithdrawals get_expected_withdrawals(forkyState.data),
parentBeaconBlockRoot: beaconHead.blck.bid.root.asBlockHash))
when consensusFork >= ConsensusFork.Deneb:
callForkchoiceUpdated(PayloadAttributesV3(
timestamp: Quantity timestamp,
Expand Down
40 changes: 38 additions & 2 deletions beacon_chain/el/el_manager.nim
Expand Up @@ -561,6 +561,32 @@ func asEngineExecutionPayload*(executionPayload: deneb.ExecutionPayload):
blobGasUsed: Quantity(executionPayload.blob_gas_used),
excessBlobGas: Quantity(executionPayload.excess_blob_gas))

func asEngineExecutionPayload*(executionPayload: electra.ExecutionPayload):
ExecutionPayloadV3 =
debugRaiseAssert "asEngineExecutionPayload for electra.ExecutionPayload probably won't use ExecutionPayloadV3"
template getTypedTransaction(tt: bellatrix.Transaction): TypedTransaction =
TypedTransaction(tt.distinctBase)

engine_api.ExecutionPayloadV3(
parentHash: executionPayload.parent_hash.asBlockHash,
feeRecipient: Address(executionPayload.fee_recipient.data),
stateRoot: executionPayload.state_root.asBlockHash,
receiptsRoot: executionPayload.receipts_root.asBlockHash,
logsBloom:
FixedBytes[BYTES_PER_LOGS_BLOOM](executionPayload.logs_bloom.data),
prevRandao: executionPayload.prev_randao.asBlockHash,
blockNumber: Quantity(executionPayload.block_number),
gasLimit: Quantity(executionPayload.gas_limit),
gasUsed: Quantity(executionPayload.gas_used),
timestamp: Quantity(executionPayload.timestamp),
extraData: DynamicBytes[0, MAX_EXTRA_DATA_BYTES](executionPayload.extra_data),
baseFeePerGas: executionPayload.base_fee_per_gas,
blockHash: executionPayload.block_hash.asBlockHash,
transactions: mapIt(executionPayload.transactions, it.getTypedTransaction),
withdrawals: mapIt(executionPayload.withdrawals, it.asEngineWithdrawal),
blobGasUsed: Quantity(executionPayload.blob_gas_used),
excessBlobGas: Quantity(executionPayload.excess_blob_gas))

func isConnected(connection: ELConnection): bool =
connection.web3.isSome

Expand Down Expand Up @@ -752,6 +778,10 @@ template EngineApiResponseType*(T: type capella.ExecutionPayloadForSigning): typ
template EngineApiResponseType*(T: type deneb.ExecutionPayloadForSigning): type =
engine_api.GetPayloadV3Response

template EngineApiResponseType*(T: type electra.ExecutionPayloadForSigning): type =
debugRaiseAssert "EngineApiResponseType electra.ExecutionPayloadForSigning; presumably will be a GetPayloadV4Response"
engine_api.GetPayloadV3Response

template toEngineWithdrawals*(withdrawals: seq[capella.Withdrawal]): seq[WithdrawalV1] =
mapIt(withdrawals, toEngineWithdrawal(it))

Expand Down Expand Up @@ -850,9 +880,15 @@ proc getPayload*(m: ELManager,

deadline.cancelSoon()

if bestPayloadIdx.isSome:
return ok requests[bestPayloadIdx.get].value().asConsensusType
when PayloadType.kind != ConsensusFork.Electra:
if bestPayloadIdx.isSome:
return ok requests[bestPayloadIdx.get].value().asConsensusType
else:
return err()
else:
# right now, asConsensusType is confused by Deneb and Electra sharing a
# Payload type. this will probably be resolved in time naturally.
debugRaiseAssert "getPayload ForkyExecutionPayloadForSigning"
return err()

proc waitELToSyncDeposits(connection: ELConnection,
Expand Down
2 changes: 2 additions & 0 deletions beacon_chain/era_db.nim
Expand Up @@ -447,6 +447,8 @@ iterator getBlockIds*(
if not getPartialState(
db, historical_roots, historical_summaries, stateSlot, state[]):
state = nil # No `return` in iterators
of ConsensusFork.Electra:
debugRaiseAssert "getBlockIds"

if state == nil:
break
Expand Down
12 changes: 8 additions & 4 deletions beacon_chain/gossip_processing/block_processor.nim
Expand Up @@ -328,7 +328,7 @@ proc newExecutionPayload*(
proc getExecutionValidity(
elManager: ELManager,
blck: bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock |
deneb.SignedBeaconBlock):
deneb.SignedBeaconBlock | electra.SignedBeaconBlock):
Future[NewPayloadStatus] {.async: (raises: [CancelledError]).} =
if not blck.message.is_execution_block:
return NewPayloadStatus.valid # vacuously
Expand Down Expand Up @@ -361,9 +361,10 @@ proc getExecutionValidity(
blck = shortLog(blck)
return NewPayloadStatus.noResponse

proc checkBloblessSignature(self: BlockProcessor,
signed_beacon_block: deneb.SignedBeaconBlock):
Result[void, cstring] =
proc checkBloblessSignature(
self: BlockProcessor,
signed_beacon_block: deneb.SignedBeaconBlock | electra.SignedBeaconBlock):
Result[void, cstring] =
let dag = self.consensusManager.dag
let parent = dag.getBlockRef(signed_beacon_block.message.parent_root).valueOr:
return err("checkBloblessSignature called with orphan block")
Expand Down Expand Up @@ -674,6 +675,9 @@ proc storeBlock(
template callForkChoiceUpdated: auto =
case self.consensusManager.dag.cfg.consensusForkAtEpoch(
newHead.get.blck.bid.slot.epoch)
of ConsensusFork.Electra:
debugRaiseAssert "storeBlock, probably will become PayloadAttributesV3"
callExpectValidFCU(payloadAttributeType = PayloadAttributesV3)
of ConsensusFork.Deneb:
callExpectValidFCU(payloadAttributeType = PayloadAttributesV3)
of ConsensusFork.Capella:
Expand Down
3 changes: 2 additions & 1 deletion beacon_chain/gossip_processing/gossip_validation.nim
Expand Up @@ -268,7 +268,8 @@ template validateBeaconBlockBellatrix(
signed_beacon_block:
bellatrix.SignedBeaconBlock |
capella.SignedBeaconBlock |
deneb.SignedBeaconBlock,
deneb.SignedBeaconBlock |
electra.SignedBeaconBlock,
parent: BlockRef): untyped =
# If the execution is enabled for the block -- i.e.
# is_execution_enabled(state, block.body) then validate the following:
Expand Down

0 comments on commit d09bf3b

Please sign in to comment.