-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
attempts.go
185 lines (164 loc) · 6 KB
/
attempts.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package txmgr
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/smartcontractkit/chainlink/core/assets"
"github.com/smartcontractkit/chainlink/core/chains/evm/gas"
)
func (c *ChainKeyStore) NewDynamicFeeAttempt(etx EthTx, fee gas.DynamicFee, gasLimit uint32) (attempt EthTxAttempt, err error) {
if err = validateDynamicFeeGas(c.config, fee, gasLimit, etx); err != nil {
return attempt, errors.Wrap(err, "error validating gas")
}
var al types.AccessList
if etx.AccessList.Valid {
al = etx.AccessList.AccessList
}
d := newDynamicFeeTransaction(
uint64(*etx.Nonce),
etx.ToAddress,
&etx.Value,
gasLimit,
&c.chainID,
fee.TipCap,
fee.FeeCap,
etx.EncodedPayload,
al,
)
tx := types.NewTx(&d)
attempt, err = c.newSignedAttempt(etx, tx)
if err != nil {
return attempt, err
}
attempt.GasTipCap = fee.TipCap
attempt.GasFeeCap = fee.FeeCap
attempt.ChainSpecificGasLimit = gasLimit
attempt.TxType = 2
return attempt, nil
}
var Max256BitUInt = big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil)
// validateDynamicFeeGas is a sanity check - we have other checks elsewhere, but this
// makes sure we _never_ create an invalid attempt
func validateDynamicFeeGas(cfg Config, fee gas.DynamicFee, gasLimit uint32, etx EthTx) error {
gasTipCap, gasFeeCap := fee.TipCap, fee.FeeCap
if gasTipCap == nil {
panic("gas tip cap missing")
}
if gasFeeCap == nil {
panic("gas fee cap missing")
}
// Assertions from: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md
// Prevent impossibly large numbers
if gasFeeCap.ToInt().Cmp(Max256BitUInt) > 0 {
return errors.New("impossibly large fee cap")
}
if gasTipCap.ToInt().Cmp(Max256BitUInt) > 0 {
return errors.New("impossibly large tip cap")
}
// The total must be at least as large as the tip
if gasFeeCap.Cmp(gasTipCap) < 0 {
return errors.Errorf("gas fee cap must be greater than or equal to gas tip cap (fee cap: %s, tip cap: %s)", gasFeeCap.String(), gasTipCap.String())
}
// Configuration sanity-check
max := cfg.KeySpecificMaxGasPriceWei(etx.FromAddress)
if gasFeeCap.Cmp(max) > 0 {
return errors.Errorf("cannot create tx attempt: specified gas fee cap of %s would exceed max configured gas price of %s for key %s", gasFeeCap.String(), max.String(), etx.FromAddress.Hex())
}
// Tip must be above minimum
minTip := cfg.EvmGasTipCapMinimum()
if gasTipCap.Cmp(minTip) < 0 {
return errors.Errorf("cannot create tx attempt: specified gas tip cap of %s is below min configured gas tip of %s for key %s", gasTipCap.String(), minTip.String(), etx.FromAddress.Hex())
}
return nil
}
func newDynamicFeeTransaction(nonce uint64, to common.Address, value *assets.Eth, gasLimit uint32, chainID *big.Int, gasTipCap, gasFeeCap *assets.Wei, data []byte, accessList types.AccessList) types.DynamicFeeTx {
return types.DynamicFeeTx{
ChainID: chainID,
Nonce: nonce,
GasTipCap: gasTipCap.ToInt(),
GasFeeCap: gasFeeCap.ToInt(),
Gas: uint64(gasLimit),
To: &to,
Value: value.ToInt(),
Data: data,
AccessList: accessList,
}
}
func (c *ChainKeyStore) NewLegacyAttempt(etx EthTx, gasPrice *assets.Wei, gasLimit uint32) (attempt EthTxAttempt, err error) {
if err = validateLegacyGas(c.config, gasPrice, gasLimit, etx); err != nil {
return attempt, errors.Wrap(err, "error validating gas")
}
tx := newLegacyTransaction(
uint64(*etx.Nonce),
etx.ToAddress,
etx.Value.ToInt(),
gasLimit,
gasPrice,
etx.EncodedPayload,
)
transaction := types.NewTx(&tx)
hash, signedTxBytes, err := c.SignTx(etx.FromAddress, transaction)
if err != nil {
return attempt, errors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress.String(), etx.ID)
}
attempt.State = EthTxAttemptInProgress
attempt.SignedRawTx = signedTxBytes
attempt.EthTxID = etx.ID
attempt.GasPrice = gasPrice
attempt.Hash = hash
attempt.TxType = 0
attempt.ChainSpecificGasLimit = gasLimit
attempt.EthTx = etx
return attempt, nil
}
// validateLegacyGas is a sanity check - we have other checks elsewhere, but this
// makes sure we _never_ create an invalid attempt
func validateLegacyGas(cfg Config, gasPrice *assets.Wei, gasLimit uint32, etx EthTx) error {
if gasPrice == nil {
panic("gas price missing")
}
max := cfg.KeySpecificMaxGasPriceWei(etx.FromAddress)
if gasPrice.Cmp(max) > 0 {
return errors.Errorf("cannot create tx attempt: specified gas price of %s would exceed max configured gas price of %s for key %s", gasPrice.String(), max.String(), etx.FromAddress.Hex())
}
min := cfg.EvmMinGasPriceWei()
if gasPrice.Cmp(min) < 0 {
return errors.Errorf("cannot create tx attempt: specified gas price of %s is below min configured gas price of %s for key %s", gasPrice.String(), min.String(), etx.FromAddress.Hex())
}
return nil
}
func (c *ChainKeyStore) newSignedAttempt(etx EthTx, tx *types.Transaction) (attempt EthTxAttempt, err error) {
hash, signedTxBytes, err := c.signTx(etx.FromAddress, tx)
if err != nil {
return attempt, errors.Wrapf(err, "error using account %s to sign transaction %v", etx.FromAddress.String(), etx.ID)
}
attempt.State = EthTxAttemptInProgress
attempt.SignedRawTx = signedTxBytes
attempt.EthTxID = etx.ID
attempt.EthTx = etx
attempt.Hash = hash
return attempt, nil
}
func newLegacyTransaction(nonce uint64, to common.Address, value *big.Int, gasLimit uint32, gasPrice *assets.Wei, data []byte) types.LegacyTx {
return types.LegacyTx{
Nonce: nonce,
To: &to,
Value: value,
Gas: uint64(gasLimit),
GasPrice: gasPrice.ToInt(),
Data: data,
}
}
func (c *ChainKeyStore) signTx(address common.Address, tx *types.Transaction) (common.Hash, []byte, error) {
signedTx, err := c.keystore.SignTx(address, tx, &c.chainID)
if err != nil {
return common.Hash{}, nil, errors.Wrap(err, "signTx failed")
}
rlp := new(bytes.Buffer)
if err := signedTx.EncodeRLP(rlp); err != nil {
return common.Hash{}, nil, errors.Wrap(err, "signTx failed")
}
return signedTx.Hash(), rlp.Bytes(), nil
}