Skip to content

Commit 60bf4a3

Browse files
authored
Merge pull request base#2 from base/send-txn-sync
feat: add benchmarking setup for sending raw transactions synchronously
2 parents 987695e + 40a73ee commit 60bf4a3

File tree

3 files changed

+90
-25
lines changed

3 files changed

+90
-25
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
PRIVATE_KEY=todo
22
TO_ADDRESS=0x7d5A90C2cd5567B9966E92c710b5E9936F400d74
3+
POLLING_INTERVAL_MS=100
34
FLASHBLOCKS_URL=https://sepolia-preconf.base.org
45
BASE_URL=https://sepolia.base.org
56
REGION=texas

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.env
22
.idea/
33
*.csv
4+
data/

main.go

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/ecdsa"
66
"encoding/csv"
7+
"encoding/hex"
78
"fmt"
89
"log"
910
"math/big"
@@ -62,6 +63,25 @@ func main() {
6263
log.Fatal("BASE_URL environment variable not set")
6364
}
6465

66+
sendTxnSync := os.Getenv("SEND_TXN_SYNC") == "true"
67+
runStandardTransactionSending := os.Getenv("RUN_STANDARD_TRANSACTION_SENDING") != "false"
68+
69+
pollingIntervalMs := 100
70+
if pollingEnv := os.Getenv("POLLING_INTERVAL_MS"); pollingEnv != "" {
71+
if parsed, err := strconv.Atoi(pollingEnv); err == nil {
72+
pollingIntervalMs = parsed
73+
}
74+
}
75+
76+
log.Println("Polling interval ms", pollingIntervalMs)
77+
78+
numberOfTransactions := 100
79+
if txnCountEnv := os.Getenv("NUMBER_OF_TRANSACTIONS"); txnCountEnv != "" {
80+
if parsed, err := strconv.Atoi(txnCountEnv); err == nil {
81+
numberOfTransactions = parsed
82+
}
83+
}
84+
6585
flashblocksClient, err := ethclient.Dial(flashblocksUrl)
6686
if err != nil {
6787
log.Fatalf("Failed to connect to the Ethereum client: %v", err)
@@ -92,50 +112,60 @@ func main() {
92112
log.Fatalf("Failed to get network ID: %v", err)
93113
}
94114

95-
iterations := 100
96115
flashblockErrors := 0
97116
baseErrors := 0
98117

99-
log.Printf("Starting flashblock transactions")
100-
for i := 0; i < iterations; i++ {
101-
timing, err := timeTransaction(chainId, privateKey, fromAddress, toAddress, flashblocksClient)
118+
log.Printf("Starting flashblock transactions, syncMode=%v", sendTxnSync)
119+
for i := 0; i < numberOfTransactions; i++ {
120+
timing, err := timeTransaction(chainId, privateKey, fromAddress, toAddress, flashblocksClient, sendTxnSync, pollingIntervalMs)
102121
if err != nil {
103122
flashblockErrors += 1
104123
log.Printf("Failed to send transaction: %v", err)
105124
}
106125

107126
flashblockTimings = append(flashblockTimings, timing)
108127

109-
// wait for it to be mined -- sleep a random amount between 600ms and 1s
110-
time.Sleep(time.Duration(rand.Int63n(600)+600) * time.Millisecond)
128+
if !sendTxnSync {
129+
// wait for it to be mined -- sleep a random amount between 600ms and 1s
130+
time.Sleep(time.Duration(rand.Int63n(600)+600) * time.Millisecond)
131+
} else {
132+
time.Sleep(time.Duration(rand.Int63n(200)+100) * time.Millisecond)
133+
}
111134
}
112135

113136
// wait for the final fb transaction to land
114137
time.Sleep(5 * time.Second)
115138

116-
log.Printf("Starting regular transactions")
117-
for i := 0; i < iterations; i++ {
118-
timing, err := timeTransaction(chainId, privateKey, fromAddress, toAddress, baseClient)
119-
if err != nil {
120-
baseErrors += 1
121-
log.Printf("Failed to send transaction: %v", err)
122-
}
139+
if runStandardTransactionSending {
140+
log.Printf("Starting regular transactions")
141+
for i := 0; i < numberOfTransactions; i++ {
142+
// Currently not supported on non-flashblock endpoints
143+
timing, err := timeTransaction(chainId, privateKey, fromAddress, toAddress, baseClient, false, pollingIntervalMs)
144+
if err != nil {
145+
baseErrors += 1
146+
log.Printf("Failed to send transaction: %v", err)
147+
}
123148

124-
baseTimings = append(baseTimings, timing)
149+
baseTimings = append(baseTimings, timing)
125150

126-
// wait for it to be mined -- sleep a random amount between 4s and 3s
127-
time.Sleep(time.Duration(rand.Int63n(1000)+4000) * time.Millisecond)
151+
// wait for it to be mined -- sleep a random amount between 4s and 3s
152+
time.Sleep(time.Duration(rand.Int63n(1000)+4000) * time.Millisecond)
153+
}
154+
} else {
155+
log.Printf("Skipping regular transactions (RUN_STANDARD_TRANSACTION_SENDING=false)")
128156
}
129157

130-
if err := writeToFile(fmt.Sprintf("/data/flashblocks-%s.csv", region), flashblockTimings); err != nil {
158+
if err := writeToFile(fmt.Sprintf("./data/flashblocks-%s.csv", region), flashblockTimings); err != nil {
131159
log.Fatalf("Failed to write to file: %v", err)
132160
}
133161

134-
if err := writeToFile(fmt.Sprintf("/data/base-%s.csv", region), baseTimings); err != nil {
135-
log.Fatalf("Failed to write to file: %v", err)
162+
if runStandardTransactionSending {
163+
if err := writeToFile(fmt.Sprintf("./data/base-%s.csv", region), baseTimings); err != nil {
164+
log.Fatalf("Failed to write to file: %v", err)
165+
}
136166
}
137167

138-
log.Printf("Completed test iteration count", iterations)
168+
log.Printf("Completed test with %d transactions", numberOfTransactions)
139169
log.Printf("Flashblock errors: %v", flashblockErrors)
140170
log.Printf("BaseErrors: %v", baseErrors)
141171
}
@@ -170,7 +200,7 @@ func writeToFile(filename string, data []stats) error {
170200
return nil
171201
}
172202

173-
func timeTransaction(chainId *big.Int, privateKey *ecdsa.PrivateKey, fromAddress common.Address, toAddress common.Address, client *ethclient.Client) (stats, error) {
203+
func timeTransaction(chainId *big.Int, privateKey *ecdsa.PrivateKey, fromAddress common.Address, toAddress common.Address, client *ethclient.Client, useSyncRPC bool, pollingIntervalMs int) (stats, error) {
174204
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
175205
if err != nil {
176206
return stats{}, fmt.Errorf("unable to get nonce: %v", err)
@@ -204,24 +234,57 @@ func timeTransaction(chainId *big.Int, privateKey *ecdsa.PrivateKey, fromAddress
204234
return stats{}, fmt.Errorf("unable to sign transaction: %v", err)
205235
}
206236

237+
if useSyncRPC {
238+
return sendTransactionSync(client, signedTx)
239+
}
240+
241+
return sendTransactionAsync(client, signedTx, pollingIntervalMs)
242+
}
243+
244+
func sendTransactionSync(client *ethclient.Client, signedTx *types.Transaction) (stats, error) {
245+
rawTx, err := signedTx.MarshalBinary()
246+
if err != nil {
247+
return stats{}, fmt.Errorf("unable to marshal transaction: %v", err)
248+
}
249+
250+
txnData := "0x" + hex.EncodeToString(rawTx)
251+
252+
sentAt := time.Now()
253+
var receipt *types.Receipt
254+
err = client.Client().CallContext(context.Background(), &receipt, "eth_sendRawTransactionSync", txnData)
255+
if err != nil {
256+
return stats{}, fmt.Errorf("unable to send sync transaction: %v", err)
257+
}
258+
259+
log.Println("Transaction sent sync: ", signedTx.Hash().Hex())
260+
now := time.Now()
261+
return stats{
262+
SentAt: sentAt,
263+
InclusionDelay: now.Sub(sentAt),
264+
TxnHash: signedTx.Hash().Hex(),
265+
IncludedInBlock: receipt.BlockNumber.Uint64(),
266+
}, nil
267+
}
268+
269+
func sendTransactionAsync(client *ethclient.Client, signedTx *types.Transaction, pollingIntervalMs int) (stats, error) {
207270
sentAt := time.Now()
208-
err = client.SendTransaction(context.Background(), signedTx)
271+
err := client.SendTransaction(context.Background(), signedTx)
209272
if err != nil {
210273
return stats{}, fmt.Errorf("unable to send transaction: %v", err)
211274
}
212275

213-
log.Println("Transaction sent: ", signedTx.Hash().Hex())
276+
log.Println("Transaction sent async: ", signedTx.Hash().Hex())
214277

215278
for i := 0; i < 1000; i++ {
216279
receipt, err := client.TransactionReceipt(context.Background(), signedTx.Hash())
217280
if err != nil {
218-
time.Sleep(10 * time.Millisecond)
281+
time.Sleep(time.Duration(pollingIntervalMs) * time.Millisecond)
219282
} else {
220283
now := time.Now()
221284
return stats{
222285
SentAt: sentAt,
223286
InclusionDelay: now.Sub(sentAt),
224-
TxnHash: tx.Hash().Hex(),
287+
TxnHash: signedTx.Hash().Hex(),
225288
IncludedInBlock: receipt.BlockNumber.Uint64(),
226289
}, nil
227290
}

0 commit comments

Comments
 (0)