diff --git a/benchmark/helper.go b/benchmark/helper.go new file mode 100644 index 0000000000..a3bce08e1e --- /dev/null +++ b/benchmark/helper.go @@ -0,0 +1,174 @@ +package benchmark + +import ( + "encoding/hex" + "math" + "math/big" + "path/filepath" + "testing" + + "github.com/0xPolygon/polygon-edge/chain" + "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" + "github.com/0xPolygon/polygon-edge/state" + itrie "github.com/0xPolygon/polygon-edge/state/immutable-trie" + "github.com/0xPolygon/polygon-edge/state/runtime" + "github.com/0xPolygon/polygon-edge/txrelayer" + "github.com/0xPolygon/polygon-edge/types" + "github.com/hashicorp/go-hclog" + "github.com/stretchr/testify/require" + "github.com/umbracle/ethgo" + "github.com/umbracle/ethgo/abi" + "github.com/umbracle/ethgo/wallet" +) + +var ( + singleContCalcFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["compute"] + singleContGetFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["getValue"] + singleContSetFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["addValue"] + multiContSetAAddrFunc = contractsapi.TestBenchmarkA.Abi.Methods["setContractAddr"] + multiContSetBAddrFunc = contractsapi.TestBenchmarkA.Abi.Methods["setContractAddr"] + multiContFnA = contractsapi.TestBenchmarkA.Abi.Methods["fnA"] +) + +// deployContractOnRootAndChild deploys contract code on both root and child chain +func deployContractOnRootAndChild( + b *testing.B, + childTxRelayer txrelayer.TxRelayer, + rootTxRelayer txrelayer.TxRelayer, + sender ethgo.Key, + byteCode []byte) (ethgo.Address, ethgo.Address) { + b.Helper() + + // deploy contract on the child chain + contractChildAddr := deployContract(b, childTxRelayer, sender, byteCode) + + // deploy contract on the root chain + contractRootAddr := deployContract(b, rootTxRelayer, sender, byteCode) + + return contractChildAddr, contractRootAddr +} + +// deployContract deploys contract code for the given relayer +func deployContract(b *testing.B, txRelayer txrelayer.TxRelayer, sender ethgo.Key, byteCode []byte) ethgo.Address { + b.Helper() + + txn := ðgo.Transaction{ + To: nil, // contract deployment + Input: byteCode, + } + + receipt, err := txRelayer.SendTransaction(txn, sender) + require.NoError(b, err) + require.Equal(b, uint64(types.ReceiptSuccess), receipt.Status) + require.NotEqual(b, ethgo.ZeroAddress, receipt.ContractAddress) + + return receipt.ContractAddress +} + +// getTxInput returns input for sending tx, given the abi encoded method and call parameters +func getTxInput(b *testing.B, method *abi.Method, args interface{}) []byte { + b.Helper() + + var ( + input []byte + err error + ) + + if args != nil { + input, err = method.Encode(args) + } else { + input = method.ID() + } + + require.NoError(b, err) + + return input +} + +// setContractDependencyAddress calls setContract function on caller contract, to set address of the callee contract +func setContractDependencyAddress(b *testing.B, txRelayer txrelayer.TxRelayer, callerContractAddr ethgo.Address, + calleeContractAddr ethgo.Address, setContractAbiMethod *abi.Method, sender ethgo.Key) { + b.Helper() + + input := getTxInput(b, setContractAbiMethod, []interface{}{calleeContractAddr}) + receipt, err := txRelayer.SendTransaction( + ðgo.Transaction{ + To: &callerContractAddr, + Input: input, + }, sender) + require.NoError(b, err) + require.Equal(b, uint64(types.ReceiptSuccess), receipt.Status) +} + +// getPrivateKey initializes a private key from provided raw private key +func getPrivateKey(b *testing.B, privateKeyRaw string) ethgo.Key { + b.Helper() + + dec, err := hex.DecodeString(privateKeyRaw) + require.NoError(b, err) + + privateKey, err := wallet.NewWalletFromPrivKey(dec) + require.NoError(b, err) + + return privateKey +} + +func transitionDeployContract(b *testing.B, transition *state.Transition, byteCode []byte, + sender types.Address) types.Address { + b.Helper() + + deployResult := transition.Create2(sender, byteCode, big.NewInt(0), 1e9) + require.NoError(b, deployResult.Err) + + return deployResult.Address +} + +func transitionCallContract(b *testing.B, transition *state.Transition, contractAddress types.Address, + sender types.Address, input []byte) *runtime.ExecutionResult { + b.Helper() + + result := transition.Call2(sender, contractAddress, input, big.NewInt(0), math.MaxUint64) + require.NoError(b, result.Err) + + return result +} + +func newTestTransition(b *testing.B, alloc map[types.Address]*chain.GenesisAccount, disk bool) *state.Transition { + b.Helper() + + var st *itrie.State + + if disk { + stateStorage, err := itrie.NewLevelDBStorage(filepath.Join(b.TempDir(), "trie"), hclog.NewNullLogger()) + require.NoError(b, err) + + st = itrie.NewState(stateStorage) + } else { + st = itrie.NewState(itrie.NewMemoryStorage()) + } + + ex := state.NewExecutor(&chain.Params{ + Forks: chain.AllForksEnabled, + BurnContract: map[uint64]string{ + 0: types.ZeroAddress.String(), + }, + }, st, hclog.NewNullLogger()) + + rootHash, err := ex.WriteGenesis(alloc, types.Hash{}) + require.NoError(b, err) + + ex.GetHash = func(h *types.Header) state.GetHashByNumber { + return func(i uint64) types.Hash { + return rootHash + } + } + + transition, err := ex.BeginTxn( + rootHash, + &types.Header{}, + types.ZeroAddress, + ) + require.NoError(b, err) + + return transition +} diff --git a/benchmark/root_child_benchmark_test.go b/benchmark/root_child_benchmark_test.go new file mode 100644 index 0000000000..3c6d21b57c --- /dev/null +++ b/benchmark/root_child_benchmark_test.go @@ -0,0 +1,21 @@ +package benchmark + +import ( + "testing" +) + +// The rootChildSendTx function executes test cases that measure transaction execution on both the root and child chains +// To do this, it first calls RootChildSendTxSetUp to set up the testing environment, +// which may include starting the cluster, deploying contracts, and building the test cases. +// After building the test cases, rootChildSendTx returns them along with a cleanup function that should be called +// after the test cases have been executed. The test cases are executed by the TxTestCasesExecutor. +func Benchmark_RootChildSendTx(b *testing.B) { + // set up environment, get test cases and clean up fn + testCases, cleanUpFn := RootChildSendTxSetUp(b, "", "", "", true) + defer cleanUpFn() + + // Loop over the test cases and measure the execution time of the transactions + for _, testInput := range testCases { + TxTestCasesExecutor(b, testInput) + } +} diff --git a/benchmark/root_child_send_tx.go b/benchmark/root_child_send_tx.go new file mode 100644 index 0000000000..50669ddfb9 --- /dev/null +++ b/benchmark/root_child_send_tx.go @@ -0,0 +1,250 @@ +package benchmark + +import ( + "math/big" + "sync" + "testing" + "time" + + "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" + "github.com/0xPolygon/polygon-edge/e2e-polybft/framework" + "github.com/0xPolygon/polygon-edge/txrelayer" + "github.com/0xPolygon/polygon-edge/types" + "github.com/stretchr/testify/require" + "github.com/umbracle/ethgo" + "github.com/umbracle/ethgo/wallet" +) + +// RootChildSendTxSetUp sets environment for execution of sentTx test cases on both root and child chains and +// returns test cases and clean up fn. +// The rootJSONRPC, childJSONRPC, privateKey and startCluster params are used to configure the testing environment. +// If startCluster is false, then the local cluster will not be started and the provided addresses +// will be used as the endpoints to the root and child chains. +// If startCluster is set to true, the local cluster will be started automatically. +// If the private key is specified, it will be used as the transaction sender. +// Otherwise, the local cluster will generate a sender key. +// If startCluster is set to false, then the sender must have enough funds for sending transactions. +func RootChildSendTxSetUp(b *testing.B, rootNodeAddr, childNodeAddr, + privateKey string, startCluster bool) ([]TxTestCase, func()) { + b.Helper() + // check if test is called with the root and child node addresses and private key set. + // if that is the case use that json rpc addresses, otherwise run the cluster + require.True(b, startCluster || rootNodeAddr != "" && childNodeAddr != "" && privateKey != "") + + var sender ethgo.Key + // if the privateKey flag is set then recover the key, otherwise recover the key + if privateKey != "" { + sender = getPrivateKey(b, privateKey) + } else { + var err error + sender, err = wallet.GenerateKey() + require.NoError(b, err) + } + + senderEthAddr := sender.Address() + + // start the cluster if needed + cleanUpFn := func() {} + + if startCluster { + b.Log("Starting the cluster...") + cluster := framework.NewTestCluster(b, 5, + framework.WithPremine(types.Address(senderEthAddr)), + framework.WithEpochSize(5)) + + cleanUpFn = func() { cluster.Stop() } + + cluster.WaitForReady(b) + rootNodeAddr = cluster.Bridge.JSONRPCAddr() + childNodeAddr = cluster.Servers[0].JSONRPCAddr() + } + + //create tx relayers + rootTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(rootNodeAddr)) + require.NoError(b, err) + + childTxRelayer, err := txrelayer.NewTxRelayer(txrelayer.WithIPAddress(childNodeAddr)) + require.NoError(b, err) + + if startCluster { + // fund sender on root chain to be able to deploy contracts and execute transactions + txn := ðgo.Transaction{To: &senderEthAddr, Value: ethgo.Ether(1)} + receipt, err := rootTxRelayer.SendTransactionLocal(txn) + require.NoError(b, err) + require.Equal(b, uint64(types.ReceiptSuccess), receipt.Status) + } + + // deploy contracts + singleContChildAddr, singleContRootAddr := deployContractOnRootAndChild(b, childTxRelayer, rootTxRelayer, + sender, contractsapi.TestBenchmarkSingle.Bytecode) + multiAContChildAddr, multiAContRootAddr := deployContractOnRootAndChild(b, childTxRelayer, rootTxRelayer, + sender, contractsapi.TestBenchmarkA.Bytecode) + multiBContChildAddr, multiBContRootAddr := deployContractOnRootAndChild(b, childTxRelayer, rootTxRelayer, + sender, contractsapi.TestBenchmarkB.Bytecode) + multiCContChildAddr, multiCContRootAddr := deployContractOnRootAndChild(b, childTxRelayer, rootTxRelayer, + sender, contractsapi.TestBenchmarkC.Bytecode) + + // set callee contract addresses for multi call contracts (A->B->C) + // set B contract address in A contract + setContractDependencyAddress(b, childTxRelayer, multiAContChildAddr, multiBContChildAddr, + multiContSetAAddrFunc, sender) + setContractDependencyAddress(b, rootTxRelayer, multiAContRootAddr, multiBContRootAddr, + multiContSetAAddrFunc, sender) + // set C contract address in B contract + setContractDependencyAddress(b, childTxRelayer, multiBContChildAddr, multiCContChildAddr, + multiContSetBAddrFunc, sender) + setContractDependencyAddress(b, rootTxRelayer, multiBContRootAddr, multiCContRootAddr, + multiContSetBAddrFunc, sender) + + // create inputs for contract calls + singleContInputs := map[string][]byte{ + "calc": getTxInput(b, singleContCalcFunc, []interface{}{big.NewInt(50), big.NewInt(150)}), + "set": getTxInput(b, singleContSetFunc, []interface{}{big.NewInt(10)}), + "get": getTxInput(b, singleContGetFunc, nil), + } + multiContInput := getTxInput(b, multiContFnA, nil) + + // test cases + testCases := []TxTestCase{ + { + Name: "[Child chain] setter 5tx", + Relayer: childTxRelayer, + ContractAddr: singleContChildAddr, + Input: [][]byte{singleContInputs["set"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Root chain] setter 5tx", + Relayer: rootTxRelayer, + ContractAddr: singleContRootAddr, + Input: [][]byte{singleContInputs["set"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Child chain] getter 5tx", + Relayer: childTxRelayer, + ContractAddr: singleContChildAddr, + Input: [][]byte{singleContInputs["get"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Root chain] getter 5tx", + Relayer: rootTxRelayer, + ContractAddr: singleContRootAddr, + Input: [][]byte{singleContInputs["get"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Child chain] set,get,calculate 15tx", + Relayer: childTxRelayer, + ContractAddr: singleContChildAddr, + Input: [][]byte{singleContInputs["get"], singleContInputs["set"], singleContInputs["calc"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Root chain] set,get,calculate 15tx", + Relayer: rootTxRelayer, + ContractAddr: singleContRootAddr, + Input: [][]byte{singleContInputs["get"], singleContInputs["set"], singleContInputs["calc"]}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Child chain] multi contract call 5tx", + Relayer: childTxRelayer, + ContractAddr: multiAContChildAddr, + Input: [][]byte{multiContInput}, + Sender: sender, + TxNumber: 5, + }, + { + Name: "[Root chain] multi contract call 5tx", + Relayer: rootTxRelayer, + ContractAddr: multiAContRootAddr, + Input: [][]byte{multiContInput}, + Sender: sender, + TxNumber: 5, + }, + } + + return testCases, cleanUpFn +} + +// TxTestCase represents a test case data to be run with txTestCasesExecutor +type TxTestCase struct { + Name string + Relayer txrelayer.TxRelayer + ContractAddr ethgo.Address + Input [][]byte + Sender ethgo.Key + TxNumber int +} + +// TxTestCasesExecutor executes transactions from testInput and waits in separate +// go routins for each tx receipt +func TxTestCasesExecutor(b *testing.B, testInput TxTestCase) { + b.Helper() + b.Run(testInput.Name, func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + var wg sync.WaitGroup + + // submit all tx 'repeatCall' times + for i := 0; i < testInput.TxNumber; i++ { + // call contract for the all inputs + for j := 0; j < len(testInput.Input); j++ { + nonce, err := testInput.Relayer.Client().Eth().GetNonce(testInput.Sender.Address(), ethgo.Pending) + require.NoError(b, err) + + // the tx is submitted to the blockchain without waiting for the receipt, + // since we want to have multiple tx in one block + txHash, err := testInput.Relayer.SumbitTransaction( + ðgo.Transaction{ + To: &testInput.ContractAddr, + Input: testInput.Input[j], + }, testInput.Sender) + require.NoError(b, err) + require.NotEqual(b, ethgo.ZeroHash, txHash) + + wg.Add(1) + + // wait for receipt of submitted tx in a separate routine, and continue with the next tx + go func(hash ethgo.Hash) { + defer wg.Done() + + receipt, err := testInput.Relayer.WaitForReceipt(hash) + require.NoError(b, err) + require.Equal(b, uint64(types.ReceiptSuccess), receipt.Status) + }(txHash) + + // wait for tx to be added in mem pool so that we can create tx with the next nonce + waitForNextNonce(b, nonce, testInput.Sender.Address(), testInput.Relayer) + } + } + + wg.Wait() + }) +} + +func waitForNextNonce(b *testing.B, nonce uint64, address ethgo.Address, txRelayer txrelayer.TxRelayer) { + b.Helper() + + startTime := time.Now().UTC() + + for { + newNonce, err := txRelayer.Client().Eth().GetNonce(address, ethgo.Pending) + require.NoError(b, err) + + if newNonce > nonce { + return + } + + elapsedTime := time.Since(startTime) + require.True(b, elapsedTime <= 5*time.Second, "tx not added to the mem poolin 2s") + } +} diff --git a/benchmark/transition_benchmark_test.go b/benchmark/transition_benchmark_test.go new file mode 100644 index 0000000000..66ad8fbb70 --- /dev/null +++ b/benchmark/transition_benchmark_test.go @@ -0,0 +1,112 @@ +package benchmark + +import ( + "math/big" + "strings" + "testing" + + "github.com/0xPolygon/polygon-edge/chain" + "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/ethgo" +) + +func Benchmark_TransitionMultiContractMemDb(b *testing.B) { + multiContractTest(b, false) +} + +func Benchmark_TransitionMultiContractLevelDb(b *testing.B) { + multiContractTest(b, true) +} + +func multiContractTest(b *testing.B, disk bool) { + b.Helper() + + senderAddr := types.Address{1} // account that sends transactions + + alloc := map[types.Address]*chain.GenesisAccount{ + senderAddr: {Balance: ethgo.Ether(100)}, // give some ethers to sender + } + + transition := newTestTransition(b, alloc, disk) + + // deploy contracts + singleContractAddr := transitionDeployContract(b, transition, contractsapi.TestBenchmarkSingle.Bytecode, senderAddr) + contractAAddr := transitionDeployContract(b, transition, contractsapi.TestBenchmarkA.Bytecode, senderAddr) + contractBAddr := transitionDeployContract(b, transition, contractsapi.TestBenchmarkB.Bytecode, senderAddr) + contractCAddr := transitionDeployContract(b, transition, contractsapi.TestBenchmarkC.Bytecode, senderAddr) + + //set multi contracts dependency address + input := getTxInput(b, multiContSetAAddrFunc, []interface{}{contractBAddr}) + transitionCallContract(b, transition, contractAAddr, senderAddr, input) + input = getTxInput(b, multiContSetBAddrFunc, []interface{}{contractCAddr}) + transitionCallContract(b, transition, contractBAddr, senderAddr, input) + + testCases := []struct { + contractAddr types.Address + input []byte + }{ + { + contractAddr: singleContractAddr, + input: getTxInput(b, singleContSetFunc, []interface{}{big.NewInt(10)}), + }, + { + contractAddr: singleContractAddr, + input: getTxInput(b, singleContGetFunc, nil), + }, + { + contractAddr: singleContractAddr, + input: getTxInput(b, singleContCalcFunc, []interface{}{big.NewInt(50), big.NewInt(150)}), + }, + { + contractAddr: contractAAddr, + input: getTxInput(b, multiContFnA, nil), + }, + } + + b.ReportAllocs() + b.ResetTimer() + // execute transactions + for i := 0; i < b.N; i++ { + for j := 0; j < 400; j++ { + for _, tc := range testCases { + transitionCallContract(b, transition, tc.contractAddr, senderAddr, tc.input) + } + } + } +} + +func Benchmark_SampleContractMemDb(b *testing.B) { + sampleContractTest(b, false) +} + +func Benchmark_SampleContractLevelDb(b *testing.B) { + sampleContractTest(b, true) +} + +func sampleContractTest(b *testing.B, disk bool) { + b.Helper() + + senderAddr := types.Address{1} // account that sends transactions + alloc := map[types.Address]*chain.GenesisAccount{ + senderAddr: {Balance: ethgo.Ether(100)}, // give some ethers to sender + } + transition := newTestTransition(b, alloc, disk) + code := contractsapi.BigDataContract.Bytecode + newData := strings.Repeat("A", 100000) + input := getTxInput(b, contractsapi.BigDataContract.Abi.Methods["writeData"], []interface{}{[]byte(newData)}) + + // deploy contracts + contractAddr := transitionDeployContract(b, transition, code, senderAddr) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + for j := 0; j < 100; j++ { + transitionCallContract(b, transition, contractAddr, senderAddr, input) + } + } + b.StopTimer() + transition.Commit() +} diff --git a/command/benchmark/benchmark.go b/command/benchmark/benchmark.go new file mode 100644 index 0000000000..7bdfd1c508 --- /dev/null +++ b/command/benchmark/benchmark.go @@ -0,0 +1,135 @@ +package benchmark + +import ( + "bytes" + "errors" + "fmt" + "testing" + + "github.com/0xPolygon/polygon-edge/benchmark" + "github.com/0xPolygon/polygon-edge/command" + "github.com/spf13/cobra" +) + +const ( + rootJSONRPCFlag = "rootJSONRPC" + childJSONRPCFlag = "childJSONRPC" + privateKeyFlag = "privateKey" + startClusterFlag = "startCluster" +) + +type benchmarkParams struct { + rootJSONRPC string + childJSONRPC string + privateKey string + startCluster bool +} + +type benchmarkResult struct { + name string + result string +} + +var ( + params *benchmarkParams = &benchmarkParams{} +) + +func GetCommand() *cobra.Command { + benchmarkCmd := &cobra.Command{ + Use: "benchmark-test", + Short: "Run benchmark tests", + Example: getCmdExample(), + PreRunE: preRunCommand, + Run: runCommand, + } + + setFlags(benchmarkCmd) + + return benchmarkCmd +} + +func runCommand(cmd *cobra.Command, _ []string) { + outputter := command.InitializeOutputter(cmd) + defer outputter.WriteOutput() + + // set up the testing environment + testing.Init() + + // set up environment, get test cases and clean up fn + testCases, cleanUpFn := benchmark.RootChildSendTxSetUp( + &testing.B{}, + params.rootJSONRPC, + params.childJSONRPC, + params.privateKey, + params.startCluster, + ) + defer cleanUpFn() + + // Loop over the test cases and call the benchmark test + for _, testInput := range testCases { + sendTxResult := testing.Benchmark(func(b *testing.B) { + b.Helper() + benchmark.TxTestCasesExecutor(b, testInput) + }) + benchmarkResult := &benchmarkResult{ + name: testInput.Name, + result: fmt.Sprintf("%s %s", sendTxResult.String(), sendTxResult.MemString()), + } + outputter.SetCommandResult(benchmarkResult) + outputter.WriteOutput() + } +} + +func setFlags(cmd *cobra.Command) { + cmd.Flags().StringVar( + ¶ms.rootJSONRPC, + rootJSONRPCFlag, + "", + "JSONRPC address of the root node", + ) + cmd.Flags().StringVar( + ¶ms.childJSONRPC, + childJSONRPCFlag, + "", + "JSONRPC address of the child node", + ) + cmd.Flags().StringVar( + ¶ms.privateKey, + privateKeyFlag, + "", + "private key that will be used to send tx (it is expected that this address has enough funds)", + ) + cmd.Flags().BoolVar( + ¶ms.startCluster, + startClusterFlag, + false, + "startCluster tells if the local cluster should be started", + ) + + cmd.MarkFlagsMutuallyExclusive(startClusterFlag, rootJSONRPCFlag) + cmd.MarkFlagsMutuallyExclusive(startClusterFlag, childJSONRPCFlag) +} + +func (br *benchmarkResult) GetOutput() string { + var buffer bytes.Buffer + + buffer.WriteString(fmt.Sprintf("\n%s: %s\n", br.result, br.name)) + + return buffer.String() +} + +func getCmdExample() string { + return fmt.Sprintf("%s %s %s %s %s", "polygon-edge", "benchmark-test", + "--childJSONRPC=\"http://127.0.0.1:12001\"", "--rootJSONRPC=\"http://127.0.0.1:8545\"", + "--privateKey=\"aa75e9a7d427efc732f8e4f1a5b7646adcc61fd5bae40f80d13c8419c9f43d6d\"") +} + +func preRunCommand(_ *cobra.Command, _ []string) error { + if !params.startCluster { + if params.rootJSONRPC == "" || params.childJSONRPC == "" || params.privateKey == "" { + return errors.New("if startCluster is not set then rootJSONRPC, childJSONRPC and privateKey must be set") + } + } + + return nil +} diff --git a/command/root/root.go b/command/root/root.go index 05805f9b11..75d15953ff 100644 --- a/command/root/root.go +++ b/command/root/root.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/cobra" "github.com/0xPolygon/polygon-edge/command/backup" + "github.com/0xPolygon/polygon-edge/command/benchmark" "github.com/0xPolygon/polygon-edge/command/bridge" "github.com/0xPolygon/polygon-edge/command/genesis" "github.com/0xPolygon/polygon-edge/command/helper" @@ -63,6 +64,7 @@ func (rc *RootCommand) registerSubCommands() { polybft.GetCommand(), bridge.GetCommand(), regenesis.GetCommand(), + benchmark.GetCommand(), ) } diff --git a/consensus/polybft/checkpoint_manager_test.go b/consensus/polybft/checkpoint_manager_test.go index fa177b4ab3..165c760adf 100644 --- a/consensus/polybft/checkpoint_manager_test.go +++ b/consensus/polybft/checkpoint_manager_test.go @@ -507,6 +507,16 @@ func (d *dummyTxRelayer) SendTransactionLocal(txn *ethgo.Transaction) (*ethgo.Re return args.Get(0).(*ethgo.Receipt), args.Error(1) //nolint:forcetypeassert } +// SumbitTransaction signs given transaction by provided key and sends it to the blockchain without waiting for the receipt +func (d *dummyTxRelayer) SumbitTransaction(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) { + return ethgo.ZeroHash, errors.New("SumbitTransaction is not implemented") +} + +// WaitForReceipt waits for tx receipt (this is only for testing purposes) +func (d *dummyTxRelayer) WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { + return nil, errors.New("WaitForReceipt is not implemented") +} + func (d *dummyTxRelayer) Client() *jsonrpc.Client { return nil } diff --git a/consensus/polybft/contractsapi/init.go b/consensus/polybft/contractsapi/init.go index 7ec7a977bb..f033034f74 100644 --- a/consensus/polybft/contractsapi/init.go +++ b/consensus/polybft/contractsapi/init.go @@ -52,6 +52,11 @@ var ( RootERC20 *artifact.Artifact TestSimple *artifact.Artifact TestRewardToken *artifact.Artifact + TestBenchmarkA *artifact.Artifact + TestBenchmarkB *artifact.Artifact + TestBenchmarkC *artifact.Artifact + TestBenchmarkSingle *artifact.Artifact + BigDataContract *artifact.Artifact ) func init() { @@ -202,6 +207,31 @@ func init() { log.Fatal(err) } + TestBenchmarkA, err = artifact.DecodeArtifact(readTestContractContent("TestBenchmarkA.json")) + if err != nil { + log.Fatal(err) + } + + TestBenchmarkB, err = artifact.DecodeArtifact(readTestContractContent("TestBenchmarkB.json")) + if err != nil { + log.Fatal(err) + } + + TestBenchmarkC, err = artifact.DecodeArtifact(readTestContractContent("TestBenchmarkC.json")) + if err != nil { + log.Fatal(err) + } + + BigDataContract, err = artifact.DecodeArtifact(readTestContractContent("BigDataContract.json")) + if err != nil { + log.Fatal(err) + } + + TestBenchmarkSingle, err = artifact.DecodeArtifact(readTestContractContent("TestBenchmarkSingle.json")) + if err != nil { + log.Fatal(err) + } + CustomSupernetManager, err = artifact.DecodeArtifact([]byte(CustomSupernetManagerArtifact)) if err != nil { log.Fatal(err) diff --git a/consensus/polybft/contractsapi/test-contracts/BigDataContract.json b/consensus/polybft/contractsapi/test-contracts/BigDataContract.json new file mode 100644 index 0000000000..7cf62a1312 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/BigDataContract.json @@ -0,0 +1,49 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "BigDataContract", + "sourceName": "contracts/test/BigDataContract.sol", + "abi": [ + { + "anonymous": false, + "inputs": [], + "name": "Written", + "type": "event" + }, + { + "inputs": [], + "name": "data", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "newData", + "type": "bytes" + } + ], + "name": "writeData", + "outputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061050d806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806373d4a13a1461003b578063f275d3ec14610059575b600080fd5b61004361006c565b604051610050919061020d565b60405180910390f35b610043610067366004610256565b6100fa565b6000805461007990610307565b80601f01602080910402602001604051908101604052809291908181526020018280546100a590610307565b80156100f25780601f106100c7576101008083540402835291602001916100f2565b820191906000526020600020905b8154815290600101906020018083116100d557829003601f168201915b505050505081565b6060600082604051602001610110929190610341565b6040516020818303038152906040526000908161012d9190610417565b506040517f6cf6fd6d80b7dfd898e622a3e48e8ab90bf067accc098070fd9ae2dba15f493f90600090a16000805461016490610307565b80601f016020809104026020016040519081016040528092919081815260200182805461019090610307565b80156101dd5780601f106101b2576101008083540402835291602001916101dd565b820191906000526020600020905b8154815290600101906020018083116101c057829003601f168201915b50505050509050919050565b60005b838110156102045781810151838201526020016101ec565b50506000910152565b602081526000825180602084015261022c8160408501602087016101e9565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561026857600080fd5b813567ffffffffffffffff8082111561028057600080fd5b818401915084601f83011261029457600080fd5b8135818111156102a6576102a6610240565b604051601f8201601f19908116603f011681019083821181831017156102ce576102ce610240565b816040528281528760208487010111156102e757600080fd5b826020860160208301376000928101602001929092525095945050505050565b600181811c9082168061031b57607f821691505b60208210810361033b57634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461034f81610307565b60018281168015610367576001811461037c576103ab565b60ff19841687528215158302870194506103ab565b8860005260208060002060005b858110156103a25781548a820152908401908201610389565b50505082870194505b5050505083516103bf8183602088016101e9565b01949350505050565b601f82111561041257600081815260208120601f850160051c810160208610156103ef5750805b601f850160051c820191505b8181101561040e578281556001016103fb565b5050505b505050565b815167ffffffffffffffff81111561043157610431610240565b6104458161043f8454610307565b846103c8565b602080601f83116001811461047a57600084156104625750858301515b600019600386901b1c1916600185901b17855561040e565b600085815260208120601f198616915b828110156104a95788860151825594840194600190910190840161048a565b50858210156104c75787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220066615155ab5f27a9cd866f718c66bfaed325ec934c74727b8b79240e844fc5b64736f6c63430008110033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806373d4a13a1461003b578063f275d3ec14610059575b600080fd5b61004361006c565b604051610050919061020d565b60405180910390f35b610043610067366004610256565b6100fa565b6000805461007990610307565b80601f01602080910402602001604051908101604052809291908181526020018280546100a590610307565b80156100f25780601f106100c7576101008083540402835291602001916100f2565b820191906000526020600020905b8154815290600101906020018083116100d557829003601f168201915b505050505081565b6060600082604051602001610110929190610341565b6040516020818303038152906040526000908161012d9190610417565b506040517f6cf6fd6d80b7dfd898e622a3e48e8ab90bf067accc098070fd9ae2dba15f493f90600090a16000805461016490610307565b80601f016020809104026020016040519081016040528092919081815260200182805461019090610307565b80156101dd5780601f106101b2576101008083540402835291602001916101dd565b820191906000526020600020905b8154815290600101906020018083116101c057829003601f168201915b50505050509050919050565b60005b838110156102045781810151838201526020016101ec565b50506000910152565b602081526000825180602084015261022c8160408501602087016101e9565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561026857600080fd5b813567ffffffffffffffff8082111561028057600080fd5b818401915084601f83011261029457600080fd5b8135818111156102a6576102a6610240565b604051601f8201601f19908116603f011681019083821181831017156102ce576102ce610240565b816040528281528760208487010111156102e757600080fd5b826020860160208301376000928101602001929092525095945050505050565b600181811c9082168061031b57607f821691505b60208210810361033b57634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461034f81610307565b60018281168015610367576001811461037c576103ab565b60ff19841687528215158302870194506103ab565b8860005260208060002060005b858110156103a25781548a820152908401908201610389565b50505082870194505b5050505083516103bf8183602088016101e9565b01949350505050565b601f82111561041257600081815260208120601f850160051c810160208610156103ef5750805b601f850160051c820191505b8181101561040e578281556001016103fb565b5050505b505050565b815167ffffffffffffffff81111561043157610431610240565b6104458161043f8454610307565b846103c8565b602080601f83116001811461047a57600084156104625750858301515b600019600386901b1c1916600185901b17855561040e565b600085815260208120601f198616915b828110156104a95788860151825594840194600190910190840161048a565b50858210156104c75787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056fea2646970667358221220066615155ab5f27a9cd866f718c66bfaed325ec934c74727b8b79240e844fc5b64736f6c63430008110033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/BigDataContract.sol b/consensus/polybft/contractsapi/test-contracts/BigDataContract.sol new file mode 100644 index 0000000000..e3a91a2a0a --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/BigDataContract.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +contract BigDataContract { + bytes public data; + event Written(); + + function writeData(bytes memory newData) external returns (bytes memory) { + data = abi.encodePacked(data, newData); + emit Written(); + return data; + } +} diff --git a/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkB.json b/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkB.json new file mode 100644 index 0000000000..1c8f393171 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkB.json @@ -0,0 +1,24 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ITestBenchmarkB", + "sourceName": "contracts/test/TestBenchmarkA.sol", + "abi": [ + { + "inputs": [], + "name": "fnB", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkC.json b/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkC.json new file mode 100644 index 0000000000..9ec29ff956 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/ITestBenchmarkC.json @@ -0,0 +1,24 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "ITestBenchmarkC", + "sourceName": "contracts/test/TestBenchmarkB.sol", + "abi": [ + { + "inputs": [], + "name": "fnC1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.json b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.json new file mode 100644 index 0000000000..3207437512 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.json @@ -0,0 +1,37 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestBenchmarkA", + "sourceName": "contracts/test/TestBenchmarkA.sol", + "abi": [ + { + "inputs": [], + "name": "fnA", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "setContractAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061017b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063286d2e3a1461003b57806368685ad31461006d575b600080fd5b61006b6100493660046100fc565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b005b610075610087565b60405190815260200160405180910390f35b6000805460408051636cde00cd60e01b8152905183926001600160a01b031691636cde00cd916004808301926020929190829003018187875af11580156100d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f6919061012c565b92915050565b60006020828403121561010e57600080fd5b81356001600160a01b038116811461012557600080fd5b9392505050565b60006020828403121561013e57600080fd5b505191905056fea2646970667358221220a8ce85649b9a1ab453ecb852ac36c64f7be380547a5cf2faf83ffdbb59b6424464736f6c63430008130033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063286d2e3a1461003b57806368685ad31461006d575b600080fd5b61006b6100493660046100fc565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b005b610075610087565b60405190815260200160405180910390f35b6000805460408051636cde00cd60e01b8152905183926001600160a01b031691636cde00cd916004808301926020929190829003018187875af11580156100d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f6919061012c565b92915050565b60006020828403121561010e57600080fd5b81356001600160a01b038116811461012557600080fd5b9392505050565b60006020828403121561013e57600080fd5b505191905056fea2646970667358221220a8ce85649b9a1ab453ecb852ac36c64f7be380547a5cf2faf83ffdbb59b6424464736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.sol b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.sol new file mode 100644 index 0000000000..aade227025 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkA.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface ITestBenchmarkB { + function fnB() external returns (uint256); +} +contract TestBenchmarkA { + address contractAddr; + + function setContractAddr(address _contract) public { + contractAddr = _contract; + } + + function fnA() public returns (uint256) { + uint256 valB = ITestBenchmarkB(contractAddr).fnB(); + return valB; + } + } \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.json b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.json new file mode 100644 index 0000000000..a5c9e28157 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.json @@ -0,0 +1,50 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestBenchmarkB", + "sourceName": "contracts/test/TestBenchmarkB.sol", + "abi": [ + { + "inputs": [], + "name": "fnB", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_contract", + "type": "address" + } + ], + "name": "setContractAddr", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "valB", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506101db806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063286d2e3a146100465780636cde00cd14610078578063735b7e6f14610092575b600080fd5b610076610054366004610135565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b005b61008061009b565b60405190815260200160405180910390f35b61008060005481565b600080600160009054906101000a90046001600160a01b03166001600160a01b03166349ec07186040518163ffffffff1660e01b81526004016020604051808303816000875af11580156100f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101179190610165565b90508060008082825461012a919061017e565b909155509092915050565b60006020828403121561014757600080fd5b81356001600160a01b038116811461015e57600080fd5b9392505050565b60006020828403121561017757600080fd5b5051919050565b8082018082111561019f57634e487b7160e01b600052601160045260246000fd5b9291505056fea26469706673582212209f01d2971a8238a2a1c208a8dde49ecc84579475e54782314375cb36d1bf60ea64736f6c63430008130033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063286d2e3a146100465780636cde00cd14610078578063735b7e6f14610092575b600080fd5b610076610054366004610135565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b005b61008061009b565b60405190815260200160405180910390f35b61008060005481565b600080600160009054906101000a90046001600160a01b03166001600160a01b03166349ec07186040518163ffffffff1660e01b81526004016020604051808303816000875af11580156100f3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101179190610165565b90508060008082825461012a919061017e565b909155509092915050565b60006020828403121561014757600080fd5b81356001600160a01b038116811461015e57600080fd5b9392505050565b60006020828403121561017757600080fd5b5051919050565b8082018082111561019f57634e487b7160e01b600052601160045260246000fd5b9291505056fea26469706673582212209f01d2971a8238a2a1c208a8dde49ecc84579475e54782314375cb36d1bf60ea64736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.sol b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.sol new file mode 100644 index 0000000000..d4b033f43c --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkB.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface ITestBenchmarkC { + function fnC1() external returns (uint256); +} +contract TestBenchmarkB { + uint256 public valB; + address contractAddr; + + function setContractAddr(address _contract) public { + contractAddr = _contract; + } + + function fnB() external returns (uint256) { + uint256 valC = ITestBenchmarkC(contractAddr).fnC1(); + valB += valC; + return valC; + } +} \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.json b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.json new file mode 100644 index 0000000000..8830848643 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.json @@ -0,0 +1,50 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestBenchmarkC", + "sourceName": "contracts/test/TestBenchmarkC.sol", + "abi": [ + { + "inputs": [], + "name": "fnC1", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "fnC2", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "valC", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061015f806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80631990ceb9146100465780633b3cf4e31461006057806349ec071814610069575b600080fd5b61004e610071565b60405190815260200160405180910390f35b61004e60005481565b61004e6100b9565b600060644244604051602001610091929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c6100b491906100e0565b905090565b6000806100c4610071565b60008054919250806100d583610102565b909155509092915050565b6000826100fd57634e487b7160e01b600052601260045260246000fd5b500690565b60006001820161012257634e487b7160e01b600052601160045260246000fd5b506001019056fea264697066735822122092ffbbf005793faaa839fdfd4e894697301342a305e3e8734927815308198d7964736f6c63430008130033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c80631990ceb9146100465780633b3cf4e31461006057806349ec071814610069575b600080fd5b61004e610071565b60405190815260200160405180910390f35b61004e60005481565b61004e6100b9565b600060644244604051602001610091929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c6100b491906100e0565b905090565b6000806100c4610071565b60008054919250806100d583610102565b909155509092915050565b6000826100fd57634e487b7160e01b600052601260045260246000fd5b500690565b60006001820161012257634e487b7160e01b600052601160045260246000fd5b506001019056fea264697066735822122092ffbbf005793faaa839fdfd4e894697301342a305e3e8734927815308198d7964736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.sol b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.sol new file mode 100644 index 0000000000..0203ed2de9 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkC.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +contract TestBenchmarkC { + uint256 public valC; + function fnC1() external returns (uint256) { + uint256 valC2 = fnC2(); + valC++; + return valC2; + } + + function fnC2() public view returns (uint256) { + return uint256(keccak256(abi.encode(block.timestamp, block.difficulty))) % 100; + } +} \ No newline at end of file diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.json b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.json new file mode 100644 index 0000000000..ba764ff24f --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.json @@ -0,0 +1,61 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "TestBenchmarkSingle", + "sourceName": "contracts/test/TestBenchmarkSingle.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "addValue", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "x", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "y", + "type": "uint256" + } + ], + "name": "compute", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getValue", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610271806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806320965255146100465780635b9af12b146100645780637a85644b146100a6575b600080fd5b61004e6100c7565b60405161005b9190610163565b60405180910390f35b6100a46100723660046101a7565b600080546001810182559080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630155565b005b6100b96100b43660046101c0565b61011f565b60405190815260200161005b565b6060600080548060200260200160405190810160405280929190818152602001828054801561011557602002820191906000526020600020905b815481526020019060010190808311610101575b5050505050905090565b60008061012c83856101f8565b905060005b600a8110156101595761014582600261020b565b91508061015181610222565b915050610131565b5090505b92915050565b6020808252825182820181905260009190848201906040850190845b8181101561019b5783518352928401929184019160010161017f565b50909695505050505050565b6000602082840312156101b957600080fd5b5035919050565b600080604083850312156101d357600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b8082018082111561015d5761015d6101e2565b808202811582820484141761015d5761015d6101e2565b600060018201610234576102346101e2565b506001019056fea2646970667358221220c572f74159ad307792361021be5daa28297e481a4cac587b5b311e5679e9d9da64736f6c63430008130033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100415760003560e01c806320965255146100465780635b9af12b146100645780637a85644b146100a6575b600080fd5b61004e6100c7565b60405161005b9190610163565b60405180910390f35b6100a46100723660046101a7565b600080546001810182559080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5630155565b005b6100b96100b43660046101c0565b61011f565b60405190815260200161005b565b6060600080548060200260200160405190810160405280929190818152602001828054801561011557602002820191906000526020600020905b815481526020019060010190808311610101575b5050505050905090565b60008061012c83856101f8565b905060005b600a8110156101595761014582600261020b565b91508061015181610222565b915050610131565b5090505b92915050565b6020808252825182820181905260009190848201906040850190845b8181101561019b5783518352928401929184019160010161017f565b50909695505050505050565b6000602082840312156101b957600080fd5b5035919050565b600080604083850312156101d357600080fd5b50508035926020909101359150565b634e487b7160e01b600052601160045260246000fd5b8082018082111561015d5761015d6101e2565b808202811582820484141761015d5761015d6101e2565b600060018201610234576102346101e2565b506001019056fea2646970667358221220c572f74159ad307792361021be5daa28297e481a4cac587b5b311e5679e9d9da64736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.sol b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.sol new file mode 100644 index 0000000000..432f7990e6 --- /dev/null +++ b/consensus/polybft/contractsapi/test-contracts/TestBenchmarkSingle.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +contract TestBenchmarkSingle { + uint256[] private val; + + function addValue(uint256 value) public { + val.push(value); + } + + function getValue() public view returns (uint256[] memory) { + return val; + } + + function compute(uint256 x, uint256 y) public pure returns (uint256) { + uint256 result = x + y; + for (uint256 i = 0; i < 10; i++) { + result = result * 2; + } + return result; + } + } \ No newline at end of file diff --git a/consensus/polybft/stake_manager_test.go b/consensus/polybft/stake_manager_test.go index 540ad586da..92103f9cb2 100644 --- a/consensus/polybft/stake_manager_test.go +++ b/consensus/polybft/stake_manager_test.go @@ -1,6 +1,7 @@ package polybft import ( + "errors" "math/big" "testing" @@ -512,3 +513,13 @@ func (d *dummyStakeTxRelayer) SendTransactionLocal(txn *ethgo.Transaction) (*eth func (d *dummyStakeTxRelayer) Client() *jsonrpc.Client { return nil } + +// SumbitTransaction signs given transaction by provided key and sends it to the blockchain without waiting for the receipt +func (d *dummyStakeTxRelayer) SumbitTransaction(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) { + return ethgo.ZeroHash, errors.New("SumbitTransaction is not implemented") +} + +// WaitForReceipt waits for tx receipt (this is only for testing purposes) +func (d *dummyStakeTxRelayer) WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { + return nil, errors.New("WaitForReceipt is not implemented") +} diff --git a/consensus/polybft/statesyncrelayer/state_sync_relayer_test.go b/consensus/polybft/statesyncrelayer/state_sync_relayer_test.go index e523bb0b40..8bebc93c32 100644 --- a/consensus/polybft/statesyncrelayer/state_sync_relayer_test.go +++ b/consensus/polybft/statesyncrelayer/state_sync_relayer_test.go @@ -1,6 +1,7 @@ package statesyncrelayer import ( + "errors" "math/big" "testing" @@ -43,6 +44,16 @@ func (t *txRelayerMock) Client() *jsonrpc.Client { return nil } +// SumbitTransaction signs given transaction by provided key and sends it to the blockchain without waiting for the receipt +func (t *txRelayerMock) SumbitTransaction(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) { + return ethgo.ZeroHash, errors.New("SumbitTransaction is not implemented") +} + +// WaitForReceipt waits for tx receipt (this is only for testing purposes) +func (t *txRelayerMock) WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { + return nil, errors.New("WaitForReceipt is not implemented") +} + func Test_executeStateSync(t *testing.T) { t.Parallel() diff --git a/e2e-polybft/framework/test-bridge.go b/e2e-polybft/framework/test-bridge.go index b7475a5644..0f2712c747 100644 --- a/e2e-polybft/framework/test-bridge.go +++ b/e2e-polybft/framework/test-bridge.go @@ -23,16 +23,16 @@ import ( ) type TestBridge struct { - t *testing.T + t testing.TB clusterConfig *TestClusterConfig node *node } -func NewTestBridge(t *testing.T, clusterConfig *TestClusterConfig) (*TestBridge, error) { - t.Helper() +func NewTestBridge(tb testing.TB, clusterConfig *TestClusterConfig) (*TestBridge, error) { + tb.Helper() bridge := &TestBridge{ - t: t, + t: tb, clusterConfig: clusterConfig, } diff --git a/e2e-polybft/framework/test-cluster.go b/e2e-polybft/framework/test-cluster.go index f2c594b5e5..b915cf9e4d 100644 --- a/e2e-polybft/framework/test-cluster.go +++ b/e2e-polybft/framework/test-cluster.go @@ -70,7 +70,7 @@ func resolveBinary() string { } type TestClusterConfig struct { - t *testing.T + t testing.TB Name string Premine []string // address[:amount] @@ -372,13 +372,13 @@ func NewPropertyTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOp return NewTestCluster(t, validatorsCount, opts...) } -func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *TestCluster { - t.Helper() +func NewTestCluster(tb testing.TB, validatorsCount int, opts ...ClusterOption) *TestCluster { + tb.Helper() var err error config := &TestClusterConfig{ - t: t, + t: tb, WithLogs: isTrueEnv(envLogsEnabled), WithStdout: isTrueEnv(envStdoutEnabled), Binary: resolveBinary(), @@ -404,11 +404,11 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T testType = "integration" } - t.Skip(fmt.Sprintf("%s tests are disabled.", testType)) + tb.Skip(fmt.Sprintf("%s tests are disabled.", testType)) } config.TmpDir, err = os.MkdirTemp("/tmp", "e2e-polybft-") - require.NoError(t, err) + require.NoError(tb, err) cluster := &TestCluster{ Servers: []*TestServer{}, @@ -425,7 +425,7 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T // run init accounts for validators addresses, err := cluster.InitSecrets(cluster.Config.ValidatorPrefix, int(cluster.Config.ValidatorSetSize)) - require.NoError(t, err) + require.NoError(tb, err) if cluster.Config.SecretsCallback != nil { cluster.Config.SecretsCallback(addresses, cluster.Config) @@ -436,7 +436,7 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T // we don't call secrets callback on non-validators, // since we have nothing to premine nor stake for non validators _, err = cluster.InitSecrets(nonValidatorPrefix, config.NonValidatorCount) - require.NoError(t, err) + require.NoError(tb, err) } genesisPath := path.Join(config.TmpDir, "genesis.json") @@ -482,7 +482,7 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T validators, err := genesis.ReadValidatorsByPrefix( cluster.Config.TmpDir, cluster.Config.ValidatorPrefix) - require.NoError(t, err) + require.NoError(tb, err) if cluster.Config.BootnodeCount > 0 { bootNodesCnt := cluster.Config.BootnodeCount @@ -562,60 +562,60 @@ func NewTestCluster(t *testing.T, validatorsCount int, opts ...ClusterOption) *T // run genesis command with all the arguments err = cluster.cmdRun(args...) - require.NoError(t, err) + require.NoError(tb, err) } if !cluster.Config.WithoutBridge { // start bridge - cluster.Bridge, err = NewTestBridge(t, cluster.Config) - require.NoError(t, err) + cluster.Bridge, err = NewTestBridge(tb, cluster.Config) + require.NoError(tb, err) // deploy rootchain contracts err := cluster.Bridge.deployRootchainContracts(genesisPath) - require.NoError(t, err) + require.NoError(tb, err) polybftConfig, chainID, err := polybft.LoadPolyBFTConfig(genesisPath) - require.NoError(t, err) + require.NoError(tb, err) // fund validators on the rootchain err = cluster.Bridge.fundRootchainValidators(polybftConfig) - require.NoError(t, err) + require.NoError(tb, err) // whitelist genesis validators on the rootchain err = cluster.Bridge.whitelistValidators(addresses, polybftConfig) - require.NoError(t, err) + require.NoError(tb, err) // register genesis validators on the rootchain err = cluster.Bridge.registerGenesisValidators(polybftConfig) - require.NoError(t, err) + require.NoError(tb, err) // do initial staking for genesis validators on the rootchain err = cluster.Bridge.initialStakingOfGenesisValidators(polybftConfig, chainID) - require.NoError(t, err) + require.NoError(tb, err) // finalize genesis validators on the rootchain err = cluster.Bridge.finalizeGenesis(genesisPath, polybftConfig) - require.NoError(t, err) + require.NoError(tb, err) } for i := 1; i <= int(cluster.Config.ValidatorSetSize); i++ { dir := cluster.Config.ValidatorPrefix + strconv.Itoa(i) - cluster.InitTestServer(t, dir, cluster.Bridge.JSONRPCAddr(), + cluster.InitTestServer(tb, dir, cluster.Bridge.JSONRPCAddr(), true, !cluster.Config.WithoutBridge && i == 1 /* relayer */) } for i := 1; i <= cluster.Config.NonValidatorCount; i++ { dir := nonValidatorPrefix + strconv.Itoa(i) - cluster.InitTestServer(t, dir, cluster.Bridge.JSONRPCAddr(), + cluster.InitTestServer(tb, dir, cluster.Bridge.JSONRPCAddr(), false, false /* relayer */) } return cluster } -func (c *TestCluster) InitTestServer(t *testing.T, +func (c *TestCluster) InitTestServer(tb testing.TB, dataDir string, bridgeJSONRPC string, isValidator bool, relayer bool) { - t.Helper() + tb.Helper() logLevel := os.Getenv(envLogLevel) @@ -623,11 +623,11 @@ func (c *TestCluster) InitTestServer(t *testing.T, if c.Config.InitialTrieDB != "" { err := CopyDir(c.Config.InitialTrieDB, filepath.Join(dataDir, "trie")) if err != nil { - t.Fatal(err) + tb.Fatal(err) } } - srv := NewTestServer(t, c.Config, bridgeJSONRPC, func(config *TestServerConfig) { + srv := NewTestServer(tb, c.Config, bridgeJSONRPC, func(config *TestServerConfig) { config.DataDir = dataDir config.Seal = isValidator config.Chain = c.Config.Dir("genesis.json") @@ -706,10 +706,10 @@ func (c *TestCluster) WaitUntil(timeout, pollFrequency time.Duration, handler fu } } -func (c *TestCluster) WaitForReady(t *testing.T) { - t.Helper() +func (c *TestCluster) WaitForReady(tb testing.TB) { + tb.Helper() - require.NoError(t, c.WaitForBlock(1, time.Minute)) + require.NoError(tb, c.WaitForBlock(1, time.Minute)) } func (c *TestCluster) WaitForBlock(n uint64, timeout time.Duration) error { @@ -819,11 +819,11 @@ func (c *TestCluster) InitSecrets(prefix string, count int) ([]types.Address, er return result, nil } -func (c *TestCluster) ExistsCode(t *testing.T, addr ethgo.Address) bool { - t.Helper() +func (c *TestCluster) ExistsCode(tb testing.TB, addr ethgo.Address) bool { + tb.Helper() client, err := jsonrpc.NewClient(c.Servers[0].JSONRPCAddr()) - require.NoError(t, err) + require.NoError(tb, err) code, err := client.Eth().GetCode(addr, ethgo.Latest) if err != nil { @@ -861,10 +861,10 @@ func (c *TestCluster) Call(t *testing.T, to types.Address, method *abi.Method, return output } -func (c *TestCluster) Deploy(t *testing.T, sender ethgo.Key, bytecode []byte) *TestTxn { - t.Helper() +func (c *TestCluster) Deploy(tb testing.TB, sender ethgo.Key, bytecode []byte) *TestTxn { + tb.Helper() - return c.SendTxn(t, sender, ðgo.Transaction{Input: bytecode}) + return c.SendTxn(tb, sender, ðgo.Transaction{Input: bytecode}) } func (c *TestCluster) Transfer(t *testing.T, sender ethgo.Key, target types.Address, value *big.Int) *TestTxn { @@ -884,8 +884,8 @@ func (c *TestCluster) MethodTxn(t *testing.T, sender ethgo.Key, target types.Add } // SendTxn sends a transaction -func (c *TestCluster) SendTxn(t *testing.T, sender ethgo.Key, txn *ethgo.Transaction) *TestTxn { - t.Helper() +func (c *TestCluster) SendTxn(tb testing.TB, sender ethgo.Key, txn *ethgo.Transaction) *TestTxn { + tb.Helper() // since we might use get nonce to query the latest nonce and that value is only // updated if the transaction is on the pool, it is recommended to lock the whole @@ -895,12 +895,12 @@ func (c *TestCluster) SendTxn(t *testing.T, sender ethgo.Key, txn *ethgo.Transac defer c.sendTxnLock.Unlock() client, err := jsonrpc.NewClient(c.Servers[0].JSONRPCAddr()) - require.NoError(t, err) + require.NoError(tb, err) // initialize transaction values if not set if txn.Nonce == 0 { nonce, err := client.Eth().GetNonce(sender.Address(), ethgo.Latest) - require.NoError(t, err) + require.NoError(tb, err) txn.Nonce = nonce } @@ -914,17 +914,17 @@ func (c *TestCluster) SendTxn(t *testing.T, sender ethgo.Key, txn *ethgo.Transac } chainID, err := client.Eth().ChainID() - require.NoError(t, err) + require.NoError(tb, err) signer := wallet.NewEIP155Signer(chainID.Uint64()) signedTxn, err := signer.SignTx(txn, sender) - require.NoError(t, err) + require.NoError(tb, err) txnRaw, err := signedTxn.MarshalRLPTo(nil) - require.NoError(t, err) + require.NoError(tb, err) hash, err := client.Eth().SendRawTransaction(txnRaw) - require.NoError(t, err) + require.NoError(tb, err) tTxn := &TestTxn{ client: client.Eth(), diff --git a/e2e-polybft/framework/test-server.go b/e2e-polybft/framework/test-server.go index 8dc94ab994..603cf6668e 100644 --- a/e2e-polybft/framework/test-server.go +++ b/e2e-polybft/framework/test-server.go @@ -48,7 +48,7 @@ func getOpenPortForServer() int64 { } type TestServer struct { - t *testing.T + t testing.TB address types.Address clusterConfig *TestClusterConfig @@ -99,9 +99,9 @@ func (t *TestServer) TxnPoolOperator() txpoolProto.TxnPoolOperatorClient { return txpoolProto.NewTxnPoolOperatorClient(conn) } -func NewTestServer(t *testing.T, clusterConfig *TestClusterConfig, +func NewTestServer(tb testing.TB, clusterConfig *TestClusterConfig, bridgeJSONRPC string, callback TestServerConfigCallback) *TestServer { - t.Helper() + tb.Helper() config := &TestServerConfig{ Name: uuid.New().String(), @@ -117,19 +117,19 @@ func NewTestServer(t *testing.T, clusterConfig *TestClusterConfig, if config.DataDir == "" { dataDir, err := ioutil.TempDir("/tmp", "edge-e2e-") - require.NoError(t, err) + require.NoError(tb, err) config.DataDir = dataDir } secretsManager, err := polybftsecrets.GetSecretsManager(config.DataDir, "", true) - require.NoError(t, err) + require.NoError(tb, err) key, err := wallet.GetEcdsaFromSecret(secretsManager) - require.NoError(t, err) + require.NoError(tb, err) srv := &TestServer{ - t: t, + t: tb, clusterConfig: clusterConfig, address: types.Address(key.Address()), config: config, diff --git a/txrelayer/txrelayer.go b/txrelayer/txrelayer.go index 95e0671da0..a6411b3498 100644 --- a/txrelayer/txrelayer.go +++ b/txrelayer/txrelayer.go @@ -30,6 +30,11 @@ type TxRelayer interface { // SendTransactionLocal sends non-signed transaction // (this function is meant only for testing purposes and is about to be removed at some point) SendTransactionLocal(txn *ethgo.Transaction) (*ethgo.Receipt, error) + // Waits for tx receipt to be written to the blockchain + WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) + // SumbitTransaction signs given transaction by provided key and sends it to the blockchain + // without waiting for the receipt + SumbitTransaction(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) // Client returns jsonrpc client Client() *jsonrpc.Client } @@ -78,12 +83,12 @@ func (t *TxRelayerImpl) Call(from ethgo.Address, to ethgo.Address, input []byte) // SendTransaction signs given transaction by provided key and sends it to the blockchain func (t *TxRelayerImpl) SendTransaction(txn *ethgo.Transaction, key ethgo.Key) (*ethgo.Receipt, error) { - txnHash, err := t.sendTransactionLocked(txn, key) + txnHash, err := t.SumbitTransaction(txn, key) if err != nil { return nil, err } - return t.waitForReceipt(txnHash) + return t.WaitForReceipt(txnHash) } // Client returns jsonrpc client @@ -91,7 +96,9 @@ func (t *TxRelayerImpl) Client() *jsonrpc.Client { return t.client } -func (t *TxRelayerImpl) sendTransactionLocked(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) { +// SumbitTransaction signs given transaction by provided key and sends it to the blockchain +// without waiting for the receipt +func (t *TxRelayerImpl) SumbitTransaction(txn *ethgo.Transaction, key ethgo.Key) (ethgo.Hash, error) { t.lock.Lock() defer t.lock.Unlock() @@ -149,10 +156,10 @@ func (t *TxRelayerImpl) SendTransactionLocal(txn *ethgo.Transaction) (*ethgo.Rec return nil, err } - return t.waitForReceipt(txnHash) + return t.WaitForReceipt(txnHash) } -func (t *TxRelayerImpl) waitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { +func (t *TxRelayerImpl) WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { count := uint(0) for {