/
simulated.go
252 lines (207 loc) · 7.39 KB
/
simulated.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package simulated
import (
"context"
"errors"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/params"
"github.com/ipfs/go-log"
"github.com/lmittmann/w3/w3types"
"github.com/stretchr/testify/assert"
commonBackend "github.com/synapsecns/sanguine/ethergo/backends"
"github.com/synapsecns/sanguine/ethergo/backends/base"
"github.com/synapsecns/sanguine/ethergo/backends/simulated/multibackend"
"github.com/synapsecns/sanguine/ethergo/chain"
"github.com/synapsecns/sanguine/ethergo/chain/client"
"github.com/synapsecns/sanguine/ethergo/signer/nonce"
"github.com/teivah/onecontext"
"math/big"
"testing"
"time"
)
var logger = log.Logger("simulated-logger")
// Backend is the simulated backend.
type Backend struct {
t *testing.T
// base backend is the base backend
*base.Backend
// backend handles commits
simulatedBackend *multibackend.SimulatedBackend
// faucetAddr is the address funde at genesis
faucetAddr *keystore.Key
// gasLimit is the block gas limit
gasLimit uint64
// chainConfig is the chainConfig for this chain
chainConfig *params.ChainConfig
}
func (s *Backend) BatchWithContext(_ context.Context, _ ...w3types.Caller) error {
return errors.New("rpc calls not supported on simulated backend")
}
// Signer gets the signer for the backend.
func (s *Backend) Signer() types.Signer {
latestBlock, err := s.BlockByNumber(s.Context(), nil)
assert.Nil(s.T(), err)
return types.MakeSigner(s.chainConfig, latestBlock.Number())
}
// T gets the testing object for the backend.
func (s *Backend) T() *testing.T {
s.t.Helper()
return s.t
}
// SetT sets the testing object on the backend.
func (s *Backend) SetT(t *testing.T) {
t.Helper()
s.t = t
}
// BackendName is the name of the simulated backend.
const BackendName = "SimulatedBackend"
// BackendName gets the name of SimulatedBackend.
func (s *Backend) BackendName() string {
return BackendName
}
// Commit commits pending txes to the backend. Does not thing if no txes are pending.
func (s *Backend) Commit() {
s.simulatedBackend.Commit()
}
// EmptyBlock mines an empty block.
func (s *Backend) EmptyBlock(blockTime time.Time) {
s.simulatedBackend.EmptyBlock(blockTime)
}
// AdjustTime adjusts the time of the most recent block.
func (s *Backend) AdjustTime(adjustment time.Duration) error {
//nolint: wrapcheck
return s.simulatedBackend.AdjustTime(adjustment)
}
// getFaucetTxContext gets a signed transaction from the faucet address.
func (s *Backend) getFaucetTxContext(ctx context.Context) *bind.TransactOpts {
auth, err := bind.NewKeyedTransactorWithChainID(s.faucetAddr.PrivateKey, s.Chain.GetBigChainID())
assert.Nil(s.T(), err)
//nolint: ineffassign
gasPrice, err := s.SuggestGasPrice(ctx)
assert.Nil(s.T(), err)
// standard eth value tx price
auth.GasLimit = 21000
auth.GasPrice = gasPrice
return auth
}
// faucetSignTx will sign a tx with the faucet addr.
func (s *Backend) faucetSignTx(tx *types.Transaction) *types.Transaction {
tx, err := s.SignTx(tx, s.Signer(), s.faucetAddr.PrivateKey)
assert.Nil(s.T(), err)
return tx
}
// ChainConfig gets the chain config for the simulated backend.
func (s *Backend) ChainConfig() *params.ChainConfig {
return s.chainConfig
}
// GetFundedAccount returns an account with the requested balance. (Note: if genesis acount has an insufficient
// balance, blocks may be mined here).
func (s *Backend) GetFundedAccount(ctx context.Context, requestBalance *big.Int) *keystore.Key {
key := s.MockAccount()
s.Store(key)
s.FundAccount(ctx, key.Address, *requestBalance)
return key
}
// FundAccount fundsa new account.
func (s *Backend) FundAccount(ctx context.Context, address common.Address, amount big.Int) {
rawTx := s.getFaucetTxContext(ctx)
tx := s.faucetSignTx(types.NewTx(&types.LegacyTx{
To: &address,
Value: &amount,
Gas: rawTx.GasLimit,
GasPrice: rawTx.GasPrice,
}))
assert.Nil(s.T(), s.SendTransaction(ctx, tx))
s.Commit()
// wait for tx confirmation
s.WaitForConfirmation(ctx, tx)
}
// SendTransaction sends a transaction and commits it mining a new block
// in cases where you would not like to commit automatically, you can run
// s.Client().SendTransaction().
func (s *Backend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
err := s.Client().SendTransaction(ctx, tx)
s.Commit()
//nolint: wrapcheck
return err
}
// GetTxContext gets a signed transaction from full backend.
func (s *Backend) GetTxContext(ctx context.Context, address *common.Address) (res commonBackend.AuthType) {
ctx, cancel := onecontext.Merge(ctx, s.Context())
defer cancel()
var acct *keystore.Key
// TODO handle storing accounts to conform to get tx context
if address != nil {
acct = s.GetAccount(*address)
if acct == nil {
s.T().Errorf("could not get account %s", address.String())
return res
}
} else {
acct = s.GetFundedAccount(ctx, big.NewInt(0).Mul(big.NewInt(params.Ether), big.NewInt(10)))
s.Store(acct)
}
auth, err := s.NewKeyedTransactorFromKey(acct.PrivateKey)
assert.Nil(s.T(), err)
gasBlock, err := s.BlockByNumber(ctx, nil)
assert.Nil(s.T(), err)
//nolint: ineffassign
err = s.Chain.GasSetter().SetGasFee(ctx, auth, gasBlock.NumberU64(), gasprice.DefaultMaxPrice)
assert.Nil(s.T(), err)
return commonBackend.AuthType{
TransactOpts: auth,
PrivateKey: acct.PrivateKey,
}
}
// BlockGasLimit is the gas limit used for the block.
const BlockGasLimit = uint64(91712388)
// NewSimulatedBackend gets a simulated backend from geth for testing and creates an account
// with balance. ChainID is 1337.
func NewSimulatedBackend(ctx context.Context, t *testing.T) *Backend {
t.Helper()
return NewSimulatedBackendWithChainID(ctx, t, params.AllEthashProtocolChanges.ChainID)
}
// NewSimulatedBackendWithChainID gets a simulated backend from geth for testing and creates an account
// with balance.
func NewSimulatedBackendWithChainID(ctx context.Context, t *testing.T, chainID *big.Int) *Backend {
t.Helper()
return NewSimulatedBackendWithConfig(ctx, t, multibackend.NewConfigWithChainID(chainID))
}
// NewSimulatedBackendWithConfig gets a simulated backend from geth for testing and creates an account
// with balance.
func NewSimulatedBackendWithConfig(ctx context.Context, t *testing.T, config *params.ChainConfig) *Backend {
t.Helper()
// 100 million ether
balance := big.NewInt(0).Mul(big.NewInt(params.Ether), big.NewInt(100000000))
key := base.MockAccount(t)
genesisAlloc := map[common.Address]core.GenesisAccount{
key.Address: {
Balance: balance,
},
}
simulatedBackend := multibackend.NewSimulatedBackendWithConfig(genesisAlloc, BlockGasLimit, config)
baseClient := Client{simulatedBackend}
chn, err := chain.NewFromClient(ctx, &client.Config{
RPCUrl: []string{""},
ChainID: int(config.ChainID.Uint64()),
}, baseClient)
chn.SetChainConfig(config)
assert.Nil(t, err)
baseBackend, err := base.NewBaseBackend(ctx, t, chn)
assert.Nil(t, err)
backend := Backend{
Backend: baseBackend,
simulatedBackend: simulatedBackend,
chainConfig: config,
}
backend.SetT(t)
backend.Manager = nonce.NewNonceManager(ctx, &backend, backend.GetBigChainID())
backend.faucetAddr = key
backend.gasLimit = BlockGasLimit
return &backend
}
var _ commonBackend.SimulatedTestBackend = &Backend{}