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

web3.js: add getSignatureStatuses to confirm transaction #28290

Conversation

abrzezinski94
Copy link
Contributor

@abrzezinski94 abrzezinski94 commented Oct 7, 2022

Problem

If you call confirmTransaction for a signature that has already been processed, the signatureSubscribe subscription will never fire a notification. This will certainly mean that confirmTransaction() will throw an expiry error for a transaction that is, in actual fact, confirmed.

solana-labs/solana-web3.js#1107

Summary of Changes

add getSignatureStatuses

Fixes #1107.

@codecov
Copy link

codecov bot commented Oct 7, 2022

Codecov Report

Merging #28290 (bed9ea7) into master (831ed96) will decrease coverage by 0.1%.
The diff coverage is 65.4%.

❗ Current head bed9ea7 differs from pull request most recent head 78b36ee. Consider uploading reports for the commit 78b36ee to get more accurate results

@@            Coverage Diff            @@
##           master   solana-labs/solana#28290     +/-   ##
=========================================
- Coverage    77.1%    77.0%   -0.2%     
=========================================
  Files          55       55             
  Lines        2934     2977     +43     
  Branches      408      422     +14     
=========================================
+ Hits         2264     2294     +30     
- Misses        529      535      +6     
- Partials      141      148      +7     

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.

Hey! Thanks for this PR; I really appreciate the help to make transaction confirmation more robust.

I have ideas to make this way simpler.

You've implemented a polling strategy here, and have done so without removing the existing subscription strategy. This means that we now have two competing strategies, where what we could do instead is to make them compliment each other.

In the original issue, I wrote:

  1. Before subscribing or honouring expiry signals, check the signature status using the getSignatureStatuses method.
  2. If the signature has not been confirmed and you have not received an expiry signal, subscribe for status updates.
  3. Race the expiry strategy and the confirmation strategy.

What I was getting at there is that – between the time you send the transaction and you subscribe for the signature – you can miss the subscription notification.

The solution for this should be simple:

  1. Subscribe first.
  2. Immediately after the subscription is live, send a single getSignatureStatuses to rule out the possibility of having missed a prior signature change.

This should make this PR almost literally one line, and should eliminate the inefficiency of polling!

Want to take a crack at that?

@abrzezinski94
Copy link
Contributor Author

abrzezinski94 commented Oct 7, 2022

Hey! Thanks for this PR; I really appreciate the help to make transaction confirmation more robust.

I have ideas to make this way simpler.

You've implemented a polling strategy here, and have done so without removing the existing subscription strategy. This means that we now have two competing strategies, where what we could do instead is to make them compliment each other.

In the original issue, I wrote:

  1. Before subscribing or honouring expiry signals, check the signature status using the getSignatureStatuses method.
  2. If the signature has not been confirmed and you have not received an expiry signal, subscribe for status updates.
  3. Race the expiry strategy and the confirmation strategy.

What I was getting at there is that – between the time you send the transaction and you subscribe for the signature – you can miss the subscription notification.

The solution for this should be simple:

  1. Subscribe first.
  2. Immediately after the subscription is live, send a single getSignatureStatuses to rule out the possibility of having missed a prior signature change.

This should make this PR almost literally one line, and should eliminate the inefficiency of polling!

Want to take a crack at that?

Hi we use pooling like that in mango/gov ui and its quite nice. Websockets can sometimes fail and its good to have a backup method to confirm tx.

What do you think about adding option to config it for example:

a. websockets only
b. pooling only
c. websockets + pooling (i would leave it as default method for reliability)

and change suggested by you ofc to shot first with getSignatureStatuses for websockets

@steveluscher
Copy link
Contributor

I would rather resolve the architectural decision here amongst ourselves, rather than to push it out to library users as a choice.

Some reasons I disfavor polling:

  • It is very low resolution. The maximum confirmation resolution that you can achieve is half the sampling frequency (as per Nyquist).
  • It's wasteful of client connections, and RPC capacity; one subscription channel can report on n signatures. Polling requires n connections, m times per second.

I think I would need some evidence that websockets are so unreliable – other than the known problem of being able to ‘miss’ signatures that this PR aims to solve – as to warrant making every Solana application that uses web3.js start sending all extra network requests to RPCs. If there's evidence of the WS transport being that unreliable, we should fix that first.

@mergify mergify bot dismissed steveluscher’s stale review October 8, 2022 14:09

Pull request has been modified.

