extend light client protocol for Electra

Add missing Electra support for light client protocol:

- https://github.com/ethereum/consensus-specs/pull/3811

Tested against PR consensus-spec-tests, the test runner automatically
picks up the new tests once available.
This commit is contained in:
Etan Kissling 2024-06-21 15:02:01 +02:00
parent 31653d5869
commit 65ae4d8eb1
No known key found for this signature in database
GPG Key ID: B21DA824C5A3D03D
11 changed files with 758 additions and 130 deletions

View File

@ -542,12 +542,22 @@ proc new*(T: type BeaconChainDB,
"lc_deneb_headers" "lc_deneb_headers"
else: else:
"", "",
electraHeaders:
if cfg.DENEB_FORK_EPOCH != FAR_FUTURE_EPOCH:
"lc_electra_headers"
else:
"",
altairCurrentBranches: "lc_altair_current_branches", altairCurrentBranches: "lc_altair_current_branches",
electraCurrentBranches:
if cfg.ELECTRA_FORK_EPOCH != FAR_FUTURE_EPOCH:
"lc_electra_current_branches"
else:
"",
altairSyncCommittees: "lc_altair_sync_committees", altairSyncCommittees: "lc_altair_sync_committees",
legacyAltairBestUpdates: "lc_altair_best_updates", legacyAltairBestUpdates: "lc_altair_best_updates",
bestUpdates: "lc_best_updates", bestUpdates: "lc_best_updates",
sealedPeriods: "lc_sealed_periods")).expectDb() sealedPeriods: "lc_sealed_periods")).expectDb()
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
var blobs : KvStoreRef var blobs : KvStoreRef
if cfg.DENEB_FORK_EPOCH != FAR_FUTURE_EPOCH: if cfg.DENEB_FORK_EPOCH != FAR_FUTURE_EPOCH:

View File

