Use rpcProxy in lc proxy (#1238)

* Use rpcProxy in lc proxy

* Remove nimbus config from cors handler
This commit is contained in:
KonradStaniec 2022-09-24 13:57:27 +02:00 committed by GitHub
parent 8dff89ecad
commit 2b7bc4f2ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 53 deletions

View File

@ -14,31 +14,20 @@ import
std/[os, strutils], std/[os, strutils],
chronicles, chronicles/chronos_tools, chronos, chronicles, chronicles/chronos_tools, chronos,
eth/keys, eth/keys,
json_rpc/[rpcserver, rpcclient], json_rpc/rpcproxy,
beacon_chain/eth1/eth1_monitor, beacon_chain/eth1/eth1_monitor,
beacon_chain/gossip_processing/optimistic_processor, beacon_chain/gossip_processing/optimistic_processor,
beacon_chain/networking/topic_params, beacon_chain/networking/topic_params,
beacon_chain/spec/beaconstate, beacon_chain/spec/beaconstate,
beacon_chain/spec/datatypes/[phase0, altair, bellatrix], beacon_chain/spec/datatypes/[phase0, altair, bellatrix],
beacon_chain/[light_client, nimbus_binary_common, version], beacon_chain/[light_client, nimbus_binary_common, version],
../nimbus/rpc/cors,
./rpc/rpc_eth_lc_api, ./rpc/rpc_eth_lc_api,
./lc_proxy_conf ./lc_proxy_conf
from beacon_chain/consensus_object_pools/consensus_manager import runForkchoiceUpdated
from beacon_chain/gossip_processing/block_processor import newExecutionPayload from beacon_chain/gossip_processing/block_processor import newExecutionPayload
from beacon_chain/gossip_processing/eth2_processor import toValidationResult from beacon_chain/gossip_processing/eth2_processor import toValidationResult
proc initRpcClient(config: LcProxyConf): Future[RpcClient] {.async.} =
case config.web3ClientConfig.kind
of WsClient:
let wssClient = newRpcWebSocketClient()
await wssClient.connect(config.web3ClientConfig.url)
return wssClient
of HttpClient:
let httpClient = newRpcHttpClient()
await httpClient.connect(config.web3ClientConfig.url)
return httpClient
func getConfiguredChainId(networkMetadata: Eth2NetworkMetadata): Quantity = func getConfiguredChainId(networkMetadata: Eth2NetworkMetadata): Quantity =
if networkMetadata.eth1Network.isSome(): if networkMetadata.eth1Network.isSome():
let let
@ -105,13 +94,18 @@ proc run() {.raises: [Exception, Defect].} =
forkDigests, getBeaconTime, genesis_validators_root forkDigests, getBeaconTime, genesis_validators_root
) )
rpcClient = waitFor initRpcClient(config) # TODO: for now we serve all cross origin requests
authHooks = @[httpCors(@[])]
rpcHttpServer = newRpcHttpServer( clientConfig = config.web3url.asClientConfig()
[initTAddress(config.rpcAddress, config.rpcPort)]
rpcProxy = RpcProxy.new(
[initTAddress(config.rpcAddress, config.rpcPort)],
clientConfig,
authHooks
) )
lcProxy = LightClientRpcProxy.new(rpcHttpServer, rpcClient, chainId) lcProxy = LightClientRpcProxy.new(rpcProxy, chainId)
optimisticHandler = proc(signedBlock: ForkedMsgTrustedSignedBeaconBlock): optimisticHandler = proc(signedBlock: ForkedMsgTrustedSignedBeaconBlock):
Future[void] {.async.} = Future[void] {.async.} =
@ -156,7 +150,7 @@ proc run() {.raises: [Exception, Defect].} =
waitFor network.startListening() waitFor network.startListening()
waitFor network.start() waitFor network.start()
rpcHttpServer.start() waitFor rpcProxy.start()
waitFor lcProxy.verifyChaindId() waitFor lcProxy.verifyChaindId()
proc onFinalizedHeader( proc onFinalizedHeader(

View File

@ -14,7 +14,8 @@ import
std/os, std/os,
json_serialization/std/net, json_serialization/std/net,
beacon_chain/light_client, beacon_chain/light_client,
beacon_chain/conf beacon_chain/conf,
json_rpc/[rpcproxy]
export net, conf export net, conf
@ -28,19 +29,17 @@ proc defaultLCPDataDir*(): string =
getHomeDir() / dataDir getHomeDir() / dataDir
type Web3ClientType* = enum
WsClient, HttpClient
type Web3ClientConfig* = object
kind*: Web3ClientType
url*: string
const const
defaultWeb3Address* = (static "http://127.0.0.1:8546")
defaultWeb3ClientConfig* = Web3ClientConfig(url: defaultWeb3Address, kind: HttpClient)
defaultDataLCPDirDesc* = defaultLCPDataDir() defaultDataLCPDirDesc* = defaultLCPDataDir()
type
Web3UrlKind* = enum
HttpUrl, WsUrl
ValidatedWeb3Url* = object
kind*: Web3UrlKind
web3Url*: string
type LcProxyConf* = object type LcProxyConf* = object
# Config # Config
configFile* {. configFile* {.
@ -164,25 +163,25 @@ type LcProxyConf* = object
defaultValueDesc: $defaultAdminListenAddressDesc defaultValueDesc: $defaultAdminListenAddressDesc
name: "rpc-address" .}: ValidIpAddress name: "rpc-address" .}: ValidIpAddress
web3ClientConfig* {. # There is no default as its need to be provided by the user
web3url* {.
desc: "url of web3 data provider" desc: "url of web3 data provider"
defaultValue: defaultWeb3ClientConfig name: "web3-url" .}: ValidatedWeb3Url
name: "web3-url" .}: Web3ClientConfig
proc parseCmdArg*(T: type Web3ClientConfig, p: TaintedString): T proc parseCmdArg*(T: type ValidatedWeb3Url, p: TaintedString): T
{.raises: [Defect, ConfigurationError].} = {.raises: [Defect, ConfigurationError].} =
let url = parseUri(p) let url = parseUri(p)
let normalizedScheme = url.scheme.toLowerAscii() let normalizedScheme = url.scheme.toLowerAscii()
if (normalizedScheme == "http" or normalizedScheme == "https"): if (normalizedScheme == "http" or normalizedScheme == "https"):
Web3ClientConfig(kind: HttpClient, url: p) ValidatedWeb3Url(kind: HttpUrl, web3Url: p)
elif (normalizedScheme == "ws" or normalizedScheme == "wss"): elif (normalizedScheme == "ws" or normalizedScheme == "wss"):
Web3ClientConfig(kind: WsClient, url: p) ValidatedWeb3Url(kind: WsUrl, web3Url: p)
else: else:
raise newException( raise newException(
ConfigurationError, "Web3 client uri should have defined scheme (http/https/ws/wss)" ConfigurationError, "Web3 url should have defined scheme (http/https/ws/wss)"
) )
proc completeCmdArg*(T: type Web3ClientConfig, val: TaintedString): seq[string] = proc completeCmdArg*(T: type ValidatedWeb3Url, val: TaintedString): seq[string] =
return @[] return @[]
func asLightClientConf*(pc: LcProxyConf): LightClientConf = func asLightClientConf*(pc: LcProxyConf): LightClientConf =
@ -210,3 +209,13 @@ func asLightClientConf*(pc: LcProxyConf): LightClientConf =
jwtSecret: none(string), jwtSecret: none(string),
stopAtEpoch: 0 stopAtEpoch: 0
) )
# TODO: Cannot use ClientConfig in LcProxyConf due to the fact that
# it contain `set[TLSFlags]` which does not have proper toml serialization
func asClientConfig*(url: ValidatedWeb3Url): ClientConfig =
case url.kind
of HttpUrl:
getHttpClientConfig(url.web3Url)
of WsUrl:
getWebSocketClientConfig(url.web3Url, flags = {})

