nimbus-eth1/fluffy/tests/rpc_tests/test_portal_rpc_client.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, history_type_conversions, history_validation],
../../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.contains(contentId)
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()