disable NewBlockHashes and NewBlock of eth wire handler after POS transition

fix #1133
This commit is contained in:
jangko 2022-11-14 14:32:33 +07:00
parent c7b3c374f0
commit 43f4b99a1b
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
8 changed files with 126 additions and 53 deletions

View File

@ -22,7 +22,8 @@ import
rpc/debug, rpc/debug,
rpc/jwt_auth, rpc/jwt_auth,
sync/protocol, sync/protocol,
utils/tx_pool utils/tx_pool,
merge/merger
], ],
../../../tests/test_helpers, ../../../tests/test_helpers,
"."/[clmock, engine_client] "."/[clmock, engine_client]
@ -112,8 +113,9 @@ proc setupELClient*(t: TestEnv, chainFile: string, enableAuth: bool) =
txPool, EngineStopped txPool, EngineStopped
) )
let merger = MergerRef.new(t.chainDB)
setupEthRpc(t.ethNode, t.ctx, t.chainDB, txPool, t.rpcServer) setupEthRpc(t.ethNode, t.ctx, t.chainDB, txPool, t.rpcServer)
setupEngineAPI(t.sealingEngine, t.rpcServer) setupEngineAPI(t.sealingEngine, t.rpcServer, merger)
setupDebugRpc(t.chainDB, t.rpcServer) setupDebugRpc(t.chainDB, t.rpcServer)
# Do not start clique sealing engine if we are using a Proof of Work chain file # Do not start clique sealing engine if we are using a Proof of Work chain file

View File

@ -13,60 +13,56 @@ type
# Merger is an internal help structure used to track the eth1/2 transition status. # Merger is an internal help structure used to track the eth1/2 transition status.
# It's a common structure can be used in both full node and light client. # It's a common structure can be used in both full node and light client.
Merger* = object MergerRef* = ref object
db : TrieDatabaseRef db : TrieDatabaseRef
status: TransitionStatus status: TransitionStatus
proc write(db: TrieDatabaseRef, status: TransitionStatus) = proc writeStatus(db: TrieDatabaseRef, status: TransitionStatus) =
db.put(transitionStatusKey().toOpenArray(), rlp.encode(status)) db.put(transitionStatusKey().toOpenArray(), rlp.encode(status))
proc read(db: TrieDatabaseRef, status: var TransitionStatus) = proc readStatus(db: TrieDatabaseRef): TransitionStatus =
var bytes = db.get(transitionStatusKey().toOpenArray()) var bytes = db.get(transitionStatusKey().toOpenArray())
if bytes.len > 0: if bytes.len > 0:
try: try:
status = rlp.decode(bytes, typeof status) result = rlp.decode(bytes, typeof result)
except: except:
error "Failed to decode transition status" error "Failed to decode POS transition status"
proc init*(m: var Merger, db: TrieDatabaseRef) = proc new*(_: type MergerRef, db: TrieDatabaseRef): MergerRef =
m.db = db MergerRef(
db.read(m.status) db: db,
status: db.readStatus()
)
proc init*(m: var Merger, db: BaseChainDB) = proc new*(_: type MergerRef, db: BaseChainDB): MergerRef =
init(m, db.db) MergerRef.new(db.db)
proc initMerger*(db: BaseChainDB): Merger =
result.init(db)
proc initMerger*(db: TrieDatabaseRef): Merger =
result.init(db)
# ReachTTD is called whenever the first NewHead message received # ReachTTD is called whenever the first NewHead message received
# from the consensus-layer. # from the consensus-layer.
proc reachTTD*(m: var Merger) = proc reachTTD*(m: MergerRef) =
if m.status.leftPoW: if m.status.leftPoW:
return return
m.status = TransitionStatus(leftPoW: true) m.status = TransitionStatus(leftPoW: true)
m.db.write(m.status) m.db.writeStatus(m.status)
info "Left PoW stage" info "Left PoW stage"
# FinalizePoS is called whenever the first FinalisedBlock message received # FinalizePoS is called whenever the first FinalisedBlock message received
# from the consensus-layer. # from the consensus-layer.
proc finalizePoS*(m: var Merger) = proc finalizePoS*(m: MergerRef) =
if m.status.enteredPoS: if m.status.enteredPoS:
return return
m.status = TransitionStatus(leftPoW: true, enteredPoS: true) m.status = TransitionStatus(leftPoW: true, enteredPoS: true)
m.db.write(m.status) m.db.writeStatus(m.status)
info "Entered PoS stage" info "Entered PoS stage"
# TTDReached reports whether the chain has left the PoW stage. # TTDReached reports whether the chain has left the PoW stage.
proc ttdReached*(m: Merger): bool = proc ttdReached*(m: MergerRef): bool =
m.status.leftPoW m.status.leftPoW
# PoSFinalized reports whether the chain has entered the PoS stage. # PoSFinalized reports whether the chain has entered the PoS stage.
proc posFinalized*(m: Merger): bool = proc posFinalized*(m: MergerRef): bool =
m.status.enteredPoS m.status.enteredPoS