View File

@ -11,7 +11,7 @@ import
stint, stint,
stew/byteutils, stew/byteutils,
chronicles, chronicles,
json_rpc/[rpcserver, rpcclient], json_rpc/[rpcproxy, rpcserver, rpcclient],
web3, web3,
web3/[ethhexstrings, ethtypes], web3/[ethhexstrings, ethtypes],
beacon_chain/eth1/eth1_monitor, beacon_chain/eth1/eth1_monitor,
@ -36,8 +36,7 @@ template encodeQuantity(value: Quantity): HexQuantityStr =
hexQuantityStr(encodeQuantity(value.uint64)) hexQuantityStr(encodeQuantity(value.uint64))
type LightClientRpcProxy* = ref object type LightClientRpcProxy* = ref object
client*: RpcClient proxy: RpcProxy
server*: RpcServer
executionPayload*: Opt[ExecutionPayloadV1] executionPayload*: Opt[ExecutionPayloadV1]
chainId: Quantity chainId: Quantity
@ -54,13 +53,15 @@ template checkPreconditions(payload: Opt[ExecutionPayloadV1], quantityTag: strin
# fetched on demand. # fetched on demand.
raise newException(ValueError, "Only latest block is supported") raise newException(ValueError, "Only latest block is supported")
template rpcClient(lcProxy: LightClientRpcProxy): RpcClient = lcProxy.proxy.getClient()
proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) = proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
template payload(): Opt[ExecutionPayloadV1] = lcProxy.executionPayload template payload(): Opt[ExecutionPayloadV1] = lcProxy.executionPayload
lcProxy.server.rpc("eth_chainId") do() -> HexQuantityStr: lcProxy.proxy.rpc("eth_chainId") do() -> HexQuantityStr:
return encodeQuantity(lcProxy.chainId) return encodeQuantity(lcProxy.chainId)
lcProxy.server.rpc("eth_blockNumber") do() -> HexQuantityStr: lcProxy.proxy.rpc("eth_blockNumber") do() -> HexQuantityStr:
## Returns the number of most recent block. ## Returns the number of most recent block.
if payload.isNone: if payload.isNone:
raise newException(ValueError, "Syncing") raise newException(ValueError, "Syncing")
@ -68,7 +69,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
return encodeQuantity(payload.get.blockNumber) return encodeQuantity(payload.get.blockNumber)
# TODO quantity tag should be better typed # TODO quantity tag should be better typed
lcProxy.server.rpc("eth_getBalance") do(address: Address, quantityTag: string) -> HexQuantityStr: lcProxy.proxy.rpc("eth_getBalance") do(address: Address, quantityTag: string) -> HexQuantityStr:
checkPreconditions(payload, quantityTag) checkPreconditions(payload, quantityTag)
# When requesting state for `latest` block number, we need to translate # When requesting state for `latest` block number, we need to translate
@ -81,7 +82,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
info "Forwarding get_Balance", executionBn = blockNumber info "Forwarding get_Balance", executionBn = blockNumber
let proof = await lcProxy.client.eth_getProof(address, @[], blockId(blockNumber)) let proof = await lcProxy.rpcClient.eth_getProof(address, @[], blockId(blockNumber))
let accountResult = getAccountFromProof( let accountResult = getAccountFromProof(
executionPayload.stateRoot, executionPayload.stateRoot,
@ -98,7 +99,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
else: else:
raise newException(ValueError, accountResult.error) raise newException(ValueError, accountResult.error)
lcProxy.server.rpc("eth_getStorageAt") do(address: Address, slot: HexDataStr, quantityTag: string) -> HexDataStr: lcProxy.proxy.rpc("eth_getStorageAt") do(address: Address, slot: HexDataStr, quantityTag: string) -> HexDataStr:
checkPreconditions(payload, quantityTag) checkPreconditions(payload, quantityTag)
let let
@ -108,7 +109,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
info "Forwarding eth_getStorageAt", executionBn = blockNumber info "Forwarding eth_getStorageAt", executionBn = blockNumber
let proof = await lcProxy.client.eth_getProof(address, @[uslot], blockId(blockNumber)) let proof = await lcProxy.rpcClient.eth_getProof(address, @[uslot], blockId(blockNumber))
let dataResult = getStorageData(executionPayload.stateRoot, uslot, proof) let dataResult = getStorageData(executionPayload.stateRoot, uslot, proof)
@ -118,15 +119,23 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
else: else:
raise newException(ValueError, dataResult.error) raise newException(ValueError, dataResult.error)
# TODO This methods are forwarded directly to provider therefore thay are not
# validated in any way
lcProxy.proxy.registerProxyMethod("net_version")
lcProxy.proxy.registerProxyMethod("eth_call")
# TODO cache blocks received from light client, and respond using them in this
# call. It would also enable handling of numerical `quantityTag` for the
# set of cached blocks
lcProxy.proxy.registerProxyMethod("eth_getBlockByNumber")
proc new*( proc new*(
T: type LightClientRpcProxy, T: type LightClientRpcProxy,
server: RpcServer, proxy: RpcProxy,
client: RpcClient,
chainId: Quantity): T = chainId: Quantity): T =
return LightClientRpcProxy( return LightClientRpcProxy(
client: client, proxy: proxy,
server: server,
chainId: chainId chainId: chainId
) )
@ -136,7 +145,7 @@ proc verifyChaindId*(p: LightClientRpcProxy): Future[void] {.async.} =
# retry 2 times, if the data provider will fail despite re-tries, propagate # retry 2 times, if the data provider will fail despite re-tries, propagate
# exception to the caller. # exception to the caller.
let providerId = awaitWithRetries( let providerId = awaitWithRetries(
p.client.eth_chainId(), p.rpcClient.eth_chainId(),
retries = 2, retries = 2,
timeout = seconds(30) timeout = seconds(30)
) )

View File

@ -14,8 +14,7 @@ import
chronos/apps/http/[httptable, httpserver], chronos/apps/http/[httptable, httpserver],
json_rpc/rpcserver, json_rpc/rpcserver,
httputils, httputils,
websock/websock as ws, websock/websock as ws
../config
proc sameOrigin(a, b: Uri): bool = proc sameOrigin(a, b: Uri): bool =
a.hostname == b.hostname and a.hostname == b.hostname and