@ -28,13 +28,15 @@ logScope: topics = "lcdata"
# - Altair: ~38 KB per `SyncCommitteePeriod` (~1.0 MB per month) # - Altair: ~38 KB per `SyncCommitteePeriod` (~1.0 MB per month)
# - Capella: ~221 KB per `SyncCommitteePeriod` (~6.0 MB per month) # - Capella: ~221 KB per `SyncCommitteePeriod` (~6.0 MB per month)
# - Deneb: ~225 KB per `SyncCommitteePeriod` (~6.2 MB per month) # - Deneb: ~225 KB per `SyncCommitteePeriod` (~6.2 MB per month)
# - Electra: ~249 KB per `SyncCommitteePeriod` (~6.8 MB per month)
# #
# `lc_altair_current_branches` holds Merkle proofs needed to # `lc_xxxxx_current_branches` holds Merkle proofs needed to
# construct `LightClientBootstrap` objects. # construct `LightClientBootstrap` objects.
# SSZ because this data does not compress well, and because this data # SSZ because this data does not compress well, and because this data
# needs to be bundled together with other data to fulfill requests. # needs to be bundled together with other data to fulfill requests.
# Mainnet data size (all columns): # Mainnet data size (all columns):
# - Altair ... Deneb: ~42 KB per `SyncCommitteePeriod` (~1.1 MB per month) # - Altair ... Deneb: ~42 KB per `SyncCommitteePeriod` (~1.1 MB per month)
# - Electra: ~50 KB per `SyncCommitteePeriod` (~1.4 MB per month)
# #
# `lc_altair_sync_committees` contains a copy of finalized sync committees. # `lc_altair_sync_committees` contains a copy of finalized sync committees.
# They are initially populated from the main DAG (usually a fast state access). # They are initially populated from the main DAG (usually a fast state access).
@ -42,7 +44,7 @@ logScope: topics = "lcdata"
# SSZ because this data does not compress well, and because this data # SSZ because this data does not compress well, and because this data
# needs to be bundled together with other data to fulfill requests. # needs to be bundled together with other data to fulfill requests.
# Mainnet data size (all columns): # Mainnet data size (all columns):
# - Altair ... Deneb: ~24 KB per `SyncCommitteePeriod` (~0.7 MB per month) # - Altair ... Electra: ~24 KB per `SyncCommitteePeriod` (~0.7 MB per month)
# #
# `lc_best_updates` holds full `LightClientUpdate` objects in SSZ form. # `lc_best_updates` holds full `LightClientUpdate` objects in SSZ form.
# These objects are frequently queried in bulk, but there is only one per # These objects are frequently queried in bulk, but there is only one per
@ -59,6 +61,7 @@ logScope: topics = "lcdata"
# - Altair: ~25 KB per `SyncCommitteePeriod` (~0.7 MB per month) # - Altair: ~25 KB per `SyncCommitteePeriod` (~0.7 MB per month)
# - Capella: ~26 KB per `SyncCommitteePeriod` (~0.7 MB per month) # - Capella: ~26 KB per `SyncCommitteePeriod` (~0.7 MB per month)
# - Deneb: ~26 KB per `SyncCommitteePeriod` (~0.7 MB per month) # - Deneb: ~26 KB per `SyncCommitteePeriod` (~0.7 MB per month)
# - Electra: ~27 KB per `SyncCommitteePeriod` (~0.7 MB per month)
# #
# `lc_sealed_periods` contains the sync committee periods for which # `lc_sealed_periods` contains the sync committee periods for which
# full light client data was imported. Data for these periods may no longer # full light client data was imported. Data for these periods may no longer
@ -73,12 +76,16 @@ logScope: topics = "lcdata"
# 600 = 32+20+32+32+256+32+8+8+8+8+4+32+32+32+32+32 # 600 = 32+20+32+32+256+32+8+8+8+8+4+32+32+32+32+32
# - Deneb: 256*(112+4+616+128+40)/1024*28/1024 # - Deneb: 256*(112+4+616+128+40)/1024*28/1024
# 616 = 32+20+32+32+256+32+8+8+8+8+4+32+32+32+32+32+8+8 # 616 = 32+20+32+32+256+32+8+8+8+8+4+32+32+32+32+32+8+8
# - Electra: 256*(112+4+712+128+40)/1024*28/1024
# 712 = 32+20+32+32+256+32+8+8+8+8+4+32+32+32+32+32+8+8+32+32+32
# #
# Committee branch computations: # Committee branch computations:
# - Altair: 256*(5*32+8)/1024*28/1024 # - Altair: 256*(5*32+8)/1024*28/1024
# - Electra: 256*(6*32+8)/1024*28/1024
# #
# Finality branch computations: # Finality branch computations:
# - Altair: 256*(6*32+8)/1024*28/1024 # - Altair: 256*(6*32+8)/1024*28/1024
# - Electra: 256*(7*32+8)/1024*28/1024
# #
# Committee computations: # Committee computations:
# - Altair: (24624+8)/1024*28/1024 # - Altair: (24624+8)/1024*28/1024
@ -91,6 +98,7 @@ logScope: topics = "lcdata"
# - Altair: (112+24624+5*32+112+6*32+112+8+9)/1024*28/1024 # - Altair: (112+24624+5*32+112+6*32+112+8+9)/1024*28/1024
# - Capella: (4+884+24624+5*32+4+884+6*32+112+8+9)/1024*28/1024 # - Capella: (4+884+24624+5*32+4+884+6*32+112+8+9)/1024*28/1024
# - Deneb: (4+900+24624+5*32+4+900+6*32+112+8+9)/1024*28/1024 # - Deneb: (4+900+24624+5*32+4+900+6*32+112+8+9)/1024*28/1024
# - Electra: (4+996+24624+6*32+4+996+7*32+112+8+9)/1024*28/1024
type type
LightClientHeaderStore = object LightClientHeaderStore = object
@ -98,6 +106,11 @@ type
putStmt: SqliteStmt[(array[32, byte], int64, seq[byte]), void] putStmt: SqliteStmt[(array[32, byte], int64, seq[byte]), void]
keepFromStmt: SqliteStmt[int64, void] keepFromStmt: SqliteStmt[int64, void]
BranchFork {.pure.} = enum
None = 0,
Altair,
Electra
CurrentSyncCommitteeBranchStore = object CurrentSyncCommitteeBranchStore = object
containsStmt: SqliteStmt[int64, int64] containsStmt: SqliteStmt[int64, int64]
getStmt: SqliteStmt[int64, seq[byte]] getStmt: SqliteStmt[int64, seq[byte]]
@ -135,8 +148,8 @@ type
## Eth2Digest -> (Slot, LightClientHeader) ## Eth2Digest -> (Slot, LightClientHeader)
## Cached block headers to support longer retention than block storage. ## Cached block headers to support longer retention than block storage.
currentBranches: CurrentSyncCommitteeBranchStore currentBranches: array[BranchFork, CurrentSyncCommitteeBranchStore]
## Slot -> altair.CurrentSyncCommitteeBranch ## Slot -> CurrentSyncCommitteeBranch
## Cached data for creating future `LightClientBootstrap` instances. ## Cached data for creating future `LightClientBootstrap` instances.
## Key is the block slot of which the post state was used to get the data. ## Key is the block slot of which the post state was used to get the data.
## Data stored for all finalized epoch boundary blocks. ## Data stored for all finalized epoch boundary blocks.
@ -234,12 +247,14 @@ func putHeader*[T: ForkyLightClientHeader](
proc initCurrentBranchesStore( proc initCurrentBranchesStore(
backend: SqStoreRef, backend: SqStoreRef,
name: string): KvResult[CurrentSyncCommitteeBranchStore] = name, typeName: string): KvResult[CurrentSyncCommitteeBranchStore] =
if name == "":
return ok CurrentSyncCommitteeBranchStore()
if not backend.readOnly: if not backend.readOnly:
? backend.exec(""" ? backend.exec("""
CREATE TABLE IF NOT EXISTS `""" & name & """` ( CREATE TABLE IF NOT EXISTS `""" & name & """` (
`slot` INTEGER PRIMARY KEY, -- `Slot` (up through 2^63-1) `slot` INTEGER PRIMARY KEY, -- `Slot` (up through 2^63-1)
`branch` BLOB -- `altair.CurrentSyncCommitteeBranch` (SSZ) `branch` BLOB -- `""" & typeName & """` (SSZ)
); );
""") """)
if not ? backend.hasTable(name): if not ? backend.hasTable(name):
@ -278,40 +293,46 @@ func close(store: var CurrentSyncCommitteeBranchStore) =
store.putStmt.disposeSafe() store.putStmt.disposeSafe()
store.keepFromStmt.disposeSafe() store.keepFromStmt.disposeSafe()
func hasCurrentSyncCommitteeBranch*( template kind(x: typedesc[altair.CurrentSyncCommitteeBranch]): BranchFork =
BranchFork.Altair
template kind(x: typedesc[electra.CurrentSyncCommitteeBranch]): BranchFork =
BranchFork.Electra
func hasCurrentSyncCommitteeBranch*[T: ForkyCurrentSyncCommitteeBranch](
db: LightClientDataDB, slot: Slot): bool = db: LightClientDataDB, slot: Slot): bool =
if not slot.isSupportedBySQLite or if not slot.isSupportedBySQLite or
distinctBase(db.currentBranches.containsStmt) == nil: distinctBase(db.currentBranches[T.kind].containsStmt) == nil:
return false return false
var exists: int64 var exists: int64
for res in db.currentBranches.containsStmt.exec(slot.int64, exists): for res in db.currentBranches[T.kind].containsStmt.exec(slot.int64, exists):
res.expect("SQL query OK") res.expect("SQL query OK")
doAssert exists == 1 doAssert exists == 1
return true return true
false false
proc getCurrentSyncCommitteeBranch*( proc getCurrentSyncCommitteeBranch*[T: ForkyCurrentSyncCommitteeBranch](
db: LightClientDataDB, slot: Slot): Opt[altair.CurrentSyncCommitteeBranch] = db: LightClientDataDB, slot: Slot): Opt[T] =
if not slot.isSupportedBySQLite or if not slot.isSupportedBySQLite or
distinctBase(db.currentBranches.getStmt) == nil: distinctBase(db.currentBranches[T.kind].getStmt) == nil:
return Opt.none(altair.CurrentSyncCommitteeBranch) return Opt.none(T)
var branch: seq[byte] var branch: seq[byte]
for res in db.currentBranches.getStmt.exec(slot.int64, branch): for res in db.currentBranches[T.kind].getStmt.exec(slot.int64, branch):
res.expect("SQL query OK") res.expect("SQL query OK")
try: try:
return ok SSZ.decode(branch, altair.CurrentSyncCommitteeBranch) return ok SSZ.decode(branch, T)
except SerializationError as exc: except SerializationError as exc:
error "LC data store corrupted", store = "currentBranches", error "LC data store corrupted", store = "currentBranches", kind = T.kind,
slot, exc = exc.msg slot, exc = exc.msg
return Opt.none(altair.CurrentSyncCommitteeBranch) return Opt.none(T)
func putCurrentSyncCommitteeBranch*( func putCurrentSyncCommitteeBranch*[T: ForkyCurrentSyncCommitteeBranch](
db: LightClientDataDB, slot: Slot, db: LightClientDataDB, slot: Slot, branch: T) =
branch: altair.CurrentSyncCommitteeBranch) =
doAssert not db.backend.readOnly # All `stmt` are non-nil doAssert not db.backend.readOnly # All `stmt` are non-nil
if not slot.isSupportedBySQLite: if not slot.isSupportedBySQLite:
return return
let res = db.currentBranches.putStmt.exec((slot.int64, SSZ.encode(branch))) let res = db.currentBranches[T.kind].putStmt.exec(
(slot.int64, SSZ.encode(branch)))
res.expect("SQL query OK") res.expect("SQL query OK")
proc initSyncCommitteesStore( proc initSyncCommitteesStore(
@ -643,9 +664,11 @@ func keepPeriodsFrom*(
let res = db.syncCommittees.keepFromStmt.exec(minPeriod.int64) let res = db.syncCommittees.keepFromStmt.exec(minPeriod.int64)
res.expect("SQL query OK") res.expect("SQL query OK")
let minSlot = min(minPeriod.start_slot, int64.high.Slot) let minSlot = min(minPeriod.start_slot, int64.high.Slot)
block: for branchFork, store in db.currentBranches:
let res = db.currentBranches.keepFromStmt.exec(minSlot.int64) if branchFork > BranchFork.None and
res.expect("SQL query OK") distinctBase(store.keepFromStmt) != nil:
let res = store.keepFromStmt.exec(minSlot.int64)
res.expect("SQL query OK")
for lcDataFork, store in db.headers: for lcDataFork, store in db.headers:
if lcDataFork > LightClientDataFork.None and if lcDataFork > LightClientDataFork.None and
distinctBase(store.keepFromStmt) != nil: distinctBase(store.keepFromStmt) != nil:
@ -656,7 +679,9 @@ type LightClientDataDBNames* = object
altairHeaders*: string altairHeaders*: string
capellaHeaders*: string capellaHeaders*: string
denebHeaders*: string denebHeaders*: string
electraHeaders*: string
altairCurrentBranches*: string altairCurrentBranches*: string
electraCurrentBranches*: string
altairSyncCommittees*: string altairSyncCommittees*: string
legacyAltairBestUpdates*: string legacyAltairBestUpdates*: string
bestUpdates*: string bestUpdates*: string
@ -665,7 +690,7 @@ type LightClientDataDBNames* = object
proc initLightClientDataDB*( proc initLightClientDataDB*(
backend: SqStoreRef, backend: SqStoreRef,
names: LightClientDataDBNames): KvResult[LightClientDataDB] = names: LightClientDataDBNames): KvResult[LightClientDataDB] =
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
let let
headers = [ headers = [
# LightClientDataFork.None # LightClientDataFork.None
@ -678,10 +703,21 @@ proc initLightClientDataDB*(
names.capellaHeaders, "capella.LightClientHeader"), names.capellaHeaders, "capella.LightClientHeader"),
# LightClientDataFork.Deneb # LightClientDataFork.Deneb
? backend.initHeadersStore( ? backend.initHeadersStore(
names.denebHeaders, "deneb.LightClientHeader") names.denebHeaders, "deneb.LightClientHeader"),
# LightClientDataFork.Electra
? backend.initHeadersStore(
names.electraHeaders, "electra.LightClientHeader"),
]
currentBranches = [
# BranchFork.None
CurrentSyncCommitteeBranchStore(),
# BranchFork.Altair
? backend.initCurrentBranchesStore(
names.altairCurrentBranches, "altair.CurrentSyncCommitteeBranch"),
# BranchFork.Electra
? backend.initCurrentBranchesStore(
names.electraCurrentBranches, "electra.CurrentSyncCommitteeBranch"),
] ]
currentBranches =
? backend.initCurrentBranchesStore(names.altairCurrentBranches)
syncCommittees = syncCommittees =
? backend.initSyncCommitteesStore(names.altairSyncCommittees) ? backend.initSyncCommitteesStore(names.altairSyncCommittees)
legacyBestUpdates = legacyBestUpdates =
@ -706,7 +742,9 @@ proc close*(db: LightClientDataDB) =
for lcDataFork in LightClientDataFork: for lcDataFork in LightClientDataFork:
if lcDataFork > LightClientDataFork.None: if lcDataFork > LightClientDataFork.None:
db.headers[lcDataFork].close() db.headers[lcDataFork].close()
db.currentBranches.close() for branchFork in BranchFork:
if branchFork > BranchFork.None:
db.currentBranches[branchFork].close()
db.syncCommittees.close() db.syncCommittees.close()
db.legacyBestUpdates.close() db.legacyBestUpdates.close()
db.bestUpdates.close() db.bestUpdates.close()

View File

@ -33,11 +33,13 @@ type
CachedLightClientData* = object CachedLightClientData* = object
## Cached data from historical non-finalized states to improve speed when ## Cached data from historical non-finalized states to improve speed when
## creating future `LightClientUpdate` and `LightClientBootstrap` instances. ## creating future `LightClientUpdate` and `LightClientBootstrap` instances.
current_sync_committee_branch*: altair.CurrentSyncCommitteeBranch current_sync_committee_branch*:
next_sync_committee_branch*: altair.NextSyncCommitteeBranch LightClientDataFork.high.CurrentSyncCommitteeBranch
next_sync_committee_branch*:
LightClientDataFork.high.NextSyncCommitteeBranch
finalized_slot*: Slot finalized_slot*: Slot
finality_branch*: altair.FinalityBranch finality_branch*: LightClientDataFork.high.FinalityBranch
current_period_best_update*: ref ForkedLightClientUpdate current_period_best_update*: ref ForkedLightClientUpdate
latest_signature_slot*: Slot latest_signature_slot*: Slot

View File

@ -22,6 +22,15 @@ template nextEpochBoundarySlot(slot: Slot): Slot =
## referring to a block at given slot. ## referring to a block at given slot.
(slot + (SLOTS_PER_EPOCH - 1)).epoch.start_slot (slot + (SLOTS_PER_EPOCH - 1)).epoch.start_slot
func hasCurrentSyncCommitteeBranch(dag: ChainDAGRef, slot: Slot): bool =
let epoch = dag.cfg.consensusForkAtEpoch(slot.epoch)
withLcDataFork(lcDataForkAtConsensusFork(epoch)):
when lcDataFork > LightClientDataFork.None:
hasCurrentSyncCommitteeBranch[lcDataFork.CurrentSyncCommitteeBranch](
dag.lcDataStore.db, slot)
else:
true
proc updateExistingState( proc updateExistingState(
dag: ChainDAGRef, state: var ForkedHashedBeaconState, bsi: BlockSlotId, dag: ChainDAGRef, state: var ForkedHashedBeaconState, bsi: BlockSlotId,
save: bool, cache: var StateCache): bool = save: bool, cache: var StateCache): bool =
@ -226,7 +235,7 @@ proc initLightClientBootstrapForPeriod(
bid = bsi.bid bid = bsi.bid
boundarySlot = bid.slot.nextEpochBoundarySlot boundarySlot = bid.slot.nextEpochBoundarySlot
if boundarySlot == nextBoundarySlot and bid.slot >= lowSlot and if boundarySlot == nextBoundarySlot and bid.slot >= lowSlot and
not dag.lcDataStore.db.hasCurrentSyncCommitteeBranch(bid.slot): not dag.hasCurrentSyncCommitteeBranch(bid.slot):
let bdata = dag.getExistingForkedBlock(bid).valueOr: let bdata = dag.getExistingForkedBlock(bid).valueOr:
dag.handleUnexpectedLightClientError(bid.slot) dag.handleUnexpectedLightClientError(bid.slot)
res.err() res.err()
@ -246,7 +255,7 @@ proc initLightClientBootstrapForPeriod(
forkyBlck.toLightClientHeader(lcDataFork)) forkyBlck.toLightClientHeader(lcDataFork))
dag.lcDataStore.db.putCurrentSyncCommitteeBranch( dag.lcDataStore.db.putCurrentSyncCommitteeBranch(
bid.slot, forkyState.data.build_proof( bid.slot, forkyState.data.build_proof(
altair.CURRENT_SYNC_COMMITTEE_GINDEX).get) lcDataFork.CURRENT_SYNC_COMMITTEE_GINDEX).get)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
res res
@ -393,13 +402,13 @@ proc initLightClientUpdateForPeriod(
update = ForkedLightClientUpdate.init(lcDataFork.LightClientUpdate( update = ForkedLightClientUpdate.init(lcDataFork.LightClientUpdate(
attested_header: forkyBlck.toLightClientHeader(lcDataFork), attested_header: forkyBlck.toLightClientHeader(lcDataFork),
next_sync_committee: forkyState.data.next_sync_committee, next_sync_committee: forkyState.data.next_sync_committee,
next_sync_committee_branch: next_sync_committee_branch: forkyState.data.build_proof(
forkyState.data.build_proof(altair.NEXT_SYNC_COMMITTEE_GINDEX).get, lcDataFork.NEXT_SYNC_COMMITTEE_GINDEX).get,
finality_branch: finality_branch:
if finalizedBid.slot != FAR_FUTURE_SLOT: if finalizedBid.slot != FAR_FUTURE_SLOT:
forkyState.data.build_proof(altair.FINALIZED_ROOT_GINDEX).get forkyState.data.build_proof(lcDataFork.FINALIZED_ROOT_GINDEX).get
else: else:
default(FinalityBranch))) default(lcDataFork.FinalityBranch)))
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: do:
dag.handleUnexpectedLightClientError(attestedBid.slot) dag.handleUnexpectedLightClientError(attestedBid.slot)
@ -464,17 +473,21 @@ proc cacheLightClientData(
## Cache data for a given block and its post-state to speed up creating future ## Cache data for a given block and its post-state to speed up creating future
## `LightClientUpdate` and `LightClientBootstrap` instances that refer to this ## `LightClientUpdate` and `LightClientBootstrap` instances that refer to this
## block and state. ## block and state.
const lcDataFork = lcDataForkAtConsensusFork(typeof(state).kind)
let let
bid = blck.toBlockId() bid = blck.toBlockId()
cachedData = CachedLightClientData( cachedData = CachedLightClientData(
current_sync_committee_branch: current_sync_committee_branch: normalize_merkle_branch(
state.data.build_proof(altair.CURRENT_SYNC_COMMITTEE_GINDEX).get, state.data.build_proof(lcDataFork.CURRENT_SYNC_COMMITTEE_GINDEX).get,
next_sync_committee_branch: LightClientDataFork.high.CURRENT_SYNC_COMMITTEE_GINDEX),
state.data.build_proof(altair.NEXT_SYNC_COMMITTEE_GINDEX).get, next_sync_committee_branch: normalize_merkle_branch(
state.data.build_proof(lcDataFork.NEXT_SYNC_COMMITTEE_GINDEX).get,
LightClientDataFork.high.NEXT_SYNC_COMMITTEE_GINDEX),
finalized_slot: finalized_slot:
state.data.finalized_checkpoint.epoch.start_slot, state.data.finalized_checkpoint.epoch.start_slot,
finality_branch: finality_branch: normalize_merkle_branch(
state.data.build_proof(altair.FINALIZED_ROOT_GINDEX).get, state.data.build_proof(lcDataFork.FINALIZED_ROOT_GINDEX).get,
LightClientDataFork.high.FINALIZED_ROOT_GINDEX),
current_period_best_update: current_period_best_update:
current_period_best_update, current_period_best_update,
latest_signature_slot: latest_signature_slot:
@ -538,15 +551,18 @@ proc assignLightClientData(
when lcDataFork > LightClientDataFork.None: when lcDataFork > LightClientDataFork.None:
forkyObject.next_sync_committee = forkyObject.next_sync_committee =
next_sync_committee.get next_sync_committee.get
forkyObject.next_sync_committee_branch = forkyObject.next_sync_committee_branch = normalize_merkle_branch(
attested_data.next_sync_committee_branch attested_data.next_sync_committee_branch,
lcDataFork.NEXT_SYNC_COMMITTEE_GINDEX)
else: else:
doAssert next_sync_committee.isNone doAssert next_sync_committee.isNone
var finalized_slot = attested_data.finalized_slot var finalized_slot = attested_data.finalized_slot
withForkyObject(obj): withForkyObject(obj):
when lcDataFork > LightClientDataFork.None: when lcDataFork > LightClientDataFork.None:
if finalized_slot == forkyObject.finalized_header.beacon.slot: if finalized_slot == forkyObject.finalized_header.beacon.slot:
forkyObject.finality_branch = attested_data.finality_branch forkyObject.finality_branch = normalize_merkle_branch(
attested_data.finality_branch,
lcDataFork.FINALIZED_ROOT_GINDEX)
elif finalized_slot < max(dag.tail.slot, dag.backfill.slot): elif finalized_slot < max(dag.tail.slot, dag.backfill.slot):
forkyObject.finalized_header.reset() forkyObject.finalized_header.reset()
forkyObject.finality_branch.reset() forkyObject.finality_branch.reset()
@ -564,10 +580,14 @@ proc assignLightClientData(
attested_data.finalized_slot = finalized_slot attested_data.finalized_slot = finalized_slot
dag.lcDataStore.cache.data[attested_bid] = attested_data dag.lcDataStore.cache.data[attested_bid] = attested_data
if finalized_slot == forkyObject.finalized_header.beacon.slot: if finalized_slot == forkyObject.finalized_header.beacon.slot:
forkyObject.finality_branch = attested_data.finality_branch forkyObject.finality_branch = normalize_merkle_branch(
attested_data.finality_branch,
lcDataFork.FINALIZED_ROOT_GINDEX)
elif finalized_slot == GENESIS_SLOT: elif finalized_slot == GENESIS_SLOT:
forkyObject.finalized_header.reset() forkyObject.finalized_header.reset()
forkyObject.finality_branch = attested_data.finality_branch forkyObject.finality_branch = normalize_merkle_branch(
attested_data.finality_branch,
lcDataFork.FINALIZED_ROOT_GINDEX)
else: else:
var fin_header = dag.getExistingLightClientHeader(finalized_bid) var fin_header = dag.getExistingLightClientHeader(finalized_bid)
if fin_header.kind == LightClientDataFork.None: if fin_header.kind == LightClientDataFork.None:
@ -577,7 +597,9 @@ proc assignLightClientData(
else: else:
fin_header.migrateToDataFork(lcDataFork) fin_header.migrateToDataFork(lcDataFork)
forkyObject.finalized_header = fin_header.forky(lcDataFork) forkyObject.finalized_header = fin_header.forky(lcDataFork)
forkyObject.finality_branch = attested_data.finality_branch forkyObject.finality_branch = normalize_merkle_branch(
attested_data.finality_branch,
lcDataFork.FINALIZED_ROOT_GINDEX)
withForkyObject(obj): withForkyObject(obj):
when lcDataFork > LightClientDataFork.None: when lcDataFork > LightClientDataFork.None:
forkyObject.sync_aggregate = sync_aggregate forkyObject.sync_aggregate = sync_aggregate
@ -1014,7 +1036,7 @@ proc getLightClientBootstrap(
# Ensure `current_sync_committee_branch` is known # Ensure `current_sync_committee_branch` is known
if dag.lcDataStore.importMode == LightClientDataImportMode.OnDemand and if dag.lcDataStore.importMode == LightClientDataImportMode.OnDemand and
not dag.lcDataStore.db.hasCurrentSyncCommitteeBranch(slot): not dag.hasCurrentSyncCommitteeBranch(slot):
let let
bsi = dag.getExistingBlockIdAtSlot(slot).valueOr: bsi = dag.getExistingBlockIdAtSlot(slot).valueOr:
return default(ForkedLightClientBootstrap) return default(ForkedLightClientBootstrap)
@ -1022,13 +1044,14 @@ proc getLightClientBootstrap(
dag.withUpdatedExistingState(tmpState[], bsi) do: dag.withUpdatedExistingState(tmpState[], bsi) do:
withState(updatedState): withState(updatedState):
when consensusFork >= ConsensusFork.Altair: when consensusFork >= ConsensusFork.Altair:
const lcDataFork = lcDataForkAtConsensusFork(consensusFork)
if not dag.lcDataStore.db.hasSyncCommittee(period): if not dag.lcDataStore.db.hasSyncCommittee(period):
dag.lcDataStore.db.putSyncCommittee( dag.lcDataStore.db.putSyncCommittee(
period, forkyState.data.current_sync_committee) period, forkyState.data.current_sync_committee)
dag.lcDataStore.db.putHeader(header) dag.lcDataStore.db.putHeader(header)
dag.lcDataStore.db.putCurrentSyncCommitteeBranch( dag.lcDataStore.db.putCurrentSyncCommitteeBranch(
slot, forkyState.data.build_proof( slot, forkyState.data.build_proof(
altair.CURRENT_SYNC_COMMITTEE_GINDEX).get) lcDataFork.CURRENT_SYNC_COMMITTEE_GINDEX).get)
else: raiseAssert "Unreachable" else: raiseAssert "Unreachable"
do: return default(ForkedLightClientBootstrap) do: return default(ForkedLightClientBootstrap)
@ -1050,7 +1073,8 @@ proc getLightClientBootstrap(
debug "LC bootstrap unavailable: Sync committee not cached", period debug "LC bootstrap unavailable: Sync committee not cached", period
return default(ForkedLightClientBootstrap)), return default(ForkedLightClientBootstrap)),
current_sync_committee_branch: (block: current_sync_committee_branch: (block:
dag.lcDataStore.db.getCurrentSyncCommitteeBranch(slot).valueOr: getCurrentSyncCommitteeBranch[lcDataFork.CurrentSyncCommitteeBranch](
dag.lcDataStore.db, slot).valueOr:
debug "LC bootstrap unavailable: Committee branch not cached", slot debug "LC bootstrap unavailable: Committee branch not cached", slot
return default(ForkedLightClientBootstrap)))) return default(ForkedLightClientBootstrap))))

View File

@ -626,14 +626,16 @@ func kzg_commitment_inclusion_proof_gindex*(
BLOB_KZG_COMMITMENTS_FIRST_GINDEX + index BLOB_KZG_COMMITMENTS_FIRST_GINDEX + index
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/deneb/light-client/sync-protocol.md#modified-get_lc_execution_root # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/deneb/light-client/sync-protocol.md#modified-get_lc_execution_root
func get_lc_execution_root*( func get_lc_execution_root*(
header: LightClientHeader, cfg: RuntimeConfig): Eth2Digest = header: LightClientHeader, cfg: RuntimeConfig): Eth2Digest =
let epoch = header.beacon.slot.epoch let epoch = header.beacon.slot.epoch
# [New in Deneb]
if epoch >= cfg.DENEB_FORK_EPOCH: if epoch >= cfg.DENEB_FORK_EPOCH:
return hash_tree_root(header.execution) return hash_tree_root(header.execution)
# [Modified in Deneb]
if epoch >= cfg.CAPELLA_FORK_EPOCH: if epoch >= cfg.CAPELLA_FORK_EPOCH:
let execution_header = capella.ExecutionPayloadHeader( let execution_header = capella.ExecutionPayloadHeader(
parent_hash: header.execution.parent_hash, parent_hash: header.execution.parent_hash,
@ -655,11 +657,12 @@ func get_lc_execution_root*(
ZERO_HASH ZERO_HASH
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/deneb/light-client/sync-protocol.md#modified-is_valid_light_client_header # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/deneb/light-client/sync-protocol.md#modified-is_valid_light_client_header
func is_valid_light_client_header*( func is_valid_light_client_header*(
header: LightClientHeader, cfg: RuntimeConfig): bool = header: LightClientHeader, cfg: RuntimeConfig): bool =
let epoch = header.beacon.slot.epoch let epoch = header.beacon.slot.epoch
# [New in Deneb:EIP4844]
if epoch < cfg.DENEB_FORK_EPOCH: if epoch < cfg.DENEB_FORK_EPOCH:
if header.execution.blob_gas_used != 0 or if header.execution.blob_gas_used != 0 or
header.execution.excess_blob_gas != 0: header.execution.excess_blob_gas != 0:

View File

@ -29,24 +29,25 @@ from stew/bitops2 import log2trunc
from stew/byteutils import to0xHex from stew/byteutils import to0xHex
from ./altair import from ./altair import
EpochParticipationFlags, InactivityScores, SyncAggregate, SyncCommittee, EpochParticipationFlags, InactivityScores, SyncAggregate, SyncCommittee,
TrustedSyncAggregate TrustedSyncAggregate, num_active_participants
from ./bellatrix import BloomLogs, ExecutionAddress, Transaction from ./bellatrix import BloomLogs, ExecutionAddress, Transaction
from ./capella import from ./capella import
HistoricalSummary, SignedBLSToExecutionChangeList, Withdrawal ExecutionBranch, HistoricalSummary, SignedBLSToExecutionChangeList,
Withdrawal, EXECUTION_PAYLOAD_GINDEX
from ./deneb import Blobs, BlobsBundle, KzgCommitments, KzgProofs from ./deneb import Blobs, BlobsBundle, KzgCommitments, KzgProofs
export json_serialization, base, kzg4844 export json_serialization, base, kzg4844
const const
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/altair/light-client/sync-protocol.md#constants # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/sync-protocol.md#constants
# All of these indices are rooted in `BeaconState`. # All of these indices are rooted in `BeaconState`.
# The first member (`genesis_time`) is 64, subsequent members +1 each. # The first member (`genesis_time`) is 64, subsequent members +1 each.
# If there are ever more than 64 members in `BeaconState`, indices change! # If there are ever more than 64 members in `BeaconState`, indices change!
# `FINALIZED_ROOT_GINDEX` is one layer deeper, i.e., `84 * 2 + 1`. # `FINALIZED_ROOT_GINDEX` is one layer deeper, i.e., `84 * 2 + 1`.
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/ssz/merkle-proofs.md # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/ssz/merkle-proofs.md
FINALIZED_ROOT_GINDEX = 169.GeneralizedIndex # finalized_checkpoint > root FINALIZED_ROOT_GINDEX* = 169.GeneralizedIndex # finalized_checkpoint > root
CURRENT_SYNC_COMMITTEE_GINDEX = 86.GeneralizedIndex # current_sync_committee CURRENT_SYNC_COMMITTEE_GINDEX* = 86.GeneralizedIndex # current_sync_committee
NEXT_SYNC_COMMITTEE_GINDEX = 87.GeneralizedIndex # next_sync_committee NEXT_SYNC_COMMITTEE_GINDEX* = 87.GeneralizedIndex # next_sync_committee
type type
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#depositrequest # https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/beacon-chain.md#depositrequest
@ -182,15 +183,6 @@ type
source_pubkey*: ValidatorPubKey source_pubkey*: ValidatorPubKey
target_pubkey*: ValidatorPubKey target_pubkey*: ValidatorPubKey
FinalityBranch =
array[log2trunc(FINALIZED_ROOT_GINDEX), Eth2Digest]
CurrentSyncCommitteeBranch =
array[log2trunc(CURRENT_SYNC_COMMITTEE_GINDEX), Eth2Digest]
NextSyncCommitteeBranch =
array[log2trunc(NEXT_SYNC_COMMITTEE_GINDEX), Eth2Digest]
# https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/validator.md#aggregateandproof # https://github.com/ethereum/consensus-specs/blob/v1.4.0/specs/phase0/validator.md#aggregateandproof
AggregateAndProof* = object AggregateAndProof* = object
aggregator_index*: uint64 # `ValidatorIndex` after validation aggregator_index*: uint64 # `ValidatorIndex` after validation
@ -202,6 +194,15 @@ type
message*: AggregateAndProof message*: AggregateAndProof
signature*: ValidatorSig signature*: ValidatorSig
FinalityBranch* =
array[log2trunc(FINALIZED_ROOT_GINDEX), Eth2Digest]
CurrentSyncCommitteeBranch* =
array[log2trunc(CURRENT_SYNC_COMMITTEE_GINDEX), Eth2Digest]
NextSyncCommitteeBranch* =
array[log2trunc(NEXT_SYNC_COMMITTEE_GINDEX), Eth2Digest]
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/light-client/sync-protocol.md#modified-lightclientheader # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/capella/light-client/sync-protocol.md#modified-lightclientheader
LightClientHeader* = object LightClientHeader* = object
beacon*: BeaconBlockHeader beacon*: BeaconBlockHeader
@ -675,6 +676,233 @@ func shortLog*(v: ExecutionPayload): auto =
excess_blob_gas: $(v.excess_blob_gas) excess_blob_gas: $(v.excess_blob_gas)
) )
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/sync-protocol.md#modified-get_lc_execution_root
func get_lc_execution_root*(
header: LightClientHeader, cfg: RuntimeConfig): Eth2Digest =
let epoch = header.beacon.slot.epoch
# [New in Electra]
if epoch >= cfg.ELECTRA_FORK_EPOCH:
return hash_tree_root(header.execution)
# [Modified in Electra]
if epoch >= cfg.DENEB_FORK_EPOCH:
let execution_header = deneb.ExecutionPayloadHeader(
parent_hash: header.execution.parent_hash,
fee_recipient: header.execution.fee_recipient,
state_root: header.execution.state_root,
receipts_root: header.execution.receipts_root,
logs_bloom: header.execution.logs_bloom,
prev_randao: header.execution.prev_randao,
block_number: header.execution.block_number,
gas_limit: header.execution.gas_limit,
gas_used: header.execution.gas_used,
timestamp: header.execution.timestamp,
extra_data: header.execution.extra_data,
base_fee_per_gas: header.execution.base_fee_per_gas,
block_hash: header.execution.block_hash,
transactions_root: header.execution.transactions_root,
withdrawals_root: header.execution.withdrawals_root,
blob_gas_used: header.execution.blob_gas_used,
excess_blob_gas: header.execution.excess_blob_gas)
return hash_tree_root(execution_header)
if epoch >= cfg.CAPELLA_FORK_EPOCH:
let execution_header = capella.ExecutionPayloadHeader(
parent_hash: header.execution.parent_hash,
fee_recipient: header.execution.fee_recipient,
state_root: header.execution.state_root,
receipts_root: header.execution.receipts_root,
logs_bloom: header.execution.logs_bloom,
prev_randao: header.execution.prev_randao,
block_number: header.execution.block_number,
gas_limit: header.execution.gas_limit,
gas_used: header.execution.gas_used,
timestamp: header.execution.timestamp,
extra_data: header.execution.extra_data,
base_fee_per_gas: header.execution.base_fee_per_gas,
block_hash: header.execution.block_hash,
transactions_root: header.execution.transactions_root,
withdrawals_root: header.execution.withdrawals_root)
return hash_tree_root(execution_header)
ZERO_HASH
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/sync-protocol.md#modified-is_valid_light_client_header
func is_valid_light_client_header*(
header: LightClientHeader, cfg: RuntimeConfig): bool =
let epoch = header.beacon.slot.epoch
# [New in Electra:EIP6110:EIP7002:EIP7251]
if epoch < cfg.ELECTRA_FORK_EPOCH:
if not header.execution.deposit_requests_root.isZero or
not header.execution.withdrawal_requests_root.isZero or
not header.execution.consolidation_requests_root.isZero:
return false
if epoch < cfg.DENEB_FORK_EPOCH:
if header.execution.blob_gas_used != 0 or
header.execution.excess_blob_gas != 0:
return false
if epoch < cfg.CAPELLA_FORK_EPOCH:
return
header.execution == default(ExecutionPayloadHeader) and
header.execution_branch == default(ExecutionBranch)
is_valid_merkle_branch(
get_lc_execution_root(header, cfg),
header.execution_branch,
log2trunc(EXECUTION_PAYLOAD_GINDEX),
get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
header.beacon.body_root)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#normalize_merkle_branch
func normalize_merkle_branch*[N](
branch: array[N, Eth2Digest],
gindex: static GeneralizedIndex): array[log2trunc(gindex), Eth2Digest] =
const depth = log2trunc(gindex)
var res: array[depth, Eth2Digest]
when depth >= branch.len:
const num_extra = depth - branch.len
res[num_extra ..< depth] = branch
else:
const num_extra = branch.len - depth
for node in branch[0 ..< num_extra]:
doAssert node.isZero, "Truncation of Merkle branch cannot lose info"
res[0 ..< depth] = branch[num_extra ..< branch.len]
res
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-light-client-data
func upgrade_lc_header_to_electra*(
pre: deneb.LightClientHeader): LightClientHeader =
LightClientHeader(
beacon: pre.beacon,
execution: ExecutionPayloadHeader(
parent_hash: pre.execution.parent_hash,
fee_recipient: pre.execution.fee_recipient,
state_root: pre.execution.state_root,
receipts_root: pre.execution.receipts_root,
logs_bloom: pre.execution.logs_bloom,
prev_randao: pre.execution.prev_randao,
block_number: pre.execution.block_number,
gas_limit: pre.execution.gas_limit,
gas_used: pre.execution.gas_used,
timestamp: pre.execution.timestamp,
extra_data: pre.execution.extra_data,
base_fee_per_gas: pre.execution.base_fee_per_gas,
block_hash: pre.execution.block_hash,
transactions_root: pre.execution.transactions_root,
withdrawals_root: pre.execution.withdrawals_root,
blob_gas_used: pre.execution.blob_gas_used,
excess_blob_gas: pre.execution.blob_gas_used,
deposit_requests_root: ZERO_HASH, # [New in Electra:EIP6110]
withdrawal_requests_root: ZERO_HASH, # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root: ZERO_HASH), # [New in Electra:EIP7251]
execution_branch: pre.execution_branch)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-light-client-data
func upgrade_lc_bootstrap_to_electra*(
pre: deneb.LightClientBootstrap): LightClientBootstrap =
LightClientBootstrap(
header: upgrade_lc_header_to_electra(pre.header),
current_sync_committee: pre.current_sync_committee,
current_sync_committee_branch: normalize_merkle_branch(
pre.current_sync_committee_branch, CURRENT_SYNC_COMMITTEE_GINDEX))
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-light-client-data
func upgrade_lc_update_to_electra*(
pre: deneb.LightClientUpdate): LightClientUpdate =
LightClientUpdate(
attested_header: upgrade_lc_header_to_electra(pre.attested_header),
next_sync_committee: pre.next_sync_committee,
next_sync_committee_branch: normalize_merkle_branch(
pre.next_sync_committee_branch, NEXT_SYNC_COMMITTEE_GINDEX),
finalized_header: upgrade_lc_header_to_electra(pre.finalized_header),
finality_branch: normalize_merkle_branch(
pre.finality_branch, FINALIZED_ROOT_GINDEX),
sync_aggregate: pre.sync_aggregate,
signature_slot: pre.signature_slot)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-light-client-data
func upgrade_lc_finality_update_to_electra*(
pre: deneb.LightClientFinalityUpdate): LightClientFinalityUpdate =
LightClientFinalityUpdate(
attested_header: upgrade_lc_header_to_electra(pre.attested_header),
finalized_header: upgrade_lc_header_to_electra(pre.finalized_header),
finality_branch: normalize_merkle_branch(
pre.finality_branch, FINALIZED_ROOT_GINDEX),
sync_aggregate: pre.sync_aggregate,
signature_slot: pre.signature_slot)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-light-client-data
func upgrade_lc_optimistic_update_to_electra*(
pre: deneb.LightClientOptimisticUpdate): LightClientOptimisticUpdate =
LightClientOptimisticUpdate(
attested_header: upgrade_lc_header_to_electra(pre.attested_header),
sync_aggregate: pre.sync_aggregate,
signature_slot: pre.signature_slot)
func shortLog*(v: LightClientHeader): auto =
(
beacon: shortLog(v.beacon),
execution: (
block_hash: v.execution.block_hash,
block_number: v.execution.block_number)
)
func shortLog*(v: LightClientBootstrap): auto =
(
header: shortLog(v.header)
)
func shortLog*(v: LightClientUpdate): auto =
(
attested: shortLog(v.attested_header),
has_next_sync_committee:
v.next_sync_committee != default(typeof(v.next_sync_committee)),
finalized: shortLog(v.finalized_header),
num_active_participants: v.sync_aggregate.num_active_participants,
signature_slot: v.signature_slot
)
func shortLog*(v: LightClientFinalityUpdate): auto =
(
attested: shortLog(v.attested_header),
finalized: shortLog(v.finalized_header),
num_active_participants: v.sync_aggregate.num_active_participants,
signature_slot: v.signature_slot
)
func shortLog*(v: LightClientOptimisticUpdate): auto =
(
attested: shortLog(v.attested_header),
num_active_participants: v.sync_aggregate.num_active_participants,
signature_slot: v.signature_slot,
)
chronicles.formatIt LightClientBootstrap: shortLog(it)
chronicles.formatIt LightClientUpdate: shortLog(it)
chronicles.formatIt LightClientFinalityUpdate: shortLog(it)
chronicles.formatIt LightClientOptimisticUpdate: shortLog(it)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/fork.md#upgrading-the-store
func upgrade_lc_store_to_electra*(
pre: deneb.LightClientStore): LightClientStore =
let best_valid_update =
if pre.best_valid_update.isNone:
Opt.none(LightClientUpdate)
else:
Opt.some upgrade_lc_update_to_electra(pre.best_valid_update.get)
LightClientStore(
finalized_header: upgrade_lc_header_to_electra(pre.finalized_header),
current_sync_committee: pre.current_sync_committee,
next_sync_committee: pre.next_sync_committee,
best_valid_update: best_valid_update,
optimistic_header: upgrade_lc_header_to_electra(pre.optimistic_header),
previous_max_active_participants: pre.previous_max_active_participants,
current_max_active_participants: pre.current_max_active_participants)
template asSigned*( template asSigned*(
x: SigVerifiedSignedBeaconBlock | x: SigVerifiedSignedBeaconBlock |
MsgTrustedSignedBeaconBlock | MsgTrustedSignedBeaconBlock |

View File

@ -252,6 +252,11 @@ RestJson.useDefaultSerializationFor(
electra.ExecutionPayload, electra.ExecutionPayload,
electra.ExecutionPayloadHeader, electra.ExecutionPayloadHeader,
electra.IndexedAttestation, electra.IndexedAttestation,
electra.LightClientBootstrap,
electra.LightClientFinalityUpdate,
electra.LightClientHeader,
electra.LightClientOptimisticUpdate,
electra.LightClientUpdate,
electra.SignedBeaconBlock, electra.SignedBeaconBlock,
electra.TrustedAttestation, electra.TrustedAttestation,
electra_mev.BlindedBeaconBlock, electra_mev.BlindedBeaconBlock,

View File

@ -1325,8 +1325,10 @@ func forkVersion*(cfg: RuntimeConfig, consensusFork: ConsensusFork): Version =
func lcDataForkAtConsensusFork*( func lcDataForkAtConsensusFork*(
consensusFork: ConsensusFork): LightClientDataFork = consensusFork: ConsensusFork): LightClientDataFork =
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
if consensusFork >= ConsensusFork.Deneb: if consensusFork >= ConsensusFork.Electra:
LightClientDataFork.Electra
elif consensusFork >= ConsensusFork.Deneb:
LightClientDataFork.Deneb LightClientDataFork.Deneb
elif consensusFork >= ConsensusFork.Capella: elif consensusFork >= ConsensusFork.Capella:
LightClientDataFork.Capella LightClientDataFork.Capella

View File

@ -16,32 +16,42 @@ type
None = 0, # only use non-0 in DB to detect accidentally uninitialized data None = 0, # only use non-0 in DB to detect accidentally uninitialized data
Altair = 1, Altair = 1,
Capella = 2, Capella = 2,
Deneb = 3 Deneb = 3,
Electra = 4
ForkyCurrentSyncCommitteeBranch* =
altair.CurrentSyncCommitteeBranch |
electra.CurrentSyncCommitteeBranch
ForkyLightClientHeader* = ForkyLightClientHeader* =
altair.LightClientHeader | altair.LightClientHeader |
capella.LightClientHeader | capella.LightClientHeader |
deneb.LightClientHeader deneb.LightClientHeader |
electra.LightClientHeader
ForkyLightClientBootstrap* = ForkyLightClientBootstrap* =
altair.LightClientBootstrap | altair.LightClientBootstrap |
capella.LightClientBootstrap | capella.LightClientBootstrap |
deneb.LightClientBootstrap deneb.LightClientBootstrap |
electra.LightClientBootstrap
ForkyLightClientUpdate* = ForkyLightClientUpdate* =
altair.LightClientUpdate | altair.LightClientUpdate |
capella.LightClientUpdate | capella.LightClientUpdate |
deneb.LightClientUpdate deneb.LightClientUpdate |
electra.LightClientUpdate
ForkyLightClientFinalityUpdate* = ForkyLightClientFinalityUpdate* =
altair.LightClientFinalityUpdate | altair.LightClientFinalityUpdate |
capella.LightClientFinalityUpdate | capella.LightClientFinalityUpdate |
deneb.LightClientFinalityUpdate deneb.LightClientFinalityUpdate |
electra.LightClientFinalityUpdate
ForkyLightClientOptimisticUpdate* = ForkyLightClientOptimisticUpdate* =
altair.LightClientOptimisticUpdate | altair.LightClientOptimisticUpdate |
capella.LightClientOptimisticUpdate | capella.LightClientOptimisticUpdate |
deneb.LightClientOptimisticUpdate deneb.LightClientOptimisticUpdate |
electra.LightClientOptimisticUpdate
SomeForkyLightClientUpdateWithSyncCommittee* = SomeForkyLightClientUpdateWithSyncCommittee* =
ForkyLightClientUpdate ForkyLightClientUpdate
@ -62,7 +72,8 @@ type
ForkyLightClientStore* = ForkyLightClientStore* =
altair.LightClientStore | altair.LightClientStore |
capella.LightClientStore | capella.LightClientStore |
deneb.LightClientStore deneb.LightClientStore |
electra.LightClientStore
ForkedLightClientHeader* = object ForkedLightClientHeader* = object
case kind*: LightClientDataFork case kind*: LightClientDataFork
@ -74,6 +85,8 @@ type
capellaData*: capella.LightClientHeader capellaData*: capella.LightClientHeader
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientHeader denebData*: deneb.LightClientHeader
of LightClientDataFork.Electra:
electraData*: electra.LightClientHeader
ForkedLightClientBootstrap* = object ForkedLightClientBootstrap* = object
case kind*: LightClientDataFork case kind*: LightClientDataFork
@ -85,6 +98,8 @@ type
capellaData*: capella.LightClientBootstrap capellaData*: capella.LightClientBootstrap
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientBootstrap denebData*: deneb.LightClientBootstrap
of LightClientDataFork.Electra:
electraData*: electra.LightClientBootstrap
ForkedLightClientUpdate* = object ForkedLightClientUpdate* = object
case kind*: LightClientDataFork case kind*: LightClientDataFork
@ -96,6 +111,8 @@ type
capellaData*: capella.LightClientUpdate capellaData*: capella.LightClientUpdate
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientUpdate denebData*: deneb.LightClientUpdate
of LightClientDataFork.Electra:
electraData*: electra.LightClientUpdate
ForkedLightClientFinalityUpdate* = object ForkedLightClientFinalityUpdate* = object
case kind*: LightClientDataFork case kind*: LightClientDataFork
@ -107,6 +124,8 @@ type
capellaData*: capella.LightClientFinalityUpdate capellaData*: capella.LightClientFinalityUpdate
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientFinalityUpdate denebData*: deneb.LightClientFinalityUpdate
of LightClientDataFork.Electra:
electraData*: electra.LightClientFinalityUpdate
ForkedLightClientOptimisticUpdate* = object ForkedLightClientOptimisticUpdate* = object
case kind*: LightClientDataFork case kind*: LightClientDataFork
@ -118,6 +137,8 @@ type
capellaData*: capella.LightClientOptimisticUpdate capellaData*: capella.LightClientOptimisticUpdate
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientOptimisticUpdate denebData*: deneb.LightClientOptimisticUpdate
of LightClientDataFork.Electra:
electraData*: electra.LightClientOptimisticUpdate
SomeForkedLightClientUpdateWithSyncCommittee* = SomeForkedLightClientUpdateWithSyncCommittee* =
ForkedLightClientUpdate ForkedLightClientUpdate
@ -145,11 +166,15 @@ type
capellaData*: capella.LightClientStore capellaData*: capella.LightClientStore
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData*: deneb.LightClientStore denebData*: deneb.LightClientStore
of LightClientDataFork.Electra:
electraData*: electra.LightClientStore
func lcDataForkAtEpoch*( func lcDataForkAtEpoch*(
cfg: RuntimeConfig, epoch: Epoch): LightClientDataFork = cfg: RuntimeConfig, epoch: Epoch): LightClientDataFork =
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
if epoch >= cfg.DENEB_FORK_EPOCH: if epoch >= cfg.ELECTRA_FORK_EPOCH:
LightClientDataFork.Electra
elif epoch >= cfg.DENEB_FORK_EPOCH:
LightClientDataFork.Deneb LightClientDataFork.Deneb
elif epoch >= cfg.CAPELLA_FORK_EPOCH: elif epoch >= cfg.CAPELLA_FORK_EPOCH:
LightClientDataFork.Capella LightClientDataFork.Capella
@ -188,8 +213,71 @@ template kind*(
deneb.LightClientStore]): LightClientDataFork = deneb.LightClientStore]): LightClientDataFork =
LightClientDataFork.Deneb LightClientDataFork.Deneb
template kind*(
x: typedesc[ # `SomeLightClientObject` doesn't work here (Nim 1.6)
electra.LightClientHeader |
electra.LightClientBootstrap |
electra.LightClientUpdate |
electra.LightClientFinalityUpdate |
electra.LightClientOptimisticUpdate |
electra.LightClientStore]): LightClientDataFork =
LightClientDataFork.Electra
template FINALIZED_ROOT_GINDEX*(
kind: static LightClientDataFork): GeneralizedIndex =
when kind >= LightClientDataFork.Electra:
electra.FINALIZED_ROOT_GINDEX
elif kind >= LightClientDataFork.Altair:
altair.FINALIZED_ROOT_GINDEX
else:
static: raiseAssert "Unreachable"
template FinalityBranch*(kind: static LightClientDataFork): auto =
when kind >= LightClientDataFork.Electra:
typedesc[electra.FinalityBranch]
elif kind >= LightClientDataFork.Altair:
typedesc[altair.FinalityBranch]
else:
static: raiseAssert "Unreachable"
template CURRENT_SYNC_COMMITTEE_GINDEX*(
kind: static LightClientDataFork): GeneralizedIndex =
when kind >= LightClientDataFork.Electra:
electra.CURRENT_SYNC_COMMITTEE_GINDEX
elif kind >= LightClientDataFork.Altair:
altair.CURRENT_SYNC_COMMITTEE_GINDEX
else:
static: raiseAssert "Unreachable"
template CurrentSyncCommitteeBranch*(kind: static LightClientDataFork): auto =
when kind >= LightClientDataFork.Electra:
typedesc[electra.CurrentSyncCommitteeBranch]
elif kind >= LightClientDataFork.Altair:
typedesc[altair.CurrentSyncCommitteeBranch]
else:
static: raiseAssert "Unreachable"
template NEXT_SYNC_COMMITTEE_GINDEX*(
kind: static LightClientDataFork): GeneralizedIndex =
when kind >= LightClientDataFork.Electra:
electra.NEXT_SYNC_COMMITTEE_GINDEX
elif kind >= LightClientDataFork.Altair:
altair.NEXT_SYNC_COMMITTEE_GINDEX
else:
static: raiseAssert "Unreachable"
template NextSyncCommitteeBranch*(kind: static LightClientDataFork): auto =
when kind >= LightClientDataFork.Electra:
typedesc[electra.NextSyncCommitteeBranch]
elif kind >= LightClientDataFork.Altair:
typedesc[altair.NextSyncCommitteeBranch]
else:
static: raiseAssert "Unreachable"
template LightClientHeader*(kind: static LightClientDataFork): auto = template LightClientHeader*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientHeader]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientHeader] typedesc[deneb.LightClientHeader]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientHeader] typedesc[capella.LightClientHeader]
@ -199,7 +287,9 @@ template LightClientHeader*(kind: static LightClientDataFork): auto =
static: raiseAssert "Unreachable" static: raiseAssert "Unreachable"
template LightClientBootstrap*(kind: static LightClientDataFork): auto = template LightClientBootstrap*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientBootstrap]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientBootstrap] typedesc[deneb.LightClientBootstrap]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientBootstrap] typedesc[capella.LightClientBootstrap]
@ -209,7 +299,9 @@ template LightClientBootstrap*(kind: static LightClientDataFork): auto =
static: raiseAssert "Unreachable" static: raiseAssert "Unreachable"
template LightClientUpdate*(kind: static LightClientDataFork): auto = template LightClientUpdate*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientUpdate]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientUpdate] typedesc[deneb.LightClientUpdate]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientUpdate] typedesc[capella.LightClientUpdate]
@ -219,7 +311,9 @@ template LightClientUpdate*(kind: static LightClientDataFork): auto =
static: raiseAssert "Unreachable" static: raiseAssert "Unreachable"
template LightClientFinalityUpdate*(kind: static LightClientDataFork): auto = template LightClientFinalityUpdate*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientFinalityUpdate]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientFinalityUpdate] typedesc[deneb.LightClientFinalityUpdate]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientFinalityUpdate] typedesc[capella.LightClientFinalityUpdate]
@ -229,7 +323,9 @@ template LightClientFinalityUpdate*(kind: static LightClientDataFork): auto =
static: raiseAssert "Unreachable" static: raiseAssert "Unreachable"
template LightClientOptimisticUpdate*(kind: static LightClientDataFork): auto = template LightClientOptimisticUpdate*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientOptimisticUpdate]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientOptimisticUpdate] typedesc[deneb.LightClientOptimisticUpdate]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientOptimisticUpdate] typedesc[capella.LightClientOptimisticUpdate]
@ -239,7 +335,9 @@ template LightClientOptimisticUpdate*(kind: static LightClientDataFork): auto =
static: raiseAssert "Unreachable" static: raiseAssert "Unreachable"
template LightClientStore*(kind: static LightClientDataFork): auto = template LightClientStore*(kind: static LightClientDataFork): auto =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
typedesc[electra.LightClientStore]
elif kind == LightClientDataFork.Deneb:
typedesc[deneb.LightClientStore] typedesc[deneb.LightClientStore]
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
typedesc[capella.LightClientStore] typedesc[capella.LightClientStore]
@ -298,7 +396,10 @@ template Forked*(x: typedesc[ForkyLightClientStore]): auto =
template withAll*( template withAll*(
x: typedesc[LightClientDataFork], body: untyped): untyped = x: typedesc[LightClientDataFork], body: untyped): untyped =
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
block:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
body
block: block:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
body body
@ -315,6 +416,9 @@ template withAll*(
template withLcDataFork*( template withLcDataFork*(
x: LightClientDataFork, body: untyped): untyped = x: LightClientDataFork, body: untyped): untyped =
case x case x
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
body body
@ -331,6 +435,10 @@ template withLcDataFork*(
template withForkyHeader*( template withForkyHeader*(
x: ForkedLightClientHeader, body: untyped): untyped = x: ForkedLightClientHeader, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyHeader: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyHeader: untyped {.inject, used.} = x.denebData template forkyHeader: untyped {.inject, used.} = x.denebData
@ -350,6 +458,10 @@ template withForkyHeader*(
template withForkyBootstrap*( template withForkyBootstrap*(
x: ForkedLightClientBootstrap, body: untyped): untyped = x: ForkedLightClientBootstrap, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyBootstrap: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyBootstrap: untyped {.inject, used.} = x.denebData template forkyBootstrap: untyped {.inject, used.} = x.denebData
@ -369,6 +481,10 @@ template withForkyBootstrap*(
template withForkyUpdate*( template withForkyUpdate*(
x: ForkedLightClientUpdate, body: untyped): untyped = x: ForkedLightClientUpdate, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyUpdate: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyUpdate: untyped {.inject, used.} = x.denebData template forkyUpdate: untyped {.inject, used.} = x.denebData
@ -388,6 +504,10 @@ template withForkyUpdate*(
template withForkyFinalityUpdate*( template withForkyFinalityUpdate*(
x: ForkedLightClientFinalityUpdate, body: untyped): untyped = x: ForkedLightClientFinalityUpdate, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyFinalityUpdate: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyFinalityUpdate: untyped {.inject, used.} = x.denebData template forkyFinalityUpdate: untyped {.inject, used.} = x.denebData
@ -407,6 +527,10 @@ template withForkyFinalityUpdate*(
template withForkyOptimisticUpdate*( template withForkyOptimisticUpdate*(
x: ForkedLightClientOptimisticUpdate, body: untyped): untyped = x: ForkedLightClientOptimisticUpdate, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyOptimisticUpdate: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyOptimisticUpdate: untyped {.inject, used.} = x.denebData template forkyOptimisticUpdate: untyped {.inject, used.} = x.denebData
@ -426,6 +550,10 @@ template withForkyOptimisticUpdate*(
template withForkyObject*( template withForkyObject*(
x: SomeForkedLightClientObject, body: untyped): untyped = x: SomeForkedLightClientObject, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyObject: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyObject: untyped {.inject, used.} = x.denebData template forkyObject: untyped {.inject, used.} = x.denebData
@ -445,6 +573,10 @@ template withForkyObject*(
template withForkyStore*( template withForkyStore*(
x: ForkedLightClientStore, body: untyped): untyped = x: ForkedLightClientStore, body: untyped): untyped =
case x.kind case x.kind
of LightClientDataFork.Electra:
const lcDataFork {.inject, used.} = LightClientDataFork.Electra
template forkyStore: untyped {.inject, used.} = x.electraData
body
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
const lcDataFork {.inject, used.} = LightClientDataFork.Deneb const lcDataFork {.inject, used.} = LightClientDataFork.Deneb
template forkyStore: untyped {.inject, used.} = x.denebData template forkyStore: untyped {.inject, used.} = x.denebData
@ -473,7 +605,9 @@ func init*(
type ResultType = typeof(forkyData).Forked type ResultType = typeof(forkyData).Forked
static: doAssert ResultType is x static: doAssert ResultType is x
const kind = typeof(forkyData).kind const kind = typeof(forkyData).kind
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
ResultType(kind: kind, electraData: forkyData)
elif kind == LightClientDataFork.Deneb:
ResultType(kind: kind, denebData: forkyData) ResultType(kind: kind, denebData: forkyData)
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
ResultType(kind: kind, capellaData: forkyData) ResultType(kind: kind, capellaData: forkyData)
@ -488,7 +622,9 @@ template forky*(
SomeForkedLightClientObject | SomeForkedLightClientObject |
ForkedLightClientStore, ForkedLightClientStore,
kind: static LightClientDataFork): untyped = kind: static LightClientDataFork): untyped =
when kind == LightClientDataFork.Deneb: when kind == LightClientDataFork.Electra:
x.electraData
elif kind == LightClientDataFork.Deneb:
x.denebData x.denebData
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
x.capellaData x.capellaData
@ -641,7 +777,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_header_to_deneb( denebData: upgrade_lc_header_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientHeader(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_header_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migrateToDataFork*( func migrateToDataFork*(
@ -676,7 +820,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_bootstrap_to_deneb( denebData: upgrade_lc_bootstrap_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientBootstrap(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_bootstrap_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migrateToDataFork*( func migrateToDataFork*(
@ -711,7 +863,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_update_to_deneb( denebData: upgrade_lc_update_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientUpdate(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_update_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migrateToDataFork*( func migrateToDataFork*(
@ -746,7 +906,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_finality_update_to_deneb( denebData: upgrade_lc_finality_update_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientFinalityUpdate(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_finality_update_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migrateToDataFork*( func migrateToDataFork*(
@ -781,7 +949,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_optimistic_update_to_deneb( denebData: upgrade_lc_optimistic_update_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientOptimisticUpdate(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_optimistic_update_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migrateToDataFork*( func migrateToDataFork*(
@ -816,7 +992,15 @@ func migrateToDataFork*(
denebData: upgrade_lc_store_to_deneb( denebData: upgrade_lc_store_to_deneb(
x.forky(LightClientDataFork.Capella))) x.forky(LightClientDataFork.Capella)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Deneb # Upgrade to Electra
when newKind >= LightClientDataFork.Electra:
if x.kind == LightClientDataFork.Deneb:
x = ForkedLightClientStore(
kind: LightClientDataFork.Electra,
electraData: upgrade_lc_store_to_electra(
x.forky(LightClientDataFork.Deneb)))
static: doAssert LightClientDataFork.high == LightClientDataFork.Electra
doAssert x.kind == newKind doAssert x.kind == newKind
func migratingToDataFork*[ func migratingToDataFork*[
@ -951,6 +1135,108 @@ func toDenebLightClientHeader(
execution_branch: blck.message.body.build_proof( execution_branch: blck.message.body.build_proof(
capella.EXECUTION_PAYLOAD_GINDEX).get) capella.EXECUTION_PAYLOAD_GINDEX).get)
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/full-node.md#modified-block_to_light_client_header
func toElectraLightClientHeader(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock |
altair.SignedBeaconBlock | altair.TrustedSignedBeaconBlock |
bellatrix.SignedBeaconBlock | bellatrix.TrustedSignedBeaconBlock
): electra.LightClientHeader =
# Note that during fork transitions, `finalized_header` may still
# point to earlier forks. While Bellatrix blocks also contain an
# `ExecutionPayload` (minus `withdrawals_root`), it was not included
# in the corresponding light client data. To ensure compatibility
# with legacy data going through `upgrade_lc_header_to_capella`,
# leave out execution data.
electra.LightClientHeader(
beacon: blck.message.toBeaconBlockHeader())
func toElectraLightClientHeader(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
capella.SignedBeaconBlock | capella.TrustedSignedBeaconBlock
): electra.LightClientHeader =
template payload: untyped = blck.message.body.execution_payload
electra.LightClientHeader(
beacon: blck.message.toBeaconBlockHeader(),
execution: electra.ExecutionPayloadHeader(
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: hash_tree_root(payload.transactions),
withdrawals_root: hash_tree_root(payload.withdrawals)),
execution_branch: blck.message.body.build_proof(
capella.EXECUTION_PAYLOAD_GINDEX).get)
func toElectraLightClientHeader(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock
): electra.LightClientHeader =
template payload: untyped = blck.message.body.execution_payload
electra.LightClientHeader(
beacon: blck.message.toBeaconBlockHeader(),
execution: electra.ExecutionPayloadHeader(
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: hash_tree_root(payload.transactions),
withdrawals_root: hash_tree_root(payload.withdrawals),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas),
execution_branch: blck.message.body.build_proof(
capella.EXECUTION_PAYLOAD_GINDEX).get)
func toElectraLightClientHeader(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock
): electra.LightClientHeader =
template payload: untyped = blck.message.body.execution_payload
electra.LightClientHeader(
beacon: blck.message.toBeaconBlockHeader(),
execution: electra.ExecutionPayloadHeader(
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: hash_tree_root(payload.transactions),
withdrawals_root: hash_tree_root(payload.withdrawals),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
deposit_requests_root: hash_tree_root(payload.deposit_requests),
withdrawal_requests_root: hash_tree_root(payload.withdrawal_requests),
consolidation_requests_root:
hash_tree_root(payload.consolidation_requests)),
execution_branch: blck.message.body.build_proof(
capella.EXECUTION_PAYLOAD_GINDEX).get)
func toLightClientHeader*( func toLightClientHeader*(
blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6) blck: # `SomeSignedBeaconBlock` doesn't work here (Nim 1.6)
phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock | phase0.SignedBeaconBlock | phase0.TrustedSignedBeaconBlock |
@ -960,9 +1246,8 @@ func toLightClientHeader*(
deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock | deneb.SignedBeaconBlock | deneb.TrustedSignedBeaconBlock |
electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock, electra.SignedBeaconBlock | electra.TrustedSignedBeaconBlock,
kind: static LightClientDataFork): auto = kind: static LightClientDataFork): auto =
when blck is electra.SignedBeaconBlock or blck is electra.TrustedSignedBeaconBlock: when kind == LightClientDataFork.Electra:
debugComment "toLightClientHeader electra missing" blck.toElectraLightClientHeader()
default(deneb.LightClientHeader)
elif kind == LightClientDataFork.Deneb: elif kind == LightClientDataFork.Deneb:
blck.toDenebLightClientHeader() blck.toDenebLightClientHeader()
elif kind == LightClientDataFork.Capella: elif kind == LightClientDataFork.Capella:
@ -990,9 +1275,13 @@ func shortLog*[
capellaData: typeof(x.capellaData.shortLog()) capellaData: typeof(x.capellaData.shortLog())
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
denebData: typeof(x.denebData.shortLog()) denebData: typeof(x.denebData.shortLog())
of LightClientDataFork.Electra:
electraData: typeof(x.electraData.shortLog())
let xKind = x.kind # Nim 1.6.12: Using `kind: x.kind` inside case is broken let xKind = x.kind # Nim 1.6.12: Using `kind: x.kind` inside case is broken
case xKind case xKind
of LightClientDataFork.Electra:
ResultType(kind: xKind, electraData: x.electraData.shortLog())
of LightClientDataFork.Deneb: of LightClientDataFork.Deneb:
ResultType(kind: xKind, denebData: x.denebData.shortLog()) ResultType(kind: xKind, denebData: x.denebData.shortLog())
of LightClientDataFork.Capella: of LightClientDataFork.Capella:

View File

@ -15,6 +15,21 @@ import
from ../consensus_object_pools/block_pools_types import VerifierError from ../consensus_object_pools/block_pools_types import VerifierError
export block_pools_types.VerifierError export block_pools_types.VerifierError
# https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.3/specs/electra/light-client/sync-protocol.md#is_valid_normalized_merkle_branch
func is_valid_normalized_merkle_branch[N](
leaf: Eth2Digest,
branch: array[N, Eth2Digest],
gindex: static GeneralizedIndex,
root: Eth2Digest): bool =
const
depth = log2trunc(gindex)
index = get_subtree_index(gindex)
num_extra = branch.len - depth
for i in 0 ..< num_extra:
if not branch[i].isZero:
return false
is_valid_merkle_branch(leaf, branch[num_extra .. ^1], depth, index, root)
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/altair/light-client/sync-protocol.md#initialize_light_client_store # https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/altair/light-client/sync-protocol.md#initialize_light_client_store
func initialize_light_client_store*( func initialize_light_client_store*(
trusted_block_root: Eth2Digest, trusted_block_root: Eth2Digest,
@ -29,13 +44,15 @@ func initialize_light_client_store*(
if hash_tree_root(bootstrap.header.beacon) != trusted_block_root: if hash_tree_root(bootstrap.header.beacon) != trusted_block_root:
return ResultType.err(VerifierError.Invalid) return ResultType.err(VerifierError.Invalid)
if not is_valid_merkle_branch( withLcDataFork(lcDataForkAtConsensusFork(
hash_tree_root(bootstrap.current_sync_committee), cfg.consensusForkAtEpoch(bootstrap.header.beacon.slot.epoch))):
bootstrap.current_sync_committee_branch, when lcDataFork > LightClientDataFork.None:
log2trunc(altair.CURRENT_SYNC_COMMITTEE_GINDEX), if not is_valid_normalized_merkle_branch(
get_subtree_index(altair.CURRENT_SYNC_COMMITTEE_GINDEX), hash_tree_root(bootstrap.current_sync_committee),
bootstrap.header.beacon.state_root): bootstrap.current_sync_committee_branch,
return ResultType.err(VerifierError.Invalid) lcDataFork.CURRENT_SYNC_COMMITTEE_GINDEX,
bootstrap.header.beacon.state_root):
return ResultType.err(VerifierError.Invalid)
return ResultType.ok(typeof(bootstrap).kind.LightClientStore( return ResultType.ok(typeof(bootstrap).kind.LightClientStore(
finalized_header: bootstrap.header, finalized_header: bootstrap.header,
@ -109,13 +126,15 @@ proc validate_light_client_update*(
finalized_root.reset() finalized_root.reset()
else: else:
return err(VerifierError.Invalid) return err(VerifierError.Invalid)
if not is_valid_merkle_branch( withLcDataFork(lcDataForkAtConsensusFork(
finalized_root, cfg.consensusForkAtEpoch(update.attested_header.beacon.slot.epoch))):
update.finality_branch, when lcDataFork > LightClientDataFork.None:
log2trunc(altair.FINALIZED_ROOT_GINDEX), if not is_valid_normalized_merkle_branch(
get_subtree_index(altair.FINALIZED_ROOT_GINDEX), finalized_root,
update.attested_header.beacon.state_root): update.finality_branch,
return err(VerifierError.Invalid) lcDataFork.FINALIZED_ROOT_GINDEX,
update.attested_header.beacon.state_root):
return err(VerifierError.Invalid)
# Verify that the `next_sync_committee`, if present, actually is the # Verify that the `next_sync_committee`, if present, actually is the
# next sync committee saved in the state of the `attested_header` # next sync committee saved in the state of the `attested_header`
@ -128,13 +147,15 @@ proc validate_light_client_update*(
if attested_period == store_period and is_next_sync_committee_known: if attested_period == store_period and is_next_sync_committee_known:
if update.next_sync_committee != store.next_sync_committee: if update.next_sync_committee != store.next_sync_committee:
return err(VerifierError.UnviableFork) return err(VerifierError.UnviableFork)
if not is_valid_merkle_branch( withLcDataFork(lcDataForkAtConsensusFork(
hash_tree_root(update.next_sync_committee), cfg.consensusForkAtEpoch(update.attested_header.beacon.slot.epoch))):
update.next_sync_committee_branch, when lcDataFork > LightClientDataFork.None:
log2trunc(altair.NEXT_SYNC_COMMITTEE_GINDEX), if not is_valid_normalized_merkle_branch(
get_subtree_index(altair.NEXT_SYNC_COMMITTEE_GINDEX), hash_tree_root(update.next_sync_committee),
update.attested_header.beacon.state_root): update.next_sync_committee_branch,
return err(VerifierError.Invalid) lcDataFork.NEXT_SYNC_COMMITTEE_GINDEX,
update.attested_header.beacon.state_root):
return err(VerifierError.Invalid)
# Verify sync committee aggregate signature # Verify sync committee aggregate signature
let sync_committee = let sync_committee =

View File

@ -172,14 +172,15 @@ proc runTest(storeDataFork: static LightClientDataFork) =
# Sync committee signing the attested_header # Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[]) (sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
next_sync_committee = SyncCommittee() next_sync_committee = SyncCommittee()
next_sync_committee_branch = default(altair.NextSyncCommitteeBranch) next_sync_committee_branch =
default(storeDataFork.NextSyncCommitteeBranch)
# Ensure that finality checkpoint is genesis # Ensure that finality checkpoint is genesis
check state.finalized_checkpoint.epoch == 0 check state.finalized_checkpoint.epoch == 0
# Finality is unchanged # Finality is unchanged
let let
finality_header = default(storeDataFork.LightClientHeader) finality_header = default(storeDataFork.LightClientHeader)
finality_branch = default(altair.FinalityBranch) finality_branch = default(storeDataFork.FinalityBranch)
update = storeDataFork.LightClientUpdate( update = storeDataFork.LightClientUpdate(
attested_header: attested_header, attested_header: attested_header,
@ -228,11 +229,12 @@ proc runTest(storeDataFork: static LightClientDataFork) =
# Sync committee signing the attested_header # Sync committee signing the attested_header
(sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[]) (sync_aggregate, signature_slot) = get_sync_aggregate(cfg, forked[])
next_sync_committee = SyncCommittee() next_sync_committee = SyncCommittee()
next_sync_committee_branch = default(altair.NextSyncCommitteeBranch) next_sync_committee_branch =
default(storeDataFork.NextSyncCommitteeBranch)
# Finality is unchanged # Finality is unchanged
finality_header = default(storeDataFork.LightClientHeader) finality_header = default(storeDataFork.LightClientHeader)
finality_branch = default(altair.FinalityBranch) finality_branch = default(storeDataFork.FinalityBranch)
update = storeDataFork.LightClientUpdate( update = storeDataFork.LightClientUpdate(
attested_header: attested_header, attested_header: attested_header,
@ -283,12 +285,13 @@ proc runTest(storeDataFork: static LightClientDataFork) =
# Sync committee is updated # Sync committee is updated
template next_sync_committee(): auto = state.next_sync_committee template next_sync_committee(): auto = state.next_sync_committee
let let
next_sync_committee_branch = next_sync_committee_branch = normalize_merkle_branch(
state.build_proof(altair.NEXT_SYNC_COMMITTEE_GINDEX).get state.build_proof(altair.NEXT_SYNC_COMMITTEE_GINDEX).get,
storeDataFork.NEXT_SYNC_COMMITTEE_GINDEX)
# Finality is unchanged # Finality is unchanged
finality_header = default(storeDataFork.LightClientHeader) finality_header = default(storeDataFork.LightClientHeader)
finality_branch = default(altair.FinalityBranch) finality_branch = default(storeDataFork.FinalityBranch)
update = storeDataFork.LightClientUpdate( update = storeDataFork.LightClientUpdate(
attested_header: attested_header, attested_header: attested_header,
@ -345,7 +348,8 @@ proc runTest(storeDataFork: static LightClientDataFork) =
# Updated sync_committee and finality # Updated sync_committee and finality
next_sync_committee = SyncCommittee() next_sync_committee = SyncCommittee()
next_sync_committee_branch = default(altair.NextSyncCommitteeBranch) next_sync_committee_branch =
default(storeDataFork.NextSyncCommitteeBranch)
finalized_block = blocks[SLOTS_PER_EPOCH - 1].altairData finalized_block = blocks[SLOTS_PER_EPOCH - 1].altairData
finalized_header = finalized_block.toLightClientHeader(storeDataFork) finalized_header = finalized_block.toLightClientHeader(storeDataFork)
check: check:
@ -354,7 +358,9 @@ proc runTest(storeDataFork: static LightClientDataFork) =
finalized_header.beacon.hash_tree_root() == finalized_header.beacon.hash_tree_root() ==
state.finalized_checkpoint.root state.finalized_checkpoint.root
let let
finality_branch = state.build_proof(altair.FINALIZED_ROOT_GINDEX).get finality_branch = normalize_merkle_branch(
state.build_proof(altair.FINALIZED_ROOT_GINDEX).get,
storeDataFork.FINALIZED_ROOT_GINDEX)
update = storeDataFork.LightClientUpdate( update = storeDataFork.LightClientUpdate(
attested_header: attested_header, attested_header: attested_header,