Skip to content
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

Add isBlockhashValid method to web3.js #25888

Closed
wants to merge 16 commits into from
Closed

Conversation

zhe-t
Copy link

@zhe-t zhe-t commented Jun 9, 2022

Problem

Attempting to resolve solana-labs/solana-web3.js#1113 - Add isBlockhashValid support

Summary of Changes

Added isBlockhashValid method to web3.js sdk

Fixes #

@mergify mergify bot added the community Community contribution label Jun 9, 2022
@mergify mergify bot requested a review from a team June 9, 2022 22:08
Copy link
Contributor

@steveluscher steveluscher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @zhe-t!

Now all this PR needs is some tests. Use the ‘get block time’ test in connection.test.ts as a starting point. Run both yarn test and yarn:test-live-with-test-validator. You can turn on only the test you're working on by adding .only to the end of it or describe.

Write tests that cover:

  • the RPC returning a value when you supply a blockhash
  • the RPC returning a value when you supply a blockhash and a min slot
  • (LIVE mode only) getting the recent blockhash then asking if it's valid (expect to be true)
  • (LIVE mode only) getting the recent blockhash and slot then asking if it's valid with a min slot one slot higher (expect to be false)
  • (LIVE mode only) supplying a junk blockhash then asking if it's valid (expect to be false)

web3.js/src/connection.ts Outdated Show resolved Hide resolved
web3.js/src/connection.ts Outdated Show resolved Hide resolved
web3.js/src/connection.ts Outdated Show resolved Hide resolved
@mergify mergify bot dismissed steveluscher’s stale review June 10, 2022 11:31

Pull request has been modified.

@mergify mergify bot requested a review from a team June 10, 2022 11:31
@zhe-t
Copy link
Author

zhe-t commented Jun 10, 2022

Thanks @steveluscher - working on tests now.

@zhe-t
Copy link
Author

zhe-t commented Jun 10, 2022

@steveluscher could you help me with the

(LIVE mode only) getting the recent blockhash and slot then asking if it's valid with a min slot one slot higher (expect to be false)

test please?

The test is called is blockhash valid - blockhash only and mint slot (live) in ca50893#diff-0ff00ad7672d836c05bc2a1180365eb6a757b5e06c80edc7e9b8a7456b9a52d6R2991.

I must have over looked something. The recent slot is always 0 when using yarn:test-live-with-test-validator.

@zhe-t zhe-t marked this pull request as ready for review June 10, 2022 12:49
@steveluscher
Copy link
Contributor

The recent slot is always 0 when using yarn:test-live-with-test-validator.

For sure! In cases where it's the first test you run, this will be the case.

Just loop, waiting for a minimum of slots to pass.

while ((await connection.getSlot()) <= 1) {
continue;
}

@zhe-t
Copy link
Author

zhe-t commented Jun 13, 2022

Tests added

  • the RPC returning a value when you supply a blockhash
  • the RPC returning a value when you supply a blockhash and a min slot
  • (LIVE mode only) getting the recent blockhash then asking if it's valid (expect to be true)
  • (LIVE mode only) getting the recent blockhash and slot then asking if it's valid with a min slot one slot higher (expect to be false)
  • (LIVE mode only) supplying a junk blockhash then asking if it's valid (expect to be false)

I've tried using the slot returned by getLatestBlockhashAndContext and using getSlot (which are both now non-0) but the method returns a true value when using this slot value+1. Do you have an idea of what I have overlooked? @steveluscher

@zhe-t
Copy link
Author

zhe-t commented Jun 18, 2022

cc @steveluscher

@steveluscher
Copy link
Contributor

steveluscher commented Jun 18, 2022

Sorry for the delay here!

I don't really know, but here are some thoughts:

  • One of the values that you get back from getLatestBlockhash is one called lastValidBlockheight. What happens if you set minContextSlot to lastValidBlockheight + 1?
  • Do you have to wait for the lastValidBlockheight to pass before making your assertion?

I don't really know what the effect of minContextSlot is here, but what I do know is that blockhashes are only valid up to a certain slot, and that slot is given by lastValidBlockheight.

@jstarry
Copy link
Member

jstarry commented Jun 20, 2022

What happens if you set minContextSlot to lastValidBlockheight + 1?

Block height and slot are two totally different things fyi

Copy link
Member

@jstarry jstarry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great to see minContextSlot being used here :)

