aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWei-Ning Huang <w@dexon.org>2018-11-23 12:12:10 +0800
committerWei-Ning Huang <w@byzantine-lab.io>2019-06-12 17:27:18 +0800
commit41c03e07c6ad09b30c336c929442087618fefd66 (patch)
treefb685c2f7411c03504b9f32dbf66e1effc644fbb
parentfc2df4ef1c189accd7a4c4ed90d8e3562b5d9e60 (diff)
downloadgo-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar.gz
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar.bz2
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar.lz
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar.xz
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.tar.zst
go-tangerine-41c03e07c6ad09b30c336c929442087618fefd66.zip
api: allow sending batch of raw transactions
-rw-r--r--cmd/monkey/gambler.go33
-rw-r--r--cmd/monkey/key1
-rw-r--r--cmd/monkey/monkey.go140
-rw-r--r--dex/api_backend.go4
-rw-r--r--eth/api_backend.go4
-rw-r--r--ethclient/ethclient.go16
-rw-r--r--internal/ethapi/api.go39
-rw-r--r--internal/ethapi/backend.go1
-rw-r--r--les/api_backend.go5
9 files changed, 164 insertions, 79 deletions
diff --git a/cmd/monkey/gambler.go b/cmd/monkey/gambler.go
index ecddb7d0f..8151f2c22 100644
--- a/cmd/monkey/gambler.go
+++ b/cmd/monkey/gambler.go
@@ -18,7 +18,6 @@
package main
import (
- "crypto/ecdsa"
"fmt"
"math"
"math/big"
@@ -55,7 +54,14 @@ func (m *Monkey) Gamble() {
if err != nil {
panic(err)
}
- m.call(m.source, contract, input, new(big.Int).Set(oneDEX), 0, math.MaxUint64)
+ m.transfer(&transferContext{
+ Key: m.source,
+ ToAddress: contract,
+ Amount: new(big.Int).Set(oneDEX),
+ Data: input,
+ Gas: 0,
+ Nonce: math.MaxUint64,
+ })
time.Sleep(5 * time.Second)
@@ -64,20 +70,27 @@ func (m *Monkey) Gamble() {
panic(err)
}
- call := func(key *ecdsa.PrivateKey, nonce uint64) {
- m.call(key, contract, input, big.NewInt(100000), uint64(32740), nonce)
- }
-
nonce := uint64(0)
for {
fmt.Println("nonce", nonce)
- for _, key := range m.keys {
- if *parallel {
- go call(key, nonce)
+ ctxs := make([]*transferContext, len(m.keys))
+ for i, key := range m.keys {
+ ctx := &transferContext{
+ Key: key,
+ ToAddress: contract,
+ Amount: big.NewInt(1),
+ Data: input,
+ Nonce: nonce,
+ }
+ if *batch {
+ ctxs[i] = ctx
} else {
- call(key, nonce)
+ m.transfer(ctx)
}
}
+ if *batch {
+ m.batchTransfer(ctxs)
+ }
nonce += 1
time.Sleep(time.Duration(*sleep) * time.Millisecond)
}
diff --git a/cmd/monkey/key b/cmd/monkey/key
deleted file mode 100644
index a9fe18413..000000000
--- a/cmd/monkey/key
+++ /dev/null
@@ -1 +0,0 @@
-fa30b47a7a3d5ab6935d873ffaeb8ca5b9782d102c4094be6da6b7f2fc04b5bd \ No newline at end of file
diff --git a/cmd/monkey/monkey.go b/cmd/monkey/monkey.go
index 6ac9be5f3..f55d195a6 100644
--- a/cmd/monkey/monkey.go
+++ b/cmd/monkey/monkey.go
@@ -40,7 +40,7 @@ var key = flag.String("key", "", "private key path")
var endpoint = flag.String("endpoint", "http://127.0.0.1:8545", "JSON RPC endpoint")
var n = flag.Int("n", 100, "number of random accounts")
var gambler = flag.Bool("gambler", false, "make this monkey a gambler")
-var parallel = flag.Bool("parallel", false, "monkeys will send transaction in parallel")
+var batch = flag.Bool("batch", false, "monkeys will send transaction in batch")
var sleep = flag.Int("sleep", 500, "time in millisecond that monkeys sleep between each transaction")
type Monkey struct {
@@ -79,35 +79,70 @@ func New(ep string, source *ecdsa.PrivateKey, num int) *Monkey {
}
}
-func (m *Monkey) transfer(
- key *ecdsa.PrivateKey, toAddress common.Address, amount *big.Int, nonce uint64) {
+type transferContext struct {
+ Key *ecdsa.PrivateKey
+ ToAddress common.Address
+ Amount *big.Int
+ Data []byte
+ Nonce uint64
+ Gas uint64
+}
- if nonce == math.MaxUint64 {
+func (m *Monkey) prepareTx(ctx *transferContext) *types.Transaction {
+ if ctx.Nonce == math.MaxUint64 {
var err error
- address := crypto.PubkeyToAddress(key.PublicKey)
- nonce, err = m.client.PendingNonceAt(context.Background(), address)
+ address := crypto.PubkeyToAddress(ctx.Key.PublicKey)
+ ctx.Nonce, err = m.client.PendingNonceAt(context.Background(), address)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ if ctx.Gas == uint64(0) {
+ var err error
+ ctx.Gas, err = m.client.EstimateGas(context.Background(), dexon.CallMsg{
+ Data: ctx.Data,
+ })
if err != nil {
panic(err)
}
}
tx := types.NewTransaction(
- uint64(nonce),
- toAddress,
- amount,
+ ctx.Nonce,
+ ctx.ToAddress,
+ ctx.Amount,
uint64(21000),
big.NewInt(1e9),
nil)
signer := types.NewEIP155Signer(m.networkID)
- tx, err := types.SignTx(tx, signer, key)
+ tx, err := types.SignTx(tx, signer, ctx.Key)
if err != nil {
panic(err)
}
+ return tx
+}
+
+func (m *Monkey) transfer(ctx *transferContext) {
+ tx := m.prepareTx(ctx)
+
fmt.Println("Sending TX", "fullhash", tx.Hash().String())
+ err := m.client.SendTransaction(context.Background(), tx)
+ if err != nil {
+ panic(err)
+ }
+}
- err = m.client.SendTransaction(context.Background(), tx)
+func (m *Monkey) batchTransfer(ctxs []*transferContext) {
+ txs := make([]*types.Transaction, len(ctxs))
+ for i, ctx := range ctxs {
+ txs[i] = m.prepareTx(ctx)
+ fmt.Println("Sending TX", "fullhash", txs[i].Hash().String())
+ }
+
+ err := m.client.SendTransactions(context.Background(), txs)
if err != nil {
panic(err)
}
@@ -139,7 +174,7 @@ func (m *Monkey) deploy(
}
tx := types.NewContractCreation(
- uint64(nonce),
+ nonce,
amount,
gas,
big.NewInt(1e9),
@@ -171,53 +206,6 @@ func (m *Monkey) deploy(
}
}
-func (m *Monkey) call(
- key *ecdsa.PrivateKey, contract common.Address, input []byte, amount *big.Int, gas uint64, nonce uint64) {
-
- address := crypto.PubkeyToAddress(key.PublicKey)
- if nonce == math.MaxUint64 {
- var err error
- nonce, err = m.client.PendingNonceAt(context.Background(), address)
- if err != nil {
- panic(err)
- }
- }
-
- if gas == uint64(0) {
- var err error
- gas, err = m.client.EstimateGas(context.Background(), dexon.CallMsg{
- From: address,
- To: &contract,
- Value: amount,
- Data: input,
- })
- if err != nil {
- panic(err)
- }
- }
-
- tx := types.NewTransaction(
- uint64(nonce),
- contract,
- amount,
- gas,
- big.NewInt(1e9),
- input)
-
- signer := types.NewEIP155Signer(m.networkID)
- tx, err := types.SignTx(tx, signer, key)
- if err != nil {
- panic(err)
- }
-
- fmt.Println("Sending TX", "fullhash", tx.Hash().String())
-
- err = m.client.SendTransaction(context.Background(), tx)
- if err != nil {
- panic(err)
- }
-}
-
func (m *Monkey) Distribute() {
fmt.Println("Distributing DEX to random accounts ...")
address := crypto.PubkeyToAddress(m.source.PublicKey)
@@ -226,32 +214,48 @@ func (m *Monkey) Distribute() {
panic(err)
}
- for _, key := range m.keys {
+ ctxs := make([]*transferContext, len(m.keys))
+ for i, key := range m.keys {
address := crypto.PubkeyToAddress(key.PublicKey)
amount := new(big.Int)
amount.SetString("1000000000000000000", 10)
- m.transfer(m.source, address, amount, nonce)
+ ctxs[i] = &transferContext{
+ Key: m.source,
+ ToAddress: address,
+ Amount: amount,
+ Nonce: nonce,
+ Gas: 21000,
+ }
nonce += 1
}
+ m.batchTransfer(ctxs)
time.Sleep(20 * time.Second)
}
func (m *Monkey) Crazy() {
fmt.Println("Performing random transfers ...")
nonce := uint64(0)
- transfer := func(key *ecdsa.PrivateKey, to common.Address, nonce uint64) {
- m.transfer(key, to, big.NewInt(1), nonce)
- }
for {
fmt.Println("nonce", nonce)
- for _, key := range m.keys {
+ ctxs := make([]*transferContext, len(m.keys))
+ for i, key := range m.keys {
to := crypto.PubkeyToAddress(m.keys[rand.Int()%len(m.keys)].PublicKey)
- if *parallel {
- go transfer(key, to, nonce)
+ ctx := &transferContext{
+ Key: key,
+ ToAddress: to,
+ Amount: big.NewInt(1),
+ Nonce: nonce,
+ Gas: 21000,
+ }
+ if *batch {
+ ctxs[i] = ctx
} else {
- transfer(key, to, nonce)
+ m.transfer(ctx)
}
}
+ if *batch {
+ m.batchTransfer(ctxs)
+ }
nonce += 1
time.Sleep(time.Duration(*sleep) * time.Millisecond)
}
diff --git a/dex/api_backend.go b/dex/api_backend.go
index 9929b062d..80fe1e39b 100644
--- a/dex/api_backend.go
+++ b/dex/api_backend.go
@@ -154,6 +154,10 @@ func (b *DexAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
return b.dex.txPool.AddLocal(signedTx)
}
+func (b *DexAPIBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error {
+ return b.dex.txPool.AddLocals(signedTxs)
+}
+
func (b *DexAPIBackend) GetPoolTransactions() (types.Transactions, error) {
pending, err := b.dex.txPool.Pending()
if err != nil {
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 1c27b5d5c..696b6c64a 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -157,6 +157,10 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
return b.eth.txPool.AddLocal(signedTx)
}
+func (b *EthAPIBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error {
+ return b.eth.txPool.AddLocals(signedTxs)
+}
+
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
pending, err := b.eth.txPool.Pending()
if err != nil {
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 12eeaf0f6..c8a8e5eb5 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -506,6 +506,22 @@ func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) er
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
}
+// SendTransactions injects a batch of signed transactions into the pending pool for execution.
+//
+// If a transaction was a contract creation use the TransactionReceipt method to get the
+// contract address after the transaction has been mined.
+func (ec *Client) SendTransactions(ctx context.Context, txs []*types.Transaction) error {
+ txData := make([]interface{}, len(txs))
+ for i, tx := range txs {
+ data, err := rlp.EncodeToBytes(tx)
+ if err != nil {
+ return err
+ }
+ txData[i] = common.ToHex(data)
+ }
+ return ec.c.CallContext(ctx, nil, "eth_sendRawTransactions", txData)
+}
+
func toCallArg(msg ethereum.CallMsg) interface{} {
arg := map[string]interface{}{
"from": msg.From,
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 8c950d5cf..3dec0b114 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -1288,6 +1288,31 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
return tx.Hash(), nil
}
+// submitTransactions is a helper function that submits batch of tx to txPool and logs a message.
+func submitTransactions(ctx context.Context, b Backend, txs []*types.Transaction) ([]common.Hash, error) {
+ errs := b.SendTxs(ctx, txs)
+ var hashes []common.Hash
+ for i, err := range errs {
+ if err != nil {
+ return nil, err
+ }
+ tx := txs[i]
+ if tx.To() == nil {
+ signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
+ from, err := types.Sender(signer, tx)
+ if err != nil {
+ return nil, err
+ }
+ addr := crypto.CreateAddress(from, tx.Nonce())
+ log.Info("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex())
+ } else {
+ log.Info("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To())
+ }
+ hashes = append(hashes, tx.Hash())
+ }
+ return hashes, nil
+}
+
// SendTransaction creates a transaction for the given argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
@@ -1335,6 +1360,20 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
return submitTransaction(ctx, s.b, tx)
}
+// SendRawTransactions will add the signed transaction to the transaction pool.
+// The sender is responsible for signing the transaction and using the correct nonce.
+func (s *PublicTransactionPoolAPI) SendRawTransactions(ctx context.Context, encodedTxs []hexutil.Bytes) ([]common.Hash, error) {
+ var txs []*types.Transaction
+ for _, encodedTx := range encodedTxs {
+ tx := new(types.Transaction)
+ if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
+ return nil, err
+ }
+ txs = append(txs, tx)
+ }
+ return submitTransactions(ctx, s.b, txs)
+}
+
// Sign calculates an ECDSA signature for:
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message).
//
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 801b55d5c..da13d3661 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -65,6 +65,7 @@ type Backend interface {
// TxPool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
+ SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error
GetPoolTransactions() (types.Transactions, error)
GetPoolTransaction(txHash common.Hash) *types.Transaction
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
diff --git a/les/api_backend.go b/les/api_backend.go
index 148b29c0b..f69e67c60 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -115,6 +115,11 @@ func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
return b.eth.txPool.Add(ctx, signedTx)
}
+func (b *LesApiBackend) SendTxs(ctx context.Context, signedTxs []*types.Transaction) []error {
+ b.eth.txPool.AddBatch(ctx, signedTxs)
+ return nil
+}
+
func (b *LesApiBackend) RemoveTx(txHash common.Hash) {
b.eth.txPool.RemoveTx(txHash)
}