View File

@ -37,8 +37,8 @@ type
hash: Hash256 hash: Hash256
header: EthBlockHeader header: EthBlockHeader
EngineAPI* = ref object EngineApiRef* = ref object
merger*: Merger merger: MergerRef
payloadQueue: SimpleQueue[MaxTrackedPayloads, PayloadItem] payloadQueue: SimpleQueue[MaxTrackedPayloads, PayloadItem]
headerQueue: SimpleQueue[MaxTrackedHeaders, HeaderItem] headerQueue: SimpleQueue[MaxTrackedHeaders, HeaderItem]
@ -54,27 +54,33 @@ iterator items[M, T](x: SimpleQueue[M, T]): T =
if z.used: if z.used:
yield z.data yield z.data
proc new*(_: type EngineAPI, db: BaseChainDB): EngineAPI = template new*(_: type EngineApiRef): EngineApiRef =
new result {.error: "EngineApiRef should be created with merger param " & $instantiationInfo().}
if not db.isNil:
result.merger.init(db)
proc put*(api: EngineAPI, hash: Hash256, header: EthBlockHeader) = proc new*(_: type EngineApiRef, merger: MergerRef): EngineApiRef =
EngineApiRef(
merger: merger
)
proc put*(api: EngineApiRef, hash: Hash256, header: EthBlockHeader) =
api.headerQueue.put(HeaderItem(hash: hash, header: header)) api.headerQueue.put(HeaderItem(hash: hash, header: header))
proc get*(api: EngineAPI, hash: Hash256, header: var EthBlockHeader): bool = proc get*(api: EngineApiRef, hash: Hash256, header: var EthBlockHeader): bool =
for x in api.headerQueue: for x in api.headerQueue:
if x.hash == hash: if x.hash == hash:
header = x.header header = x.header
return true return true
false false
proc put*(api: EngineAPI, id: PayloadID, payload: ExecutionPayloadV1) = proc put*(api: EngineApiRef, id: PayloadID, payload: ExecutionPayloadV1) =
api.payloadQueue.put(PayloadItem(id: id, payload: payload)) api.payloadQueue.put(PayloadItem(id: id, payload: payload))
proc get*(api: EngineAPI, id: PayloadID, payload: var ExecutionPayloadV1): bool = proc get*(api: EngineApiRef, id: PayloadID, payload: var ExecutionPayloadV1): bool =
for x in api.payloadQueue: for x in api.payloadQueue:
if x.id == id: if x.id == id:
payload = x.payload payload = x.payload
return true return true
false false
proc merger*(api: EngineApiRef): MergerRef =
api.merger

View File

@ -28,7 +28,8 @@ import
./p2p/[chain, clique/clique_desc, clique/clique_sealer], ./p2p/[chain, clique/clique_desc, clique/clique_sealer],
./rpc/[common, debug, engine_api, jwt_auth, p2p, cors], ./rpc/[common, debug, engine_api, jwt_auth, p2p, cors],
./sync/[fast, full, protocol, snap, protocol/les_protocol, handlers], ./sync/[fast, full, protocol, snap, protocol/les_protocol, handlers],
./utils/tx_pool ./utils/tx_pool,
./merge/merger
when defined(evmc_enabled): when defined(evmc_enabled):
import transaction/evmc_dynamic_loader import transaction/evmc_dynamic_loader
@ -57,6 +58,7 @@ type
networkLoop: Future[void] networkLoop: Future[void]
dbBackend: ChainDB dbBackend: ChainDB
peerManager: PeerManagerRef peerManager: PeerManagerRef
merger: MergerRef
proc importBlocks(conf: NimbusConf, chainDB: BaseChainDB) = proc importBlocks(conf: NimbusConf, chainDB: BaseChainDB) =
if string(conf.blocksFile).len > 0: if string(conf.blocksFile).len > 0:
@ -81,6 +83,11 @@ proc basicServices(nimbus: NimbusNode,
nimbus.chainRef.extraValidation = 0 < verifyFrom nimbus.chainRef.extraValidation = 0 < verifyFrom
nimbus.chainRef.verifyFrom = verifyFrom nimbus.chainRef.verifyFrom = verifyFrom
# this is temporary workaround to track POS transition
# until we have proper chain config and hard fork module
# see issue #640
nimbus.merger = MergerRef.new(chainDB)
proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) = proc manageAccounts(nimbus: NimbusNode, conf: NimbusConf) =
if string(conf.keyStore).len > 0: if string(conf.keyStore).len > 0:
let res = nimbus.ctx.am.loadKeystores(string conf.keyStore) let res = nimbus.ctx.am.loadKeystores(string conf.keyStore)
@ -141,7 +148,8 @@ proc setupP2P(nimbus: NimbusNode, conf: NimbusConf,
let ethWireHandler = EthWireRef.new( let ethWireHandler = EthWireRef.new(
nimbus.chainRef, nimbus.chainRef,
nimbus.txPool, nimbus.txPool,
nimbus.ethNode.peerPool nimbus.ethNode.peerPool,
nimbus.merger
) )
nimbus.ethNode.addCapability(protocol.eth, ethWireHandler) nimbus.ethNode.addCapability(protocol.eth, ethWireHandler)
case conf.syncMode: case conf.syncMode:
@ -336,11 +344,11 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf,
[initTAddress(conf.engineApiAddress, conf.engineApiPort)], [initTAddress(conf.engineApiAddress, conf.engineApiPort)],
authHooks = @[httpJwtAuthHook, httpCorsHook] authHooks = @[httpJwtAuthHook, httpCorsHook]
) )
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer) setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiServer, nimbus.merger)
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiServer) setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiServer)
nimbus.engineApiServer.start() nimbus.engineApiServer.start()
else: else:
setupEngineAPI(nimbus.sealingEngine, nimbus.rpcServer) setupEngineAPI(nimbus.sealingEngine, nimbus.rpcServer, nimbus.merger)
info "Starting engine API server", port = conf.engineApiPort info "Starting engine API server", port = conf.engineApiPort
@ -350,11 +358,11 @@ proc localServices(nimbus: NimbusNode, conf: NimbusConf,
initTAddress(conf.engineApiWsAddress, conf.engineApiWsPort), initTAddress(conf.engineApiWsAddress, conf.engineApiWsPort),
authHooks = @[wsJwtAuthHook, wsCorsHook] authHooks = @[wsJwtAuthHook, wsCorsHook]
) )
setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiWsServer) setupEngineAPI(nimbus.sealingEngine, nimbus.engineApiWsServer, nimbus.merger)
setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiWsServer) setupEthRpc(nimbus.ethNode, nimbus.ctx, chainDB, nimbus.txPool, nimbus.engineApiWsServer)
nimbus.engineApiWsServer.start() nimbus.engineApiWsServer.start()
else: else:
setupEngineAPI(nimbus.sealingEngine, nimbus.wsRpcServer) setupEngineAPI(nimbus.sealingEngine, nimbus.wsRpcServer, nimbus.merger)
info "Starting WebSocket engine API server", port = conf.engineApiWsPort info "Starting WebSocket engine API server", port = conf.engineApiWsPort

View File

@ -41,12 +41,13 @@ proc invalidFCU(db: BaseChainDB, header: EthBlockHeader): ForkchoiceUpdatedRespo
let blockHash = latestValidHash(db, parent, db.ttd()) let blockHash = latestValidHash(db, parent, db.ttd())
invalidFCU(blockHash) invalidFCU(blockHash)
proc setupEngineAPI*( proc setupEngineApi*(
sealingEngine: SealingEngineRef, sealingEngine: SealingEngineRef,
server: RpcServer) = server: RpcServer,
merger: MergerRef) =
# TODO: put this singleton somewhere else # TODO: put this singleton somewhere else
let api = EngineAPI.new(sealingEngine.chain.db) let api = EngineApiRef.new(merger)
# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv1 # https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md#engine_newpayloadv1
# cannot use `params` as param name. see https:#github.com/status-im/nim-json-rpc/issues/128 # cannot use `params` as param name. see https:#github.com/status-im/nim-json-rpc/issues/128

