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][SOAK] - Soak Testing TxExpirationRebroadcast feature #962

Draft
wants to merge 62 commits into
base: develop
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
2d1a82d
refactor so txm owns blockhash assignment
Farber98 Nov 15, 2024
50dfef0
lastValidBlockHeight shouldn't be exported
Farber98 Nov 15, 2024
4e545e2
better comment
Farber98 Nov 15, 2024
4ded53c
refactor sendWithRetry to make it clearer
Farber98 Nov 15, 2024
9e1be6d
confirm loop refactor
Farber98 Nov 18, 2024
7dd2028
fix infinite loop
Farber98 Nov 18, 2024
6c675f2
move accountID inside msg
Farber98 Nov 19, 2024
b0d9426
lint fix
Farber98 Nov 19, 2024
1b38665
base58 does not contain lower l
Farber98 Nov 19, 2024
6923ddf
fix hash errors
Farber98 Nov 19, 2024
462844b
fix generate random hash
Farber98 Nov 19, 2024
fd785d0
remove blockhash as we only need block height
Farber98 Nov 19, 2024
cf958a4
expired tx changes without tests
Farber98 Nov 19, 2024
c5e957b
add maybe to mocks
Farber98 Nov 19, 2024
a505993
expiration tests
Farber98 Nov 19, 2024
adc8b1c
send txes through queue
Farber98 Nov 19, 2024
7d77f99
revert pendingtx leakage of information. overwrite blockhash
Farber98 Nov 20, 2024
92a280b
fix order of confirm loop and not found signature check
Farber98 Nov 20, 2024
2598e19
fix mocks
Farber98 Nov 20, 2024
42b3da1
prevent confirmation loop to mark tx as errored when it needs to be r…
Farber98 Nov 20, 2024
89af1f3
fix test
Farber98 Nov 20, 2024
5e8a0da
fix pointer
Farber98 Nov 20, 2024
75c1dcd
add comments
Farber98 Nov 21, 2024
4ff2d23
reduce rpc calls + refactors
Farber98 Nov 21, 2024
84e423e
tests + check to save rpc calls
Farber98 Nov 21, 2024
7d8319e
address feedback + remove redundant impl
Farber98 Nov 22, 2024
68f3a3e
iface comment
Farber98 Nov 22, 2024
780179f
address feedback on compute unit limit and lastValidBlockHeight assig…
Farber98 Nov 25, 2024
98f0246
blockhash assignment inside txm.sendWithRetry
Farber98 Nov 25, 2024
cbf55f6
address feedback
Farber98 Nov 26, 2024
90daf33
Merge branch 'develop' into nonevm-706-support-custom-bumping-strateg…
Farber98 Nov 26, 2024
77b28cf
refactors after merge
Farber98 Nov 26, 2024
0c4a7d8
fix interactive rebase
Farber98 Nov 26, 2024
849ac48
fix whitespace diffs
Farber98 Nov 26, 2024
20a1548
fix import
Farber98 Nov 26, 2024
a4d4770
fix mocks
Farber98 Nov 26, 2024
56a64da
add on prebroadcaste error
Farber98 Nov 26, 2024
9148d7d
remove rebroadcast count and fix package
Farber98 Nov 27, 2024
caf2cbf
improve docs
Farber98 Nov 27, 2024
1fbd63f
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Nov 28, 2024
5c22af2
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 5, 2024
02ffd1a
fix comparison against blockHeight instead of slotHeight
Farber98 Dec 5, 2024
c00494c
address feedback
Farber98 Dec 5, 2024
6ac30b0
fix lint
Farber98 Dec 5, 2024
36ee4ec
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 5, 2024
0e38174
fix log
Farber98 Dec 5, 2024
b04653c
config for soaks
Farber98 Dec 5, 2024
d240021
address feedback
Farber98 Dec 6, 2024
4389e13
remove useless slot height
Farber98 Dec 6, 2024
6bcdd8f
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 9, 2024
50dd10f
address feedback
Farber98 Dec 10, 2024
10b0453
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 11, 2024
2ea0c50
validate that tx doesn't exist in any of maps when adding new tx
Farber98 Dec 11, 2024
b5a6927
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 11, 2024
b4b4fd5
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 13, 2024
e7d7680
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 16, 2024
409fd1c
callers set lastValidBlockheight + get blockhash on expiration + inte…
Farber98 Dec 17, 2024
b900abe
Merge branch 'backup-branch-fee-bumping' into soak-expiration-rebroad…
Farber98 Dec 17, 2024
31a65a0
Merge branch 'develop' into backup-branch-fee-bumping
Farber98 Dec 17, 2024
1be704a
add enq iface comm to help callers
Farber98 Dec 17, 2024
9ee8977
address feedback
Farber98 Dec 17, 2024
105e890
Merge branch 'backup-branch-fee-bumping' into soak-expiration-rebroad…
Farber98 Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
expired tx changes without tests
  • Loading branch information
