diff --git a/premix/downloader.nim b/premix/downloader.nim index af7df1c39..b877f2dac 100644 --- a/premix/downloader.nim +++ b/premix/downloader.nim @@ -21,8 +21,8 @@ type proc request*(methodName: string, params: JsonNode): JsonNode = var client = newRpcHttpClient() client.httpMethod(MethodPost) - waitFor client.connect("localhost", Port(8545)) - result = waitFor client.call(methodName, params) + waitFor client.connect("127.0.0.1", Port(8545)) + result = waitFor client.call(methodName, params) waitFor client.close() proc requestBlockBody(n: JsonNode, blockNumber: BlockNumber): BlockBody = @@ -31,6 +31,7 @@ proc requestBlockBody(n: JsonNode, blockNumber: BlockNumber): BlockBody = result.transactions = newSeqOfCap[Transaction](txs.len) for tx in txs: let txn = parseTransaction(tx) + validateTxSenderAndHash(tx, txn) result.transactions.add txn let uncles = n["uncles"] diff --git a/premix/graphql_downloader.nim b/premix/graphql_downloader.nim new file mode 100644 index 000000000..e752c2a13 --- /dev/null +++ b/premix/graphql_downloader.nim @@ -0,0 +1,106 @@ +import + std/json, + chronos, stew/results, eth/common, + graphql/httpclient, + ./parser + +const ethQuery = """ +fragment headerFields on Block { + parentHash: parent { value: hash } + sha3Uncles: ommerHash + miner { value: address } + stateRoot + transactionsRoot + receiptsRoot + logsBloom + difficulty + number + gasLimit + gasUsed + timestamp + extraData + mixHash + nonce + baseFeePerGas # EIP-1559 +} + +query getBlock($blockNumber: Long!) { + chainID # EIP-1559 + block(number: $blockNumber) { + ... headerFields + ommerCount + ommers { + ... headerFields + } + transactionCount + transactions { + nonce + gasPrice + gas + to {value: address} + value + input: inputData + v + r + s + maxFeePerGas # EIP-1559 + maxPriorityFeePerGas # EIP-1559 + effectiveGasPrice # EIP-1559 + type + hash + from {value: address} + accessList { + address + storageKeys + } + } + } +} +""" + +type + Block* = object + header*: BlockHeader + body*: BlockBody + +proc fromJson(_: type ChainId, n: JsonNode, name: string): ChainId = + var chainId: int + fromJson(n, name, chainId) + ChainId(chainId) + +proc requestBlock*(blockNumber: BlockNumber): Block = + let address = initTAddress("127.0.0.1:8545") + let clientRes = GraphqlHttpClientRef.new(address) + if clientRes.isErr: + raise newException(ValueError, clientRes.error) + + let client = clientRes.get() + client.addVar("blockNumber", $blockNumber) + + let res = waitFor client.sendRequest(ethQuery) + if res.isErr: + raise newException(ValueError, res.error) + + let resp = res.get() + + let n = json.parseJson(resp.response) + if n.hasKey("errors"): + debugEcho n.pretty + quit(1) + + let nh = n["data"]["block"] + let chainId = ChainId.fromJson(n["data"], "chainID") + result.header = parseBlockHeader(nh) + + let txs = nh["transactions"] + for txn in txs: + var tx = parseTransaction(txn) + tx.chainId = chainId + validateTxSenderAndHash(txn, tx) + result.body.transactions.add tx + + let uncles = nh["ommers"] + for un in uncles: + result.body.uncles.add parseBlockHeader(un) + + waitFor client.closeWait() diff --git a/premix/parser.nim b/premix/parser.nim index 05290f4d4..fbeb72794 100644 --- a/premix/parser.nim +++ b/premix/parser.nim @@ -3,7 +3,7 @@ import eth/[rlp, common], httputils, nimcrypto, stint, stew/byteutils -import ../nimbus/transaction +import ../nimbus/transaction, ../nimbus/utils/ec_recover from ../nimbus/rpc/hexstrings import encodeQuantity func hexToInt*(s: string, T: typedesc[SomeInteger]): T = @@ -34,24 +34,45 @@ type SomeData* = EthAddress | BloomFilter | BlockNonce proc fromJson*(n: JsonNode, name: string, x: var SomeData) = - hexToByteArray(n[name].getStr(), x) - doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name) + let node = n[name] + if node.kind == JString: + hexToByteArray(node.getStr(), x) + doAssert(x.prefixHex == toLowerAscii(node.getStr()), name) + else: + hexToByteArray(node["value"].getStr(), x) + doAssert(x.prefixHex == toLowerAscii(node["value"].getStr()), name) proc fromJson*(n: JsonNode, name: string, x: var Hash256) = - hexToByteArray(n[name].getStr(), x.data) - doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name) + let node = n[name] + if node.kind == JString: + hexToByteArray(node.getStr(), x.data) + doAssert(x.prefixHex == toLowerAscii(node.getStr()), name) + else: + hexToByteArray(node["value"].getStr(), x.data) + doAssert(x.prefixHex == toLowerAscii(node["value"].getStr()), name) proc fromJson*(n: JsonNode, name: string, x: var Blob) = x = hexToSeqByte(n[name].getStr()) doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name) proc fromJson*(n: JsonNode, name: string, x: var UInt256) = - x = UInt256.fromHex(n[name].getStr()) - doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name) + let node = n[name] + if node.kind == JString: + x = UInt256.fromHex(node.getStr()) + doAssert(x.prefixHex == toLowerAscii(node.getStr()), name) + else: + x = node.getInt().u256 + doAssert($x == $node.getInt, name) proc fromJson*(n: JsonNode, name: string, x: var SomeInteger) = - x = hexToInt(n[name].getStr(), type(x)) - doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name) + let node = n[name] + if node.kind == JString: + x = hexToInt(node.getStr(), type(x)) + doAssert(x.prefixHex == toLowerAscii(node.getStr()), name) + else: + type T = type x + x = T(node.getInt) + doAssert($x == $node.getInt, name) proc fromJson*(n: JsonNode, name: string, x: var EthTime) = x = initTime(hexToInt(n[name].getStr(), int64), 0) @@ -81,6 +102,16 @@ proc parseBlockHeader*(n: JsonNode): BlockHeader = n.fromJson "nonce", result.nonce n.fromJson "baseFeePerGas", result.fee + if result.baseFee == 0.u256: + # probably geth bug + result.fee = none(Uint256) + +proc parseAccessPair(n: JsonNode): AccessPair = + n.fromJson "address", result.address + let keys = n["storageKeys"] + for kn in keys: + result.storageKeys.add hexToByteArray[32](kn.getStr()) + proc parseTransaction*(n: JsonNode): Transaction = var tx = Transaction(txType: TxLegacy) n.fromJson "nonce", tx.nonce @@ -98,11 +129,28 @@ proc parseTransaction*(n: JsonNode): Transaction = n.fromJson "r", tx.R n.fromJson "s", tx.S - var sender = tx.getSender() - doAssert sender.prefixHex == n["from"].getStr() - doAssert n["hash"].getStr() == tx.rlpHash().prefixHex + if n["type"].kind != JNull: + tx.txType = TxType(n["type"].getInt) + + if tx.txType == TxEip1559: + n.fromJson "maxPriorityFeePerGas", tx.maxPriorityFee + n.fromJson "maxFeePerGas", tx.maxFee + + if tx.txType == TxEip2930: + # chainId is set from top level query + let accessList = n["accessList"] + if accessList.len > 0: + for acn in accessList: + tx.accessList.add parseAccessPair(acn) tx +proc validateTxSenderAndHash*(n: JsonNode, tx: Transaction) = + var sender = tx.getSender() + var fromAddr: EthAddress + n.fromJson "from", fromAddr + doAssert sender.prefixHex == fromAddr.prefixHex + doAssert n["hash"].getStr() == tx.rlpHash().prefixHex + proc parseLog(n: JsonNode): Log = n.fromJson "address", result.address n.fromJson "data", result.data diff --git a/premix/persist.nim b/premix/persist.nim index afa2f05b9..6597b9f05 100644 --- a/premix/persist.nim +++ b/premix/persist.nim @@ -2,8 +2,8 @@ import eth/[common, rlp], stint, - chronicles, downloader, configuration, - ../nimbus/errors + chronicles, configuration, + ../nimbus/[errors, chain_config] import eth/trie/[hexary, db], @@ -11,6 +11,11 @@ import ../nimbus/[genesis], ../nimbus/p2p/chain +when defined(graphql): + import graphql_downloader +else: + import downloader + const manualCommit = nimbus_db_backend == "lmdb" @@ -37,7 +42,7 @@ proc main() {.used.} = let conf = configuration.getConfiguration() let db = newChainDb(conf.dataDir) let trieDB = trieDB db - let chainDB = newBaseChainDB(trieDB, false, conf.netId) + let chainDB = newBaseChainDB(trieDB, false, conf.netId, networkParams(conf.netId)) # move head to block number ... if conf.head != 0.u256: