-
Notifications
You must be signed in to change notification settings - Fork 32
Issue #17: Implemented eth_getBlockByNumber() #30
Conversation
Pull Request Test Coverage Report for Build 41
💛 - Coveralls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @yurenju, thanks so much for this PR, this could have come straight from a textbook, really like this clean structure you have already set up and the completeness with the tests and everything.
Have made some comments directly in the code.
P.S.: Croatia was great, we already visited Rovinj, still have all these beautiful sights swirling around in my head. 🏖 ⛪️ 🐚
lib/rpc/rpc-manager.js
Outdated
@@ -0,0 +1,21 @@ | |||
const modules = require('./modules') | |||
|
|||
class RPCServer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably RPCManager
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to rename this one, nice catch!
lib/index.js
Outdated
|
||
if (config.rpc) { | ||
const rpc = new RPCServer(cm.getBlockchain()) | ||
rpc.listen(config.rpcport) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a info log msg either here or in the server constructor (not sure which one is more clean?) that the server has been started, also mentioning the port?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to follow convention from other modules to have an argument config
into this module and save _config
and _logger
into RPCServer and RPCManager
lib/rpc/modules/eth.js
Outdated
cb(err, block.toJSON(true)) | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you already mentioned Ganache in the issue description: I just looked into the ganache-core
code a bit and I am generally wondering if we are replicating a lot of of the code here (or will do in the future) if one is looking at e.g. this.
I tried to extract the structure from there a bit but have not yet a clear view on the similarities and differences, they seem to have there own blockchain implementation and statemanager. Do you have already formed an opinion on this?
Might be worth to analyze this a bit deeper before we start implementing all the RPC methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part do you want to replicate from ganache-core
?
took a look I feel we should expand argument like function(block_number, include_full_transactions, callback)
instead of (params, cb)
to make it more clear.
I'm not familiar with ganache-core
codebase, so I don't have any opinion for that yet, but I agree we should analyze ganache-core
deeper to get a better idea to implement rpc.
tests/rpc.js
Outdated
t.equal(blockchain.getBlock.firstCall.args[0], 1) | ||
t.end() | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So great that you already added some framing for test execution, really like this sinon
library, didn't know that before.
I think our general reference for RPC execution should be Geth behavior, this is also really practical to test running the JS client and Geth with RPC and different port side by side and then have a look at the results.
I've done this with JS 8545
and Geth
8546
and get divergent behavior e.g. for:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1", true],"id":1}' http://127.0.0.1:8545
{...JSON Result...}
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1", true],"id":1}' http://127.0.0.1:8546
invalid content type, only application/json is supported
So Geth requires a content header -H "Content-Type: application/json"
which I think we should then require as well.
Also things like leading zeros (0x01) differ:
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x01", true],"id":1}' -H "Content-Type: application/json" http://127.0.0.1:8545|jq
{...JSON Result...}
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x01", true],"id":1}' -H "Content-Type: application/json" http://127.0.0.1:8546|jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 197 100 117 100 80 73079 49968 --:--:-- --:--:-- --:--:-- 114k
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "invalid argument 0: hex number with leading zero digits"
}
}
Can you test some basic edge cases (e.g. not-existing/downloaded block) and see how things behave and then adopt the code base to make sure that the output is the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@holgerd77 sure go ahead! I may update my pr in two days when I get more free time :D, will rebase after #29 is landed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and thanks for testing with js client and geth side by side, I will add some basic edge cases!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel the way I handled rpc is too simple since we need to fit JSON-RPC spec, e.g., batch query of JSON-RPC. there are few Node.js packages for JSON-RPC and seems jayson is a well-maintain solution.
so jayson
module will be introduced on next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I didn't even know until now that JSON-RPC is a dedicated standard, lol. Yes, that makes definitely sense then, library seems to be a good choice on first look.
Just heads up I didn't finish my commit this weekend but already added some edge test case for invalid parameters, only accept content-type as I pushed my WIP commit to the previous PR to show the progress. hopefully I can finish this commit this week. |
Ah, already running this with I now merged #29, can you rebase your PR on top? |
just rebased it. the last commit introduces some changes:
let me know if you found anything can be improved :D |
@yurenju Thanks again, looking into it right now (think I will learn a lot reviewing your work...). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Yuren, really wanted to criticize something but didn't found anything, thanks again for the fantastic work!
Also tested this locally, will merge now.
.option('rpcaddr', { | ||
describe: 'HTTP-RPC server listening interface', | ||
default: 'localhost' | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice that you also added the rpcaddr
now.
const manager = new RPCManager(cm.getBlockchain(), config) | ||
const server = jayson.server(manager.getMethods()) | ||
config.logger.info(`HTTP endpoint opened: http://${rpcaddr}:${rpcport}`) | ||
server.http().listen(rpcport) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was a bit sceptical about the naming but wasn't aware that consts
in an if
clause remain local not trickling in the global space (tested this), so this should be ok.
|
||
this.getBlockByNumber = middleware(this.getBlockByNumber.bind(this), 2, | ||
[validators.hex, validators.bool]) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a really super-nice middleware structure.
*/ | ||
middleware (method, requiredParamsCount, validators) { | ||
return function (params, cb) { | ||
if (params.length < requiredParamsCount) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder if this should be a !==
comparison and a bit more general error msg, but we can leave this for now. Another possibility would also to make an extra check for >
, this is maybe even more user-friendly for the caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this because want to align with go-ethereum behaviors, but you are right we can handle this better than geth :D
id: 1 | ||
} | ||
|
||
request(server) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice setup with this supertest
library - didn't know that yet - really sleak.
For testing this locally: curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x1b4", true],"id":1}' -H "Content-Type: application/json" http://127.0.0.1:8545|jq ( Result: {
"jsonrpc": "2.0",
"id": 1,
"result": {
"header": {
"parentHash": "0xe99e022112df268087ea7eafaf4790497fd21dbeeb6bd7a1721df161a6657a54",
"uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"coinbase": "0xbb7b8287f3f0a933474a79eae42cbca977791171",
"stateRoot": "0xddc8b0234c2e0cad087c8b389aa7ef01f7d79b2570bccb77ce48648aa61c904d",
"transactionsTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"receiptTrie": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x04ea3f27bc",
"number": "0x01b4",
"gasLimit": "0x1388",
"gasUsed": "0x",
"timestamp": "0x55ba467c",
"extraData": "0x476574682f4c5649562f76312e302e302f6c696e75782f676f312e342e32",
"mixHash": "0x4fffe9ae21f1c9e15207b1f472d5bbdd68c9595d461666602f2be20daf5e7843",
"nonce": "0x689056015818adbe"
},
"transactions": [],
"uncleHeaders": []
}
} |
No description provided.