devnet: fix node startup on macOS (#8569)

* call getEnode before NodeStarted to make sure it is ready for RPC
calls
* fix connection error detection on macOS
* use a non-default p2p port to avoid conflicts
* disable bor milestones on local heimdall
* generate node keys for static peers config
This commit is contained in:
battlmonstr 2023-10-26 07:58:01 +02:00 committed by GitHub
parent 043ccef4ca
commit f1c81dc14e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 159 additions and 99 deletions

View File

@ -1,7 +1,11 @@
package args
import (
"crypto/ecdsa"
"encoding/hex"
"fmt"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/p2p/enode"
"math/big"
"net"
"path/filepath"
@ -44,7 +48,11 @@ type Node struct {
StaticPeers string `arg:"--staticpeers" json:"staticpeers,omitempty"`
WithoutHeimdall bool `arg:"--bor.withoutheimdall" flag:"" default:"false" json:"bor.withoutheimdall,omitempty"`
HeimdallGRpc string `arg:"--bor.heimdallgRPC" json:"bor.heimdallgRPC,omitempty"`
WithHeimdallMilestones bool `arg:"--bor.milestone" json:"bor.milestone"`
VMDebug bool `arg:"--vmdebug" flag:"" default:"false" json:"dmdebug"`
NodeKey *ecdsa.PrivateKey `arg:"-"`
NodeKeyHex string `arg:"--nodekeyhex" json:"nodekeyhex,omitempty"`
}
func (node *Node) configure(base Node, nodeNumber int) error {
@ -62,14 +70,19 @@ func (node *Node) configure(base Node, nodeNumber int) error {
node.StaticPeers = base.StaticPeers
var err error
node.NodeKey, err = crypto.GenerateKey()
if err != nil {
return err
}
node.NodeKeyHex = hex.EncodeToString(crypto.FromECDSA(node.NodeKey))
node.Metrics = base.Metrics
node.MetricsPort = base.MetricsPort
node.MetricsAddr = base.MetricsAddr
node.Snapshots = base.Snapshots
var err error
node.PrivateApiAddr, _, err = portFromBase(base.PrivateApiAddr, nodeNumber, 1)
if err != nil {
@ -86,13 +99,24 @@ func (node *Node) configure(base Node, nodeNumber int) error {
node.Port = base.Port + nodeNumber
node.WithHeimdallMilestones = base.WithHeimdallMilestones
return nil
}
func (node Node) ChainID() *big.Int {
func (node *Node) ChainID() *big.Int {
return &big.Int{}
}
func (node *Node) GetHttpPort() int {
return node.HttpPort
}
func (node *Node) GetEnodeURL() string {
port := node.Port
return enode.NewV4(&node.NodeKey.PublicKey, net.ParseIP("127.0.0.1"), port, port).URLv4()
}
type BlockProducer struct {
Node
Mine bool `arg:"--mine" flag:"true"`
@ -105,11 +129,10 @@ type BlockProducer struct {
account *accounts.Account
}
func (m BlockProducer) Configure(baseNode Node, nodeNumber int) (int, interface{}, error) {
func (m *BlockProducer) Configure(baseNode Node, nodeNumber int) (interface{}, error) {
err := m.configure(baseNode, nodeNumber)
if err != nil {
return -1, nil, err
return nil, err
}
switch m.Chain {
@ -131,18 +154,18 @@ func (m BlockProducer) Configure(baseNode Node, nodeNumber int) (int, interface{
m.Etherbase = m.account.Address.Hex()
}
return m.HttpPort, m, nil
return m, nil
}
func (n BlockProducer) Name() string {
func (n *BlockProducer) Name() string {
return n.Node.Name
}
func (n BlockProducer) Account() *accounts.Account {
func (n *BlockProducer) Account() *accounts.Account {
return n.account
}
func (n BlockProducer) IsBlockProducer() bool {
func (n *BlockProducer) IsBlockProducer() bool {
return true
}
@ -153,25 +176,24 @@ type NonBlockProducer struct {
NoDiscover string `arg:"--nodiscover" flag:"" default:"true" json:"nodiscover"`
}
func (n NonBlockProducer) Configure(baseNode Node, nodeNumber int) (int, interface{}, error) {
func (n *NonBlockProducer) Configure(baseNode Node, nodeNumber int) (interface{}, error) {
err := n.configure(baseNode, nodeNumber)
if err != nil {
return -1, nil, err
return nil, err
}
return n.HttpPort, n, nil
return n, nil
}
func (n NonBlockProducer) Name() string {
func (n *NonBlockProducer) Name() string {
return n.Node.Name
}
func (n NonBlockProducer) IsBlockProducer() bool {
func (n *NonBlockProducer) IsBlockProducer() bool {
return false
}
func (n NonBlockProducer) Account() *accounts.Account {
func (n *NonBlockProducer) Account() *accounts.Account {
return nil
}

View File

@ -162,8 +162,26 @@ func producingNodeArgs(dataDir string, nodeNumber int) []string {
authrpcPortArg, _ := parameterFromArgument("--authrpc.port", "8551")
natArg, _ := parameterFromArgument("--nat", "none")
accountSlotsArg, _ := parameterFromArgument("--txpool.accountslots", "16")
withHeimdallMilestonesArg, _ := parameterFromArgument("--bor.milestone", "false")
return []string{buildDirArg, dataDirArg, chainType, privateApiAddr, httpPortArg, authrpcPortArg, mine, httpApi, ws, natArg, devPeriod, consoleVerbosity, p2pProtocol, downloaderArg, accountSlotsArg}
return []string{
buildDirArg,
dataDirArg,
chainType,
privateApiAddr,
httpPortArg,
authrpcPortArg,
mine,
httpApi,
ws,
natArg,
devPeriod,
consoleVerbosity,
p2pProtocol,
downloaderArg,
accountSlotsArg,
withHeimdallMilestonesArg,
}
}
// nonMiningNodeArgs returns custom args for starting a non-mining node
@ -182,6 +200,24 @@ func nonProducingNodeArgs(dataDir string, nodeNumber int, enode string) []string
authrpcPortArg, _ := parameterFromArgument("--authrpc.port", "8551")
natArg, _ := parameterFromArgument("--nat", "none")
ws := wsArg
withHeimdallMilestonesArg, _ := parameterFromArgument("--bor.milestone", "false")
return []string{buildDirArg, dataDirArg, chainType, privateApiAddr, httpPortArg, authrpcPortArg, httpApi, ws, natArg, staticPeers, noDiscover, consoleVerbosity, torrentPort, p2pProtocol, downloaderArg}
return []string{
buildDirArg,
dataDirArg,
chainType,
privateApiAddr,
httpPortArg,
authrpcPortArg,
httpApi,
ws,
natArg,
staticPeers,
noDiscover,
consoleVerbosity,
torrentPort,
p2pProtocol,
downloaderArg,
withHeimdallMilestonesArg,
}
}

View File

@ -4,9 +4,9 @@ import (
"context"
"errors"
"fmt"
"github.com/ledgerwatch/erigon/cmd/utils"
"math/big"
"net"
"net/url"
"os"
"reflect"
"strings"
@ -40,6 +40,7 @@ type Network struct {
BorStateSyncDelay time.Duration
BorPeriod time.Duration
BorMinBlockSize int
BorWithMilestones *bool
wg sync.WaitGroup
peers []string
namedNodes map[string]Node
@ -55,11 +56,6 @@ func (nw *Network) ChainID() *big.Int {
// Start starts the process for multiple erigon nodes running on the dev chain
func (nw *Network) Start(ctx context.Context) error {
type configurable interface {
Configure(baseNode args.Node, nodeNumber int) (int, interface{}, error)
}
for _, service := range nw.Services {
if err := service.Start(ctx); err != nil {
nw.Stop()
@ -76,28 +72,37 @@ func (nw *Network) Start(ctx context.Context) error {
Snapshots: nw.Snapshots,
}
if nw.BorWithMilestones != nil {
baseNode.WithHeimdallMilestones = *nw.BorWithMilestones
} else {
baseNode.WithHeimdallMilestones = utils.WithHeimdallMilestones.Value
}
cliCtx := CliContext(ctx)
metricsEnabled := cliCtx.Bool("metrics")
metricsNode := cliCtx.Int("metrics.node")
nw.namedNodes = map[string]Node{}
for i, node := range nw.Nodes {
if configurable, ok := node.(configurable); ok {
for i, nodeConfig := range nw.Nodes {
{
base := baseNode
if metricsEnabled && metricsNode == i {
base.Metrics = true
base.MetricsPort = cliCtx.Int("metrics.port")
}
base.StaticPeers = strings.Join(nw.peers, ",")
nodePort, args, err := configurable.Configure(base, i)
if err == nil {
node, err = nw.createNode(fmt.Sprintf("%s:%d", nw.BaseRPCHost, nodePort), args)
argsObj, err := nodeConfig.Configure(base, i)
if err != nil {
nw.Stop()
return err
}
nodePort := nodeConfig.GetHttpPort()
nodeAddr := fmt.Sprintf("%s:%d", nw.BaseRPCHost, nodePort)
node, err := nw.createNode(nodeAddr, argsObj)
if err != nil {
nw.Stop()
return err
@ -105,6 +110,7 @@ func (nw *Network) Start(ctx context.Context) error {
nw.Nodes[i] = node
nw.namedNodes[node.Name()] = node
nw.peers = append(nw.peers, nodeConfig.GetEnodeURL())
for _, service := range nw.Services {
service.NodeCreated(ctx, node)
@ -114,7 +120,6 @@ func (nw *Network) Start(ctx context.Context) error {
for _, node := range nw.Nodes {
err := nw.startNode(node)
if err != nil {
nw.Stop()
return err
@ -123,25 +128,6 @@ func (nw *Network) Start(ctx context.Context) error {
for _, service := range nw.Services {
service.NodeStarted(ctx, node)
}
// get the enode of the node
// - note this has the side effect of waiting for the node to start
enode, err := getEnode(node)
if err != nil {
if errors.Is(err, devnetutils.ErrInvalidEnodeString) {
continue
}
nw.Stop()
return err
}
nw.peers = append(nw.peers, enode)
// TODO do we need to call AddPeer to the nodes to make them aware of this one
// the current model only works for an appending node network where the peers gossip
// connections - not sure if this is the case ?
}
return nil
@ -201,28 +187,10 @@ func (nw *Network) startNode(n Node) error {
node := n.(*node)
args, err := args.AsArgs(node.args)
if err != nil {
return err
}
if len(nw.peers) > 0 {
peersIndex := -1
for i, arg := range args {
if strings.HasPrefix(arg, "--staticpeers") {
peersIndex = i
break
}
}
if peersIndex >= 0 {
args[peersIndex] = args[peersIndex] + "," + strings.Join(nw.peers, ",")
} else {
args = append(args, "--staticpeers="+strings.Join(nw.peers, ","))
}
}
go func() {
nw.Logger.Info("Running node", "name", node.Name(), "args", args)
@ -254,6 +222,14 @@ func (nw *Network) startNode(n Node) error {
return nil
}
func isConnectionError(err error) bool {
var opErr *net.OpError
if errors.As(err, &opErr) {
return opErr.Op == "dial"
}
return false
}
// getEnode returns the enode of the netowrk node
func getEnode(n Node) (string, error) {
reqCount := 0
@ -268,21 +244,10 @@ func getEnode(n Node) (string, error) {
}
}
if reqCount < 10 {
var urlErr *url.Error
if errors.As(err, &urlErr) {
var opErr *net.OpError
if errors.As(urlErr.Err, &opErr) {
var callErr *os.SyscallError
if errors.As(opErr.Err, &callErr) {
if strings.HasPrefix(callErr.Syscall, "connect") {
reqCount++
time.Sleep(time.Duration(devnetutils.RandomInt(5)) * time.Second)
continue
}
}
}
}
if isConnectionError(err) && (reqCount < 10) {
reqCount++
time.Sleep(time.Duration(devnetutils.RandomInt(5)) * time.Second)
continue
}
return "", err

View File

@ -2,8 +2,10 @@ package devnet
import (
"context"
"errors"
"fmt"
"math/big"
"net"
"net/http"
"sync"
@ -14,6 +16,7 @@ import (
"github.com/ledgerwatch/erigon/diagnostics"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/node/nodecfg"
p2p_enode "github.com/ledgerwatch/erigon/p2p/enode"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/turbo/debug"
enode "github.com/ledgerwatch/erigon/turbo/node"
@ -25,8 +28,11 @@ type Node interface {
requests.RequestGenerator
Name() string
ChainID() *big.Int
GetHttpPort() int
GetEnodeURL() string
Account() *accounts.Account
IsBlockProducer() bool
Configure(baseNode args.Node, nodeNumber int) (interface{}, error)
}
type NodeSelector interface {
@ -98,6 +104,10 @@ func (n *node) done() {
}
}
func (n *node) Configure(args.Node, int) (interface{}, error) {
return nil, errors.New("N/A")
}
func (n *node) IsBlockProducer() bool {
_, isBlockProducer := n.args.(args.BlockProducer)
return isBlockProducer
@ -127,6 +137,15 @@ func (n *node) ChainID() *big.Int {
return nil
}
func (n *node) GetHttpPort() int {
return n.nodeCfg.HTTPPort
}
func (n *node) GetEnodeURL() string {
port := n.nodeCfg.P2P.ListenPort()
return p2p_enode.NewV4(&n.nodeCfg.P2P.PrivateKey.PublicKey, net.ParseIP("127.0.0.1"), port, port).URLv4()
}
// run configures, creates and serves an erigon node
func (n *node) run(ctx *cli.Context) error {
var logger log.Logger

View File

@ -3,6 +3,7 @@ package main
import (
"context"
"fmt"
"github.com/ledgerwatch/erigon/cmd/utils"
"os"
"os/signal"
"path/filepath"
@ -332,7 +333,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
DataDir: dataDir,
Chain: networkname.BorDevnetChainName,
Logger: logger,
BasePort: 30303,
BasePort: 40303,
BasePrivateApiAddr: "localhost:10090",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
@ -344,7 +345,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
account_services.NewFaucet(networkname.BorDevnetChainName, faucetSource),
},
Nodes: []devnet.Node{
args.BlockProducer{
&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -352,7 +353,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
},
AccountSlots: 200,
},
args.NonBlockProducer{
&args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -364,11 +365,14 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
} else {
var heimdallGrpc string
var services []devnet.Service
var withMilestones = utils.WithHeimdallMilestones.Value
checkpointOwner := accounts.NewAccount("checkpoint-owner")
if ctx.Bool(LocalHeimdallFlag.Name) {
config := *params.BorDevnetChainConfig
// milestones are not supported yet on the local heimdall
withMilestones = false
if sprintSize := uint64(ctx.Int(BorSprintSizeFlag.Name)); sprintSize > 0 {
config.Bor.Sprint = map[string]uint64{"0": sprintSize}
@ -389,17 +393,18 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
DataDir: dataDir,
Chain: networkname.BorDevnetChainName,
Logger: logger,
BasePort: 30303,
BasePort: 40303,
BasePrivateApiAddr: "localhost:10090",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
BorStateSyncDelay: 5 * time.Second,
BorWithMilestones: &withMilestones,
Services: append(services, account_services.NewFaucet(networkname.BorDevnetChainName, faucetSource)),
Alloc: types.GenesisAlloc{
faucetSource.Address: {Balance: accounts.EtherAmount(200_000)},
},
Nodes: []devnet.Node{
args.BlockProducer{
&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -407,7 +412,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
},
AccountSlots: 200,
},
args.BlockProducer{
&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -415,7 +420,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
},
AccountSlots: 200,
},
/*args.BlockProducer{
/*&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -423,7 +428,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
},
AccountSlots: 200,
},*/
args.NonBlockProducer{
&args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -439,14 +444,14 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
BasePort: 30403,
BasePrivateApiAddr: "localhost:10190",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
BaseRPCPort: baseRpcPort + 1000,
Services: append(services, account_services.NewFaucet(networkname.DevChainName, faucetSource)),
Alloc: types.GenesisAlloc{
faucetSource.Address: {Balance: accounts.EtherAmount(200_000)},
checkpointOwner.Address: {Balance: accounts.EtherAmount(10_000)},
},
Nodes: []devnet.Node{
args.BlockProducer{
&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
@ -456,7 +461,7 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
DevPeriod: 5,
AccountSlots: 200,
},
args.NonBlockProducer{
&args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "3",
@ -482,14 +487,14 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
account_services.NewFaucet(networkname.DevChainName, faucetSource),
},
Nodes: []devnet.Node{
args.BlockProducer{
&args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
},
AccountSlots: 200,
},
args.NonBlockProducer{
&args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",

View File

@ -26,6 +26,7 @@ import (
"fmt"
"net"
"sort"
"strconv"
"sync"
"sync/atomic"
"time"
@ -173,6 +174,18 @@ type Config struct {
MetricsEnabled bool
}
func (config *Config) ListenPort() int {
_, portStr, err := net.SplitHostPort(config.ListenAddr)
if err != nil {
return 0
}
port, err := strconv.Atoi(portStr)
if err != nil {
return 0
}
return port
}
// Server manages all peer connections.
type Server struct {
// Config fields may not be modified while the server is running.