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

[NONEVM-706][Solana][deprecated] - Refactor TXM + Rebroadcast Expired Tx functionality #928

Conversation

Farber98
Copy link
Contributor

@Farber98 Farber98 commented Nov 15, 2024

Description

Refactor confirm and sendWithRetry funcs

Adding a new TxExpirationRebroadcast toggable routine:

  • Inside confirm loop, after checking confirmations, we will determine if we need to rebroadcast expired transactions that where not processed yet. Rebroadcasting a transaction involves:
    • Removing previous tx and creating a new one with updated blockhash
      • Consideration: txid will be maintained, in case it was set by caller
    • calling sendWithRetry directly without enqueuing
    • Consideration: Implicitly it may be "bumping" despite config being off. We may get a new higher price for the rebroadcasted tx when we build and get a new compute unit price.

https://smartcontract-it.atlassian.net/browse/NONEVM-706

@Farber98 Farber98 self-assigned this Nov 15, 2024
// Otherwise, it marks the transaction as errored.
func (txm *Txm) handleErrorSignatureStatus(sig solanaGo.Signature, status *rpc.SignatureStatusesResult) {
// We want to rebroadcast rather than drop tx if expiration rebroadcast is enabled when blockhash was not found.
if status.Err != nil && status.Err == client.ErrBlockhashNotFound && txm.cfg.TxExpirationRebroadcast() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Huh I didn't realize we had these error patterns already defined in the client package. But don't think we can just do an == here for this match. The status.Err interface doesn't return like a normal RPC string. Think it looks something like this so don't think we can use these patterns. We probably have to do something like this then a strings.Contains

Comment on lines 166 to 171
mc.On("LatestBlockhash", mock.Anything).Return(&rpc.GetLatestBlockhashResult{
Value: &rpc.LatestBlockhashResult{
LastValidBlockHeight: 100,
Blockhash: solana.Hash{},
},
}, nil).Once()
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we mock the LatestBlockhash the same way for all of the tests. Could we move this one layer higher so we only have to mock it once? We can ditch the .Once() unless some tests don't get that far then I'm ok with replacing it with .Maybe()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ohh yes removing the once makes that generalization work 👍

@@ -908,6 +983,12 @@ func TestTxm_compute_unit_limit_estimation(t *testing.T) {
)

t.Run("simulation_succeeds", func(t *testing.T) {
mc.On("LatestBlockhash", mock.Anything).Return(&rpc.GetLatestBlockhashResult{
Copy link
Contributor

Choose a reason for hiding this comment

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

Same for this test

cfg := config.NewDefault()
cfg.Chain.FeeEstimatorMode = &estimator
cfg.Chain.TxConfirmTimeout = relayconfig.MustNewDuration(5 * time.Second)
cfg.Chain.TxRetentionTimeout = relayconfig.MustNewDuration(10 * time.Second) // Enable retention to keep transactions after finality and be able to check.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: think this comment is maybe incomplete?

Suggested change
cfg.Chain.TxRetentionTimeout = relayconfig.MustNewDuration(10 * time.Second) // Enable retention to keep transactions after finality and be able to check.
cfg.Chain.TxRetentionTimeout = relayconfig.MustNewDuration(10 * time.Second) // Enable retention to keep transactions after finality and be able to check their statuses.

txID := "test-rebroadcast"
assert.NoError(t, txm.Enqueue(ctx, t.Name(), tx, &txID))
wg.Wait()
time.Sleep(2 * time.Second) // Sleep to allow for rebroadcasting
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't have to use this if 2 second sleep is deterministic so rebroadcast always happen in that time. If not, maybe it's safer to use something like waitFor(t, waitDuration, txm, prom, empty) which we use in other tests above to wait till all tx have gone through.

slotHeightFunc := func() (uint64, error) {
return uint64(1500), nil
}
// Mock LatestBlockhash to return a invalid blockhash in the first 3 attempts (initial + 2 rebroadcasts)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: typo

Suggested change
// Mock LatestBlockhash to return a invalid blockhash in the first 3 attempts (initial + 2 rebroadcasts)
// Mock LatestBlockhash to return an invalid blockhash in the first 3 attempts (initial + 2 rebroadcasts)

Comment on lines 11 to 12
"github.com/gagliardetto/solana-go"
solanaGo "github.com/gagliardetto/solana-go"
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we have duplicate imports here

@@ -84,7 +85,12 @@ func TestTxm_SendWithRetry_Race(t *testing.T) {

t.Run("delay in rebroadcasting tx", func(t *testing.T) {
client := clientmocks.NewReaderWriter(t)
// client mock
client.On("LatestBlockhash", mock.Anything).Return(&rpc.GetLatestBlockhashResult{
Copy link
Contributor

Choose a reason for hiding this comment

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

Like in the other tests, I think we can move this mock to the top level since we return the same thing for all of them

@Farber98 Farber98 force-pushed the nonevm-706-support-custom-bumping-strategy-rpc-expiration-within-confirmation branch from 9f5ded1 to a45fa1e Compare November 26, 2024 19:05
lastValidBlockHeight shouldn't be exported

better comment

refactor sendWithRetry to make it clearer

confirm loop refactor

move accountID inside msg

lint fix

base58 does not contain lower l

fix hash errors

fix generate random hash

remove blockhash as we only need block height

expired tx changes without tests

add maybe to mocks

expiration tests

send txes through queue

revert pendingtx leakage of information. overwrite blockhash

fix order of confirm loop and not found signature check

fix mocks

fix test

fix pointer

add comments

reduce rpc calls + refactors

tests + check to save rpc calls

address feedback + remove redundant impl

iface comment

address feedback on compute unit limit and lastValidBlockHeight assignment

blockhash assignment inside txm.sendWithRetry

address feedback
@Farber98 Farber98 force-pushed the nonevm-706-support-custom-bumping-strategy-rpc-expiration-within-confirmation branch from 1fc7ee7 to 19336a5 Compare November 26, 2024 21:02
@cl-sonarqube-production
Copy link

Quality Gate failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 75%)

See analysis details on SonarQube

@Farber98 Farber98 changed the title [NONEVM-706][Solana] - Refactor TXM + Rebroadcast Expired Tx functionality [NONEVM-706][Solana][deprecated] - Refactor TXM + Rebroadcast Expired Tx functionality Nov 26, 2024
@Farber98 Farber98 closed this Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants