Skip to content

Commit 83cdacc

Browse files
Merge branch 'thiago/cmd-publish' of github.com:maticnetwork/polygon-cli into thiago/cmd-publish
2 parents 49919db + fa70388 commit 83cdacc

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed

cmd/publish/output.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ func (s *output) Print() {
5252
summaryString := fmt.Sprintf(`-----------------------------------
5353
Summary
5454
-----------------------------------
55+
Concurrency: %d
56+
JobQueueSize: %d
57+
RateLimit: %d
58+
-----------------------------------
5559
Input Data Source: %s
5660
Input Data Count: %d
5761
Valid Inputs: %d
@@ -64,10 +68,15 @@ Txs Sent Successfully: %d
6468
Txs Sent Unsuccessfully: %d
6569
Success Ratio: %.2f%%
6670
-----------------------------------`,
71+
*publishInputArgs.concurrency,
72+
*publishInputArgs.jobQueueSize,
73+
*publishInputArgs.rateLimit,
74+
6775
s.InputDataSource,
6876
s.InputDataCount.Load(),
6977
s.ValidInputs.Load(),
7078
s.InvalidInputs.Load(),
79+
7180
elapsed,
7281
txSent,
7382
txsSendPerSecond,

cmd/publish/script/filegen.go

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
// This script generates a file containing a specified number of Ethereum transactions
2+
// that transfer a small amount of ETH from a funding account to a newly created account.
3+
//
4+
// Transactions are encoded in RLP format and written to a file.
5+
//
6+
// The funding account is specified by its private key, and the transactions are created
7+
// with a fixed gas limit and fee structure.
8+
//
9+
// The script logs the progress of transaction creation and file writing, including
10+
// the address and private key of the newly created account.
11+
12+
package main
13+
14+
import (
15+
"crypto/ecdsa"
16+
"fmt"
17+
"math/big"
18+
"os"
19+
"path/filepath"
20+
21+
"github.com/ethereum/go-ethereum/common"
22+
"github.com/ethereum/go-ethereum/core/types"
23+
"github.com/ethereum/go-ethereum/crypto"
24+
"github.com/rs/zerolog"
25+
"github.com/rs/zerolog/log"
26+
)
27+
28+
const (
29+
// number of transactions to add to the file
30+
numberOfTransactions = uint64(1000000)
31+
32+
// chain ID for the Ethereum mainnet
33+
chainID = uint64(1337)
34+
35+
// funding account private key in hex format
36+
fundingPrivateKeyHex = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
37+
38+
// gas limit for the ETH transfer transactions
39+
transferGasLimit = uint64(21000) // Standard gas limit for ETH transfer
40+
41+
// fee per gas for the transactions
42+
feePerGas = uint64(1000000000)
43+
44+
// fee tip cap for the transactions
45+
feeTipCap = uint64(1000000000)
46+
47+
// log every % of the transactions added to the file
48+
logEveryPercent = uint64(20)
49+
logEveryTxs = numberOfTransactions * logEveryPercent / 100
50+
)
51+
52+
type account struct {
53+
addr common.Address
54+
key ecdsa.PrivateKey
55+
}
56+
57+
func (a account) keyHex() string {
58+
privateKeyBytes := crypto.FromECDSA(&a.key)
59+
privateKeyHex := fmt.Sprintf("0x%x", privateKeyBytes)
60+
61+
return privateKeyHex
62+
}
63+
64+
func main() {
65+
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
66+
zerolog.SetGlobalLevel(zerolog.DebugLevel)
67+
68+
fundingAccPrivateKey, err := loadPrivateKey(fundingPrivateKeyHex)
69+
checkErr(err, "failed to load funding account private key")
70+
71+
fundingAcc, err := loadAccount(*fundingAccPrivateKey)
72+
checkErr(err, "failed to load funding account")
73+
74+
acc, err := newAccount()
75+
checkErr(err, "failed to create new account")
76+
log.Info().
77+
Str("address", acc.addr.String()).
78+
Str("privateKey", acc.keyHex()).
79+
Msg("new account created")
80+
81+
transferAmount := big.NewInt(1)
82+
83+
outputFilename := "txs"
84+
outputFilePath := filepath.Join(".", outputFilename)
85+
outputFilePath, err = filepath.Abs(outputFilePath)
86+
checkErr(err, "failed to get absolute path for file")
87+
88+
f, err := deleteAndCreateFile(outputFilePath)
89+
checkErr(err, "failed to create temp file")
90+
defer f.Close()
91+
log.Info().
92+
Str("filePath", outputFilePath).
93+
Msg("file created for transactions")
94+
95+
log.Info().
96+
Uint64("numberOfTransactions", numberOfTransactions).
97+
Msg("adding transactions to file")
98+
99+
logPageSize := logEveryTxs
100+
if logPageSize == 0 {
101+
logPageSize = 1
102+
}
103+
104+
nonce := uint64(0)
105+
for nonce < numberOfTransactions {
106+
nonce++
107+
108+
tx, err := createTxToTransferETH(acc, fundingAcc, transferAmount, nonce)
109+
checkErr(err, "failed to write to file")
110+
111+
txRLP, err := rlp(tx)
112+
checkErr(err, "failed to encode transaction to RLP")
113+
114+
err = writeToFile(f, txRLP)
115+
checkErr(err, "failed to write transaction to file")
116+
117+
txJSONB, err := tx.MarshalJSON()
118+
checkErr(err, "failed to marshal transaction to JSON")
119+
txJSON := string(txJSONB)
120+
121+
log.Trace().
122+
Str("tx", txJSON).
123+
Str("rlp", txRLP).
124+
Msg("transaction written to file")
125+
126+
if nonce%logPageSize == 0 {
127+
log.Info().
128+
Uint64("numberOfTransactions", nonce).
129+
Msg("transactions added to file")
130+
}
131+
}
132+
if nonce%logPageSize != 0 {
133+
log.Info().
134+
Uint64("numberOfTransactions", nonce).
135+
Msg("transactions added to file")
136+
}
137+
138+
fundingAmountInWei := computeFundingAmountInWei(transferAmount)
139+
castSendCommand := fmt.Sprintf("cast send --private-key %s --value %d %s --rpc-url http://127.0.0.1:8545", fundingPrivateKeyHex, fundingAmountInWei, acc.addr.String())
140+
log.Info().
141+
Str("cmd", castSendCommand).
142+
Msg("cast send command to fund account created")
143+
}
144+
145+
func computeFundingAmountInWei(transferAmount *big.Int) *big.Int {
146+
numberOfTransactionsBig := big.NewInt(0).SetUint64(numberOfTransactions)
147+
transferGasLimitBig := big.NewInt(0).SetUint64(transferGasLimit)
148+
149+
feePerGasBig := big.NewInt(0).SetUint64(feePerGas)
150+
feeTipCapBig := big.NewInt(0).SetUint64(feeTipCap)
151+
152+
feePerTx := big.NewInt(0).Mul(feePerGasBig, transferGasLimitBig)
153+
154+
costPerTx := big.NewInt(0)
155+
costPerTx.Add(transferAmount, feePerTx)
156+
costPerTx.Add(costPerTx, feeTipCapBig)
157+
158+
fundingAmount := big.NewInt(0).Mul(costPerTx, numberOfTransactionsBig)
159+
return fundingAmount
160+
}
161+
162+
func deleteAndCreateFile(filePath string) (*os.File, error) {
163+
if _, err := os.Stat(filePath); err == nil {
164+
if iErr := os.Remove(filePath); iErr != nil {
165+
return nil, fmt.Errorf("failed to delete existing file: %w", err)
166+
}
167+
} else if !os.IsNotExist(err) {
168+
return nil, fmt.Errorf("failed to check if file exists: %w", err)
169+
}
170+
171+
f, err := os.Create(filePath)
172+
if err != nil {
173+
return nil, fmt.Errorf("failed to create file: %w", err)
174+
}
175+
return f, nil
176+
}
177+
178+
func newAccount() (*account, error) {
179+
privateKey, err := crypto.GenerateKey()
180+
if err != nil {
181+
return nil, fmt.Errorf("failed to generate new private key: %w", err)
182+
}
183+
184+
return loadAccount(*privateKey)
185+
}
186+
187+
func loadAccount(privateKey ecdsa.PrivateKey) (*account, error) {
188+
pubKey := privateKey.Public().(*ecdsa.PublicKey)
189+
address := crypto.PubkeyToAddress(*pubKey)
190+
191+
acc := &account{
192+
addr: address,
193+
key: privateKey,
194+
}
195+
196+
return acc, nil
197+
}
198+
199+
func loadPrivateKey(privateKeyHex string) (*ecdsa.PrivateKey, error) {
200+
privateKeyBytes, err := crypto.HexToECDSA(privateKeyHex[2:])
201+
if err != nil {
202+
return nil, fmt.Errorf("failed to load private key: %w", err)
203+
}
204+
return privateKeyBytes, nil
205+
}
206+
207+
func rlp(tx *types.Transaction) (string, error) {
208+
txBytes, err := tx.MarshalBinary()
209+
if err != nil {
210+
return "", fmt.Errorf("failed to marshal transaction to binary: %w", err)
211+
}
212+
return fmt.Sprintf("0x%x", txBytes), nil
213+
}
214+
215+
func createTxToTransferETH(fromAcc, toAcc *account, value *big.Int, forceNonce uint64) (*types.Transaction, error) {
216+
tx := types.NewTx(&types.DynamicFeeTx{
217+
// destination
218+
To: &toAcc.addr,
219+
Nonce: forceNonce,
220+
Value: value,
221+
222+
// fees
223+
Gas: transferGasLimit, // Standard gas limit for ETH transfer
224+
GasFeeCap: big.NewInt(0).SetUint64(feePerGas),
225+
GasTipCap: big.NewInt(0).SetUint64(feeTipCap),
226+
227+
// identification
228+
ChainID: big.NewInt(0).SetUint64(chainID),
229+
})
230+
231+
// signature
232+
signer := types.LatestSignerForChainID(big.NewInt(0).SetUint64(chainID))
233+
signedTx, err := types.SignTx(tx, signer, &fromAcc.key)
234+
if err != nil {
235+
return nil, fmt.Errorf("failed to sign transaction: %w", err)
236+
}
237+
238+
return signedTx, nil
239+
}
240+
241+
func writeToFile(f *os.File, data string) error {
242+
if _, err := f.WriteString(data + "\n"); err != nil {
243+
return fmt.Errorf("failed to write to file: %w", err)
244+
}
245+
return nil
246+
}
247+
248+
func checkErr(err error, msg string) {
249+
if err != nil {
250+
log.Fatal().
251+
Err(err).
252+
Msg(msg)
253+
}
254+
}

0 commit comments

Comments
 (0)