Skip to content

Commit 1791b16

Browse files
committed
refactor tx sending
Refactor sending evm txs so that we store the txs before sending and are accepting of send errors. This avoids a scenario where we send the tx, but still receive an error, perhaps because of a bad connection or other problem with the rpc provider. Implement a send queue so that the caller doesn't have to wait for the send. This should speed up ticks in core if the rpc provider is functional but slow to respond.
1 parent ae2fbd0 commit 1791b16

8 files changed

Lines changed: 289 additions & 143 deletions

File tree

client/asset/eth/deploy.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,11 @@ func (contractDeployer) nodeAndRate(
351351
return nil, nil, nil, fmt.Errorf("error creating wallet: %w", err)
352352
}
353353

354-
cl, err := newMultiRPCClient(walletDir, providers, log, chainCfg, 3, net)
354+
creds, err := walletCredentials(chainCfg.ChainID, walletDir, net)
355+
if err != nil {
356+
return nil, nil, nil, fmt.Errorf("error generating wallet credentials: %w", err)
357+
}
358+
cl, err := newMultiRPCClient(creds, providers, log, chainCfg, 3, net)
355359
if err != nil {
356360
return nil, nil, nil, fmt.Errorf("error creating rpc client: %w", err)
357361
}

client/asset/eth/eth.go

Lines changed: 132 additions & 45 deletions
Large diffs are not rendered by default.

client/asset/eth/eth_test.go

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"fmt"
1515
"math/big"
1616
"math/rand"
17+
"os"
1718
"sort"
1819
"strings"
1920
"sync"
@@ -29,6 +30,7 @@ import (
2930
"github.com/ethereum/go-ethereum"
3031
"github.com/ethereum/go-ethereum/accounts"
3132
"github.com/ethereum/go-ethereum/accounts/abi/bind"
33+
"github.com/ethereum/go-ethereum/accounts/keystore"
3234
"github.com/ethereum/go-ethereum/common"
3335
"github.com/ethereum/go-ethereum/core/types"
3436
"github.com/ethereum/go-ethereum/crypto"
@@ -74,13 +76,72 @@ var (
7476
SwapConf: 1,
7577
}
7678

79+
tCreds *accountCredentials
80+
tPriv *ecdsa.PrivateKey
7781
signer = types.LatestSigner(params.AllEthashProtocolChanges)
7882

7983
// simBackend = backends.NewSimulatedBackend(core.GenesisAlloc{
8084
// testAddressA: core.GenesisAccount{Balance: dexeth.GweiToWei(5e10)},
8185
// }, 1e9)
8286
)
8387

88+
func generateTestCredentials() (string, error) {
89+
privB, _, err := privKeyFromSeed(encode.RandomBytes(32))
90+
if err != nil {
91+
return "", fmt.Errorf("error generating private key bytes: %w", err)
92+
}
93+
94+
tPriv, err = crypto.ToECDSA(privB)
95+
if err != nil {
96+
return "", fmt.Errorf("error making private key: %w", err)
97+
}
98+
99+
credsDir, err := os.MkdirTemp("", "")
100+
if err != nil {
101+
return "", fmt.Errorf("error making temp dir: %w", err)
102+
}
103+
104+
pw := []byte("abc")
105+
106+
ks := keystore.NewKeyStore(credsDir, keystore.LightScryptN, keystore.LightScryptP)
107+
if err := importKeyToKeyStore(ks, tPriv, pw); err != nil {
108+
os.RemoveAll(credsDir)
109+
return "", fmt.Errorf("error making temp dir: %w", err)
110+
}
111+
112+
tCreds, err = credentialsFromKeyStore(ks, params.AllEthashProtocolChanges.ChainID)
113+
if err != nil {
114+
os.RemoveAll(credsDir)
115+
return "", fmt.Errorf("error generating credentials from keystore: %w", err)
116+
}
117+
118+
if err := importKeyToKeyStore(ks, tPriv, []byte("abc")); err != nil {
119+
os.RemoveAll(credsDir)
120+
return "", fmt.Errorf("error making temp dir: %w", err)
121+
}
122+
123+
if err := ks.Unlock(*tCreds.acct, string(pw)); err != nil {
124+
os.RemoveAll(credsDir)
125+
return "", fmt.Errorf("error unlocking keystore: %w", err)
126+
}
127+
128+
return credsDir, nil
129+
}
130+
131+
func TestMain(m *testing.M) {
132+
doIt := func() int {
133+
credsDir, err := generateTestCredentials()
134+
if err != nil {
135+
tLogger.Critical("Error generating test credentials: %v", err)
136+
return 1
137+
}
138+
defer os.RemoveAll(credsDir)
139+
return m.Run()
140+
}
141+
142+
os.Exit(doIt())
143+
}
144+
84145
type tGetTxRes struct {
85146
tx *types.Transaction
86147
height int64
@@ -120,7 +181,6 @@ type testNode struct {
120181
hdrByHash *types.Header
121182
lastSignedTx *types.Transaction
122183
sentTxs int
123-
sendTxTx *types.Transaction
124184
sendTxErr error
125185
simBackend bind.ContractBackend
126186
maxFeeRate *big.Int
@@ -244,13 +304,11 @@ func (n *testNode) signData(data []byte) (sig, pubKey []byte, err error) {
244304

245305
return sig, crypto.FromECDSAPub(&n.privKey.PublicKey), nil
246306
}
247-
func (n *testNode) sendTransaction(ctx context.Context, txOpts *bind.TransactOpts, to common.Address, data []byte, filts ...acceptabilityFilter) (*types.Transaction, error) {
248-
n.sentTxs++
249-
return n.sendTxTx, n.sendTxErr
250-
}
307+
251308
func (n *testNode) sendSignedTransaction(ctx context.Context, tx *types.Transaction, filts ...acceptabilityFilter) error {
252309
n.lastSignedTx = tx
253-
return nil
310+
n.sentTxs++
311+
return n.sendTxErr
254312
}
255313

256314
func tTx(gasFeeCap, gasTipCap, value uint64, to *common.Address, data []byte, gasLimit uint64) *types.Transaction {
@@ -694,6 +752,7 @@ func TestCheckPendingTxs(t *testing.T) {
694752
pendingTx.SubmissionTime = submissionStamp
695753
pendingTx.lastBroadcast = time.Unix(int64(submissionStamp), 0)
696754
pendingTx.lastFeeCheck = time.Unix(int64(submissionStamp), 0)
755+
pendingTx.initialSendComplete.Store(true)
697756
return pendingTx
698757
}
699758

@@ -845,17 +904,16 @@ func TestTakeAction(t *testing.T) {
845904

846905
pendingTx := eth.extendedTx(node.newTransaction(0, aGwei), asset.Send, 1, nil)
847906
eth.pendingTxs = []*extendedWalletTx{pendingTx}
848-
849-
feeCap := new(big.Int).Mul(aGwei, big.NewInt(5))
850-
tipCap := new(big.Int).Mul(aGwei, big.NewInt(2))
851-
replacementTx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{
852-
Nonce: 1,
853-
GasTipCap: tipCap,
854-
GasFeeCap: feeCap,
855-
Gas: 50_000,
856-
ChainID: node.chainConfig().ChainID,
857-
}), signer, node.privKey)
858-
node.sendTxTx = replacementTx
907+
const tipHeight = 100
908+
eth.currentTip = &types.Header{Number: big.NewInt(tipHeight)}
909+
tipRate := new(big.Int).Mul(aGwei, big.NewInt(2))
910+
c := &eth.currentFees
911+
c.Lock()
912+
c.baseRate = new(big.Int).Mul(aGwei, big.NewInt(5))
913+
c.tipRate = tipRate
914+
c.blockNum = tipHeight
915+
maxFeeRate := new(big.Int).Add(c.tipRate, new(big.Int).Mul(c.baseRate, big.NewInt(2)))
916+
c.Unlock()
859917

860918
tooCheapAction := []byte(fmt.Sprintf(`{"txID":"%s","bump":true}`, pendingTx.ID))
861919
if err := eth.TakeAction(actionTypeTooCheap, tooCheapAction); err != nil {
@@ -867,11 +925,11 @@ func TestTakeAction(t *testing.T) {
867925
t.Fatal("tx wasn't replaced")
868926
}
869927
tx, _ := newPendingTx.tx()
870-
if tx.GasFeeCap().Cmp(feeCap) != 0 {
871-
t.Fatalf("wrong fee cap. wanted %s, got %s", feeCap, tx.GasFeeCap())
928+
if tx.GasFeeCap().Cmp(maxFeeRate) != 0 {
929+
t.Fatalf("wrong fee cap. wanted %s, got %s", maxFeeRate, tx.GasFeeCap())
872930
}
873-
if tx.GasTipCap().Cmp(tipCap) != 0 {
874-
t.Fatalf("wrong tip cap. wanted %s, got %s", tipCap, tx.GasTipCap())
931+
if tx.GasTipCap().Cmp(tipRate) != 0 {
932+
t.Fatalf("wrong tip cap. wanted %s, got %s", tipRate, tx.GasTipCap())
875933
}
876934
if !newPendingTx.savedToDB {
877935
t.Fatal("didn't save to DB")
@@ -906,6 +964,9 @@ func TestTakeAction(t *testing.T) {
906964
t.Fatalf("Tx wasn't abandoned")
907965
}
908966
eth.pendingTxs = []*extendedWalletTx{pendingTx}
967+
txOpts := newTxOpts(eth.ctx, eth.addr, 1, 1, big.NewInt(1), big.NewInt(1))
968+
txOpts.Nonce = pendingTx.Nonce
969+
replacementTx, _ := eth.creds.signedTx(txOpts, eth.addr, nil)
909970
node.getTxRes = replacementTx
910971
lostNonceAction = []byte(fmt.Sprintf(`{"txID":"%s","abandon":false,"replacementID":"%s"}`, pendingTx.ID, replacementTx.Hash()))
911972
if err := eth.TakeAction(actionTypeLostNonce, lostNonceAction); err != nil {
@@ -1083,12 +1144,6 @@ func TestSyncStatus(t *testing.T) {
10831144
}
10841145

10851146
func newTestNode(assetID uint32) *tMempoolNode {
1086-
privKey, _ := crypto.HexToECDSA("9447129055a25c8496fca9e5ee1b9463e47e6043ff0c288d07169e8284860e34")
1087-
addr := common.HexToAddress("2b84C791b79Ee37De042AD2ffF1A253c3ce9bc27")
1088-
acct := &accounts.Account{
1089-
Address: addr,
1090-
}
1091-
10921147
tc := &tContractor{
10931148
gasEstimates: ethGases,
10941149
swapMap: make(map[[32]byte]*dexeth.SwapState),
@@ -1109,12 +1164,12 @@ func newTestNode(assetID uint32) *tMempoolNode {
11091164

11101165
return &tMempoolNode{
11111166
testNode: &testNode{
1112-
acct: acct,
1113-
addr: acct.Address,
1167+
acct: tCreds.acct,
1168+
addr: tCreds.acct.Address,
11141169
maxFeeRate: dexeth.GweiToWei(100),
11151170
baseFee: dexeth.GweiToWei(100),
11161171
tip: dexeth.GweiToWei(2),
1117-
privKey: privKey,
1172+
privKey: tPriv,
11181173
contractor: c,
11191174
tContractor: tc,
11201175
tokenContractor: ttc,
@@ -1163,7 +1218,8 @@ func tassetWallet(assetID uint32) (asset.Wallet, *assetWallet, *tMempoolNode, co
11631218
baseChainID: BipID,
11641219
chainID: dexeth.ChainIDs[dex.Simnet],
11651220
tokens: dexeth.Tokens,
1166-
addr: node.addr,
1221+
creds: tCreds,
1222+
addr: tCreds.addr,
11671223
net: dex.Simnet,
11681224
node: node,
11691225
ctx: ctx,
@@ -1175,6 +1231,7 @@ func tassetWallet(assetID uint32) (asset.Wallet, *assetWallet, *tMempoolNode, co
11751231
txDB: &tTxDB{},
11761232
currentTip: &types.Header{Number: new(big.Int)},
11771233
finalizeConfs: txConfsNeededToConfirm,
1234+
dontQSends: true,
11781235
},
11791236
versionedGases: versionedGases,
11801237
maxSwapGas: versionedGases[0].Swap,
@@ -4492,11 +4549,7 @@ func testSend(t *testing.T, assetID uint32) {
44924549
w, eth, node, shutdown := tassetWallet(assetID)
44934550
defer shutdown()
44944551

4495-
tx := tTx(0, 0, 0, &testAddressA, nil, 21000)
4496-
txHash := tx.Hash()
4497-
4498-
node.sendTxTx = tx
4499-
node.tokenContractor.transferTx = tx
4552+
node.tokenContractor.transferTx = tTx(0, 0, 0, &testAddressA, nil, 21000)
45004553

45014554
maxFeeRate, _, _ := eth.recommendedMaxFeeRate(eth.ctx)
45024555
ethFees := dexeth.WeiToGwei(maxFeeRate) * defaultSendGasLimit
@@ -4550,7 +4603,7 @@ func testSend(t *testing.T, assetID uint32) {
45504603
node.tokenContractor.bal = dexeth.GweiToWei(val - test.sendAdj)
45514604
node.bal = dexeth.GweiToWei(tokenFees - test.feeAdj)
45524605
}
4553-
coin, err := w.Send(test.addr, val, 0)
4606+
_, err := w.Send(test.addr, val, 0)
45544607
if test.wantErr {
45554608
if err == nil {
45564609
t.Fatalf("expected error for test %v", test.name)
@@ -4560,9 +4613,6 @@ func testSend(t *testing.T, assetID uint32) {
45604613
if err != nil {
45614614
t.Fatalf("unexpected error for test %v: %v", test.name, err)
45624615
}
4563-
if !bytes.Equal(txHash[:], coin.ID()) {
4564-
t.Fatal("coin is not the tx hash")
4565-
}
45664616
}
45674617
}
45684618

@@ -4749,9 +4799,7 @@ func testEstimateVsActualSendFees(t *testing.T, assetID uint32) {
47494799
w, _, node, shutdown := tassetWallet(assetID)
47504800
defer shutdown()
47514801

4752-
tx := tTx(0, 0, 0, &testAddressA, nil, 21000)
4753-
node.sendTxTx = tx
4754-
node.tokenContractor.transferTx = tx
4802+
node.tokenContractor.transferTx = tTx(0, 0, 0, &testAddressA, nil, 21000)
47554803

47564804
const testAddr = "dd93b447f7eBCA361805eBe056259853F3912E04"
47574805

client/asset/eth/multirpc.go

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -408,19 +408,13 @@ type multiRPCClient struct {
408408
var _ ethFetcher = (*multiRPCClient)(nil)
409409

410410
func newMultiRPCClient(
411-
dir string,
411+
creds *accountCredentials,
412412
endpoints []string,
413413
log dex.Logger,
414414
cfg *params.ChainConfig,
415415
finalizeConfs uint64,
416416
net dex.Network,
417417
) (*multiRPCClient, error) {
418-
walletDir := getWalletDir(dir, net)
419-
creds, err := pathCredentials(filepath.Join(walletDir, "keystore"))
420-
if err != nil {
421-
return nil, fmt.Errorf("error parsing credentials from %q: %w", dir, err)
422-
}
423-
424418
m := &multiRPCClient{
425419
net: net,
426420
cfg: cfg,
@@ -1317,18 +1311,8 @@ func (m *multiRPCClient) sendSignedTransaction(ctx context.Context, tx *types.Tr
13171311
return nil
13181312
}
13191313

1320-
func (m *multiRPCClient) sendTransaction(ctx context.Context, txOpts *bind.TransactOpts, to common.Address, data []byte, filts ...acceptabilityFilter) (*types.Transaction, error) {
1321-
tx, err := m.creds.ks.SignTx(*m.creds.acct, types.NewTx(&types.DynamicFeeTx{
1322-
To: &to,
1323-
ChainID: m.chainID,
1324-
Nonce: txOpts.Nonce.Uint64(),
1325-
Gas: txOpts.GasLimit,
1326-
GasFeeCap: txOpts.GasFeeCap,
1327-
GasTipCap: txOpts.GasTipCap,
1328-
Value: txOpts.Value,
1329-
Data: data,
1330-
}), m.chainID)
1331-
1314+
func (m *multiRPCClient) genSignAndSendTransaction(ctx context.Context, txOpts *bind.TransactOpts, to common.Address, data []byte, filts ...acceptabilityFilter) (*types.Transaction, error) {
1315+
tx, err := m.creds.signedTx(txOpts, to, data)
13321316
if err != nil {
13331317
return nil, fmt.Errorf("signing error: %v", err)
13341318
}

client/asset/eth/multirpc_test_util.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ func (m *MRPCTest) rpcClient(dir string, seed []byte, endpoints []string, net de
9595
return nil, fmt.Errorf("error creating wallet: %v", err)
9696
}
9797

98-
return newMultiRPCClient(dir, endpoints, log, cfg, 3, net)
98+
creds, err := walletCredentials(cfg.ChainID, dir, net)
99+
if err != nil {
100+
return nil, fmt.Errorf("error generating wallet credentials: %w", err)
101+
}
102+
return newMultiRPCClient(creds, endpoints, log, cfg, 3, net)
99103
}
100104

101105
func (m *MRPCTest) TestHTTP(t *testing.T, port string) {
@@ -157,7 +161,7 @@ func (m *MRPCTest) TestSimnetMultiRPCClient(t *testing.T, wsPort, httpPort strin
157161
if err != nil {
158162
t.Fatal(err)
159163
}
160-
if _, err := cl.sendTransaction(ctx, txOpts, alphaAddr, nil); err != nil {
164+
if _, err := cl.genSignAndSendTransaction(ctx, txOpts, alphaAddr, nil); err != nil {
161165
t.Fatalf("error sending tx %d-%d: %v", i, j, err)
162166
}
163167
}

0 commit comments

Comments
 (0)