-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Transaction Simulation to the TXM (#12503)
* Added tx simulator to maintain chain specific simulation methods * Fixed linting * Fixed linting and regenerated config docs * Generated mock * Fixed config tests * Moved the tx simulator to the chain client * Removed client from Txm struct * Removed config from test helper * Added tests and logging * Added changeset * Fixed multinode test * Fixed linting * Fixed comment * Added test for non-OOC error * Reduced context initializations in tests * Updated to account for all types of OOC errors * Removed custom zk counter method and simplified error handling * Removed zkevm chain type * Changed simulate tx method return object * Cleaned up stale comments * Removed unused error message * Changed zk overflow validation method name * Reverted method name change
- Loading branch information
1 parent
96e3901
commit dc224a2
Showing
11 changed files
with
241 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"chainlink": minor | ||
--- | ||
|
||
Added a tx simulation feature to the chain client to enable testing for zk out-of-counter (OOC) errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package client | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ethereum/go-ethereum" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
"github.com/smartcontractkit/chainlink/v2/common/config" | ||
) | ||
|
||
type simulatorClient interface { | ||
CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error | ||
} | ||
|
||
// ZK chains can return an out-of-counters error | ||
// This method allows a caller to determine if a tx would fail due to OOC error by simulating the transaction | ||
// Used as an entry point in case custom simulation is required across different chains | ||
func SimulateTransaction(ctx context.Context, client simulatorClient, lggr logger.SugaredLogger, chainType config.ChainType, msg ethereum.CallMsg) *SendError { | ||
err := simulateTransactionDefault(ctx, client, msg) | ||
return NewSendError(err) | ||
} | ||
|
||
// eth_estimateGas returns out-of-counters (OOC) error if the transaction would result in an overflow | ||
func simulateTransactionDefault(ctx context.Context, client simulatorClient, msg ethereum.CallMsg) error { | ||
var result hexutil.Big | ||
return client.CallContext(ctx, &result, "eth_estimateGas", toCallArg(msg), "pending") | ||
} | ||
|
||
func toCallArg(msg ethereum.CallMsg) interface{} { | ||
arg := map[string]interface{}{ | ||
"from": msg.From, | ||
"to": msg.To, | ||
} | ||
if len(msg.Data) > 0 { | ||
arg["input"] = hexutil.Bytes(msg.Data) | ||
} | ||
if msg.Value != nil { | ||
arg["value"] = (*hexutil.Big)(msg.Value) | ||
} | ||
if msg.Gas != 0 { | ||
arg["gas"] = hexutil.Uint64(msg.Gas) | ||
} | ||
if msg.GasPrice != nil { | ||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) | ||
} | ||
if msg.GasFeeCap != nil { | ||
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) | ||
} | ||
if msg.GasTipCap != nil { | ||
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) | ||
} | ||
return arg | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package client_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum" | ||
"github.com/stretchr/testify/require" | ||
"github.com/tidwall/gjson" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/logger" | ||
|
||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" | ||
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest" | ||
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils" | ||
) | ||
|
||
func TestSimulateTx_Default(t *testing.T) { | ||
t.Parallel() | ||
|
||
fromAddress := testutils.NewAddress() | ||
toAddress := testutils.NewAddress() | ||
ctx := testutils.Context(t) | ||
|
||
t.Run("returns without error if simulation passes", func(t *testing.T) { | ||
wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { | ||
switch method { | ||
case "eth_subscribe": | ||
resp.Result = `"0x00"` | ||
resp.Notify = headResult | ||
return | ||
case "eth_unsubscribe": | ||
resp.Result = "true" | ||
return | ||
case "eth_estimateGas": | ||
resp.Result = `"0x100"` | ||
} | ||
return | ||
}).WSURL().String() | ||
|
||
ethClient := mustNewChainClient(t, wsURL) | ||
err := ethClient.Dial(ctx) | ||
require.NoError(t, err) | ||
|
||
msg := ethereum.CallMsg{ | ||
From: fromAddress, | ||
To: &toAddress, | ||
Data: []byte("0x00"), | ||
} | ||
sendErr := client.SimulateTransaction(ctx, ethClient, logger.TestSugared(t), "", msg) | ||
require.Empty(t, sendErr) | ||
}) | ||
|
||
t.Run("returns error if simulation returns zk out-of-counters error", func(t *testing.T) { | ||
wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { | ||
switch method { | ||
case "eth_subscribe": | ||
resp.Result = `"0x00"` | ||
resp.Notify = headResult | ||
return | ||
case "eth_unsubscribe": | ||
resp.Result = "true" | ||
return | ||
case "eth_estimateGas": | ||
resp.Error.Code = -32000 | ||
resp.Result = `"0x100"` | ||
resp.Error.Message = "not enough keccak counters to continue the execution" | ||
} | ||
return | ||
}).WSURL().String() | ||
|
||
ethClient := mustNewChainClient(t, wsURL) | ||
err := ethClient.Dial(ctx) | ||
require.NoError(t, err) | ||
|
||
msg := ethereum.CallMsg{ | ||
From: fromAddress, | ||
To: &toAddress, | ||
Data: []byte("0x00"), | ||
} | ||
sendErr := client.SimulateTransaction(ctx, ethClient, logger.TestSugared(t), "", msg) | ||
require.Equal(t, true, sendErr.IsOutOfCounters()) | ||
}) | ||
|
||
t.Run("returns without error if simulation returns non-OOC error", func(t *testing.T) { | ||
wsURL := testutils.NewWSServer(t, &cltest.FixtureChainID, func(method string, params gjson.Result) (resp testutils.JSONRPCResponse) { | ||
switch method { | ||
case "eth_subscribe": | ||
resp.Result = `"0x00"` | ||
resp.Notify = headResult | ||
return | ||
case "eth_unsubscribe": | ||
resp.Result = "true" | ||
return | ||
case "eth_estimateGas": | ||
resp.Error.Code = -32000 | ||
resp.Error.Message = "something went wrong" | ||
} | ||
return | ||
}).WSURL().String() | ||
|
||
ethClient := mustNewChainClient(t, wsURL) | ||
err := ethClient.Dial(ctx) | ||
require.NoError(t, err) | ||
|
||
msg := ethereum.CallMsg{ | ||
From: fromAddress, | ||
To: &toAddress, | ||
Data: []byte("0x00"), | ||
} | ||
sendErr := client.SimulateTransaction(ctx, ethClient, logger.TestSugared(t), "", msg) | ||
require.Equal(t, false, sendErr.IsOutOfCounters()) | ||
}) | ||
} |