arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/eth/ethereum.go
eth/ethereum.goRaw
  1package eth
  2
  3import (
  4	"context"
  5	"fmt"
  6	"math/big"
  7	"time"
  8
  9	"github.com/ethereum/go-ethereum/accounts"
 10	"github.com/ethereum/go-ethereum/accounts/abi/bind"
 11	ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
 12	ethCommon "github.com/ethereum/go-ethereum/common"
 13	"github.com/ethereum/go-ethereum/core/types"
 14	"github.com/ethereum/go-ethereum/ethclient"
 15	"github.com/hermeznetwork/hermez-node/common"
 16	HEZ "github.com/hermeznetwork/hermez-node/eth/contracts/tokenHEZ"
 17	"github.com/hermeznetwork/hermez-node/log"
 18	"github.com/hermeznetwork/tracerr"
 19)
 20
 21// ERC20Consts are the constants defined in a particular ERC20 Token instance
 22type ERC20Consts struct {
 23	Name     string
 24	Symbol   string
 25	Decimals uint64
 26}
 27
 28// EthereumInterface is the interface to Ethereum
 29type EthereumInterface interface {
 30	EthLastBlock() (int64, error)
 31	// EthHeaderByNumber(context.Context, *big.Int) (*types.Header, error)
 32	EthBlockByNumber(context.Context, int64) (*common.Block, error)
 33	EthAddress() (*ethCommon.Address, error)
 34	EthTransactionReceipt(context.Context, ethCommon.Hash) (*types.Receipt, error)
 35
 36	EthERC20Consts(ethCommon.Address) (*ERC20Consts, error)
 37	EthChainID() (*big.Int, error)
 38}
 39
 40var (
 41	// ErrAccountNil is used when the calls can not be made because the account is nil
 42	ErrAccountNil = fmt.Errorf("Authorized calls can't be made when the account is nil")
 43	// ErrBlockHashMismatchEvent is used when there's a block hash mismatch
 44	// beetween different events of the same block
 45	ErrBlockHashMismatchEvent = fmt.Errorf("block hash mismatch in event log")
 46)
 47
 48const (
 49	// default values
 50	defaultCallGasLimit = 300000
 51	defaultGasPriceDiv  = 100
 52)
 53
 54// EthereumConfig defines the configuration parameters of the EthereumClient
 55type EthereumConfig struct {
 56	CallGasLimit uint64
 57	GasPriceDiv  uint64
 58}
 59
 60// EthereumClient is an ethereum client to call Smart Contract methods and check blockchain information.
 61type EthereumClient struct {
 62	client  *ethclient.Client
 63	chainID *big.Int
 64	account *accounts.Account
 65	ks      *ethKeystore.KeyStore
 66	config  *EthereumConfig
 67	opts    *bind.CallOpts
 68}
 69
 70// NewEthereumClient creates a EthereumClient instance.  The account is not mandatory (it can
 71// be nil).  If the account is nil, CallAuth will fail with ErrAccountNil.
 72func NewEthereumClient(client *ethclient.Client, account *accounts.Account, ks *ethKeystore.KeyStore, config *EthereumConfig) (*EthereumClient, error) {
 73	if config == nil {
 74		config = &EthereumConfig{
 75			CallGasLimit: defaultCallGasLimit,
 76			GasPriceDiv:  defaultGasPriceDiv,
 77		}
 78	}
 79	c := &EthereumClient{
 80		client:  client,
 81		account: account,
 82		ks:      ks,
 83		config:  config,
 84		opts:    newCallOpts(),
 85	}
 86	chainID, err := c.EthChainID()
 87	if err != nil {
 88		return nil, tracerr.Wrap(err)
 89	}
 90	c.chainID = chainID
 91	return c, nil
 92}
 93
 94// EthChainID returns the ChainID of the ethereum network
 95func (c *EthereumClient) EthChainID() (*big.Int, error) {
 96	chainID, err := c.client.ChainID(context.Background())
 97	if err != nil {
 98		return nil, tracerr.Wrap(err)
 99	}
100	return chainID, nil
101}
102
103// BalanceAt retieves information about the default account
104func (c *EthereumClient) BalanceAt(addr ethCommon.Address) (*big.Int, error) {
105	return c.client.BalanceAt(context.TODO(), addr, nil)
106}
107
108// Account returns the underlying ethereum account
109func (c *EthereumClient) Account() *accounts.Account {
110	return c.account
111}
112
113// EthAddress returns the ethereum address of the account loaded into the EthereumClient
114func (c *EthereumClient) EthAddress() (*ethCommon.Address, error) {
115	if c.account == nil {
116		return nil, tracerr.Wrap(ErrAccountNil)
117	}
118	return &c.account.Address, nil
119}
120
121// CallAuth performs a Smart Contract method call that requires authorization.
122// This call requires a valid account with Ether that can be spend during the
123// call.
124func (c *EthereumClient) CallAuth(gasLimit uint64,
125	fn func(*ethclient.Client, *bind.TransactOpts) (*types.Transaction, error)) (*types.Transaction, error) {
126	if c.account == nil {
127		return nil, tracerr.Wrap(ErrAccountNil)
128	}
129
130	gasPrice, err := c.client.SuggestGasPrice(context.Background())
131	if err != nil {
132		return nil, tracerr.Wrap(err)
133	}
134	inc := new(big.Int).Set(gasPrice)
135	inc.Div(inc, new(big.Int).SetUint64(c.config.GasPriceDiv))
136	gasPrice.Add(gasPrice, inc)
137	log.Debugw("Transaction metadata", "gasPrice", gasPrice)
138
139	auth, err := bind.NewKeyStoreTransactorWithChainID(c.ks, *c.account, c.chainID)
140	if err != nil {
141		return nil, tracerr.Wrap(err)
142	}
143	auth.Value = big.NewInt(0) // in wei
144	if gasLimit == 0 {
145		auth.GasLimit = c.config.CallGasLimit // in units
146	} else {
147		auth.GasLimit = gasLimit // in units
148	}
149	auth.GasPrice = gasPrice
150
151	tx, err := fn(c.client, auth)
152	if tx != nil {
153		log.Debugw("Transaction", "tx", tx.Hash().Hex(), "nonce", tx.Nonce())
154	}
155	return tx, tracerr.Wrap(err)
156}
157
158// ContractData contains the contract data
159type ContractData struct {
160	Address ethCommon.Address
161	Tx      *types.Transaction
162	Receipt *types.Receipt
163}
164
165// Call performs a read only Smart Contract method call.
166func (c *EthereumClient) Call(fn func(*ethclient.Client) error) error {
167	return fn(c.client)
168}
169
170// EthTransactionReceipt returns the transaction receipt of the given txHash
171func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
172	return c.client.TransactionReceipt(ctx, txHash)
173}
174
175// EthLastBlock returns the last block number in the blockchain
176func (c *EthereumClient) EthLastBlock() (int64, error) {
177	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
178	defer cancel()
179	header, err := c.client.HeaderByNumber(ctx, nil)
180	if err != nil {
181		return 0, tracerr.Wrap(err)
182	}
183	return header.Number.Int64(), nil
184}
185
186// EthHeaderByNumber internally calls ethclient.Client HeaderByNumber
187// func (c *EthereumClient) EthHeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
188// 	return c.client.HeaderByNumber(ctx, number)
189// }
190
191// EthBlockByNumber internally calls ethclient.Client BlockByNumber and returns
192// *common.Block.  If number == -1, the latests known block is returned.
193func (c *EthereumClient) EthBlockByNumber(ctx context.Context, number int64) (*common.Block, error) {
194	blockNum := big.NewInt(number)
195	if number == -1 {
196		blockNum = nil
197	}
198	block, err := c.client.BlockByNumber(ctx, blockNum)
199	if err != nil {
200		return nil, tracerr.Wrap(err)
201	}
202	b := &common.Block{
203		Num:        block.Number().Int64(),
204		Timestamp:  time.Unix(int64(block.Time()), 0),
205		ParentHash: block.ParentHash(),
206		Hash:       block.Hash(),
207	}
208	return b, nil
209}
210
211// EthERC20Consts returns the constants defined for a particular ERC20 Token instance.
212func (c *EthereumClient) EthERC20Consts(tokenAddress ethCommon.Address) (*ERC20Consts, error) {
213	// We use the HEZ token smart contract interfacehere because it's an
214	// ERC20, which allows us to access the standard ERC20 constants.
215	instance, err := HEZ.NewHEZ(tokenAddress, c.client)
216	if err != nil {
217		return nil, tracerr.Wrap(err)
218	}
219	name, err := instance.Name(c.opts)
220	if err != nil {
221		return nil, tracerr.Wrap(err)
222	}
223
224	symbol, err := instance.Symbol(c.opts)
225	if err != nil {
226		return nil, tracerr.Wrap(err)
227	}
228
229	decimals, err := instance.Decimals(c.opts)
230	if err != nil {
231		return nil, tracerr.Wrap(err)
232	}
233	return &ERC20Consts{
234		Name:     name,
235		Symbol:   symbol,
236		Decimals: uint64(decimals),
237	}, nil
238}
239
240// Client returns the internal ethclient.Client
241func (c *EthereumClient) Client() *ethclient.Client {
242	return c.client
243}
244
245// newCallOpts returns a CallOpts to be used in ethereum calls with a non-zero
246// From address.  This is a workaround for a bug in ethereumjs-vm that shows up
247// in ganache: https://github.com/hermeznetwork/hermez-node/issues/317
248func newCallOpts() *bind.CallOpts {
249	return &bind.CallOpts{
250		From: ethCommon.HexToAddress("0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"),
251	}
252}