View File

@ -9,7 +9,8 @@ import
../db/db_chain, ../db/db_chain,
../p2p/chain, ../p2p/chain,
../utils/tx_pool, ../utils/tx_pool,
../utils/tx_pool/tx_item ../utils/tx_pool/tx_item,
../merge/merger
type type
HashToTime = TableRef[Hash256, Time] HashToTime = TableRef[Hash256, Time]
@ -23,14 +24,57 @@ type
knownByPeer: Table[Peer, HashToTime] knownByPeer: Table[Peer, HashToTime]
pending: HashSet[Hash256] pending: HashSet[Hash256]
lastCleanup: Time lastCleanup: Time
merger: MergerRef
ReconnectRef = ref object
pool: PeerPool
node: Node
const const
NUM_PEERS_REBROADCAST_QUOTIENT = 4 NUM_PEERS_REBROADCAST_QUOTIENT = 4
POOLED_STORAGE_TIME_LIMIT = initDuration(minutes = 20) POOLED_STORAGE_TIME_LIMIT = initDuration(minutes = 20)
PEER_LONG_BANTIME = chronos.minutes(150)
proc hash(peer: Peer): hashes.Hash = proc hash(peer: Peer): hashes.Hash =
hash(peer.remote) hash(peer.remote)
proc banExpiredReconnect(arg: pointer) {.gcsafe, raises: [Defect].} =
# Reconnect to peer after ban period if pool is empty
try:
let reconnect = cast[ReconnectRef](arg)
if reconnect.pool.len > 0:
return
asyncSpawn reconnect.pool.connectToNode(reconnect.node)
except TransportError:
debug "Transport got closed during banExpiredReconnect"
except CatchableError as e:
debug "Exception in banExpiredReconnect", exc = e.name, err = e.msg
proc banPeer(pool: PeerPool, peer: Peer, banTime: chronos.Duration) {.async.} =
try:
await peer.disconnect(SubprotocolReason)
let expired = Moment.fromNow(banTime)
let reconnect = ReconnectRef(
pool: pool,
node: peer.remote
)
discard setTimer(
expired,
banExpiredReconnect,
cast[pointer](reconnect)
)
except TransportError:
debug "Transport got closed during banPeer"
except CatchableError as e:
debug "Exception in banPeer", exc = e.name, err = e.msg
proc cleanupKnownByPeer(ctx: EthWireRef) = proc cleanupKnownByPeer(ctx: EthWireRef) =
let now = getTime() let now = getTime()
var tmp = initHashSet[Hash256]() var tmp = initHashSet[Hash256]()
@ -151,13 +195,15 @@ proc setupPeerObserver(ctx: EthWireRef) =
proc new*(_: type EthWireRef, proc new*(_: type EthWireRef,
chain: Chain, chain: Chain,
txPool: TxPoolRef, txPool: TxPoolRef,
peerPool: PeerPool): EthWireRef = peerPool: PeerPool,
merger: MergerRef): EthWireRef =
let ctx = EthWireRef( let ctx = EthWireRef(
db: chain.db, db: chain.db,
chain: chain, chain: chain,
txPool: txPool, txPool: txPool,
peerPool: peerPool, peerPool: peerPool,
lastCleanup: getTime() merger: merger,
lastCleanup: getTime(),
) )
ctx.setupPeerObserver() ctx.setupPeerObserver()
@ -345,7 +391,7 @@ proc fetchTransactions(ctx: EthWireRef, reqHashes: seq[Hash256], peer: Peer): Fu
method handleAnnouncedTxsHashes*(ctx: EthWireRef, peer: Peer, txHashes: openArray[Hash256]) {.gcsafe.} = method handleAnnouncedTxsHashes*(ctx: EthWireRef, peer: Peer, txHashes: openArray[Hash256]) {.gcsafe.} =
if ctx.disableTxPool: if ctx.disableTxPool:
when trMissingOrDisabledGossipOk: when trMissingOrDisabledGossipOk:
notImplemented("handleAnnouncedTxsHashes") notEnabled("handleAnnouncedTxsHashes")
return return
if txHashes.len == 0: if txHashes.len == 0:
@ -373,10 +419,19 @@ method handleAnnouncedTxsHashes*(ctx: EthWireRef, peer: Peer, txHashes: openArra
asyncSpawn ctx.fetchTransactions(reqHashes, peer) asyncSpawn ctx.fetchTransactions(reqHashes, peer)
method handleNewBlock*(ctx: EthWireRef, peer: Peer, blk: EthBlock, totalDifficulty: DifficultyInt) {.gcsafe.} = method handleNewBlock*(ctx: EthWireRef, peer: Peer, blk: EthBlock, totalDifficulty: DifficultyInt) {.gcsafe.} =
notImplemented("handleNewBlock") if ctx.merger.posFinalized:
debug "Dropping peer for sending NewBlock after merge (EIP-3675)",
peer, blockNumber=blk.header.blockNumber,
blockHash=blk.header.blockHash, totalDifficulty
asyncSpawn banPeer(ctx.peerPool, peer, PEER_LONG_BANTIME)
return
method handleNewBlockHashes*(ctx: EthWireRef, peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) {.gcsafe.} = method handleNewBlockHashes*(ctx: EthWireRef, peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) {.gcsafe.} =
notImplemented("handleNewBlockHashes") if ctx.merger.posFinalized:
debug "Dropping peer for sending NewBlockHashes after merge (EIP-3675)",
peer, numHashes=hashes.len
asyncSpawn banPeer(ctx.peerPool, peer, PEER_LONG_BANTIME)
return
when defined(legacy_eth66_enabled): when defined(legacy_eth66_enabled):
method getStorageNodes*(ctx: EthWireRef, hashes: openArray[Hash256]): seq[Blob] {.gcsafe.} = method getStorageNodes*(ctx: EthWireRef, hashes: openArray[Hash256]): seq[Blob] {.gcsafe.} =

View File

@ -18,7 +18,7 @@ import
../nimbus/[config, context, genesis, sealer], ../nimbus/[config, context, genesis, sealer],
../nimbus/utils/[tx_pool], ../nimbus/utils/[tx_pool],
../nimbus/p2p/chain, ../nimbus/p2p/chain,
../nimbus/merge/mergetypes, ../nimbus/merge/[mergetypes, merger],
./test_helpers ./test_helpers
const const
@ -98,9 +98,10 @@ proc runTest(steps: Steps) =
chainRef, ctx, conf.engineSigner, chainRef, ctx, conf.engineSigner,
txPool, EnginePostMerge txPool, EnginePostMerge
) )
merger = MergerRef.new(chainDB)
setupEthRpc(ethNode, ctx, chainDB, txPool, rpcServer) setupEthRpc(ethNode, ctx, chainDB, txPool, rpcServer)
setupEngineAPI(sealingEngine, rpcServer) setupEngineAPI(sealingEngine, rpcServer, merger)
sealingEngine.start() sealingEngine.start()
rpcServer.start() rpcServer.start()
@ -138,7 +139,7 @@ proc `==`(a, b: Quantity): bool =
uint64(a) == uint64(b) uint64(a) == uint64(b)
proc testEngineApiSupport() = proc testEngineApiSupport() =
var api = EngineAPI.new() var api = EngineAPIRef.new(nil)
let let
id1 = toId(1) id1 = toId(1)
id2 = toId(2) id2 = toId(2)

View File

@ -23,4 +23,8 @@ import
../hive_integration/nodocker/consensus/extract_consensus_data, ../hive_integration/nodocker/consensus/extract_consensus_data,
../hive_integration/nodocker/consensus/consensus_sim, ../hive_integration/nodocker/consensus/consensus_sim,
../hive_integration/nodocker/graphql/graphql_sim, ../hive_integration/nodocker/graphql/graphql_sim,
../hive_integration/nodocker/engine/engine_sim ../hive_integration/nodocker/engine/engine_sim,
../tools/t8n/t8n,
../tools/t8n/t8n_test,
../tools/evmstate/evmstate,
../tools/evmstate/evmstate_test