web3.js/src/connection.ts Outdated Show resolved Hide resolved
web3.js/src/connection.ts Outdated Show resolved Hide resolved
const unsafeRes = await this._rpcRequest('isBlockhashValid', args);
const res = create(unsafeRes, jsonRpcResultAndContext(boolean()));
if ('error' in res) {
throw new Error(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have special handling for when the min context slot isn't met. It can be detected from its error code:

pub const JSON_RPC_SERVER_ERROR_MIN_CONTEXT_SLOT_NOT_REACHED: i64 = -32016;

We could retry a few times (configurable in IsBlockhashValidConfig). What do you think? I wouldn't mind this change coming later in a separate PR too. So maybe we just ensure that the thrown error object includes the error code as an integer

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat. Two questions come to mind:

  1. Should we check for this error code in res and retry a configurable amount of times before bailing?
  2. Should we return the error code be propagated in the response object of IsBlockhashValid?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. yup, that's what I had in mind
  2. no, it should be abstracted away

Copy link
Author

@zhe-t zhe-t Jul 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall I include this in the current PR? @jstarry @steveluscher

@steveluscher
Copy link
Contributor

Block height and slot are two totally different things fyi

Oof. Of course. Sorry for the misdirection @zhe-t.

Comment on lines 2991 to 3001
it('is blockhash valid - blockhash only and mint slot (live)', async () => {
const blockhash = await connection.getLatestBlockhashAndContext('finalized');

const isValid = await connection.isBlockhashValid(
blockhash.value.blockhash,
{
commitment: 'finalized',
minContextSlot: blockhash.context.slot+1
});
expect(isValid.value).to.be.false;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any advice on how to write a test that both exercises minContextSlot and fails, @jstarry? I'm missing the plot here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about setting it to the max safe integer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting to MAX_SAFE_INTEGER gives the same result 🤔 validation result is returning as true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because your code isn't actually sending minContextSlot in the rpc request

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it when writing tests pays off right away.

web3.js/src/connection.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@steveluscher steveluscher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marking as request changes -> back to your queue.

@mergify mergify bot dismissed steveluscher’s stale review July 4, 2022 13:02

Pull request has been modified.

@zhe-t
Copy link
Author

zhe-t commented Jul 4, 2022

Updated the PR to resolve the config params not being parsed/passed correctly and now all tests are passing 🎉 thanks @jstarry @steveluscher for the guidance. Ready to review if we do not want to include the special handling for when the min context slot is not met.

@zhe-t zhe-t requested review from steveluscher and jstarry July 4, 2022 23:15
Copy link
Contributor

@steveluscher steveluscher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed up a few commits to finish this up, but could you take a look at the tests @zhe-t? Write one set of tests that works in both live mode and mock mode. The key is that mockRpcResponse is a no-op in LIVE mode. Write the tests so that they would pass whether mockRpcResponse takes effect or not.

blockhash: string,
config?: IsBlockhashValidConfig
): Promise<RpcResponseAndContext<boolean>> {
const extra: Pick<IsBlockhashValidConfig, 'minContextSlot'> = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, you want to take an Omit approach rather than a Pick approach.

const extra = Omit<IsBlockhashValidConfig, 'commitment'> = {};

Comment on lines 3569 to 3582
let commitment;
if (config) {
if (config.minContextSlot) {
extra.minContextSlot = config.minContextSlot;
}

if (config.commitment) {
commitment = config.commitment;
} else {
commitment = this._commitment || 'finalized';
}
} else {
commitment = this._commitment || 'finalized';
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you wrote this PR, we created a helper for this!

const {commitment, config} = extractCommitmentFromConfig(...);

const unsafeRes = await this._rpcRequest('isBlockhashValid', args);
const res = create(unsafeRes, jsonRpcResultAndContext(boolean()));
if ('error' in res) {
console.log(res.error.message);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cat-oh-my

const res = create(unsafeRes, jsonRpcResultAndContext(boolean()));
if ('error' in res) {
console.log(res.error.message);
throw new Error(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you made this PR, we introduced SolanaJSONRPCError.

Comment on lines 3094 to 3099
await mockRpcResponse({
method: 'isBlockhashValid',
params: [blockhash],
value: true,
withContext: true
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mockRpcResponse helper actually skips when you're in live mode, so that you only have to write the tests once.

@codecov
Copy link

codecov bot commented Jul 6, 2022

Codecov Report

Merging #25888 (ff51656) into master (627d91f) will decrease coverage by 0.0%.
The diff coverage is n/a.

@@            Coverage Diff            @@
##           master   solana-labs/solana#25888     +/-   ##
=========================================
- Coverage    76.0%    76.0%   -0.1%     
=========================================
  Files          40       40             
  Lines        2370     2377      +7     
  Branches      343      344      +1     
=========================================
+ Hits         1803     1808      +5     
- Misses        449      450      +1     
- Partials      118      119      +1     

@stale
Copy link

stale bot commented Aug 13, 2022

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@stale stale bot added the stale [bot only] Added to stale content; results in auto-close after a week. label Aug 13, 2022
@steveluscher steveluscher removed the stale [bot only] Added to stale content; results in auto-close after a week. label Aug 13, 2022
@github-actions github-actions bot added the stale [bot only] Added to stale content; results in auto-close after a week. label Dec 29, 2022
@github-actions github-actions bot closed this Jan 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community Community contribution stale [bot only] Added to stale content; results in auto-close after a week.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

web3.js: Add isBlockhashValid support
3 participants