Browse Source

Add config flag to enable/disable meddler logs

- In config, add Debug.MeddlerLogs to enable meddler debug logs.  This only
  affects the node started via cli, tests swill ran with `meddler.Debug = true`
- Remove unused ethereum client functions and config parameters
- Document all the config parameters
feature/sql-semaphore1
Eduard S 3 years ago
parent
commit
f5d81ca8f8
6 changed files with 142 additions and 152 deletions
  1. +37
    -0
      cli/node/README.md
  2. +1
    -3
      cli/node/cfg.buidler.toml
  3. +81
    -27
      config/config.go
  4. +2
    -5
      coordinator/coordinator_test.go
  5. +17
    -112
      eth/ethereum.go
  6. +4
    -5
      node/node.go

+ 37
- 0
cli/node/README.md

@ -0,0 +1,37 @@
# node cli
This is the main cli for the node
## Usage
```
NAME:
hermez-node - A new cli application
USAGE:
node [global options] command [command options] [arguments...]
VERSION:
0.1.0-alpha
COMMANDS:
importkey Import ethereum private key
wipesql Wipe the SQL DB (HistoryDB and L2DB), leaving the DB in a clean state
run Run the hermez-node in the indicated mode
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--mode MODE Set node MODE (can be "sync" or "coord")
--cfg FILE Node configuration FILE
--help, -h show help (default: false)
--version, -v print the version (default: false)
```
## Configuration
You can find a testing working configuration example at
[cfg.buidler.toml](./cfg.buidler.toml)
To read the documentation of each configuration parameter, please check the
`type Node` and `type Coordinator` at
[config/config.go](../../config/config.go)

+ 1
- 3
cli/node/cfg.buidler.toml

@ -11,6 +11,7 @@ Type = "bitfinexV2"
[Debug] [Debug]
APIAddress = "localhost:12345" APIAddress = "localhost:12345"
MeddlerLogs = true
[StateDB] [StateDB]
Path = "/tmp/iden3-test/hermez/statedb" Path = "/tmp/iden3-test/hermez/statedb"
@ -68,9 +69,6 @@ MaxTx = 512
NLevels = 32 NLevels = 32
[Coordinator.EthClient] [Coordinator.EthClient]
CallGasLimit = 300000
DeployGasLimit = 1000000
GasPriceDiv = 100
ReceiptTimeout = "60s" ReceiptTimeout = "60s"
ReceiptLoopInterval = "500ms" ReceiptLoopInterval = "500ms"

+ 81
- 27
config/config.go

@ -29,6 +29,7 @@ func (d *Duration) UnmarshalText(data []byte) error {
// ServerProof is the server proof configuration data. // ServerProof is the server proof configuration data.
type ServerProof struct { type ServerProof struct {
// URL is the server proof API URL
URL string `validate:"required"` URL string `validate:"required"`
} }
@ -48,10 +49,17 @@ type Coordinator struct {
// SyncRetryInterval is the waiting interval between calls to the main // SyncRetryInterval is the waiting interval between calls to the main
// handler of a synced block after an error // handler of a synced block after an error
SyncRetryInterval Duration `validate:"required"` SyncRetryInterval Duration `validate:"required"`
L2DB struct {
// L2DB is the DB that holds the pool of L2Txs
L2DB struct {
// SafetyPeriod is the number of batches after which
// non-pending L2Txs are deleted from the pool
SafetyPeriod common.BatchNum `validate:"required"` SafetyPeriod common.BatchNum `validate:"required"`
MaxTxs uint32 `validate:"required"`
TTL Duration `validate:"required"`
// MaxTxs is the number of L2Txs that once reached triggers
// deletion of old L2Txs
MaxTxs uint32 `validate:"required"`
// TTL is the Time To Live for L2Txs in the pool. Once MaxTxs
// L2Txs is reached, L2Txs older than TTL will be deleted.
TTL Duration `validate:"required"`
// PurgeBatchDelay is the delay between batches to purge outdated transactions // PurgeBatchDelay is the delay between batches to purge outdated transactions
PurgeBatchDelay int64 `validate:"required"` PurgeBatchDelay int64 `validate:"required"`
// InvalidateBatchDelay is the delay between batches to mark invalid transactions // InvalidateBatchDelay is the delay between batches to mark invalid transactions
@ -62,23 +70,29 @@ type Coordinator struct {
InvalidateBlockDelay int64 `validate:"required"` InvalidateBlockDelay int64 `validate:"required"`
} `validate:"required"` } `validate:"required"`
TxSelector struct { TxSelector struct {
// Path where the TxSelector StateDB is stored
Path string `validate:"required"` Path string `validate:"required"`
} `validate:"required"` } `validate:"required"`
BatchBuilder struct { BatchBuilder struct {
// Path where the BatchBuilder StateDB is stored
Path string `validate:"required"` Path string `validate:"required"`
} `validate:"required"` } `validate:"required"`
ServerProofs []ServerProof `validate:"required"` ServerProofs []ServerProof `validate:"required"`
Circuit struct { Circuit struct {
// VerifierIdx uint8 `validate:"required"` // VerifierIdx uint8 `validate:"required"`
MaxTx int64 `validate:"required"`
// MaxTx is the maximum number of txs supported by the circuit
MaxTx int64 `validate:"required"`
// NLevels is the maximum number of merkle tree levels
// supported by the circuit
NLevels int64 `validate:"required"` NLevels int64 `validate:"required"`
} `validate:"required"` } `validate:"required"`
EthClient struct { EthClient struct {
CallGasLimit uint64 `validate:"required"`
DeployGasLimit uint64 `validate:"required"`
GasPriceDiv uint64 `validate:"required"`
ReceiptTimeout Duration `validate:"required"`
ReceiptLoopInterval Duration `validate:"required"`
// CallGasLimit is the default gas limit set for ethereum
// calls, except for methods where a particular gas limit is
// harcoded because it's known to be a big value
CallGasLimit uint64 `validate:"required"`
// GasPriceDiv is the gas price division
GasPriceDiv uint64 `validate:"required"`
// CheckLoopInterval is the waiting interval between receipt // CheckLoopInterval is the waiting interval between receipt
// checks of ethereum transactions in the TxManager // checks of ethereum transactions in the TxManager
CheckLoopInterval Duration `validate:"required"` CheckLoopInterval Duration `validate:"required"`
@ -88,12 +102,16 @@ type Coordinator struct {
// AttemptsDelay is delay between attempts do do an eth client // AttemptsDelay is delay between attempts do do an eth client
// RPC call // RPC call
AttemptsDelay Duration `validate:"required"` AttemptsDelay Duration `validate:"required"`
Keystore struct {
Path string `validate:"required"`
// Keystore is the ethereum keystore where private keys are kept
Keystore struct {
// Path to the keystore
Path string `validate:"required"`
// Password used to decrypt the keys in the keystore
Password string `validate:"required"` Password string `validate:"required"`
} `validate:"required"` } `validate:"required"`
} `validate:"required"` } `validate:"required"`
API struct { API struct {
// Coordinator enables the coordinator API endpoints
Coordinator bool Coordinator bool
} `validate:"required"` } `validate:"required"`
Debug struct { Debug struct {
@ -109,43 +127,79 @@ type Coordinator struct {
// Node is the hermez node configuration. // Node is the hermez node configuration.
type Node struct { type Node struct {
PriceUpdater struct { PriceUpdater struct {
// Interval between price updater calls
Interval Duration `valudate:"required"` Interval Duration `valudate:"required"`
URL string `valudate:"required"`
Type string `valudate:"required"`
// URL of the token prices provider
URL string `valudate:"required"`
// Type of the API of the token prices provider
Type string `valudate:"required"`
} `validate:"required"` } `validate:"required"`
StateDB struct { StateDB struct {
// Path where the synchronizer StateDB is stored
Path string `validate:"required"` Path string `validate:"required"`
Keep int `validate:"required"`
// Keep is the number of checkpoints to keep
Keep int `validate:"required"`
} `validate:"required"` } `validate:"required"`
PostgreSQL struct { PostgreSQL struct {
Port int `validate:"required"`
Host string `validate:"required"`
User string `validate:"required"`
// Port of the PostgreSQL server
Port int `validate:"required"`
// Host of the PostgreSQL server
Host string `validate:"required"`
// User of the PostgreSQL server
User string `validate:"required"`
// Password of the PostgreSQL server
Password string `validate:"required"` Password string `validate:"required"`
Name string `validate:"required"`
// Name of the PostgreSQL server database
Name string `validate:"required"`
} `validate:"required"` } `validate:"required"`
Web3 struct { Web3 struct {
// URL is the URL of the web3 ethereum-node RPC server
URL string `validate:"required"` URL string `validate:"required"`
} `validate:"required"` } `validate:"required"`
Synchronizer struct { Synchronizer struct {
SyncLoopInterval Duration `validate:"required"`
// SyncLoopInterval is the interval between attempts to
// synchronize a new block from an ethereum node
SyncLoopInterval Duration `validate:"required"`
// StatsRefreshPeriod is the interval between updates of the
// synchronizer state Eth parameters (`Eth.LastBlock` and
// `Eth.LastBatch`)
StatsRefreshPeriod Duration `validate:"required"` StatsRefreshPeriod Duration `validate:"required"`
} `validate:"required"` } `validate:"required"`
SmartContracts struct { SmartContracts struct {
Rollup ethCommon.Address `validate:"required"`
Auction ethCommon.Address `validate:"required"`
WDelayer ethCommon.Address `validate:"required"`
TokenHEZ ethCommon.Address `validate:"required"`
TokenHEZName string `validate:"required"`
// Rollup is the address of the Hermez.sol smart contract
Rollup ethCommon.Address `validate:"required"`
// Rollup is the address of the HermezAuctionProtocol.sol smart
// contract
Auction ethCommon.Address `validate:"required"`
// WDelayer is the address of the WithdrawalDelayer.sol smart
// contract
WDelayer ethCommon.Address `validate:"required"`
// TokenHEZ is the address of the HEZTokenFull.sol smart
// contract
TokenHEZ ethCommon.Address `validate:"required"`
// TokenHEZName is the name of the HEZ token deployed at
// TokenHEZ address
TokenHEZName string `validate:"required"`
} `validate:"required"` } `validate:"required"`
API struct { API struct {
Address string
Explorer bool
UpdateMetricsInterval Duration
// Address where the API will listen if set
Address string
// Explorer enables the Explorer API endpoints
Explorer bool
// UpdateMetricsInterval is the interval between updates of the
// API metrics
UpdateMetricsInterval Duration
// UpdateMetricsInterval is the interval between updates of the
// recommended fees
UpdateRecommendedFeeInterval Duration UpdateRecommendedFeeInterval Duration
} `validate:"required"` } `validate:"required"`
Debug struct { Debug struct {
// APIAddress is the address where the debugAPI will listen if
// set
APIAddress string APIAddress string
// MeddlerLogs enables meddler debug mode, where unused columns and struct
// fields will be logged
MeddlerLogs bool
} }
Coordinator Coordinator `validate:"-"` Coordinator Coordinator `validate:"-"`
} }

+ 2
- 5
coordinator/coordinator_test.go

@ -731,11 +731,8 @@ func TestRollupForgeBatch(t *testing.T) {
ethClient, err := ethclient.Dial(web3URL) ethClient, err := ethclient.Dial(web3URL)
require.NoError(t, err) require.NoError(t, err)
ethCfg := eth.EthereumConfig{ ethCfg := eth.EthereumConfig{
CallGasLimit: 300000,
DeployGasLimit: 1000000,
GasPriceDiv: 100,
ReceiptTimeout: 60 * time.Second,
IntervalReceiptLoop: 500 * time.Millisecond,
CallGasLimit: 300000,
GasPriceDiv: 100,
} }
scryptN := ethKeystore.LightScryptN scryptN := ethKeystore.LightScryptN
scryptP := ethKeystore.LightScryptP scryptP := ethKeystore.LightScryptP

+ 17
- 112
eth/ethereum.go

@ -40,45 +40,31 @@ type EthereumInterface interface {
var ( var (
// ErrAccountNil is used when the calls can not be made because the account is nil // ErrAccountNil is used when the calls can not be made because the account is nil
ErrAccountNil = fmt.Errorf("Authorized calls can't be made when the account is nil") ErrAccountNil = fmt.Errorf("Authorized calls can't be made when the account is nil")
// ErrReceiptStatusFailed is used when receiving a failed transaction
ErrReceiptStatusFailed = fmt.Errorf("receipt status is failed")
// ErrReceiptNotReceived is used when unable to retrieve a transaction
ErrReceiptNotReceived = fmt.Errorf("receipt not available")
// ErrBlockHashMismatchEvent is used when there's a block hash mismatch // ErrBlockHashMismatchEvent is used when there's a block hash mismatch
// beetween different events of the same block // beetween different events of the same block
ErrBlockHashMismatchEvent = fmt.Errorf("block hash mismatch in event log") ErrBlockHashMismatchEvent = fmt.Errorf("block hash mismatch in event log")
) )
const ( const (
errStrDeploy = "deployment of %s failed: %w"
errStrWaitReceipt = "wait receipt of %s deploy failed: %w"
// default values // default values
defaultCallGasLimit = 300000
defaultDeployGasLimit = 1000000
defaultGasPriceDiv = 100
defaultReceiptTimeout = 60
defaultIntervalReceiptLoop = 200
defaultCallGasLimit = 300000
defaultGasPriceDiv = 100
) )
// EthereumConfig defines the configuration parameters of the EthereumClient // EthereumConfig defines the configuration parameters of the EthereumClient
type EthereumConfig struct { type EthereumConfig struct {
CallGasLimit uint64
DeployGasLimit uint64
GasPriceDiv uint64
ReceiptTimeout time.Duration
IntervalReceiptLoop time.Duration
CallGasLimit uint64
GasPriceDiv uint64
} }
// EthereumClient is an ethereum client to call Smart Contract methods and check blockchain information. // EthereumClient is an ethereum client to call Smart Contract methods and check blockchain information.
type EthereumClient struct { type EthereumClient struct {
client *ethclient.Client
chainID *big.Int
account *accounts.Account
ks *ethKeystore.KeyStore
ReceiptTimeout time.Duration
config *EthereumConfig
opts *bind.CallOpts
client *ethclient.Client
chainID *big.Int
account *accounts.Account
ks *ethKeystore.KeyStore
config *EthereumConfig
opts *bind.CallOpts
} }
// NewEthereumClient creates a EthereumClient instance. The account is not mandatory (it can // NewEthereumClient creates a EthereumClient instance. The account is not mandatory (it can
@ -86,20 +72,16 @@ type EthereumClient struct {
func NewEthereumClient(client *ethclient.Client, account *accounts.Account, ks *ethKeystore.KeyStore, config *EthereumConfig) (*EthereumClient, error) { func NewEthereumClient(client *ethclient.Client, account *accounts.Account, ks *ethKeystore.KeyStore, config *EthereumConfig) (*EthereumClient, error) {
if config == nil { if config == nil {
config = &EthereumConfig{ config = &EthereumConfig{
CallGasLimit: defaultCallGasLimit,
DeployGasLimit: defaultDeployGasLimit,
GasPriceDiv: defaultGasPriceDiv,
ReceiptTimeout: defaultReceiptTimeout,
IntervalReceiptLoop: defaultIntervalReceiptLoop,
CallGasLimit: defaultCallGasLimit,
GasPriceDiv: defaultGasPriceDiv,
} }
} }
c := &EthereumClient{ c := &EthereumClient{
client: client,
account: account,
ks: ks,
ReceiptTimeout: config.ReceiptTimeout * time.Second,
config: config,
opts: newCallOpts(),
client: client,
account: account,
ks: ks,
config: config,
opts: newCallOpts(),
} }
chainID, err := c.EthChainID() chainID, err := c.EthChainID()
if err != nil { if err != nil {
@ -180,93 +162,16 @@ type ContractData struct {
Receipt *types.Receipt Receipt *types.Receipt
} }
// Deploy a smart contract. `name` is used to log deployment information. fn
// is a wrapper to the deploy function generated by abigen. In case of error,
// the returned `ContractData` may have some parameters filled depending on the
// kind of error that occurred.
func (c *EthereumClient) Deploy(name string,
fn func(c *ethclient.Client, auth *bind.TransactOpts) (ethCommon.Address, *types.Transaction, interface{}, error)) (ContractData, error) {
var contractData ContractData
log.Infow("Deploying", "contract", name)
tx, err := c.CallAuth(
c.config.DeployGasLimit,
func(client *ethclient.Client, auth *bind.TransactOpts) (*types.Transaction, error) {
addr, tx, _, err := fn(client, auth)
if err != nil {
return nil, tracerr.Wrap(err)
}
contractData.Address = addr
return tx, nil
},
)
if err != nil {
return contractData, tracerr.Wrap(fmt.Errorf(errStrDeploy, name, err))
}
log.Infow("Waiting receipt", "tx", tx.Hash().Hex(), "contract", name)
contractData.Tx = tx
receipt, err := c.WaitReceipt(tx)
if err != nil {
return contractData, tracerr.Wrap(fmt.Errorf(errStrWaitReceipt, name, err))
}
contractData.Receipt = receipt
return contractData, nil
}
// Call performs a read only Smart Contract method call. // Call performs a read only Smart Contract method call.
func (c *EthereumClient) Call(fn func(*ethclient.Client) error) error { func (c *EthereumClient) Call(fn func(*ethclient.Client) error) error {
return fn(c.client) return fn(c.client)
} }
// WaitReceipt will block until a transaction is confirmed. Internally it
// polls the state every 200 milliseconds.
func (c *EthereumClient) WaitReceipt(tx *types.Transaction) (*types.Receipt, error) {
return c.waitReceipt(context.TODO(), tx, c.ReceiptTimeout)
}
// GetReceipt will check if a transaction is confirmed and return
// immediately, waiting at most 1 second and returning error if the transaction
// is still pending.
func (c *EthereumClient) GetReceipt(tx *types.Transaction) (*types.Receipt, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
defer cancel()
return c.waitReceipt(ctx, tx, 0)
}
// EthTransactionReceipt returns the transaction receipt of the given txHash // EthTransactionReceipt returns the transaction receipt of the given txHash
func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) { func (c *EthereumClient) EthTransactionReceipt(ctx context.Context, txHash ethCommon.Hash) (*types.Receipt, error) {
return c.client.TransactionReceipt(ctx, txHash) return c.client.TransactionReceipt(ctx, txHash)
} }
func (c *EthereumClient) waitReceipt(ctx context.Context, tx *types.Transaction, timeout time.Duration) (*types.Receipt, error) {
var err error
var receipt *types.Receipt
txHash := tx.Hash()
log.Debugw("Waiting for receipt", "tx", txHash.Hex())
start := time.Now()
for {
receipt, err = c.client.TransactionReceipt(ctx, txHash)
if receipt != nil || time.Since(start) >= timeout {
break
}
time.Sleep(c.config.IntervalReceiptLoop * time.Millisecond)
}
if receipt != nil && receipt.Status == types.ReceiptStatusFailed {
log.Errorw("Failed transaction", "tx", txHash.Hex())
return receipt, tracerr.Wrap(ErrReceiptStatusFailed)
}
if receipt == nil {
log.Debugw("Pendingtransaction / Wait receipt timeout", "tx", txHash.Hex(), "lasterr", err)
return receipt, tracerr.Wrap(ErrReceiptNotReceived)
}
log.Debugw("Successful transaction", "tx", txHash.Hex())
return receipt, tracerr.Wrap(err)
}
// EthLastBlock returns the last block number in the blockchain // EthLastBlock returns the last block number in the blockchain
func (c *EthereumClient) EthLastBlock() (int64, error) { func (c *EthereumClient) EthLastBlock() (int64, error) {
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)

+ 4
- 5
node/node.go

@ -32,6 +32,7 @@ import (
"github.com/hermeznetwork/hermez-node/txselector" "github.com/hermeznetwork/hermez-node/txselector"
"github.com/hermeznetwork/tracerr" "github.com/hermeznetwork/tracerr"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/russross/meddler"
) )
// Mode sets the working mode of the node (synchronizer or coordinator) // Mode sets the working mode of the node (synchronizer or coordinator)
@ -71,6 +72,7 @@ type Node struct {
// NewNode creates a Node // NewNode creates a Node
func NewNode(mode Mode, cfg *config.Node) (*Node, error) { func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
meddler.Debug = cfg.Debug.MeddlerLogs
// Stablish DB connection // Stablish DB connection
db, err := dbUtils.InitSQLDB( db, err := dbUtils.InitSQLDB(
cfg.PostgreSQL.Port, cfg.PostgreSQL.Port,
@ -94,11 +96,8 @@ func NewNode(mode Mode, cfg *config.Node) (*Node, error) {
var keyStore *ethKeystore.KeyStore var keyStore *ethKeystore.KeyStore
if mode == ModeCoordinator { if mode == ModeCoordinator {
ethCfg = eth.EthereumConfig{ ethCfg = eth.EthereumConfig{
CallGasLimit: cfg.Coordinator.EthClient.CallGasLimit,
DeployGasLimit: cfg.Coordinator.EthClient.DeployGasLimit,
GasPriceDiv: cfg.Coordinator.EthClient.GasPriceDiv,
ReceiptTimeout: cfg.Coordinator.EthClient.ReceiptTimeout.Duration,
IntervalReceiptLoop: cfg.Coordinator.EthClient.ReceiptLoopInterval.Duration,
CallGasLimit: cfg.Coordinator.EthClient.CallGasLimit,
GasPriceDiv: cfg.Coordinator.EthClient.GasPriceDiv,
} }
scryptN := ethKeystore.StandardScryptN scryptN := ethKeystore.StandardScryptN

Loading…
Cancel
Save