Farber98 committed Nov 26, 2024

Verified

This commit was signed with the committer’s verified signature.
mike182uk Michael Barrett
commit cf958a46a6cb1a01159f66a6a49d39d76f8e8a00
3 changes: 2 additions & 1 deletion docs/relay/README.md
Original file line number Diff line number Diff line change
@@ -43,7 +43,8 @@ chainlink nodes solana create --name=<node-name> --chain-id=<chain-id> --url=<ur
| `OCR2CacheTTL` | stale OCR2 cache deadline | 1m | |
| `TxTimeout` | timeout to send tx to rpc endpoint | 1m | |
| `TxRetryTimeout` | duration for tx to be rebroadcast to rpc, txm stops rebroadcast after timeout | 10s | |
| `TxConfirmTimeout` | duration when confirming a tx signature before signature is discarded as unconfirmed | 30s | |
| `TxConfirmTimeout` | duration when confirming a tx signature before signature is discarded as unconfirmed | 30s |
| `TxExpirationRebroadcast` | enable or disable tx rebroadcast if expired. Expiration check is performed every `TxConfirmTimeout` interval | `false` | `true`, `false` |
| `SkipPreflight` | enable or disable preflight checks when sending tx | `true` | `true`, `false` |
| `Commitment` | Confirmation level for solana state and transactions. ([documentation](https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment)) | `confirmed` | `processed`, `confirmed`, `finalized` |
| `MaxRetries` | Parameter when sending transactions, how many times the RPC node will automatically rebroadcast a tx, default = `0` for custom txm rebroadcasting method, set to `-1` to use the RPC node's default retry strategy | `0` | |
28 changes: 17 additions & 11 deletions pkg/solana/config/config.go
Original file line number Diff line number Diff line change
@@ -11,17 +11,18 @@ import (

// Global solana defaults.
var defaultConfigSet = Chain{
BalancePollPeriod: config.MustNewDuration(5 * time.Second), // poll period for balance monitoring
ConfirmPollPeriod: config.MustNewDuration(500 * time.Millisecond), // polling for tx confirmation
OCR2CachePollPeriod: config.MustNewDuration(time.Second), // cache polling rate
OCR2CacheTTL: config.MustNewDuration(time.Minute), // stale cache deadline
TxTimeout: config.MustNewDuration(time.Minute), // timeout for send tx method in client
TxRetryTimeout: config.MustNewDuration(10 * time.Second), // duration for tx rebroadcasting to RPC node
TxConfirmTimeout: config.MustNewDuration(30 * time.Second), // duration before discarding tx as unconfirmed. Set to 0 to disable discarding tx.
TxRetentionTimeout: config.MustNewDuration(0 * time.Second), // duration to retain transactions after being marked as finalized or errored. Set to 0 to immediately drop transactions.
SkipPreflight: ptr(true), // to enable or disable preflight checks
Commitment: ptr(string(rpc.CommitmentConfirmed)),
MaxRetries: ptr(int64(0)), // max number of retries (default = 0). when config.MaxRetries < 0), interpreted as MaxRetries = nil and rpc node will do a reasonable number of retries
BalancePollPeriod: config.MustNewDuration(5 * time.Second), // poll period for balance monitoring
ConfirmPollPeriod: config.MustNewDuration(500 * time.Millisecond), // polling for tx confirmation
OCR2CachePollPeriod: config.MustNewDuration(time.Second), // cache polling rate
OCR2CacheTTL: config.MustNewDuration(time.Minute), // stale cache deadline
TxTimeout: config.MustNewDuration(time.Minute), // timeout for send tx method in client
TxRetryTimeout: config.MustNewDuration(10 * time.Second), // duration for tx rebroadcasting to RPC node
TxConfirmTimeout: config.MustNewDuration(30 * time.Second), // duration before discarding tx as unconfirmed. Set to 0 to disable discarding tx.
TxExpirationRebroadcast: ptr(false),
TxRetentionTimeout: config.MustNewDuration(0 * time.Second), // duration to retain transactions after being marked as finalized or errored. Set to 0 to immediately drop transactions.
SkipPreflight: ptr(true), // to enable or disable preflight checks
Commitment: ptr(string(rpc.CommitmentConfirmed)),
MaxRetries: ptr(int64(0)), // max number of retries (default = 0). when config.MaxRetries < 0), interpreted as MaxRetries = nil and rpc node will do a reasonable number of retries

// fee estimator
FeeEstimatorMode: ptr("fixed"),
@@ -43,6 +44,7 @@ type Config interface {
TxTimeout() time.Duration
TxRetryTimeout() time.Duration
TxConfirmTimeout() time.Duration
TxExpirationRebroadcast() bool
TxRetentionTimeout() time.Duration
SkipPreflight() bool
Commitment() rpc.CommitmentType
@@ -68,6 +70,7 @@ type Chain struct {
TxTimeout *config.Duration
TxRetryTimeout *config.Duration
TxConfirmTimeout *config.Duration
TxExpirationRebroadcast *bool
TxRetentionTimeout *config.Duration
SkipPreflight *bool
Commitment *string
@@ -105,6 +108,9 @@ func (c *Chain) SetDefaults() {
if c.TxConfirmTimeout == nil {
c.TxConfirmTimeout = defaultConfigSet.TxConfirmTimeout
}
if c.TxExpirationRebroadcast == nil {
c.TxExpirationRebroadcast = defaultConfigSet.TxExpirationRebroadcast
}
if c.TxRetentionTimeout == nil {
c.TxRetentionTimeout = defaultConfigSet.TxRetentionTimeout
}
35 changes: 13 additions & 22 deletions pkg/solana/config/mocks/config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions pkg/solana/config/toml.go
Original file line number Diff line number Diff line change
@@ -155,6 +155,9 @@ func setFromChain(c, f *Chain) {
if f.TxConfirmTimeout != nil {
c.TxConfirmTimeout = f.TxConfirmTimeout
}
if f.TxExpirationRebroadcast != nil {
c.TxExpirationRebroadcast = f.TxExpirationRebroadcast
}
if f.TxRetentionTimeout != nil {
c.TxRetentionTimeout = f.TxRetentionTimeout
}
@@ -241,6 +244,10 @@ func (c *TOMLConfig) TxConfirmTimeout() time.Duration {
return c.Chain.TxConfirmTimeout.Duration()
}

func (c *TOMLConfig) TxExpirationRebroadcast() bool {
return *c.Chain.TxExpirationRebroadcast
}

func (c *TOMLConfig) TxRetentionTimeout() time.Duration {
return c.Chain.TxRetentionTimeout.Duration()
}
19 changes: 19 additions & 0 deletions pkg/solana/txm/pendingtx.go
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ type PendingTxContext interface {
Remove(sig solana.Signature) (string, error)
// ListAll returns all of the signatures being tracked for all transactions not yet finalized or errored
ListAll() []solana.Signature
// ListAllExpiredBroadcastedTxs returns all the expired broadcasted that are in broadcasted state and have expired for given slot height.
ListAllExpiredBroadcastedTxs(currHeight uint64) []PendingTx
// Expired returns whether or not confirmation timeout amount of time has passed since creation
Expired(sig solana.Signature, confirmationTimeout time.Duration) bool
// OnProcessed marks transactions as Processed
@@ -216,6 +218,19 @@ func (c *pendingTxContext) ListAll() []solana.Signature {
return maps.Keys(c.sigToID)
}

// ListAllExpiredBroadcastedTxs returns all the expired broadcasted that are in broadcasted state and have expired for given slot height.
func (c *pendingTxContext) ListAllExpiredBroadcastedTxs(currHeight uint64) []PendingTx {
c.lock.RLock()
defer c.lock.RUnlock()
broadcastedTxes := make([]PendingTx, 0, len(c.broadcastedProcessedTxs)) // worst case, all of them
for _, tx := range c.broadcastedProcessedTxs {
if tx.state == Broadcasted && tx.lastValidBlockHeight < currHeight {
broadcastedTxes = append(broadcastedTxes, tx)
}
}
return broadcastedTxes
}

// Expired returns if the timeout for trying to confirm a signature has been reached
func (c *pendingTxContext) Expired(sig solana.Signature, confirmationTimeout time.Duration) bool {
c.lock.RLock()
@@ -604,6 +619,10 @@ func (c *pendingTxContextWithProm) ListAll() []solana.Signature {
return sigs
}

func (c *pendingTxContextWithProm) ListAllExpiredBroadcastedTxs(currHeight uint64) []PendingTx {
return c.pendingTx.ListAllExpiredBroadcastedTxs(currHeight)
}

func (c *pendingTxContextWithProm) Expired(sig solana.Signature, lifespan time.Duration) bool {
return c.pendingTx.Expired(sig, lifespan)
}
43 changes: 43 additions & 0 deletions pkg/solana/txm/txm.go
Original file line number Diff line number Diff line change
@@ -464,6 +464,9 @@ func (txm *Txm) confirm() {
txm.lggr.Errorw("failed to get client in txm.confirm", "error", err)
return
}
if txm.cfg.TxExpirationRebroadcast() {
txm.rebroadcastExpiredTxs(ctx, client)
}
txm.processConfirmations(ctx, client, sigs)
}
tick = time.After(utils.WithJitter(txm.cfg.ConfirmPollPeriod()))
@@ -497,6 +500,46 @@ func (txm *Txm) processConfirmations(ctx context.Context, client client.ReaderWr
wg.Wait() // wait for processing to finish
}

func (txm *Txm) rebroadcastExpiredTxs(ctx context.Context, client client.ReaderWriter) {
// Get current slot height to check if txes have expired when compared against their lastValidBlockHeight
currHeight, err := client.SlotHeight(ctx)
if err != nil {
txm.lggr.Errorw("failed to get current slot height", "error", err)
return
}

// Rebroadcast all expired txes
for _, tx := range txm.txs.ListAllExpiredBroadcastedTxs(currHeight) {
txm.lggr.Infow("transaction expired, rebroadcasting", "id", tx.UUID, "signature", tx.signatures)
if len(tx.signatures) == 0 { // prevent panic, shouldn't happen.
txm.lggr.Errorw("no signatures found for expired transaction", "id", tx.UUID)
continue
}
_, err := txm.txs.Remove(tx.signatures[0]) // only picking signature[0] because remove func removes all remaining signatures.
if err != nil {
txm.lggr.Errorw("failed to remove expired transaction", "id", tx.UUID, "error", err)
continue
}

rebroadcastTx := &PendingTx{
Tx: tx.Tx,
UUID: tx.UUID, // same id to handle case where set by caller. Previous ID has already been removed.
BalanceCheck: tx.BalanceCheck,
Amount: tx.Amount,
From: tx.From,
}

// Re-enqueue the transaction for rebroadcasting
err = txm.Enqueue(ctx, rebroadcastTx)
if err != nil {
txm.lggr.Errorw("failed to enqueue rebroadcast transaction", "id", tx.UUID, "error", err)
continue
}

txm.lggr.Infow("rebroadcast transaction enqueued", "id", tx.UUID)
}
}

func (txm *Txm) processSignatureStatuses(sigs []solanaGo.Signature, res []*rpc.SignatureStatusesResult) {
// Sort signatures and results process successful first
sortedSigs, sortedRes, err := SortSignaturesAndResults(sigs, res)
4 changes: 3 additions & 1 deletion pkg/solana/txm/txm_internal_test.go
Original file line number Diff line number Diff line change
@@ -132,7 +132,7 @@ func TestTxm(t *testing.T) {
},
}, nil)
mc.On("GetLatestBlock", mock.Anything).Return(&rpc.GetBlockResult{}, nil).Maybe()
mc.On("SlotHeight", mock.Anything).Return(uint64(0), nil).Maybe()
mc.On("SlotHeight", mock.Anything).Return(uint64(0), nil)

// mock solana keystore
mkey := keyMocks.NewSimpleKeystore(t)
@@ -775,6 +775,7 @@ func TestTxm_disabled_confirm_timeout_with_retention(t *testing.T) {
},
}, nil)
mc.On("GetLatestBlock", mock.Anything).Return(&rpc.GetBlockResult{}, nil).Maybe()
mc.On("SlotHeight", mock.Anything).Return(uint64(0), nil)

computeUnitLimitDefault := fees.ComputeUnitLimit(cfg.ComputeUnitLimitDefault())

@@ -980,6 +981,7 @@ func TestTxm_compute_unit_limit_estimation(t *testing.T) {
},
}, nil)
mc.On("GetLatestBlock", mock.Anything).Return(&rpc.GetBlockResult{}, nil).Maybe()
mc.On("SlotHeight", mock.Anything).Return(uint64(0), nil)

// mock solana keystore
mkey := keyMocks.NewSimpleKeystore(t)