-
Notifications
You must be signed in to change notification settings - Fork 0
/
node_info.go
354 lines (297 loc) · 11.5 KB
/
node_info.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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
// Copyright 2021 Evmos Foundation
// This file is part of Evmos' Ethermint library.
//
// The Ethermint library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The Ethermint library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the Ethermint library. If not, see https://github.com/decimalteam/ethermint/blob/main/LICENSE
package backend
import (
"fmt"
"math/big"
"time"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdkcrypto "github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdkconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
"github.com/decimalteam/ethermint/crypto/ethsecp256k1"
rpctypes "github.com/decimalteam/ethermint/rpc/types"
"github.com/decimalteam/ethermint/server/config"
ethermint "github.com/decimalteam/ethermint/types"
evmtypes "github.com/decimalteam/ethermint/x/evm/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
tmtypes "github.com/tendermint/tendermint/types"
)
// Accounts returns the list of accounts available to this node.
func (b *Backend) Accounts() ([]common.Address, error) {
addresses := make([]common.Address, 0) // return [] instead of nil if empty
infos, err := b.clientCtx.Keyring.List()
if err != nil {
return addresses, err
}
for _, info := range infos {
pubKey, err := info.GetPubKey()
if err != nil {
return nil, err
}
addressBytes := pubKey.Address().Bytes()
addresses = append(addresses, common.BytesToAddress(addressBytes))
}
return addresses, nil
}
// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not
// yet received the latest block headers from its pears. In case it is synchronizing:
// - startingBlock: block number this node started to synchronize from
// - currentBlock: block number this node is currently importing
// - highestBlock: block number of the highest block header this node has received from peers
// - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled
func (b *Backend) Syncing() (interface{}, error) {
status, err := b.clientCtx.Client.Status(b.ctx)
if err != nil {
return false, err
}
if !status.SyncInfo.CatchingUp {
return false, nil
}
return map[string]interface{}{
"startingBlock": hexutil.Uint64(status.SyncInfo.EarliestBlockHeight),
"currentBlock": hexutil.Uint64(status.SyncInfo.LatestBlockHeight),
// "highestBlock": nil, // NA
// "pulledStates": nil, // NA
// "knownStates": nil, // NA
}, nil
}
// SetEtherbase sets the etherbase of the miner
func (b *Backend) SetEtherbase(etherbase common.Address) bool {
delAddr, err := b.GetCoinbase()
if err != nil {
b.logger.Debug("failed to get coinbase address", "error", err.Error())
return false
}
withdrawAddr := sdk.AccAddress(etherbase.Bytes())
msg := distributiontypes.NewMsgSetWithdrawAddress(delAddr, withdrawAddr)
if err := msg.ValidateBasic(); err != nil {
b.logger.Debug("tx failed basic validation", "error", err.Error())
return false
}
// Assemble transaction from fields
builder, ok := b.clientCtx.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
if !ok {
b.logger.Debug("clientCtx.TxConfig.NewTxBuilder returns unsupported builder", "error", err.Error())
return false
}
err = builder.SetMsgs(msg)
if err != nil {
b.logger.Error("builder.SetMsgs failed", "error", err.Error())
return false
}
// Fetch minimun gas price to calculate fees using the configuration.
minGasPrices := b.cfg.GetMinGasPrices()
if len(minGasPrices) == 0 || minGasPrices.Empty() {
b.logger.Debug("the minimun fee is not set")
return false
}
minGasPriceValue := minGasPrices[0].Amount
denom := minGasPrices[0].Denom
delCommonAddr := common.BytesToAddress(delAddr.Bytes())
nonce, err := b.GetTransactionCount(delCommonAddr, rpctypes.EthPendingBlockNumber)
if err != nil {
b.logger.Debug("failed to get nonce", "error", err.Error())
return false
}
txFactory := tx.Factory{}
txFactory = txFactory.
WithChainID(b.clientCtx.ChainID).
WithKeybase(b.clientCtx.Keyring).
WithTxConfig(b.clientCtx.TxConfig).
WithSequence(uint64(*nonce)).
WithGasAdjustment(1.25)
_, gas, err := tx.CalculateGas(b.clientCtx, txFactory, msg)
if err != nil {
b.logger.Debug("failed to calculate gas", "error", err.Error())
return false
}
txFactory = txFactory.WithGas(gas)
value := new(big.Int).SetUint64(gas * minGasPriceValue.Ceil().TruncateInt().Uint64())
fees := sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(value))}
builder.SetFeeAmount(fees)
builder.SetGasLimit(gas)
keyInfo, err := b.clientCtx.Keyring.KeyByAddress(delAddr)
if err != nil {
b.logger.Debug("failed to get the wallet address using the keyring", "error", err.Error())
return false
}
if err := tx.Sign(txFactory, keyInfo.Name, builder, false); err != nil {
b.logger.Debug("failed to sign tx", "error", err.Error())
return false
}
// Encode transaction by default Tx encoder
txEncoder := b.clientCtx.TxConfig.TxEncoder()
txBytes, err := txEncoder(builder.GetTx())
if err != nil {
b.logger.Debug("failed to encode eth tx using default encoder", "error", err.Error())
return false
}
tmHash := common.BytesToHash(tmtypes.Tx(txBytes).Hash())
// Broadcast transaction in sync mode (default)
// NOTE: If error is encountered on the node, the broadcast will not return an error
syncCtx := b.clientCtx.WithBroadcastMode(flags.BroadcastSync)
rsp, err := syncCtx.BroadcastTx(txBytes)
if rsp != nil && rsp.Code != 0 {
err = errorsmod.ABCIError(rsp.Codespace, rsp.Code, rsp.RawLog)
}
if err != nil {
b.logger.Debug("failed to broadcast tx", "error", err.Error())
return false
}
b.logger.Debug("broadcasted tx to set miner withdraw address (etherbase)", "hash", tmHash.String())
return true
}
// ImportRawKey armors and encrypts a given raw hex encoded ECDSA key and stores it into the key directory.
// The name of the key will have the format "personal_<length-keys>", where <length-keys> is the total number of
// keys stored on the keyring.
//
// NOTE: The key will be both armored and encrypted using the same passphrase.
func (b *Backend) ImportRawKey(privkey, password string) (common.Address, error) {
priv, err := crypto.HexToECDSA(privkey)
if err != nil {
return common.Address{}, err
}
privKey := ðsecp256k1.PrivKey{Key: crypto.FromECDSA(priv)}
addr := sdk.AccAddress(privKey.PubKey().Address().Bytes())
ethereumAddr := common.BytesToAddress(addr)
// return if the key has already been imported
if _, err := b.clientCtx.Keyring.KeyByAddress(addr); err == nil {
return ethereumAddr, nil
}
// ignore error as we only care about the length of the list
list, _ := b.clientCtx.Keyring.List()
privKeyName := fmt.Sprintf("personal_%d", len(list))
armor := sdkcrypto.EncryptArmorPrivKey(privKey, password, ethsecp256k1.KeyType)
if err := b.clientCtx.Keyring.ImportPrivKey(privKeyName, armor, password); err != nil {
return common.Address{}, err
}
b.logger.Info("key successfully imported", "name", privKeyName, "address", ethereumAddr.String())
return ethereumAddr, nil
}
// ListAccounts will return a list of addresses for accounts this node manages.
func (b *Backend) ListAccounts() ([]common.Address, error) {
addrs := []common.Address{}
list, err := b.clientCtx.Keyring.List()
if err != nil {
return nil, err
}
for _, info := range list {
pubKey, err := info.GetPubKey()
if err != nil {
return nil, err
}
addrs = append(addrs, common.BytesToAddress(pubKey.Address()))
}
return addrs, nil
}
// NewAccount will create a new account and returns the address for the new account.
func (b *Backend) NewMnemonic(uid string,
_ keyring.Language,
hdPath,
bip39Passphrase string,
algo keyring.SignatureAlgo,
) (*keyring.Record, error) {
info, _, err := b.clientCtx.Keyring.NewMnemonic(uid, keyring.English, hdPath, bip39Passphrase, algo)
if err != nil {
return nil, err
}
return info, err
}
// SetGasPrice sets the minimum accepted gas price for the miner.
// NOTE: this function accepts only integers to have the same interface than go-eth
// to use float values, the gas prices must be configured using the configuration file
func (b *Backend) SetGasPrice(gasPrice hexutil.Big) bool {
appConf, err := config.GetConfig(b.clientCtx.Viper)
if err != nil {
b.logger.Debug("could not get the server config", "error", err.Error())
return false
}
var unit string
minGasPrices := appConf.GetMinGasPrices()
// fetch the base denom from the sdk Config in case it's not currently defined on the node config
if len(minGasPrices) == 0 || minGasPrices.Empty() {
var err error
unit, err = sdk.GetBaseDenom()
if err != nil {
b.logger.Debug("could not get the denom of smallest unit registered", "error", err.Error())
return false
}
} else {
unit = minGasPrices[0].Denom
}
c := sdk.NewDecCoin(unit, sdk.NewIntFromBigInt(gasPrice.ToInt()))
appConf.SetMinGasPrices(sdk.DecCoins{c})
sdkconfig.WriteConfigFile(b.clientCtx.Viper.ConfigFileUsed(), appConf)
b.logger.Info("Your configuration file was modified. Please RESTART your node.", "gas-price", c.String())
return true
}
// UnprotectedAllowed returns the node configuration value for allowing
// unprotected transactions (i.e not replay-protected)
func (b Backend) UnprotectedAllowed() bool {
return b.allowUnprotectedTxs
}
// RPCGasCap is the global gas cap for eth-call variants.
func (b *Backend) RPCGasCap() uint64 {
return b.cfg.JSONRPC.GasCap
}
// RPCEVMTimeout is the global evm timeout for eth-call variants.
func (b *Backend) RPCEVMTimeout() time.Duration {
return b.cfg.JSONRPC.EVMTimeout
}
// RPCGasCap is the global gas cap for eth-call variants.
func (b *Backend) RPCTxFeeCap() float64 {
return b.cfg.JSONRPC.TxFeeCap
}
// RPCFilterCap is the limit for total number of filters that can be created
func (b *Backend) RPCFilterCap() int32 {
return b.cfg.JSONRPC.FilterCap
}
// RPCFeeHistoryCap is the limit for total number of blocks that can be fetched
func (b *Backend) RPCFeeHistoryCap() int32 {
return b.cfg.JSONRPC.FeeHistoryCap
}
// RPCLogsCap defines the max number of results can be returned from single `eth_getLogs` query.
func (b *Backend) RPCLogsCap() int32 {
return b.cfg.JSONRPC.LogsCap
}
// RPCBlockRangeCap defines the max block range allowed for `eth_getLogs` query.
func (b *Backend) RPCBlockRangeCap() int32 {
return b.cfg.JSONRPC.BlockRangeCap
}
// RPCMinGasPrice returns the minimum gas price for a transaction obtained from
// the node config. If set value is 0, it will default to 20.
func (b *Backend) RPCMinGasPrice() int64 {
evmParams, err := b.queryClient.Params(b.ctx, &evmtypes.QueryParamsRequest{})
if err != nil {
return ethermint.DefaultGasPrice
}
minGasPrice := b.cfg.GetMinGasPrices()
amt := minGasPrice.AmountOf(evmParams.Params.EvmDenom).TruncateInt64()
if amt == 0 {
return ethermint.DefaultGasPrice
}
return amt
}