split out eth1chain into its own module (#5768)
reduces import junk in some places - more could be done here
This commit is contained in:
parent
68d0542ae1
commit
d5785677a8
|
@ -28,7 +28,7 @@ from ./spec/datatypes/deneb import TrustedSignedBeaconBlock
|
||||||
|
|
||||||
export
|
export
|
||||||
phase0, altair, eth2_ssz_serialization, eth2_merkleization, kvstore,
|
phase0, altair, eth2_ssz_serialization, eth2_merkleization, kvstore,
|
||||||
kvstore_sqlite3
|
kvstore_sqlite3, deposit_snapshots
|
||||||
|
|
||||||
logScope: topics = "bc_db"
|
logScope: topics = "bc_db"
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[deques, strformat, strutils, sequtils, tables, typetraits, uri, json],
|
std/[strformat, strutils, sequtils, typetraits, uri, json],
|
||||||
# Nimble packages:
|
# Nimble packages:
|
||||||
chronos, metrics, chronicles/timings,
|
chronos, metrics, chronicles/timings,
|
||||||
json_rpc/[client, errors],
|
json_rpc/[client, errors],
|
||||||
|
@ -16,19 +16,19 @@ import
|
||||||
eth/common/[eth_types, transaction],
|
eth/common/[eth_types, transaction],
|
||||||
eth/async_utils, results,
|
eth/async_utils, results,
|
||||||
stew/[assign2, byteutils, objects, shims/hashes, endians2],
|
stew/[assign2, byteutils, objects, shims/hashes, endians2],
|
||||||
|
eth/async_utils, stew/[assign2, byteutils, objects],
|
||||||
# Local modules:
|
# Local modules:
|
||||||
../spec/[deposit_snapshots, eth2_merkleization, forks, helpers],
|
../spec/[eth2_merkleization, forks, helpers],
|
||||||
../networking/network_metadata,
|
../networking/network_metadata,
|
||||||
../consensus_object_pools/block_pools_types,
|
".."/[beacon_node_status, future_combinators],
|
||||||
".."/[beacon_chain_db, beacon_node_status, beacon_clock, future_combinators],
|
"."/[eth1_chain, el_conf]
|
||||||
"."/[merkle_minimal, el_conf]
|
|
||||||
|
|
||||||
from std/times import getTime, inSeconds, initTime, `-`
|
from std/times import getTime, inSeconds, initTime, `-`
|
||||||
from ../spec/engine_authentication import getSignedIatToken
|
from ../spec/engine_authentication import getSignedIatToken
|
||||||
from ../spec/state_transition_block import kzg_commitment_to_versioned_hash
|
from ../spec/state_transition_block import kzg_commitment_to_versioned_hash
|
||||||
|
|
||||||
export
|
export
|
||||||
el_conf, engine_api, deques, base, DepositTreeSnapshot
|
eth1_chain, el_conf, engine_api, base
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "elmon"
|
topics = "elmon"
|
||||||
|
@ -94,47 +94,6 @@ const
|
||||||
## before declaring the connection as degraded/restored
|
## before declaring the connection as degraded/restored
|
||||||
|
|
||||||
type
|
type
|
||||||
Eth1BlockNumber* = uint64
|
|
||||||
Eth1BlockTimestamp* = uint64
|
|
||||||
|
|
||||||
Eth1BlockObj* = object
|
|
||||||
hash*: Eth2Digest
|
|
||||||
number*: Eth1BlockNumber
|
|
||||||
timestamp*: Eth1BlockTimestamp
|
|
||||||
## Basic properties of the block
|
|
||||||
## These must be initialized in the constructor
|
|
||||||
|
|
||||||
deposits*: seq[DepositData]
|
|
||||||
## Deposits inside this particular block
|
|
||||||
|
|
||||||
depositRoot*: Eth2Digest
|
|
||||||
depositCount*: uint64
|
|
||||||
## Global deposits count and hash tree root of the entire sequence
|
|
||||||
## These are computed when the block is added to the chain (see `addBlock`)
|
|
||||||
|
|
||||||
Eth1Block* = ref Eth1BlockObj
|
|
||||||
|
|
||||||
Eth1Chain* = object
|
|
||||||
db: BeaconChainDB
|
|
||||||
cfg: RuntimeConfig
|
|
||||||
finalizedBlockHash: Eth2Digest
|
|
||||||
finalizedDepositsMerkleizer: DepositsMerkleizer
|
|
||||||
## The latest block that reached a 50% majority vote from
|
|
||||||
## the Eth2 validators according to the follow distance and
|
|
||||||
## the ETH1_VOTING_PERIOD
|
|
||||||
|
|
||||||
blocks*: Deque[Eth1Block]
|
|
||||||
## A non-forkable chain of blocks ending at the block with
|
|
||||||
## ETH1_FOLLOW_DISTANCE offset from the head.
|
|
||||||
|
|
||||||
blocksByHash: Table[BlockHash, Eth1Block]
|
|
||||||
|
|
||||||
headMerkleizer: DepositsMerkleizer
|
|
||||||
## Merkleizer state after applying all `blocks`
|
|
||||||
|
|
||||||
hasConsensusViolation: bool
|
|
||||||
## The local chain contradicts the observed consensus on the network
|
|
||||||
|
|
||||||
NextExpectedPayloadParams* = object
|
NextExpectedPayloadParams* = object
|
||||||
headBlockHash*: Eth2Digest
|
headBlockHash*: Eth2Digest
|
||||||
safeBlockHash*: Eth2Digest
|
safeBlockHash*: Eth2Digest
|
||||||
|
@ -233,11 +192,6 @@ type
|
||||||
merkleTreeIndex: Int64LeBytes,
|
merkleTreeIndex: Int64LeBytes,
|
||||||
j: JsonNode) {.gcsafe, raises: [].}
|
j: JsonNode) {.gcsafe, raises: [].}
|
||||||
|
|
||||||
BlockProposalEth1Data* = object
|
|
||||||
vote*: Eth1Data
|
|
||||||
deposits*: seq[Deposit]
|
|
||||||
hasMissingDeposits*: bool
|
|
||||||
|
|
||||||
BellatrixExecutionPayloadWithValue* = object
|
BellatrixExecutionPayloadWithValue* = object
|
||||||
executionPayload*: ExecutionPayloadV1
|
executionPayload*: ExecutionPayloadV1
|
||||||
blockValue*: UInt256
|
blockValue*: UInt256
|
||||||
|
@ -256,15 +210,6 @@ declareGauge eth1_latest_head,
|
||||||
declareGauge eth1_synced_head,
|
declareGauge eth1_synced_head,
|
||||||
"Block number of the highest synchronized block according to follow distance"
|
"Block number of the highest synchronized block according to follow distance"
|
||||||
|
|
||||||
declareGauge eth1_finalized_head,
|
|
||||||
"Block number of the highest Eth1 block finalized by Eth2 consensus"
|
|
||||||
|
|
||||||
declareGauge eth1_finalized_deposits,
|
|
||||||
"Number of deposits that were finalized by the Eth2 consensus"
|
|
||||||
|
|
||||||
declareGauge eth1_chain_len,
|
|
||||||
"The length of the in-memory chain of Eth1 blocks"
|
|
||||||
|
|
||||||
declareCounter engine_api_responses,
|
declareCounter engine_api_responses,
|
||||||
"Number of successful requests to the newPayload Engine API end-point",
|
"Number of successful requests to the newPayload Engine API end-point",
|
||||||
labels = ["url", "request", "status"]
|
labels = ["url", "request", "status"]
|
||||||
|
@ -420,31 +365,6 @@ template toGaugeValue(x: Quantity): int64 =
|
||||||
# doAssert SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE < GENESIS_DELAY,
|
# doAssert SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE < GENESIS_DELAY,
|
||||||
# "Invalid configuration: GENESIS_DELAY is set too low"
|
# "Invalid configuration: GENESIS_DELAY is set too low"
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
|
||||||
func compute_time_at_slot(genesis_time: uint64, slot: Slot): uint64 =
|
|
||||||
genesis_time + slot * SECONDS_PER_SLOT
|
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
|
||||||
func voting_period_start_time(state: ForkedHashedBeaconState): uint64 =
|
|
||||||
let eth1_voting_period_start_slot =
|
|
||||||
getStateField(state, slot) - getStateField(state, slot) mod
|
|
||||||
SLOTS_PER_ETH1_VOTING_PERIOD.uint64
|
|
||||||
compute_time_at_slot(
|
|
||||||
getStateField(state, genesis_time), eth1_voting_period_start_slot)
|
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
|
||||||
func is_candidate_block(cfg: RuntimeConfig,
|
|
||||||
blk: Eth1Block,
|
|
||||||
period_start: uint64): bool =
|
|
||||||
(blk.timestamp + cfg.SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE <= period_start) and
|
|
||||||
(blk.timestamp + cfg.SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE * 2 >= period_start)
|
|
||||||
|
|
||||||
func asEth2Digest*(x: BlockHash): Eth2Digest =
|
|
||||||
Eth2Digest(data: array[32, byte](x))
|
|
||||||
|
|
||||||
template asBlockHash*(x: Eth2Digest): BlockHash =
|
|
||||||
BlockHash(x.data)
|
|
||||||
|
|
||||||
func asConsensusWithdrawal(w: WithdrawalV1): capella.Withdrawal =
|
func asConsensusWithdrawal(w: WithdrawalV1): capella.Withdrawal =
|
||||||
capella.Withdrawal(
|
capella.Withdrawal(
|
||||||
index: w.index.uint64,
|
index: w.index.uint64,
|
||||||
|
@ -642,56 +562,6 @@ func asEngineExecutionPayload*(executionPayload: deneb.ExecutionPayload):
|
||||||
blobGasUsed: Quantity(executionPayload.blob_gas_used),
|
blobGasUsed: Quantity(executionPayload.blob_gas_used),
|
||||||
excessBlobGas: Quantity(executionPayload.excess_blob_gas))
|
excessBlobGas: Quantity(executionPayload.excess_blob_gas))
|
||||||
|
|
||||||
func shortLog*(b: Eth1Block): string =
|
|
||||||
try:
|
|
||||||
&"{b.number}:{shortLog b.hash}(deposits = {b.depositCount})"
|
|
||||||
except ValueError as exc: raiseAssert exc.msg
|
|
||||||
|
|
||||||
template findBlock(chain: Eth1Chain, eth1Data: Eth1Data): Eth1Block =
|
|
||||||
getOrDefault(chain.blocksByHash, asBlockHash(eth1Data.block_hash), nil)
|
|
||||||
|
|
||||||
func makeSuccessorWithoutDeposits(existingBlock: Eth1Block,
|
|
||||||
successor: BlockObject): Eth1Block =
|
|
||||||
result = Eth1Block(
|
|
||||||
hash: successor.hash.asEth2Digest,
|
|
||||||
number: Eth1BlockNumber successor.number,
|
|
||||||
timestamp: Eth1BlockTimestamp successor.timestamp)
|
|
||||||
|
|
||||||
func latestCandidateBlock(chain: Eth1Chain, periodStart: uint64): Eth1Block =
|
|
||||||
for i in countdown(chain.blocks.len - 1, 0):
|
|
||||||
let blk = chain.blocks[i]
|
|
||||||
if is_candidate_block(chain.cfg, blk, periodStart):
|
|
||||||
return blk
|
|
||||||
|
|
||||||
proc popFirst(chain: var Eth1Chain) =
|
|
||||||
let removed = chain.blocks.popFirst
|
|
||||||
chain.blocksByHash.del removed.hash.asBlockHash
|
|
||||||
eth1_chain_len.set chain.blocks.len.int64
|
|
||||||
|
|
||||||
func getDepositsRoot*(m: var DepositsMerkleizer): Eth2Digest =
|
|
||||||
mixInLength(m.getFinalHash, int m.totalChunks)
|
|
||||||
|
|
||||||
proc addBlock*(chain: var Eth1Chain, newBlock: Eth1Block) =
|
|
||||||
for deposit in newBlock.deposits:
|
|
||||||
chain.headMerkleizer.addChunk hash_tree_root(deposit).data
|
|
||||||
|
|
||||||
newBlock.depositCount = chain.headMerkleizer.getChunkCount
|
|
||||||
newBlock.depositRoot = chain.headMerkleizer.getDepositsRoot
|
|
||||||
|
|
||||||
chain.blocks.addLast newBlock
|
|
||||||
chain.blocksByHash[newBlock.hash.asBlockHash] = newBlock
|
|
||||||
|
|
||||||
eth1_chain_len.set chain.blocks.len.int64
|
|
||||||
|
|
||||||
func toVoteData(blk: Eth1Block): Eth1Data =
|
|
||||||
Eth1Data(
|
|
||||||
deposit_root: blk.depositRoot,
|
|
||||||
deposit_count: blk.depositCount,
|
|
||||||
block_hash: blk.hash)
|
|
||||||
|
|
||||||
func hash*(x: Eth1Data): Hash =
|
|
||||||
hash(x.block_hash)
|
|
||||||
|
|
||||||
func isConnected(connection: ELConnection): bool =
|
func isConnected(connection: ELConnection): bool =
|
||||||
connection.web3.isSome
|
connection.web3.isSome
|
||||||
|
|
||||||
|
@ -1571,218 +1441,12 @@ when hasDepositRootChecks:
|
||||||
err = err.msg
|
err = err.msg
|
||||||
result = DepositCountUnavailable
|
result = DepositCountUnavailable
|
||||||
|
|
||||||
proc pruneOldBlocks(chain: var Eth1Chain, depositIndex: uint64) =
|
|
||||||
## Called on block finalization to delete old and now redundant data.
|
|
||||||
let initialChunks = chain.finalizedDepositsMerkleizer.getChunkCount
|
|
||||||
var lastBlock: Eth1Block
|
|
||||||
|
|
||||||
while chain.blocks.len > 0:
|
|
||||||
let blk = chain.blocks.peekFirst
|
|
||||||
if blk.depositCount >= depositIndex:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
for deposit in blk.deposits:
|
|
||||||
chain.finalizedDepositsMerkleizer.addChunk hash_tree_root(deposit).data
|
|
||||||
chain.popFirst()
|
|
||||||
lastBlock = blk
|
|
||||||
|
|
||||||
if chain.finalizedDepositsMerkleizer.getChunkCount > initialChunks:
|
|
||||||
chain.finalizedBlockHash = lastBlock.hash
|
|
||||||
chain.db.putDepositTreeSnapshot DepositTreeSnapshot(
|
|
||||||
eth1Block: lastBlock.hash,
|
|
||||||
depositContractState: chain.finalizedDepositsMerkleizer.toDepositContractState,
|
|
||||||
blockHeight: lastBlock.number)
|
|
||||||
|
|
||||||
eth1_finalized_head.set lastBlock.number.toGaugeValue
|
|
||||||
eth1_finalized_deposits.set lastBlock.depositCount.toGaugeValue
|
|
||||||
|
|
||||||
debug "Eth1 blocks pruned",
|
|
||||||
newTailBlock = lastBlock.hash,
|
|
||||||
depositsCount = lastBlock.depositCount
|
|
||||||
|
|
||||||
func advanceMerkleizer(chain: Eth1Chain,
|
|
||||||
merkleizer: var DepositsMerkleizer,
|
|
||||||
depositIndex: uint64): bool =
|
|
||||||
if chain.blocks.len == 0:
|
|
||||||
return depositIndex == merkleizer.getChunkCount
|
|
||||||
|
|
||||||
if chain.blocks.peekLast.depositCount < depositIndex:
|
|
||||||
return false
|
|
||||||
|
|
||||||
let
|
|
||||||
firstBlock = chain.blocks[0]
|
|
||||||
depositsInLastPrunedBlock = firstBlock.depositCount -
|
|
||||||
firstBlock.deposits.lenu64
|
|
||||||
|
|
||||||
# advanceMerkleizer should always be called shortly after prunning the chain
|
|
||||||
doAssert depositsInLastPrunedBlock == merkleizer.getChunkCount
|
|
||||||
|
|
||||||
for blk in chain.blocks:
|
|
||||||
for deposit in blk.deposits:
|
|
||||||
if merkleizer.getChunkCount < depositIndex:
|
|
||||||
merkleizer.addChunk hash_tree_root(deposit).data
|
|
||||||
else:
|
|
||||||
return true
|
|
||||||
|
|
||||||
return merkleizer.getChunkCount == depositIndex
|
|
||||||
|
|
||||||
iterator getDepositsRange*(chain: Eth1Chain, first, last: uint64): DepositData =
|
|
||||||
# TODO It's possible to make this faster by performing binary search that
|
|
||||||
# will locate the blocks holding the `first` and `last` indices.
|
|
||||||
# TODO There is an assumption here that the requested range will be present
|
|
||||||
# in the Eth1Chain. This should hold true at the call sites right now,
|
|
||||||
# but we need to guard the pre-conditions better.
|
|
||||||
for blk in chain.blocks:
|
|
||||||
if blk.depositCount <= first:
|
|
||||||
continue
|
|
||||||
|
|
||||||
let firstDepositIdxInBlk = blk.depositCount - blk.deposits.lenu64
|
|
||||||
if firstDepositIdxInBlk >= last:
|
|
||||||
break
|
|
||||||
|
|
||||||
for i in 0 ..< blk.deposits.lenu64:
|
|
||||||
let globalIdx = firstDepositIdxInBlk + i
|
|
||||||
if globalIdx >= first and globalIdx < last:
|
|
||||||
yield blk.deposits[i]
|
|
||||||
|
|
||||||
func lowerBound(chain: Eth1Chain, depositCount: uint64): Eth1Block =
|
|
||||||
# TODO: This can be replaced with a proper binary search in the
|
|
||||||
# future, but the `algorithm` module currently requires an
|
|
||||||
# `openArray`, which the `deques` module can't provide yet.
|
|
||||||
for eth1Block in chain.blocks:
|
|
||||||
if eth1Block.depositCount > depositCount:
|
|
||||||
return
|
|
||||||
result = eth1Block
|
|
||||||
|
|
||||||
proc trackFinalizedState(chain: var Eth1Chain,
|
|
||||||
finalizedEth1Data: Eth1Data,
|
|
||||||
finalizedStateDepositIndex: uint64,
|
|
||||||
blockProposalExpected = false): bool =
|
|
||||||
## This function will return true if the ELManager is synced
|
|
||||||
## to the finalization point.
|
|
||||||
|
|
||||||
if chain.blocks.len == 0:
|
|
||||||
debug "Eth1 chain not initialized"
|
|
||||||
return false
|
|
||||||
|
|
||||||
let latest = chain.blocks.peekLast
|
|
||||||
if latest.depositCount < finalizedEth1Data.deposit_count:
|
|
||||||
if blockProposalExpected:
|
|
||||||
error "The Eth1 chain is not synced",
|
|
||||||
ourDepositsCount = latest.depositCount,
|
|
||||||
targetDepositsCount = finalizedEth1Data.deposit_count
|
|
||||||
return false
|
|
||||||
|
|
||||||
let matchingBlock = chain.lowerBound(finalizedEth1Data.deposit_count)
|
|
||||||
result = if matchingBlock != nil:
|
|
||||||
if matchingBlock.depositRoot == finalizedEth1Data.deposit_root:
|
|
||||||
true
|
|
||||||
else:
|
|
||||||
error "Corrupted deposits history detected",
|
|
||||||
ourDepositsCount = matchingBlock.depositCount,
|
|
||||||
targetDepositsCount = finalizedEth1Data.deposit_count,
|
|
||||||
ourDepositsRoot = matchingBlock.depositRoot,
|
|
||||||
targetDepositsRoot = finalizedEth1Data.deposit_root
|
|
||||||
chain.hasConsensusViolation = true
|
|
||||||
false
|
|
||||||
else:
|
|
||||||
error "The Eth1 chain is in inconsistent state",
|
|
||||||
checkpointHash = finalizedEth1Data.block_hash,
|
|
||||||
checkpointDeposits = finalizedEth1Data.deposit_count,
|
|
||||||
localChainStart = shortLog(chain.blocks.peekFirst),
|
|
||||||
localChainEnd = shortLog(chain.blocks.peekLast)
|
|
||||||
chain.hasConsensusViolation = true
|
|
||||||
false
|
|
||||||
|
|
||||||
if result:
|
|
||||||
chain.pruneOldBlocks(finalizedStateDepositIndex)
|
|
||||||
|
|
||||||
template trackFinalizedState*(m: ELManager,
|
template trackFinalizedState*(m: ELManager,
|
||||||
finalizedEth1Data: Eth1Data,
|
finalizedEth1Data: Eth1Data,
|
||||||
finalizedStateDepositIndex: uint64): bool =
|
finalizedStateDepositIndex: uint64): bool =
|
||||||
trackFinalizedState(m.eth1Chain, finalizedEth1Data, finalizedStateDepositIndex)
|
trackFinalizedState(m.eth1Chain, finalizedEth1Data, finalizedStateDepositIndex)
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
|
||||||
proc getBlockProposalData*(chain: var Eth1Chain,
|
|
||||||
state: ForkedHashedBeaconState,
|
|
||||||
finalizedEth1Data: Eth1Data,
|
|
||||||
finalizedStateDepositIndex: uint64): BlockProposalEth1Data =
|
|
||||||
let
|
|
||||||
periodStart = voting_period_start_time(state)
|
|
||||||
hasLatestDeposits = chain.trackFinalizedState(finalizedEth1Data,
|
|
||||||
finalizedStateDepositIndex,
|
|
||||||
blockProposalExpected = true)
|
|
||||||
|
|
||||||
var otherVotesCountTable = initCountTable[Eth1Data]()
|
|
||||||
for vote in getStateField(state, eth1_data_votes):
|
|
||||||
let eth1Block = chain.findBlock(vote)
|
|
||||||
if eth1Block != nil and
|
|
||||||
eth1Block.depositRoot == vote.deposit_root and
|
|
||||||
vote.deposit_count >= getStateField(state, eth1_data).deposit_count and
|
|
||||||
is_candidate_block(chain.cfg, eth1Block, periodStart):
|
|
||||||
otherVotesCountTable.inc vote
|
|
||||||
else:
|
|
||||||
debug "Ignoring eth1 vote",
|
|
||||||
root = vote.block_hash,
|
|
||||||
deposits = vote.deposit_count,
|
|
||||||
depositsRoot = vote.deposit_root,
|
|
||||||
localDeposits = getStateField(state, eth1_data).deposit_count
|
|
||||||
|
|
||||||
let
|
|
||||||
stateDepositIdx = getStateField(state, eth1_deposit_index)
|
|
||||||
stateDepositsCount = getStateField(state, eth1_data).deposit_count
|
|
||||||
|
|
||||||
# A valid state should never have this condition, but it doesn't hurt
|
|
||||||
# to be extra defensive here because we are working with uint types
|
|
||||||
var pendingDepositsCount = if stateDepositsCount > stateDepositIdx:
|
|
||||||
stateDepositsCount - stateDepositIdx
|
|
||||||
else:
|
|
||||||
0
|
|
||||||
|
|
||||||
if otherVotesCountTable.len > 0:
|
|
||||||
let (winningVote, votes) = otherVotesCountTable.largest
|
|
||||||
debug "Voting on eth1 head with majority", votes
|
|
||||||
result.vote = winningVote
|
|
||||||
if uint64((votes + 1) * 2) > SLOTS_PER_ETH1_VOTING_PERIOD:
|
|
||||||
pendingDepositsCount = winningVote.deposit_count - stateDepositIdx
|
|
||||||
|
|
||||||
else:
|
|
||||||
let latestBlock = chain.latestCandidateBlock(periodStart)
|
|
||||||
if latestBlock == nil:
|
|
||||||
debug "No acceptable eth1 votes and no recent candidates. Voting no change"
|
|
||||||
result.vote = getStateField(state, eth1_data)
|
|
||||||
else:
|
|
||||||
debug "No acceptable eth1 votes. Voting for latest candidate"
|
|
||||||
result.vote = latestBlock.toVoteData
|
|
||||||
|
|
||||||
if pendingDepositsCount > 0:
|
|
||||||
if hasLatestDeposits:
|
|
||||||
let
|
|
||||||
totalDepositsInNewBlock = min(MAX_DEPOSITS, pendingDepositsCount)
|
|
||||||
postStateDepositIdx = stateDepositIdx + pendingDepositsCount
|
|
||||||
var
|
|
||||||
deposits = newSeqOfCap[DepositData](totalDepositsInNewBlock)
|
|
||||||
depositRoots = newSeqOfCap[Eth2Digest](pendingDepositsCount)
|
|
||||||
for data in chain.getDepositsRange(stateDepositIdx, postStateDepositIdx):
|
|
||||||
if deposits.lenu64 < totalDepositsInNewBlock:
|
|
||||||
deposits.add data
|
|
||||||
depositRoots.add hash_tree_root(data)
|
|
||||||
|
|
||||||
var scratchMerkleizer = chain.finalizedDepositsMerkleizer
|
|
||||||
if chain.advanceMerkleizer(scratchMerkleizer, stateDepositIdx):
|
|
||||||
let proofs = scratchMerkleizer.addChunksAndGenMerkleProofs(depositRoots)
|
|
||||||
for i in 0 ..< totalDepositsInNewBlock:
|
|
||||||
var proof: array[33, Eth2Digest]
|
|
||||||
proof[0..31] = proofs.getProof(i.int)
|
|
||||||
proof[32] = default(Eth2Digest)
|
|
||||||
proof[32].data[0..7] = toBytesLE uint64(postStateDepositIdx)
|
|
||||||
result.deposits.add Deposit(data: deposits[i], proof: proof)
|
|
||||||
else:
|
|
||||||
error "The Eth1 chain is in inconsistent state" # This should not really happen
|
|
||||||
result.hasMissingDeposits = true
|
|
||||||
else:
|
|
||||||
result.hasMissingDeposits = true
|
|
||||||
|
|
||||||
template getBlockProposalData*(m: ELManager,
|
template getBlockProposalData*(m: ELManager,
|
||||||
state: ForkedHashedBeaconState,
|
state: ForkedHashedBeaconState,
|
||||||
finalizedEth1Data: Eth1Data,
|
finalizedEth1Data: Eth1Data,
|
||||||
|
@ -1797,36 +1461,6 @@ func new*(T: type ELConnection,
|
||||||
engineUrl: engineUrl,
|
engineUrl: engineUrl,
|
||||||
depositContractSyncStatus: DepositContractSyncStatus.unknown)
|
depositContractSyncStatus: DepositContractSyncStatus.unknown)
|
||||||
|
|
||||||
proc init*(T: type Eth1Chain,
|
|
||||||
cfg: RuntimeConfig,
|
|
||||||
db: BeaconChainDB,
|
|
||||||
depositContractBlockNumber: uint64,
|
|
||||||
depositContractBlockHash: Eth2Digest): T =
|
|
||||||
let
|
|
||||||
(finalizedBlockHash, depositContractState) =
|
|
||||||
if db != nil:
|
|
||||||
let treeSnapshot = db.getDepositTreeSnapshot()
|
|
||||||
if treeSnapshot.isSome:
|
|
||||||
(treeSnapshot.get.eth1Block, treeSnapshot.get.depositContractState)
|
|
||||||
else:
|
|
||||||
let oldSnapshot = db.getUpgradableDepositSnapshot()
|
|
||||||
if oldSnapshot.isSome:
|
|
||||||
(oldSnapshot.get.eth1Block, oldSnapshot.get.depositContractState)
|
|
||||||
else:
|
|
||||||
db.putDepositTreeSnapshot DepositTreeSnapshot(
|
|
||||||
eth1Block: depositContractBlockHash,
|
|
||||||
blockHeight: depositContractBlockNumber)
|
|
||||||
(depositContractBlockHash, default(DepositContractState))
|
|
||||||
else:
|
|
||||||
(depositContractBlockHash, default(DepositContractState))
|
|
||||||
m = DepositsMerkleizer.init(depositContractState)
|
|
||||||
|
|
||||||
T(db: db,
|
|
||||||
cfg: cfg,
|
|
||||||
finalizedBlockHash: finalizedBlockHash,
|
|
||||||
finalizedDepositsMerkleizer: m,
|
|
||||||
headMerkleizer: m)
|
|
||||||
|
|
||||||
proc new*(T: type ELManager,
|
proc new*(T: type ELManager,
|
||||||
cfg: RuntimeConfig,
|
cfg: RuntimeConfig,
|
||||||
depositContractBlockNumber: uint64,
|
depositContractBlockNumber: uint64,
|
||||||
|
@ -1855,12 +1489,6 @@ proc safeCancel(fut: var Future[void]) =
|
||||||
fut.cancelSoon()
|
fut.cancelSoon()
|
||||||
fut = nil
|
fut = nil
|
||||||
|
|
||||||
func clear(chain: var Eth1Chain) =
|
|
||||||
chain.blocks.clear()
|
|
||||||
chain.blocksByHash.clear()
|
|
||||||
chain.headMerkleizer = chain.finalizedDepositsMerkleizer
|
|
||||||
chain.hasConsensusViolation = false
|
|
||||||
|
|
||||||
proc doStop(m: ELManager) {.async.} =
|
proc doStop(m: ELManager) {.async.} =
|
||||||
safeCancel m.chainSyncingLoopFut
|
safeCancel m.chainSyncingLoopFut
|
||||||
safeCancel m.exchangeTransitionConfigurationLoopFut
|
safeCancel m.exchangeTransitionConfigurationLoopFut
|
||||||
|
|
|
@ -0,0 +1,388 @@
|
||||||
|
# beacon_chain
|
||||||
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[deques, tables, strformat],
|
||||||
|
chronicles, metrics,
|
||||||
|
../beacon_chain_db,
|
||||||
|
../spec/[deposit_snapshots, digest, eth2_merkleization, forks, network],
|
||||||
|
../spec/datatypes/base,
|
||||||
|
web3/[primitives, eth_api_types],
|
||||||
|
./merkle_minimal
|
||||||
|
|
||||||
|
export beacon_chain_db, deques, digest, base, forks
|
||||||
|
|
||||||
|
declarePublicGauge eth1_finalized_head,
|
||||||
|
"Block number of the highest Eth1 block finalized by Eth2 consensus"
|
||||||
|
|
||||||
|
declarePublicGauge eth1_finalized_deposits,
|
||||||
|
"Number of deposits that were finalized by the Eth2 consensus"
|
||||||
|
|
||||||
|
declareGauge eth1_chain_len,
|
||||||
|
"The length of the in-memory chain of Eth1 blocks"
|
||||||
|
|
||||||
|
type
|
||||||
|
Eth1BlockNumber* = uint64
|
||||||
|
Eth1BlockTimestamp* = uint64
|
||||||
|
|
||||||
|
Eth1BlockObj* = object
|
||||||
|
hash*: Eth2Digest
|
||||||
|
number*: Eth1BlockNumber
|
||||||
|
timestamp*: Eth1BlockTimestamp
|
||||||
|
## Basic properties of the block
|
||||||
|
## These must be initialized in the constructor
|
||||||
|
|
||||||
|
deposits*: seq[DepositData]
|
||||||
|
## Deposits inside this particular block
|
||||||
|
|
||||||
|
depositRoot*: Eth2Digest
|
||||||
|
depositCount*: uint64
|
||||||
|
## Global deposits count and hash tree root of the entire sequence
|
||||||
|
## These are computed when the block is added to the chain (see `addBlock`)
|
||||||
|
|
||||||
|
Eth1Block* = ref Eth1BlockObj
|
||||||
|
|
||||||
|
Eth1Chain* = object
|
||||||
|
db: BeaconChainDB
|
||||||
|
cfg*: RuntimeConfig
|
||||||
|
finalizedBlockHash*: Eth2Digest
|
||||||
|
finalizedDepositsMerkleizer*: DepositsMerkleizer
|
||||||
|
## The latest block that reached a 50% majority vote from
|
||||||
|
## the Eth2 validators according to the follow distance and
|
||||||
|
## the ETH1_VOTING_PERIOD
|
||||||
|
|
||||||
|
blocks*: Deque[Eth1Block]
|
||||||
|
## A non-forkable chain of blocks ending at the block with
|
||||||
|
## ETH1_FOLLOW_DISTANCE offset from the head.
|
||||||
|
|
||||||
|
blocksByHash: Table[BlockHash, Eth1Block]
|
||||||
|
|
||||||
|
headMerkleizer: DepositsMerkleizer
|
||||||
|
## Merkleizer state after applying all `blocks`
|
||||||
|
|
||||||
|
hasConsensusViolation*: bool
|
||||||
|
## The local chain contradicts the observed consensus on the network
|
||||||
|
|
||||||
|
BlockProposalEth1Data* = object
|
||||||
|
vote*: Eth1Data
|
||||||
|
deposits*: seq[Deposit]
|
||||||
|
hasMissingDeposits*: bool
|
||||||
|
|
||||||
|
func asEth2Digest*(x: BlockHash): Eth2Digest =
|
||||||
|
Eth2Digest(data: array[32, byte](x))
|
||||||
|
|
||||||
|
template asBlockHash*(x: Eth2Digest): BlockHash =
|
||||||
|
BlockHash(x.data)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
||||||
|
func compute_time_at_slot(genesis_time: uint64, slot: Slot): uint64 =
|
||||||
|
genesis_time + slot * SECONDS_PER_SLOT
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
||||||
|
func voting_period_start_time(state: ForkedHashedBeaconState): uint64 =
|
||||||
|
let eth1_voting_period_start_slot =
|
||||||
|
getStateField(state, slot) - getStateField(state, slot) mod
|
||||||
|
SLOTS_PER_ETH1_VOTING_PERIOD.uint64
|
||||||
|
compute_time_at_slot(
|
||||||
|
getStateField(state, genesis_time), eth1_voting_period_start_slot)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
||||||
|
func is_candidate_block(cfg: RuntimeConfig,
|
||||||
|
blk: Eth1Block,
|
||||||
|
period_start: uint64): bool =
|
||||||
|
(blk.timestamp + cfg.SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE <= period_start) and
|
||||||
|
(blk.timestamp + cfg.SECONDS_PER_ETH1_BLOCK * cfg.ETH1_FOLLOW_DISTANCE * 2 >= period_start)
|
||||||
|
|
||||||
|
func shortLog*(b: Eth1Block): string =
|
||||||
|
try:
|
||||||
|
&"{b.number}:{shortLog b.hash}(deposits = {b.depositCount})"
|
||||||
|
except ValueError as exc: raiseAssert exc.msg
|
||||||
|
|
||||||
|
template findBlock(chain: Eth1Chain, eth1Data: Eth1Data): Eth1Block =
|
||||||
|
getOrDefault(chain.blocksByHash, asBlockHash(eth1Data.block_hash), nil)
|
||||||
|
|
||||||
|
func makeSuccessorWithoutDeposits*(existingBlock: Eth1Block,
|
||||||
|
successor: BlockObject): Eth1Block =
|
||||||
|
result = Eth1Block(
|
||||||
|
hash: successor.hash.asEth2Digest,
|
||||||
|
number: Eth1BlockNumber successor.number,
|
||||||
|
timestamp: Eth1BlockTimestamp successor.timestamp)
|
||||||
|
|
||||||
|
func latestCandidateBlock(chain: Eth1Chain, periodStart: uint64): Eth1Block =
|
||||||
|
for i in countdown(chain.blocks.len - 1, 0):
|
||||||
|
let blk = chain.blocks[i]
|
||||||
|
if is_candidate_block(chain.cfg, blk, periodStart):
|
||||||
|
return blk
|
||||||
|
|
||||||
|
proc popFirst(chain: var Eth1Chain) =
|
||||||
|
let removed = chain.blocks.popFirst
|
||||||
|
chain.blocksByHash.del removed.hash.asBlockHash
|
||||||
|
eth1_chain_len.set chain.blocks.len.int64
|
||||||
|
|
||||||
|
proc addBlock*(chain: var Eth1Chain, newBlock: Eth1Block) =
|
||||||
|
for deposit in newBlock.deposits:
|
||||||
|
chain.headMerkleizer.addChunk hash_tree_root(deposit).data
|
||||||
|
|
||||||
|
newBlock.depositCount = chain.headMerkleizer.getChunkCount
|
||||||
|
newBlock.depositRoot = chain.headMerkleizer.getDepositsRoot
|
||||||
|
|
||||||
|
chain.blocks.addLast newBlock
|
||||||
|
chain.blocksByHash[newBlock.hash.asBlockHash] = newBlock
|
||||||
|
|
||||||
|
eth1_chain_len.set chain.blocks.len.int64
|
||||||
|
|
||||||
|
func toVoteData(blk: Eth1Block): Eth1Data =
|
||||||
|
Eth1Data(
|
||||||
|
deposit_root: blk.depositRoot,
|
||||||
|
deposit_count: blk.depositCount,
|
||||||
|
block_hash: blk.hash)
|
||||||
|
|
||||||
|
func hash*(x: Eth1Data): Hash =
|
||||||
|
hash(x.block_hash)
|
||||||
|
|
||||||
|
proc pruneOldBlocks(chain: var Eth1Chain, depositIndex: uint64) =
|
||||||
|
## Called on block finalization to delete old and now redundant data.
|
||||||
|
let initialChunks = chain.finalizedDepositsMerkleizer.getChunkCount
|
||||||
|
var lastBlock: Eth1Block
|
||||||
|
|
||||||
|
while chain.blocks.len > 0:
|
||||||
|
let blk = chain.blocks.peekFirst
|
||||||
|
if blk.depositCount >= depositIndex:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for deposit in blk.deposits:
|
||||||
|
chain.finalizedDepositsMerkleizer.addChunk hash_tree_root(deposit).data
|
||||||
|
chain.popFirst()
|
||||||
|
lastBlock = blk
|
||||||
|
|
||||||
|
if chain.finalizedDepositsMerkleizer.getChunkCount > initialChunks:
|
||||||
|
chain.finalizedBlockHash = lastBlock.hash
|
||||||
|
chain.db.putDepositTreeSnapshot DepositTreeSnapshot(
|
||||||
|
eth1Block: lastBlock.hash,
|
||||||
|
depositContractState: chain.finalizedDepositsMerkleizer.toDepositContractState,
|
||||||
|
blockHeight: lastBlock.number)
|
||||||
|
|
||||||
|
eth1_finalized_head.set lastBlock.number.toGaugeValue
|
||||||
|
eth1_finalized_deposits.set lastBlock.depositCount.toGaugeValue
|
||||||
|
|
||||||
|
debug "Eth1 blocks pruned",
|
||||||
|
newTailBlock = lastBlock.hash,
|
||||||
|
depositsCount = lastBlock.depositCount
|
||||||
|
|
||||||
|
func advanceMerkleizer(chain: Eth1Chain,
|
||||||
|
merkleizer: var DepositsMerkleizer,
|
||||||
|
depositIndex: uint64): bool =
|
||||||
|
if chain.blocks.len == 0:
|
||||||
|
return depositIndex == merkleizer.getChunkCount
|
||||||
|
|
||||||
|
if chain.blocks.peekLast.depositCount < depositIndex:
|
||||||
|
return false
|
||||||
|
|
||||||
|
let
|
||||||
|
firstBlock = chain.blocks[0]
|
||||||
|
depositsInLastPrunedBlock = firstBlock.depositCount -
|
||||||
|
firstBlock.deposits.lenu64
|
||||||
|
|
||||||
|
# advanceMerkleizer should always be called shortly after prunning the chain
|
||||||
|
doAssert depositsInLastPrunedBlock == merkleizer.getChunkCount
|
||||||
|
|
||||||
|
for blk in chain.blocks:
|
||||||
|
for deposit in blk.deposits:
|
||||||
|
if merkleizer.getChunkCount < depositIndex:
|
||||||
|
merkleizer.addChunk hash_tree_root(deposit).data
|
||||||
|
else:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return merkleizer.getChunkCount == depositIndex
|
||||||
|
|
||||||
|
iterator getDepositsRange*(chain: Eth1Chain, first, last: uint64): DepositData =
|
||||||
|
# TODO It's possible to make this faster by performing binary search that
|
||||||
|
# will locate the blocks holding the `first` and `last` indices.
|
||||||
|
# TODO There is an assumption here that the requested range will be present
|
||||||
|
# in the Eth1Chain. This should hold true at the call sites right now,
|
||||||
|
# but we need to guard the pre-conditions better.
|
||||||
|
for blk in chain.blocks:
|
||||||
|
if blk.depositCount <= first:
|
||||||
|
continue
|
||||||
|
|
||||||
|
let firstDepositIdxInBlk = blk.depositCount - blk.deposits.lenu64
|
||||||
|
if firstDepositIdxInBlk >= last:
|
||||||
|
break
|
||||||
|
|
||||||
|
for i in 0 ..< blk.deposits.lenu64:
|
||||||
|
let globalIdx = firstDepositIdxInBlk + i
|
||||||
|
if globalIdx >= first and globalIdx < last:
|
||||||
|
yield blk.deposits[i]
|
||||||
|
|
||||||
|
func lowerBound(chain: Eth1Chain, depositCount: uint64): Eth1Block =
|
||||||
|
# TODO: This can be replaced with a proper binary search in the
|
||||||
|
# future, but the `algorithm` module currently requires an
|
||||||
|
# `openArray`, which the `deques` module can't provide yet.
|
||||||
|
for eth1Block in chain.blocks:
|
||||||
|
if eth1Block.depositCount > depositCount:
|
||||||
|
return
|
||||||
|
result = eth1Block
|
||||||
|
|
||||||
|
proc trackFinalizedState*(chain: var Eth1Chain,
|
||||||
|
finalizedEth1Data: Eth1Data,
|
||||||
|
finalizedStateDepositIndex: uint64,
|
||||||
|
blockProposalExpected = false): bool =
|
||||||
|
## This function will return true if the ELManager is synced
|
||||||
|
## to the finalization point.
|
||||||
|
|
||||||
|
if chain.blocks.len == 0:
|
||||||
|
debug "Eth1 chain not initialized"
|
||||||
|
return false
|
||||||
|
|
||||||
|
let latest = chain.blocks.peekLast
|
||||||
|
if latest.depositCount < finalizedEth1Data.deposit_count:
|
||||||
|
if blockProposalExpected:
|
||||||
|
error "The Eth1 chain is not synced",
|
||||||
|
ourDepositsCount = latest.depositCount,
|
||||||
|
targetDepositsCount = finalizedEth1Data.deposit_count
|
||||||
|
return false
|
||||||
|
|
||||||
|
let matchingBlock = chain.lowerBound(finalizedEth1Data.deposit_count)
|
||||||
|
result = if matchingBlock != nil:
|
||||||
|
if matchingBlock.depositRoot == finalizedEth1Data.deposit_root:
|
||||||
|
true
|
||||||
|
else:
|
||||||
|
error "Corrupted deposits history detected",
|
||||||
|
ourDepositsCount = matchingBlock.depositCount,
|
||||||
|
targetDepositsCount = finalizedEth1Data.deposit_count,
|
||||||
|
ourDepositsRoot = matchingBlock.depositRoot,
|
||||||
|
targetDepositsRoot = finalizedEth1Data.deposit_root
|
||||||
|
chain.hasConsensusViolation = true
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
error "The Eth1 chain is in inconsistent state",
|
||||||
|
checkpointHash = finalizedEth1Data.block_hash,
|
||||||
|
checkpointDeposits = finalizedEth1Data.deposit_count,
|
||||||
|
localChainStart = shortLog(chain.blocks.peekFirst),
|
||||||
|
localChainEnd = shortLog(chain.blocks.peekLast)
|
||||||
|
chain.hasConsensusViolation = true
|
||||||
|
false
|
||||||
|
|
||||||
|
if result:
|
||||||
|
chain.pruneOldBlocks(finalizedStateDepositIndex)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.5/specs/phase0/validator.md#get_eth1_data
|
||||||
|
proc getBlockProposalData*(chain: var Eth1Chain,
|
||||||
|
state: ForkedHashedBeaconState,
|
||||||
|
finalizedEth1Data: Eth1Data,
|
||||||
|
finalizedStateDepositIndex: uint64): BlockProposalEth1Data =
|
||||||
|
let
|
||||||
|
periodStart = voting_period_start_time(state)
|
||||||
|
hasLatestDeposits = chain.trackFinalizedState(finalizedEth1Data,
|
||||||
|
finalizedStateDepositIndex,
|
||||||
|
blockProposalExpected = true)
|
||||||
|
|
||||||
|
var otherVotesCountTable = initCountTable[Eth1Data]()
|
||||||
|
for vote in getStateField(state, eth1_data_votes):
|
||||||
|
let eth1Block = chain.findBlock(vote)
|
||||||
|
if eth1Block != nil and
|
||||||
|
eth1Block.depositRoot == vote.deposit_root and
|
||||||
|
vote.deposit_count >= getStateField(state, eth1_data).deposit_count and
|
||||||
|
is_candidate_block(chain.cfg, eth1Block, periodStart):
|
||||||
|
otherVotesCountTable.inc vote
|
||||||
|
else:
|
||||||
|
debug "Ignoring eth1 vote",
|
||||||
|
root = vote.block_hash,
|
||||||
|
deposits = vote.deposit_count,
|
||||||
|
depositsRoot = vote.deposit_root,
|
||||||
|
localDeposits = getStateField(state, eth1_data).deposit_count
|
||||||
|
|
||||||
|
let
|
||||||
|
stateDepositIdx = getStateField(state, eth1_deposit_index)
|
||||||
|
stateDepositsCount = getStateField(state, eth1_data).deposit_count
|
||||||
|
|
||||||
|
# A valid state should never have this condition, but it doesn't hurt
|
||||||
|
# to be extra defensive here because we are working with uint types
|
||||||
|
var pendingDepositsCount = if stateDepositsCount > stateDepositIdx:
|
||||||
|
stateDepositsCount - stateDepositIdx
|
||||||
|
else:
|
||||||
|
0
|
||||||
|
|
||||||
|
if otherVotesCountTable.len > 0:
|
||||||
|
let (winningVote, votes) = otherVotesCountTable.largest
|
||||||
|
debug "Voting on eth1 head with majority", votes
|
||||||
|
result.vote = winningVote
|
||||||
|
if uint64((votes + 1) * 2) > SLOTS_PER_ETH1_VOTING_PERIOD:
|
||||||
|
pendingDepositsCount = winningVote.deposit_count - stateDepositIdx
|
||||||
|
|
||||||
|
else:
|
||||||
|
let latestBlock = chain.latestCandidateBlock(periodStart)
|
||||||
|
if latestBlock == nil:
|
||||||
|
debug "No acceptable eth1 votes and no recent candidates. Voting no change"
|
||||||
|
result.vote = getStateField(state, eth1_data)
|
||||||
|
else:
|
||||||
|
debug "No acceptable eth1 votes. Voting for latest candidate"
|
||||||
|
result.vote = latestBlock.toVoteData
|
||||||
|
|
||||||
|
if pendingDepositsCount > 0:
|
||||||
|
if hasLatestDeposits:
|
||||||
|
let
|
||||||
|
totalDepositsInNewBlock = min(MAX_DEPOSITS, pendingDepositsCount)
|
||||||
|
postStateDepositIdx = stateDepositIdx + pendingDepositsCount
|
||||||
|
var
|
||||||
|
deposits = newSeqOfCap[DepositData](totalDepositsInNewBlock)
|
||||||
|
depositRoots = newSeqOfCap[Eth2Digest](pendingDepositsCount)
|
||||||
|
for data in chain.getDepositsRange(stateDepositIdx, postStateDepositIdx):
|
||||||
|
if deposits.lenu64 < totalDepositsInNewBlock:
|
||||||
|
deposits.add data
|
||||||
|
depositRoots.add hash_tree_root(data)
|
||||||
|
|
||||||
|
var scratchMerkleizer = chain.finalizedDepositsMerkleizer
|
||||||
|
if chain.advanceMerkleizer(scratchMerkleizer, stateDepositIdx):
|
||||||
|
let proofs = scratchMerkleizer.addChunksAndGenMerkleProofs(depositRoots)
|
||||||
|
for i in 0 ..< totalDepositsInNewBlock:
|
||||||
|
var proof: array[33, Eth2Digest]
|
||||||
|
proof[0..31] = proofs.getProof(i.int)
|
||||||
|
proof[32] = default(Eth2Digest)
|
||||||
|
proof[32].data[0..7] = toBytesLE uint64(postStateDepositIdx)
|
||||||
|
result.deposits.add Deposit(data: deposits[i], proof: proof)
|
||||||
|
else:
|
||||||
|
error "The Eth1 chain is in inconsistent state" # This should not really happen
|
||||||
|
result.hasMissingDeposits = true
|
||||||
|
else:
|
||||||
|
result.hasMissingDeposits = true
|
||||||
|
|
||||||
|
func clear*(chain: var Eth1Chain) =
|
||||||
|
chain.blocks.clear()
|
||||||
|
chain.blocksByHash.clear()
|
||||||
|
chain.headMerkleizer = chain.finalizedDepositsMerkleizer
|
||||||
|
chain.hasConsensusViolation = false
|
||||||
|
|
||||||
|
proc init*(T: type Eth1Chain,
|
||||||
|
cfg: RuntimeConfig,
|
||||||
|
db: BeaconChainDB,
|
||||||
|
depositContractBlockNumber: uint64,
|
||||||
|
depositContractBlockHash: Eth2Digest): T =
|
||||||
|
let
|
||||||
|
(finalizedBlockHash, depositContractState) =
|
||||||
|
if db != nil:
|
||||||
|
let treeSnapshot = db.getDepositTreeSnapshot()
|
||||||
|
if treeSnapshot.isSome:
|
||||||
|
(treeSnapshot.get.eth1Block, treeSnapshot.get.depositContractState)
|
||||||
|
else:
|
||||||
|
let oldSnapshot = db.getUpgradableDepositSnapshot()
|
||||||
|
if oldSnapshot.isSome:
|
||||||
|
(oldSnapshot.get.eth1Block, oldSnapshot.get.depositContractState)
|
||||||
|
else:
|
||||||
|
db.putDepositTreeSnapshot DepositTreeSnapshot(
|
||||||
|
eth1Block: depositContractBlockHash,
|
||||||
|
blockHeight: depositContractBlockNumber)
|
||||||
|
(depositContractBlockHash, default(DepositContractState))
|
||||||
|
else:
|
||||||
|
(depositContractBlockHash, default(DepositContractState))
|
||||||
|
m = DepositsMerkleizer.init(depositContractState)
|
||||||
|
|
||||||
|
T(db: db,
|
||||||
|
cfg: cfg,
|
||||||
|
finalizedBlockHash: finalizedBlockHash,
|
||||||
|
finalizedDepositsMerkleizer: m,
|
||||||
|
headMerkleizer: m)
|
|
@ -21,7 +21,7 @@ import
|
||||||
".."/[beacon_clock],
|
".."/[beacon_clock],
|
||||||
./batch_validation
|
./batch_validation
|
||||||
|
|
||||||
from libp2p/protocols/pubsub/pubsub import ValidationResult
|
from libp2p/protocols/pubsub/errors import ValidationResult
|
||||||
|
|
||||||
export results, ValidationResult
|
export results, ValidationResult
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ import
|
||||||
eth/trie/[db, hexary],
|
eth/trie/[db, hexary],
|
||||||
json_rpc/jsonmarshal,
|
json_rpc/jsonmarshal,
|
||||||
secp256k1,
|
secp256k1,
|
||||||
web3/eth_api_types,
|
web3/[engine_api_types, eth_api_types, conversions],
|
||||||
../el/el_manager,
|
../el/eth1_chain,
|
||||||
../spec/eth2_apis/[eth2_rest_serialization, rest_light_client_calls],
|
../spec/eth2_apis/[eth2_rest_serialization, rest_light_client_calls],
|
||||||
../spec/[helpers, light_client_sync],
|
../spec/[helpers, light_client_sync],
|
||||||
../sync/light_client_sync_helpers,
|
../sync/light_client_sync_helpers,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import
|
||||||
std/sequtils,
|
std/sequtils,
|
||||||
stew/results,
|
stew/results,
|
||||||
chronicles,
|
chronicles,
|
||||||
|
chronos/apps/http/httpserver,
|
||||||
./rest_utils,
|
./rest_utils,
|
||||||
../beacon_node
|
../beacon_node
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import std/macros,
|
import std/macros,
|
||||||
results, stew/byteutils, presto,
|
results, stew/byteutils, presto/route,
|
||||||
../spec/[forks],
|
../spec/[forks],
|
||||||
../spec/eth2_apis/[rest_types, eth2_rest_serialization, rest_common],
|
../spec/eth2_apis/[rest_types, eth2_rest_serialization, rest_common],
|
||||||
../validators/beacon_validators,
|
../validators/beacon_validators,
|
||||||
|
@ -17,8 +17,8 @@ import std/macros,
|
||||||
"."/[rest_constants, state_ttl_cache]
|
"."/[rest_constants, state_ttl_cache]
|
||||||
|
|
||||||
export
|
export
|
||||||
results, eth2_rest_serialization, blockchain_dag, presto, rest_types,
|
results, eth2_rest_serialization, blockchain_dag, rest_types,
|
||||||
rest_constants, rest_common
|
rest_constants, rest_common, route
|
||||||
|
|
||||||
proc getSyncedHead*(
|
proc getSyncedHead*(
|
||||||
node: BeaconNode,
|
node: BeaconNode,
|
||||||
|
|
|
@ -60,3 +60,6 @@ func toDepositContractState*(merkleizer: DepositsMerkleizer): DepositContractSta
|
||||||
# not populated to its maximum size.
|
# not populated to its maximum size.
|
||||||
result.branch[0..31] = merkleizer.getCombinedChunks[0..31]
|
result.branch[0..31] = merkleizer.getCombinedChunks[0..31]
|
||||||
result.deposit_count[24..31] = merkleizer.getChunkCount().toBytesBE
|
result.deposit_count[24..31] = merkleizer.getChunkCount().toBytesBE
|
||||||
|
|
||||||
|
func getDepositsRoot*(m: var DepositsMerkleizer): Eth2Digest =
|
||||||
|
mixInLength(m.getFinalHash, int m.totalChunks)
|
||||||
|
|
|
@ -24,7 +24,7 @@ import
|
||||||
from std/times import Time, toUnix, fromUnix, getTime
|
from std/times import Time, toUnix, fromUnix, getTime
|
||||||
|
|
||||||
export
|
export
|
||||||
os, sets, sequtils, chronos, presto, chronicles, confutils,
|
os, sets, sequtils, chronos, chronicles, confutils,
|
||||||
nimbus_binary_common, version, conf, tables, results, base10,
|
nimbus_binary_common, version, conf, tables, results, base10,
|
||||||
byteutils, presto_client, eth2_rest_serialization, rest_beacon_client,
|
byteutils, presto_client, eth2_rest_serialization, rest_beacon_client,
|
||||||
phase0, altair, helpers, signatures, validator, eth2_merkleization,
|
phase0, altair, helpers, signatures, validator, eth2_merkleization,
|
||||||
|
|
|
@ -11,7 +11,7 @@ import
|
||||||
std/[tables, json, streams, sequtils, uri],
|
std/[tables, json, streams, sequtils, uri],
|
||||||
chronos, chronicles, metrics,
|
chronos, chronicles, metrics,
|
||||||
json_serialization/std/net,
|
json_serialization/std/net,
|
||||||
presto, presto/client,
|
presto/client,
|
||||||
|
|
||||||
../spec/[keystore, signatures, helpers, crypto],
|
../spec/[keystore, signatures, helpers, crypto],
|
||||||
../spec/datatypes/[phase0, altair],
|
../spec/datatypes/[phase0, altair],
|
||||||
|
|
|
@ -16,6 +16,7 @@ import
|
||||||
confutils, chronicles, eth/db/kvstore_sqlite3,
|
confutils, chronicles, eth/db/kvstore_sqlite3,
|
||||||
chronos/timer, taskpools,
|
chronos/timer, taskpools,
|
||||||
../tests/testblockutil,
|
../tests/testblockutil,
|
||||||
|
../beacon_chain/el/eth1_chain,
|
||||||
../beacon_chain/spec/[forks, state_transition],
|
../beacon_chain/spec/[forks, state_transition],
|
||||||
../beacon_chain/beacon_chain_db,
|
../beacon_chain/beacon_chain_db,
|
||||||
../beacon_chain/validators/validator_pool,
|
../beacon_chain/validators/validator_pool,
|
||||||
|
@ -34,9 +35,6 @@ from ../beacon_chain/consensus_object_pools/block_quarantine import
|
||||||
from ../beacon_chain/consensus_object_pools/sync_committee_msg_pool import
|
from ../beacon_chain/consensus_object_pools/sync_committee_msg_pool import
|
||||||
SyncCommitteeMsgPool, addContribution, addSyncCommitteeMessage, init,
|
SyncCommitteeMsgPool, addContribution, addSyncCommitteeMessage, init,
|
||||||
produceContribution, produceSyncAggregate, pruneData
|
produceContribution, produceSyncAggregate, pruneData
|
||||||
from ../beacon_chain/el/el_manager import
|
|
||||||
Eth1Block, Eth1BlockNumber, Eth1BlockTimestamp, Eth1Chain, addBlock,
|
|
||||||
getBlockProposalData, getDepositsRoot, init
|
|
||||||
from ../beacon_chain/spec/beaconstate import
|
from ../beacon_chain/spec/beaconstate import
|
||||||
get_beacon_committee, get_beacon_proposer_index,
|
get_beacon_committee, get_beacon_proposer_index,
|
||||||
get_committee_count_per_slot, get_committee_indices
|
get_committee_count_per_slot, get_committee_indices
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# beacon_chain
|
# beacon_chain
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed and distributed under either of
|
# Licensed and distributed under either of
|
||||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
confutils,
|
confutils, presto,
|
||||||
../beacon_chain/spec/datatypes/capella,
|
../beacon_chain/spec/datatypes/capella,
|
||||||
../beacon_chain/rpc/rest_utils,
|
../beacon_chain/rpc/rest_utils,
|
||||||
../beacon_chain/spec/eth2_apis/rest_beacon_client
|
../beacon_chain/spec/eth2_apis/rest_beacon_client
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
std/algorithm,
|
std/algorithm,
|
||||||
presto, unittest2, chronicles, stew/[results, byteutils, io2],
|
unittest2, chronicles, stew/[results, byteutils, io2],
|
||||||
chronos/asyncproc,
|
chronos/asyncproc,
|
||||||
chronos/unittest2/asynctests,
|
chronos/unittest2/asynctests,
|
||||||
../beacon_chain/spec/[signatures, crypto],
|
../beacon_chain/spec/[signatures, crypto],
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
import std/strutils
|
import std/strutils
|
||||||
import httputils
|
import httputils
|
||||||
|
import chronos/apps/http/httpserver
|
||||||
import chronos/unittest2/asynctests
|
import chronos/unittest2/asynctests
|
||||||
import ../beacon_chain/spec/eth2_apis/eth2_rest_serialization,
|
import ../beacon_chain/spec/eth2_apis/eth2_rest_serialization,
|
||||||
../beacon_chain/validator_client/[api, common, scoring, fallback_service]
|
../beacon_chain/validator_client/[api, common, scoring, fallback_service]
|
||||||
|
|
Loading…
Reference in New Issue