@mergify mergify bot requested a review from a team October 8, 2022 14:10
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.

giphy-2

After those couple fixes we'll need these tests, and then we're done!

  1. Test that getSignatureStatuses does not get fired off until onSignature has resolved.
  2. Test that getSignatureStatuses does not get fired at all if (somehow) onSignature resolves synchronously (who knows; that could get implemented in the future)
  3. Test that transactions get confirmed if no subscription notifications get fired, but getSignatureStatuses returns a confirmation
  4. Test that transactions still get confirmed if getSignatureStatuses returns nothing, but then you get a subscription notification.

You should be able to accomplish that without writing a ‘live’ test at all.

web3.js/src/connection.ts Outdated Show resolved Hide resolved
@@ -3295,6 +3295,24 @@ export class Connection {
},
subscriptionCommitment,
);

(async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn’t quite do what we set out to do yet, because it doesn’t wait for the subscription to be set up (ie. wait for the server to vend a clientSubscriptionId). We want to wait for the subscription to be live before firing off the patch-up call to getSignatureStatuses.

his also could use a test! Assert that the gss call does not get made until onSignature resolves.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

isn't subscriptionId returned immediately from onSignature ?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, right! Those are the client ids, not the server ids which we keep internal.

Ooh, nice. This just got way more complicated. Essentially, you need a way now of observing the state change of the subscription you just set up from 'pending' to 'subscribed' before you make the gSS call. Oftentimes you’ll be the first subscription for the whole app, in which case this could take considerable time (ie. the whole WebSocket connection has to be set up first).

This will be the first time that any code needs to observe the internal state of a subscription. Any ideas on how to achieve this with a minimum of changes?

Copy link
Contributor Author

@abrzezinski94 abrzezinski94 Oct 10, 2022

Choose a reason for hiding this comment

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

at first glance maybe we could get hash like this.onSignature do it (i would need to look if for sure it will be the same).

Observe this._subscriptionsByHash (maybe event raise in _updateSubscriptions? Or just check changes in while loop) and when typeof this._subscriptionsByHash[hash] !== 'undefined' && state is 'subscribed' fire gss we can do it all in confirmation promise.

Copy link
Contributor Author

@abrzezinski94 abrzezinski94 Oct 12, 2022

Choose a reason for hiding this comment

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

Ok i pushed some solution for you to look this is just working poc and we can probably make it better. still needs to write tests if it fulfills all needs. It dont have any impact on outside code.

@mergify mergify bot dismissed steveluscher’s stale review October 12, 2022 12:14

Pull request has been modified.

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.

Let me take a crack at this.

@steveluscher steveluscher force-pushed the feature/Transaction-Signature-Confirmation branch from adbaa5c to 78b36ee Compare October 21, 2022 00:38
@mergify mergify bot dismissed steveluscher’s stale review October 21, 2022 00:38

Pull request has been modified.

@steveluscher
Copy link
Contributor

@jordansexton, other than the fact that commented-out test doesn't work, what do you think of this (see my three commits in the history here).

@brianlong
Copy link
Contributor

The solution proposed here looks good, and I do not see an undue burden on RPC nodes. The server load could go down if clients stop polling in a loop. Thanks for working on this!

@steveluscher steveluscher merged commit d16810e into solana-labs:master Nov 28, 2022
gnapoli23 pushed a commit to gnapoli23/solana that referenced this pull request Dec 16, 2022
…onfirmed txs (solana-labs#28290)

* chore: create internal method for subscribing to subscription state changes

* add status pool

* fix tests

* more tests

* syntax fix

* variable rename

* fix test

* comment fix

* remove getSignatureStatuses pooling

* rename variable

* IIFE

* wait for subscription

* fix interval clear

* test: you can now pause the establishment of subscriptions in tests

* feat: implementation of signature status check after setting up signature subscription

Co-authored-by: steveluscher <[email protected]>
nickfrosty pushed a commit to nickfrosty/solana that referenced this pull request Jan 4, 2023
…onfirmed txs (solana-labs#28290)

* chore: create internal method for subscribing to subscription state changes

* add status pool

* fix tests

* more tests

* syntax fix

* variable rename

* fix test

* comment fix

* remove getSignatureStatuses pooling

* rename variable

* IIFE

* wait for subscription

* fix interval clear

* test: you can now pause the establishment of subscriptions in tests

* feat: implementation of signature status check after setting up signature subscription

Co-authored-by: steveluscher <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community Community contribution
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants