|
4 | 4 | "context" |
5 | 5 | "crypto/ecdsa" |
6 | 6 | "encoding/csv" |
| 7 | +"encoding/hex" |
7 | 8 | "fmt" |
8 | 9 | "log" |
9 | 10 | "math/big" |
@@ -62,6 +63,25 @@ func main() { |
62 | 63 | log.Fatal("BASE_URL environment variable not set") |
63 | 64 | } |
64 | 65 |
|
| 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 | + |
65 | 85 | flashblocksClient, err := ethclient.Dial(flashblocksUrl) |
66 | 86 | if err != nil { |
67 | 87 | log.Fatalf("Failed to connect to the Ethereum client: %v", err) |
@@ -92,50 +112,60 @@ func main() { |
92 | 112 | log.Fatalf("Failed to get network ID: %v", err) |
93 | 113 | } |
94 | 114 |
|
95 | | -iterations := 100 |
96 | 115 | flashblockErrors := 0 |
97 | 116 | baseErrors := 0 |
98 | 117 |
|
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) |
102 | 121 | if err != nil { |
103 | 122 | flashblockErrors += 1 |
104 | 123 | log.Printf("Failed to send transaction: %v", err) |
105 | 124 | } |
106 | 125 |
|
107 | 126 | flashblockTimings = append(flashblockTimings, timing) |
108 | 127 |
|
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 | +} |
111 | 134 | } |
112 | 135 |
|
113 | 136 | // wait for the final fb transaction to land |
114 | 137 | time.Sleep(5 * time.Second) |
115 | 138 |
|
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 | +} |
123 | 148 |
|
124 | | -baseTimings = append(baseTimings, timing) |
| 149 | +baseTimings = append(baseTimings, timing) |
125 | 150 |
|
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)") |
128 | 156 | } |
129 | 157 |
|
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 { |
131 | 159 | log.Fatalf("Failed to write to file: %v", err) |
132 | 160 | } |
133 | 161 |
|
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 | +} |
136 | 166 | } |
137 | 167 |
|
138 | | -log.Printf("Completed test iteration count", iterations) |
| 168 | +log.Printf("Completed test with %d transactions", numberOfTransactions) |
139 | 169 | log.Printf("Flashblock errors: %v", flashblockErrors) |
140 | 170 | log.Printf("BaseErrors: %v", baseErrors) |
141 | 171 | } |
@@ -170,7 +200,7 @@ func writeToFile(filename string, data []stats) error { |
170 | 200 | return nil |
171 | 201 | } |
172 | 202 |
|
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) { |
174 | 204 | nonce, err := client.PendingNonceAt(context.Background(), fromAddress) |
175 | 205 | if err != nil { |
176 | 206 | return stats{}, fmt.Errorf("unable to get nonce: %v", err) |
@@ -204,24 +234,57 @@ func timeTransaction(chainId *big.Int, privateKey *ecdsa.PrivateKey, fromAddress |
204 | 234 | return stats{}, fmt.Errorf("unable to sign transaction: %v", err) |
205 | 235 | } |
206 | 236 |
|
| 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) { |
207 | 270 | sentAt := time.Now() |
208 | | -err = client.SendTransaction(context.Background(), signedTx) |
| 271 | +err := client.SendTransaction(context.Background(), signedTx) |
209 | 272 | if err != nil { |
210 | 273 | return stats{}, fmt.Errorf("unable to send transaction: %v", err) |
211 | 274 | } |
212 | 275 |
|
213 | | -log.Println("Transaction sent: ", signedTx.Hash().Hex()) |
| 276 | +log.Println("Transaction sent async: ", signedTx.Hash().Hex()) |
214 | 277 |
|
215 | 278 | for i := 0; i < 1000; i++ { |
216 | 279 | receipt, err := client.TransactionReceipt(context.Background(), signedTx.Hash()) |
217 | 280 | if err != nil { |
218 | | -time.Sleep(10 * time.Millisecond) |
| 281 | +time.Sleep(time.Duration(pollingIntervalMs) * time.Millisecond) |
219 | 282 | } else { |
220 | 283 | now := time.Now() |
221 | 284 | return stats{ |
222 | 285 | SentAt: sentAt, |
223 | 286 | InclusionDelay: now.Sub(sentAt), |
224 | | -TxnHash: tx.Hash().Hex(), |
| 287 | +TxnHash: signedTx.Hash().Hex(), |
225 | 288 | IncludedInBlock: receipt.BlockNumber.Uint64(), |
226 | 289 | }, nil |
227 | 290 | } |
|
0 commit comments