nimbus-eth1/fluffy/tests/rpc_tests/test_portal_rpc_client.nim
Jacek Sieka 5fc4c13ab1
Increase JSON-RPC limits, bump json-rpc (#2759)
* 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 :)
2024-10-23 10:26:56 +02:00

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()