mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-14 04:56:58 +00:00
* blocks can be bigger than the default 1mb when json-rpc-encoded - this happens on sepolia for example * json-rpc bump improves debug logging and fixes a number of bugs * json-serialization bump fixes a crash on invalid arrays in json data At some point, it would probably be better to compute the maximum block size from actual block constraints, though this is somewhat tricky and depends on gas limits etc. Until then, 16mb should be plenty. With this, sepolia can be synced :)
309 lines
9.4 KiB
Nim
309 lines
9.4 KiB
Nim
# Nimbus - Portal Network
|
|
# Copyright (c) 2021-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.
|
|
|
|
{.used.}
|
|
|
|
import
|
|
chronos,
|
|
testutils/unittests,
|
|
json_rpc/rpcserver,
|
|
json_rpc/clients/httpclient,
|
|
stint,
|
|
eth/p2p/discoveryv5/enr,
|
|
eth/common/keys,
|
|
eth/common/[headers_rlp, blocks_rlp, receipts_rlp],
|
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
|
../../network/wire/[portal_protocol, portal_stream, portal_protocol_config],
|
|
../../network/history/
|
|
[history_network, history_content, validation/historical_hashes_accumulator],
|
|
../../database/content_db,
|
|
../../rpc/[portal_rpc_client, rpc_portal_history_api],
|
|
../test_helpers
|
|
|
|
from eth/common/eth_types_rlp import rlpHash
|
|
|
|
type HistoryNode = ref object
|
|
discoveryProtocol*: discv5_protocol.Protocol
|
|
historyNetwork*: HistoryNetwork
|
|
|
|
proc newHistoryNode(rng: ref HmacDrbgContext, port: int): HistoryNode =
|
|
let
|
|
node = initDiscoveryNode(rng, PrivateKey.random(rng[]), localAddress(port))
|
|
db = ContentDB.new(
|
|
"", uint32.high, RadiusConfig(kind: Dynamic), node.localNode.id, inMemory = true
|
|
)
|
|
streamManager = StreamManager.new(node)
|
|
historyNetwork = HistoryNetwork.new(
|
|
PortalNetwork.none, node, db, streamManager, FinishedHistoricalHashesAccumulator()
|
|
)
|
|
|
|
return HistoryNode(discoveryProtocol: node, historyNetwork: historyNetwork)
|
|
|
|
proc portalProtocol(hn: HistoryNode): PortalProtocol =
|
|
hn.historyNetwork.portalProtocol
|
|
|
|
proc localNode(hn: HistoryNode): Node =
|
|
hn.discoveryProtocol.localNode
|
|
|
|
proc start(hn: HistoryNode) =
|
|
hn.historyNetwork.start()
|
|
|
|
proc stop(hn: HistoryNode) {.async.} =
|
|
discard hn.historyNetwork.stop()
|
|
await hn.discoveryProtocol.closeWait()
|
|
|
|
proc containsId(hn: HistoryNode, contentId: ContentId): bool =
|
|
return hn.historyNetwork.contentDB.get(contentId).isSome()
|
|
|
|
proc store*(hn: HistoryNode, blockHash: Hash32, blockHeader: Header) =
|
|
let
|
|
headerRlp = rlp.encode(blockHeader)
|
|
blockHeaderWithProof = BlockHeaderWithProof(
|
|
header: ByteList[2048].init(headerRlp), proof: BlockHeaderProof.init()
|
|
)
|
|
contentKeyBytes = blockHeaderContentKey(blockHash).encode()
|
|
contentId = history_content.toContentId(contentKeyBytes)
|
|
|
|
hn.portalProtocol().storeContent(
|
|
contentKeyBytes, contentId, SSZ.encode(blockHeaderWithProof)
|
|
)
|
|
|
|
proc store*(hn: HistoryNode, blockHash: Hash32, blockBody: BlockBody) =
|
|
let
|
|
contentKeyBytes = blockBodyContentKey(blockHash).encode()
|
|
contentId = history_content.toContentId(contentKeyBytes)
|
|
|
|
hn.portalProtocol().storeContent(contentKeyBytes, contentId, blockBody.encode())
|
|
|
|
proc store*(hn: HistoryNode, blockHash: Hash32, receipts: seq[Receipt]) =
|
|
let
|
|
contentKeyBytes = receiptsContentKey(blockHash).encode()
|
|
contentId = history_content.toContentId(contentKeyBytes)
|
|
|
|
hn.portalProtocol().storeContent(contentKeyBytes, contentId, receipts.encode())
|
|
|
|
type TestCase = ref object
|
|
historyNode1: HistoryNode
|
|
historyNode2: HistoryNode
|
|
server: RpcHttpServer
|
|
client: PortalRpcClient
|
|
|
|
proc setupTest(rng: ref HmacDrbgContext): Future[TestCase] {.async.} =
|
|
let
|
|
localSrvAddress = "127.0.0.1"
|
|
localSrvPort = 0 # let the OS choose a port
|
|
ta = initTAddress(localSrvAddress, localSrvPort)
|
|
client = newRpcHttpClient()
|
|
historyNode1 = newHistoryNode(rng, 20333)
|
|
historyNode2 = newHistoryNode(rng, 20334)
|
|
|
|
historyNode1.start()
|
|
historyNode2.start()
|
|
|
|
check:
|
|
historyNode1.portalProtocol().addNode(historyNode2.localNode()) == Added
|
|
historyNode2.portalProtocol().addNode(historyNode1.localNode()) == Added
|
|
|
|
let rpcHttpServer = RpcHttpServer.new()
|
|
rpcHttpServer.addHttpServer(ta, maxRequestBodySize = 16 * 1024 * 1024)
|
|
rpcHttpServer.installPortalHistoryApiHandlers(
|
|
historyNode1.historyNetwork.portalProtocol
|
|
)
|
|
rpcHttpServer.start()
|
|
|
|
await client.connect(localSrvAddress, rpcHttpServer.localAddress[0].port, false)
|
|
|
|
return TestCase(
|
|
historyNode1: historyNode1,
|
|
historyNode2: historyNode2,
|
|
server: rpcHttpServer,
|
|
client: PortalRpcClient.init(client),
|
|
)
|
|
|
|
proc stop(testCase: TestCase) {.async.} =
|
|
await testCase.server.stop()
|
|
await testCase.server.closeWait()
|
|
await testCase.historyNode1.stop()
|
|
await testCase.historyNode2.stop()
|
|
|
|
procSuite "Portal RPC Client":
|
|
let rng = newRng()
|
|
|
|
asyncTest "Test historyGetBlockHeader with validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 100)
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let blockHeaderRes =
|
|
await tc.client.historyGetBlockHeader(blockHash, validateContent = true)
|
|
check:
|
|
blockHeaderRes.isErr()
|
|
blockHeaderRes.error() == ContentNotFound
|
|
|
|
# Test content found
|
|
block:
|
|
tc.historyNode2.store(blockHash, blockHeader)
|
|
|
|
let blockHeaderRes =
|
|
await tc.client.historyGetBlockHeader(blockHash, validateContent = true)
|
|
check:
|
|
blockHeaderRes.isOk()
|
|
blockHeaderRes.value() == blockHeader
|
|
|
|
# Test content validation failed
|
|
block:
|
|
tc.historyNode1.store(blockHash, Header()) # bad header
|
|
|
|
let blockHeaderRes =
|
|
await tc.client.historyGetBlockHeader(blockHash, validateContent = true)
|
|
check:
|
|
blockHeaderRes.isErr()
|
|
blockHeaderRes.error() == ContentValidationFailed
|
|
|
|
waitFor tc.stop()
|
|
|
|
asyncTest "Test historyGetBlockHeader without validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 200)
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let blockHeaderRes =
|
|
await tc.client.historyGetBlockHeader(blockHash, validateContent = false)
|
|
check:
|
|
blockHeaderRes.isErr()
|
|
blockHeaderRes.error() == ContentNotFound
|
|
|
|
# Test content found
|
|
block:
|
|
tc.historyNode1.store(blockHash, blockHeader)
|
|
|
|
let blockHeaderRes =
|
|
await tc.client.historyGetBlockHeader(blockHash, validateContent = false)
|
|
check:
|
|
blockHeaderRes.isOk()
|
|
blockHeaderRes.value() == blockHeader
|
|
|
|
waitFor tc.stop()
|
|
|
|
asyncTest "Test historyGetBlockBody with validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 300)
|
|
blockBody = BlockBody()
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let blockBodyRes =
|
|
await tc.client.historyGetBlockBody(blockHash, validateContent = true)
|
|
check:
|
|
blockBodyRes.isErr()
|
|
blockBodyRes.error() == ContentNotFound
|
|
|
|
# Test content validation failed
|
|
block:
|
|
tc.historyNode1.store(blockHash, blockHeader)
|
|
tc.historyNode1.store(blockHash, blockBody)
|
|
|
|
let blockBodyRes =
|
|
await tc.client.historyGetBlockBody(blockHash, validateContent = true)
|
|
check:
|
|
blockBodyRes.isErr()
|
|
blockBodyRes.error() == ContentValidationFailed
|
|
|
|
waitFor tc.stop()
|
|
|
|
asyncTest "Test historyGetBlockBody without validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 300)
|
|
blockBody = BlockBody()
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let blockBodyRes =
|
|
await tc.client.historyGetBlockBody(blockHash, validateContent = false)
|
|
check:
|
|
blockBodyRes.isErr()
|
|
blockBodyRes.error() == ContentNotFound
|
|
|
|
# Test content found
|
|
block:
|
|
tc.historyNode1.store(blockHash, blockHeader)
|
|
tc.historyNode1.store(blockHash, blockBody)
|
|
|
|
let blockBodyRes =
|
|
await tc.client.historyGetBlockBody(blockHash, validateContent = false)
|
|
check:
|
|
blockBodyRes.isOk()
|
|
blockBodyRes.value() == blockBody
|
|
|
|
waitFor tc.stop()
|
|
|
|
asyncTest "Test historyGetReceipts with validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 300)
|
|
receipts = @[Receipt()]
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let receiptsRes =
|
|
await tc.client.historyGetReceipts(blockHash, validateContent = true)
|
|
check:
|
|
receiptsRes.isErr()
|
|
receiptsRes.error() == ContentNotFound
|
|
|
|
# Test content validation failed
|
|
block:
|
|
tc.historyNode1.store(blockHash, blockHeader)
|
|
tc.historyNode1.store(blockHash, receipts)
|
|
|
|
let receiptsRes =
|
|
await tc.client.historyGetReceipts(blockHash, validateContent = true)
|
|
check:
|
|
receiptsRes.isErr()
|
|
receiptsRes.error() == ContentValidationFailed
|
|
|
|
waitFor tc.stop()
|
|
|
|
asyncTest "Test historyGetReceipts without validation":
|
|
let
|
|
tc = await setupTest(rng)
|
|
blockHeader = Header(number: 300)
|
|
receipts = @[Receipt()]
|
|
blockHash = blockHeader.rlpHash()
|
|
|
|
# Test content not found
|
|
block:
|
|
let receiptsRes =
|
|
await tc.client.historyGetReceipts(blockHash, validateContent = false)
|
|
check:
|
|
receiptsRes.isErr()
|
|
receiptsRes.error() == ContentNotFound
|
|
|
|
# Test content found
|
|
block:
|
|
tc.historyNode1.store(blockHash, blockHeader)
|
|
tc.historyNode1.store(blockHash, receipts)
|
|
|
|
let receiptsRes =
|
|
await tc.client.historyGetReceipts(blockHash, validateContent = false)
|
|
check:
|
|
receiptsRes.isOk()
|
|
receiptsRes.value() == receipts
|
|
|
|
waitFor tc.stop()
|