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

[TT-1517] add fluent waiting for pending transactions #1100

Merged
merged 2 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 7 additions & 1 deletion seth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ client, err := builder.
// tracing
WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}).
// protections
WithProtections(true, true).
WithProtections(true, true, seth.MustMakeDuration(2*time.Minute)).
// artifacts folder
WithArtifactsFolder("some_folder").
// nonce manager
Expand Down Expand Up @@ -319,6 +319,12 @@ check_rpc_health_on_start = false

It will execute a simple check of transferring 10k wei from root key to root key and check if the transaction was successful.

You can also enable pending nonce protection that will check if given key has any pending transactions. By default, we will wait 1 minute for all transactions to be mined. If any of them is still pending, we will panic. You can enable it with:
```toml
pending_nonce_protection_enabled = true
pending_nonce_protection_timeout = "5m"
```

You can add more networks like this:

```toml
Expand Down
8 changes: 6 additions & 2 deletions seth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ func ValidateConfig(cfg *Config) error {
cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout}
}

if cfg.PendingNonceProtectionTimeout == nil {
cfg.PendingNonceProtectionTimeout = &Duration{D: DefaultPendingNonceProtectionTimeout}
}

return nil
}

Expand Down Expand Up @@ -752,7 +756,7 @@ func (m *Client) getProposedTransactionOptions(keyNum int) (*bind.TransactOpts,
var ctx context.Context

if m.Cfg.PendingNonceProtectionEnabled {
if nonceStatus.PendingNonce > nonceStatus.LastNonce {
if pendingErr := m.WaitUntilNoPendingTxForKeyNum(keyNum, m.Cfg.PendingNonceProtectionTimeout.Duration()); pendingErr != nil {
errMsg := `
pending nonce for key %d is higher than last nonce, there are %d pending transactions.

Expand All @@ -762,7 +766,7 @@ This issue is caused by one of two things:
`
err := fmt.Errorf(errMsg, keyNum, nonceStatus.PendingNonce-nonceStatus.LastNonce)
m.Errors = append(m.Errors, err)
// can't return nil, otherwise RPC wrapper will panic and we might lose funds on testnets/mainnets, that's why
// can't return nil, otherwise RPC wrapper will panic, and we might lose funds on testnets/mainnets, that's why
// error is passed in Context here to avoid panic, whoever is using Seth should make sure that there is no error
// present in Context before using *bind.TransactOpts
ctx = context.WithValue(context.Background(), ContextErrorKey{}, err)
Expand Down
7 changes: 4 additions & 3 deletions seth/client_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,12 @@ func (c *ClientBuilder) WithTracing(level string, outputs []string) *ClientBuild
return c
}

// WithProtections enables or disables nonce protection (fails, when key has a pending transaction and you try to submit another one) and node health check on startup.
// Default values are false for nonce protection and true for node health check.
func (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool) *ClientBuilder {
// WithProtections enables or disables nonce protection (fails, when key has a pending transaction, and you try to submit another one) and node health check on startup.
// Default values are false for nonce protection, true for node health check and 1 minute timeout.
func (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool, pendingNonceProtectionTimeout *Duration) *ClientBuilder {
c.config.PendingNonceProtectionEnabled = pendingNonceProtectionEnabled
c.config.CheckRpcHealthOnStart = nodeHealthStartupCheck
c.config.PendingNonceProtectionTimeout = pendingNonceProtectionTimeout
return c
}

Expand Down
6 changes: 4 additions & 2 deletions seth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ const (
NETWORK_ENV_VAR = "SETH_NETWORK"
URL_ENV_VAR = "SETH_URL"

DefaultNetworkName = "Default"
DefaultDialTimeout = 1 * time.Minute
DefaultNetworkName = "Default"
DefaultDialTimeout = 1 * time.Minute
DefaultPendingNonceProtectionTimeout = 1 * time.Minute

DefaultTransferGasFee = 21_000
DefaultGasPrice = 1_000_000_000 // 1 Gwei
Expand Down Expand Up @@ -59,6 +60,7 @@ type Config struct {
TracingLevel string `toml:"tracing_level"`
TraceOutputs []string `toml:"trace_outputs"`
PendingNonceProtectionEnabled bool `toml:"pending_nonce_protection_enabled"`
PendingNonceProtectionTimeout *Duration `toml:"pending_nonce_protection_timeout"`
ConfigDir string `toml:"abs_path"`
ExperimentsEnabled []string `toml:"experiments_enabled"`
CheckRpcHealthOnStart bool `toml:"check_rpc_health_on_start"`
Expand Down
2 changes: 1 addition & 1 deletion seth/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func TestConfig_MaximalBuilder(t *testing.T) {
// tracing
WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}).
// protections
WithProtections(true, true).
WithProtections(true, true, seth.MustMakeDuration(2*time.Minute)).
// artifacts folder
WithArtifactsFolder("some_folder").
// nonce manager
Expand Down
2 changes: 1 addition & 1 deletion seth/gas_bump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func TestGasBumping_Contract_Deployment_Legacy_CustomBumpingFunction(t *testing.
WithEIP1559DynamicFees(false).
WithLegacyGasPrice(1).
WithTransactionTimeout(10*time.Second).
WithProtections(false, false).
WithProtections(false, false, nil).
WithGasBumping(5, 0, func(gasPrice *big.Int) *big.Int {
customGasBumps++
return new(big.Int).Mul(gasPrice, big.NewInt(512))
Expand Down
4 changes: 4 additions & 0 deletions seth/seth.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ ephemeral_addresses_number = 0
# it when running load tests.
pending_nonce_protection_enabled = false

# When pending_nonce_protection_enabled is set to true, this value will be used to determine how long we should wait
# for all pending transactions to be mined (so that pending nonce and last nonce are equal).
# pending_nonce_protection_timeout = "10s"

# Amount to be left on root key/address, when we are using ephemeral addresses. It's the amount that will not
# be divided into ephemeral keys.
root_key_funds_buffer = 10 # 10 ether
Expand Down
Loading