-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/consensus call #70
base: development
Are you sure you want to change the base?
Conversation
…nd eth_getCode methods
@rndquu @EresDev Let me know what you guys think of this whenever you can cheers. Through my manual QA, it ofc does not reach a consensus 100% of the time (which is good?). But at first glance Although if we consider the failure rate + the fact this is a I've only read about 100 pages so far Eres, give me a week to read it a couple times and I'll go heavy on the refactoring in a dedicated PR. Great recommendation, ty. |
…RequestAndByteCodeRacePromises for clarity
Unlisted binaries (1)
|
…t and script execution
I suppose we can try using bun instead of yarn. https://github.com/0x4007/travel-stipend/blob/f5bb2faa1b873bb1ca40ba04b97ba468b0d2cc97/.github/workflows/knip.yml |
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.
@Keyrxng Pls update readme with the new method
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.
- Pls add a unit test for the
consensusCall()
method when it fails to reach the quorum - Pls update script-test-consensus.ts and add an example of using it with
eth_getTransactionReceipt
if (val instanceof Error) { | ||
val = val.message; | ||
} else { | ||
val = val.hash; |
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.
Some of the RPC methods (like eth_getTransactionReceipt) don't have the hash
property in the response so I don't fully understand the logic
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 debated this myself a little.
I went with JSON.Stringify
at first and then return returning JSON.parse
but some response data is enormous so I went with hash
instead thinking it was more universal than what you've pointed it out to be.
Other than JSON.Stringify
I'm not sure how we can match responses with certainty across all methods/request results.
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.
what about .hash ?? .transactionHash
I feel that might be better but still a lot of methods would be excluded.
Perhaps consensusCall
takes a a 3rd param which is the prop of the return data that we need to compare and then allow the user to tell us?
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've implemented .hash ?? .transactionHash
for now as that covers eth_getTransactionReceipt
at least.
A 3rd param sounds scalable across all eth_
methods, failing that, stringifying the results might be the best bet.
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.
JSON.Stringify
sounds good, we need a generalized solution that covers most of RPC methods, not just 2
…mprove type definitions
…RPCHandler and RPCService
…erbose in consensus tests
public async getFirstAvailableRpcProvider() { | ||
const rpcList = [...networkRpcs[this._networkId].rpcs].filter((rpc) => rpc.url.includes("https")); | ||
shuffleArray(rpcList); | ||
const rpcPromises: Record<string, Promise<PromiseResult>[]> = {}; | ||
|
||
for (const rpc of rpcList) { | ||
this._rpcService.createBlockRequestAndByteCodeRacePromises(this._runtimeRpcs, rpcPromises, this._rpcTimeout); | ||
const results = await Promise.allSettled(rpcPromises[rpc.url] ?? []); | ||
const hasPassedAllChecks = results.every((res) => res && res.status === "fulfilled" && res.value.success); | ||
if (hasPassedAllChecks) { | ||
return new JsonRpcProvider({ url: rpc.url, skipFetchSetup: true }, Number(this._networkId)); | ||
} | ||
} |
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.
when this function runs for me in the unit tests, this._runtimeRpcs
is always empty. This is major dependency of createBlockRequestAndByteCodeRacePromises
. Because it gets empty array where it is supposed to work on, it just doesn't do anything here. Is this._runtimeRpcs
also always empty for you? then remove redundant code here.
@@ -244,4 +227,47 @@ export class RPCService { | |||
return false; | |||
} | |||
} | |||
|
|||
createBlockRequestAndByteCodeRacePromises(runtimeRpcs: string[], rpcPromises: Record<string, Promise<PromiseResult>[]>, rpcTimeout: number) { |
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.
We discussed this in the previous PR. Do not change objects passed in arguments. rpcPromises
is being passed here, just to get filled up inside this function.
Results produced by a function should be returned, and not fill up in one of its arguments.
Also, break this down into two functions. If you will do so, name of this function will improve, and you will not have to add such checks.
if (result.value.rpcMethod === "eth_getBlockByNumber") {
} else if (result.value.rpcMethod === "eth_getCode"
return { | ||
rpcUrl, | ||
duration: performance.now() - startTime, | ||
success: true, | ||
success: "result" in (res?.data ?? {}) ? true : false, |
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.
maybe change to
success: !!res?.data?.result
But there is one difference, see if it works. But this line should be simplified even if my suggestion doesn't work.
const getBlockNumberPayload = JSON.stringify({ | ||
// this is similar to `ValidBlockData`, I didn't want to change it incase it's in other projects | ||
type JsonRpcResponse = { jsonrpc: string; id: number; result: string | { number: string; timestamp: string; hash?: string; transactionHash?: string } }; | ||
export type PromiseResult<T extends JsonRpcResponse = JsonRpcResponse> = { |
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.
PromiseResult
Naming needs improvement. You can use AI to suggest you better variable, types, and function names. See
const rpcList = [...networkRpcs[this._networkId].rpcs]; | ||
shuffleArray(rpcList); | ||
for (const rpc of rpcList) { | ||
public async consensusCall<TMethodReturnData = unknown>(requestPayload: RequestPayload, quorumThreshold: `0.${number}`): Promise<TMethodReturnData> { |
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 say let's add new class Security{}
and add this method consensusCall
to it. Later, we will keep adding more security related stuff to this class.
RpcHandler is already a god object for me. @rndquu what do you think?
export const testConfig: HandlerConstructorConfig = { | ||
networkId: "100", | ||
autoStorage: false, | ||
cacheRefreshCycles: 3, | ||
networkName: null, | ||
rpcTimeout: 10000, | ||
runtimeRpcs: rpcUrls, | ||
networkRpcs: rpcUrls.map((url) => ({ url })), | ||
proxySettings: { | ||
retryCount: 3, | ||
retryDelay: 10, | ||
logTier: "verbose", | ||
logger: new PrettyLogs(), | ||
strictLogs: 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.
this exact object is present in 3 files. Reduce duplication. Extract it to somewhere like tests/shared.ts and reuse it from here.
@@ -67,6 +69,7 @@ | |||
"jest-md-dashboard": "0.8.0", | |||
"knip": "5.27.0", | |||
"lint-staged": "^15.2.2", | |||
"nock": "13.5.6", |
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.
nock for http mocking here? We are already using msw in text-conversation-rewards
plugin and pay.ubq.fi
.
nock(rpcUrls[0]) | ||
.post("/") | ||
.reply(200, { | ||
jsonrpc: "2.0", | ||
result: { number: "0x1b4", hash: "0x1b4" }, | ||
id: 1, | ||
}); | ||
|
||
nock(rpcUrls[1]) | ||
.post("/") | ||
.reply(200, { | ||
jsonrpc: "2.0", | ||
result: { number: "0x1b4", hash: "0x1b4" }, | ||
id: 1, | ||
}); | ||
|
||
nock(rpcUrls[2]) | ||
.post("/") | ||
.reply(200, { | ||
jsonrpc: "2.0", | ||
result: { number: "0x1b4", hash: "0x1b4" }, | ||
id: 1, | ||
}); |
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.
loop to reduce duplication
|
||
/** | ||
* A test script to ensure that the module can be imported and used correctly | ||
* This script is not meant to be run in the test suite |
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.
actually this is test and all your script-***.ts files. These are e2e tests. These are very useful. Move them to tests/e2e folder. At the end of each test, instead of console.log, use expect().
}, | ||
}; | ||
|
||
const blockConsensusResponse = await handler.consensusCall(blockByNumberPayload, "0.5"); |
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.
block number consensus test will not be deterministic. So, instead of block number in e2e test, maybe you can read token symbol of UBQ.
Resolves #60
QA: