Skip to content
This repository was archived by the owner on Dec 4, 2024. It is now read-only.

Commit 4bf44e0

Browse files
committed
add transition benchmark test
1 parent 38bc3ce commit 4bf44e0

File tree

9 files changed

+490
-101
lines changed

9 files changed

+490
-101
lines changed

benchmark/benchmark_test.go

Lines changed: 0 additions & 10 deletions
This file was deleted.

benchmark/executors.go

Lines changed: 0 additions & 61 deletions
This file was deleted.

benchmark/helper.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,34 @@ package benchmark
22

33
import (
44
"encoding/hex"
5+
"math/big"
6+
"os"
7+
"path/filepath"
58
"testing"
69

10+
"github.com/0xPolygon/polygon-edge/chain"
11+
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
12+
"github.com/0xPolygon/polygon-edge/state"
13+
itrie "github.com/0xPolygon/polygon-edge/state/immutable-trie"
14+
"github.com/0xPolygon/polygon-edge/state/runtime"
715
"github.com/0xPolygon/polygon-edge/txrelayer"
816
"github.com/0xPolygon/polygon-edge/types"
17+
"github.com/hashicorp/go-hclog"
918
"github.com/stretchr/testify/require"
1019
"github.com/umbracle/ethgo"
1120
"github.com/umbracle/ethgo/abi"
1221
"github.com/umbracle/ethgo/wallet"
1322
)
1423

24+
var (
25+
singleContCalcFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["compute"]
26+
singleContGetFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["getValue"]
27+
singleContSetFunc = contractsapi.TestBenchmarkSingle.Abi.Methods["addValue"]
28+
multiContSetAAddrFunc = contractsapi.TestBenchmarkA.Abi.Methods["setContractAddr"]
29+
multiContSetBAddrFunc = contractsapi.TestBenchmarkA.Abi.Methods["setContractAddr"]
30+
multiContFnA = contractsapi.TestBenchmarkA.Abi.Methods["fnA"]
31+
)
32+
1533
// deployContractOnRootAndChild deploys contract code on both root and child chain
1634
func deployContractOnRootAndChild(
1735
b *testing.B,
@@ -94,3 +112,81 @@ func getPrivateKey(b *testing.B, privateKeyRaw string) ethgo.Key {
94112

95113
return privateKey
96114
}
115+
116+
func transitionDeployContract(b *testing.B, transition *state.Transition, byteCode []byte,
117+
sender types.Address) types.Address {
118+
b.Helper()
119+
120+
deployResult := transition.Create2(sender, byteCode, big.NewInt(0), 1e9)
121+
require.NoError(b, deployResult.Err)
122+
123+
return deployResult.Address
124+
}
125+
126+
func transitionCallContract(b *testing.B, transition *state.Transition, contractAddress types.Address,
127+
sender types.Address, input []byte) *runtime.ExecutionResult {
128+
b.Helper()
129+
130+
result := transition.Call2(sender, contractAddress, input, big.NewInt(0), 1e9)
131+
require.NoError(b, result.Err)
132+
133+
return result
134+
}
135+
136+
func newTestTransition(b *testing.B, alloc map[types.Address]*chain.GenesisAccount, disk bool) *state.Transition {
137+
b.Helper()
138+
139+
var st *itrie.State
140+
141+
if disk {
142+
testDir := createTestTempDirectory(b)
143+
stateStorage, err := itrie.NewLevelDBStorage(filepath.Join(testDir, "trie"), hclog.NewNullLogger())
144+
require.NoError(b, err)
145+
146+
st = itrie.NewState(stateStorage)
147+
} else {
148+
st = itrie.NewState(itrie.NewMemoryStorage())
149+
}
150+
151+
ex := state.NewExecutor(&chain.Params{
152+
Forks: chain.AllForksEnabled,
153+
BurnContract: map[uint64]string{
154+
0: types.ZeroAddress.String(),
155+
},
156+
}, st, hclog.NewNullLogger())
157+
158+
rootHash, err := ex.WriteGenesis(alloc, types.Hash{})
159+
require.NoError(b, err)
160+
161+
ex.GetHash = func(h *types.Header) state.GetHashByNumber {
162+
return func(i uint64) types.Hash {
163+
return rootHash
164+
}
165+
}
166+
167+
transition, err := ex.BeginTxn(
168+
rootHash,
169+
&types.Header{},
170+
types.ZeroAddress,
171+
)
172+
require.NoError(b, err)
173+
174+
return transition
175+
}
176+
177+
func createTestTempDirectory(b *testing.B) string {
178+
b.Helper()
179+
180+
path, err := os.MkdirTemp("", "temp")
181+
if err != nil {
182+
b.Logf("failed to create temp directory, err=%+v", err)
183+
184+
b.FailNow()
185+
}
186+
187+
b.Cleanup(func() {
188+
os.RemoveAll(path)
189+
})
190+
191+
return path
192+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package benchmark
2+
3+
import (
4+
"testing"
5+
)
6+
7+
// The rootChildSendTx function executes test cases that measure transaction execution on both the root and child chains
8+
// To do this, it first calls RootChildSendTxSetUp to set up the testing environment,
9+
// which may include starting the cluster, deploying contracts, and building the test cases.
10+
// After building the test cases, rootChildSendTx returns them along with a cleanup function that should be called
11+
// after the test cases have been executed. The test cases are executed by the TxTestCasesExecutor.
12+
func Benchmark_RootChildSendTx(b *testing.B) {
13+
// set up environment, get test cases and clean up fn
14+
testCases, cleanUpFn := RootChildSendTxSetUp(b, "", "", "", true)
15+
defer cleanUpFn()
16+
17+
// Loop over the test cases and measure the execution time of the transactions
18+
for _, testInput := range testCases {
19+
TxTestCasesExecutor(b, testInput)
20+
}
21+
}

benchmark/root_child_send_tx.go

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,19 @@ package benchmark
22

33
import (
44
"math/big"
5+
"sync"
56
"testing"
7+
"time"
68

79
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
810
"github.com/0xPolygon/polygon-edge/e2e-polybft/framework"
911
"github.com/0xPolygon/polygon-edge/txrelayer"
1012
"github.com/0xPolygon/polygon-edge/types"
1113
"github.com/stretchr/testify/require"
1214
"github.com/umbracle/ethgo"
13-
"github.com/umbracle/ethgo/abi"
1415
"github.com/umbracle/ethgo/wallet"
1516
)
1617

17-
var (
18-
singleContCalcFunc = abi.MustNewMethod("function compute(uint256 x, uint256 y) public returns (uint256)")
19-
singleContGetFunc = abi.MustNewMethod("function getValue() public returns (uint256[] memory)")
20-
singleContSetFunc = abi.MustNewMethod("function addValue(uint256 value) public")
21-
multiContSetAddrFunc = abi.MustNewMethod("function setContractAddr(address _contract) public")
22-
multiContFnA = abi.MustNewMethod("function fnA() public returns (uint256)")
23-
)
24-
25-
// The rootChildSendTx function executes test cases that measure transaction execution on both the root and child chains
26-
// To do this, it first calls RootChildSendTxSetUp to set up the testing environment,
27-
// which may include starting the cluster, deploying contracts, and building the test cases.
28-
// After building the test cases, rootChildSendTx returns them along with a cleanup function that should be called
29-
// after the test cases have been executed. The test cases are executed by the TxTestCasesExecutor.
30-
func rootChildSendTx(b *testing.B) {
31-
b.Helper()
32-
// set up environment, get test cases and clean up fn
33-
testCases, cleanUpFn := RootChildSendTxSetUp(b, "", "", "", true)
34-
defer cleanUpFn()
35-
36-
// Loop over the test cases and measure the execution time of the transactions
37-
for _, testInput := range testCases {
38-
TxTestCasesExecutor(b, testInput)
39-
}
40-
}
41-
4218
// RootChildSendTxSetUp sets environment for execution of sentTx test cases on both root and child chains and
4319
// returns test cases and clean up fn.
4420
// The rootJSONRPC, childJSONRPC, privateKey and startCluster params are used to configure the testing environment.
@@ -111,14 +87,14 @@ func RootChildSendTxSetUp(b *testing.B, rootNodeAddr, childNodeAddr,
11187
// set callee contract addresses for multi call contracts (A->B->C)
11288
// set B contract address in A contract
11389
setContractDependencyAddress(b, childTxRelayer, multiAContChildAddr, multiBContChildAddr,
114-
multiContSetAddrFunc, sender)
90+
multiContSetAAddrFunc, sender)
11591
setContractDependencyAddress(b, rootTxRelayer, multiAContRootAddr, multiBContRootAddr,
116-
multiContSetAddrFunc, sender)
92+
multiContSetAAddrFunc, sender)
11793
// set C contract address in B contract
11894
setContractDependencyAddress(b, childTxRelayer, multiBContChildAddr, multiCContChildAddr,
119-
multiContSetAddrFunc, sender)
95+
multiContSetBAddrFunc, sender)
12096
setContractDependencyAddress(b, rootTxRelayer, multiBContRootAddr, multiCContRootAddr,
121-
multiContSetAddrFunc, sender)
97+
multiContSetBAddrFunc, sender)
12298

12399
// create inputs for contract calls
124100
singleContInputs := map[string][]byte{
@@ -198,3 +174,74 @@ func RootChildSendTxSetUp(b *testing.B, rootNodeAddr, childNodeAddr,
198174

199175
return testCases, cleanUpFn
200176
}
177+
178+
// TxTestCase represents a test case data to be run with txTestCasesExecutor
179+
type TxTestCase struct {
180+
Name string
181+
Relayer txrelayer.TxRelayer
182+
ContractAddr ethgo.Address
183+
Input [][]byte
184+
Sender ethgo.Key
185+
TxNumber int
186+
}
187+
188+
// TxTestCasesExecutor executes transactions from testInput and waits in separate
189+
// go routins for each tx receipt
190+
func TxTestCasesExecutor(b *testing.B, testInput TxTestCase) {
191+
b.Helper()
192+
b.Run(testInput.Name, func(b *testing.B) {
193+
b.ReportAllocs()
194+
b.ResetTimer()
195+
var wg sync.WaitGroup
196+
197+
// submit all tx 'repeatCall' times
198+
for i := 0; i < testInput.TxNumber; i++ {
199+
// call contract for the all inputs
200+
for j := 0; j < len(testInput.Input); j++ {
201+
nonce, err := testInput.Relayer.Client().Eth().GetNonce(testInput.Sender.Address(), ethgo.Pending)
202+
require.NoError(b, err)
203+
204+
// the tx is submitted to the blockchain without waiting for the receipt,
205+
// since we want to have multiple tx in one block
206+
txHash, err := testInput.Relayer.SumbitTransaction(
207+
&ethgo.Transaction{
208+
To: &testInput.ContractAddr,
209+
Input: testInput.Input[j],
210+
}, testInput.Sender)
211+
require.NoError(b, err)
212+
require.NotEqual(b, ethgo.ZeroHash, txHash)
213+
214+
wg.Add(1)
215+
216+
// wait for receipt of submitted tx in a separate routine, and continue with the next tx
217+
go func(hash ethgo.Hash) {
218+
defer wg.Done()
219+
220+
receipt, err := testInput.Relayer.WaitForReceipt(hash)
221+
require.NoError(b, err)
222+
require.Equal(b, uint64(types.ReceiptSuccess), receipt.Status)
223+
}(txHash)
224+
225+
// wait for tx to be added in mem pool so that we can create tx with the next nonce
226+
waitForNextNonce(b, nonce, testInput.Sender.Address(), testInput.Relayer)
227+
}
228+
}
229+
230+
wg.Wait()
231+
})
232+
}
233+
234+
func waitForNextNonce(b *testing.B, nonce uint64, address ethgo.Address, txRelayer txrelayer.TxRelayer) {
235+
startTime := time.Now()
236+
237+
for {
238+
newNonce, err := txRelayer.Client().Eth().GetNonce(address, ethgo.Pending)
239+
require.NoError(b, err)
240+
if newNonce > nonce {
241+
return
242+
}
243+
244+
elapsedTime := time.Since(startTime)
245+
require.True(b, elapsedTime <= 5*time.Second, "tx not added to the mem poolin 2s")
246+
}
247+
}

0 commit comments

Comments
 (0)