erigon-pulse/cmd/devnet/main.go
Léo Vincent 62d9f3793e
chore(devnet): add rpc.host and rpc.port flags in devnet cmd (#8449)
## Description

Add the possibility to change the RPC address used in `devnet` without
requiring a rebuild.

## Test

- [x] `make devnet`
- [x] `./build/bin/devnet --datadir tmp`
- [x] `./build/bin/devnet --datadir tmp --rpc.host localhost --rpc.port
8546`
2023-10-12 15:18:51 +01:00

504 lines
14 KiB
Go

package main
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
dbg "runtime/debug"
"strings"
"syscall"
"time"
"github.com/ledgerwatch/erigon/cmd/devnet/accounts"
_ "github.com/ledgerwatch/erigon/cmd/devnet/accounts/steps"
_ "github.com/ledgerwatch/erigon/cmd/devnet/admin"
_ "github.com/ledgerwatch/erigon/cmd/devnet/contracts/steps"
account_services "github.com/ledgerwatch/erigon/cmd/devnet/services/accounts"
"github.com/ledgerwatch/erigon/cmd/devnet/services/polygon"
"github.com/ledgerwatch/erigon/cmd/devnet/transactions"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon-lib/common/metrics"
"github.com/ledgerwatch/erigon/cmd/devnet/args"
"github.com/ledgerwatch/erigon/cmd/devnet/devnet"
"github.com/ledgerwatch/erigon/cmd/devnet/devnetutils"
"github.com/ledgerwatch/erigon/cmd/devnet/requests"
"github.com/ledgerwatch/erigon/cmd/devnet/scenarios"
"github.com/ledgerwatch/erigon/cmd/devnet/services"
"github.com/ledgerwatch/erigon/params/networkname"
"github.com/ledgerwatch/log/v3"
"github.com/ledgerwatch/erigon/cmd/utils/flags"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/turbo/app"
"github.com/ledgerwatch/erigon/turbo/debug"
"github.com/ledgerwatch/erigon/turbo/logging"
"github.com/urfave/cli/v2"
)
var (
DataDirFlag = flags.DirectoryFlag{
Name: "datadir",
Usage: "Data directory for the devnet",
Value: flags.DirectoryString(""),
Required: true,
}
ChainFlag = cli.StringFlag{
Name: "chain",
Usage: "The devnet chain to run (dev,bor-devnet)",
Value: networkname.DevChainName,
}
ScenariosFlag = cli.StringFlag{
Name: "scenarios",
Usage: "Scenarios to be run on the devnet chain",
Value: "dynamic-tx-node-0",
}
BaseRpcHostFlag = cli.StringFlag{
Name: "rpc.host",
Usage: "The host of the base RPC service",
Value: "localhost",
}
BaseRpcPortFlag = cli.IntFlag{
Name: "rpc.port",
Usage: "The port of the base RPC service",
Value: 8545,
}
WithoutHeimdallFlag = cli.BoolFlag{
Name: "bor.withoutheimdall",
Usage: "Run without Heimdall service",
}
LocalHeimdallFlag = cli.BoolFlag{
Name: "bor.localheimdall",
Usage: "Run with a devnet local Heimdall service",
}
HeimdallgRPCAddressFlag = cli.StringFlag{
Name: "bor.heimdallgRPC",
Usage: "Address of Heimdall gRPC service",
Value: "localhost:8540",
}
BorSprintSizeFlag = cli.IntFlag{
Name: "bor.sprintsize",
Usage: "The bor sprint size to run",
}
MetricsEnabledFlag = cli.BoolFlag{
Name: "metrics",
Usage: "Enable metrics collection and reporting",
}
MetricsNodeFlag = cli.IntFlag{
Name: "metrics.node",
Usage: "Which node of the cluster to attach to",
Value: 0,
}
MetricsPortFlag = cli.IntFlag{
Name: "metrics.port",
Usage: "Metrics HTTP server listening port",
Value: metrics.DefaultConfig.Port,
}
DiagnosticsURLFlag = cli.StringFlag{
Name: "diagnostics.url",
Usage: "URL of the diagnostics system provided by the support team, include unique session PIN",
}
insecureFlag = cli.BoolFlag{
Name: "insecure",
Usage: "Allows communication with diagnostics system using self-signed TLS certificates",
}
metricsURLsFlag = cli.StringSliceFlag{
Name: "debug.urls",
Usage: "internal flag",
}
WaitFlag = cli.BoolFlag{
Name: "wait",
Usage: "Wait until interrupted after all scenarios have run",
}
)
type PanicHandler struct {
}
func (ph PanicHandler) Log(r *log.Record) error {
fmt.Printf("Msg: %s\nStack: %s\n", r.Msg, dbg.Stack())
os.Exit(1)
return nil
}
func main() {
debug.RaiseFdLimit()
app := cli.NewApp()
app.Version = params.VersionWithCommit(params.GitCommit)
app.Action = func(ctx *cli.Context) error {
return action(ctx)
}
app.Flags = []cli.Flag{
&DataDirFlag,
&ChainFlag,
&ScenariosFlag,
&BaseRpcHostFlag,
&BaseRpcPortFlag,
&WithoutHeimdallFlag,
&LocalHeimdallFlag,
&HeimdallgRPCAddressFlag,
&BorSprintSizeFlag,
&MetricsEnabledFlag,
&MetricsNodeFlag,
&MetricsPortFlag,
&DiagnosticsURLFlag,
&insecureFlag,
&metricsURLsFlag,
&WaitFlag,
&logging.LogVerbosityFlag,
&logging.LogConsoleVerbosityFlag,
&logging.LogDirVerbosityFlag,
}
app.After = func(ctx *cli.Context) error {
// unsubscribe from all the subscriptions made
services.UnsubscribeAll()
return nil
}
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
const (
recipientAddress = "0x71562b71999873DB5b286dF957af199Ec94617F7"
sendValue uint64 = 10000
)
func action(ctx *cli.Context) error {
dataDir := ctx.String("datadir")
logsDir := filepath.Join(dataDir, "logs")
if err := os.MkdirAll(logsDir, 0755); err != nil {
return err
}
logger := logging.SetupLoggerCtx("devnet", ctx, false /* rootLogger */)
// Make root logger fail
log.Root().SetHandler(PanicHandler{})
// clear all the dev files
if err := devnetutils.ClearDevDB(dataDir, logger); err != nil {
return err
}
network, err := initDevnet(ctx, logger)
if err != nil {
return err
}
metrics := ctx.Bool("metrics")
if metrics {
// TODO should get this from the network as once we have multiple nodes we'll need to iterate the
// nodes and create a series of urls - for the moment only one is supported
ctx.Set("metrics.urls", fmt.Sprintf("http://localhost:%d/debug/", ctx.Int("metrics.port")))
}
// start the network with each node in a go routine
logger.Info("Starting Devnet")
runCtx, err := network.Start(ctx, logger)
if err != nil {
return fmt.Errorf("Devnet start failed: %w", err)
}
go func() {
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, syscall.SIGTERM, syscall.SIGINT)
switch s := <-signalCh; s {
case syscall.SIGTERM:
logger.Info("Stopping networks")
network.Stop()
case syscall.SIGINT:
logger.Info("Terminating network")
os.Exit(-int(syscall.SIGINT))
}
}()
diagnosticsUrl := ctx.String("diagnostics.url")
if metrics && len(diagnosticsUrl) > 0 {
go func() {
app.ConnectDiagnostics(ctx, logger)
}()
}
if ctx.String(ChainFlag.Name) == networkname.DevChainName {
transactions.MaxNumberOfEmptyBlockChecks = 30
}
scenarios.Scenarios{
"dynamic-tx-node-0": {
Context: runCtx.
WithCurrentNetwork(0).
WithCurrentNode(0),
Steps: []*scenarios.Step{
{Text: "InitSubscriptions", Args: []any{[]requests.SubMethod{requests.Methods.ETHNewHeads}}},
{Text: "PingErigonRpc"},
{Text: "CheckTxPoolContent", Args: []any{0, 0, 0}},
{Text: "SendTxWithDynamicFee", Args: []any{recipientAddress, accounts.DevAddress, sendValue}},
{Text: "AwaitBlocks", Args: []any{2 * time.Second}},
},
},
"dynamic-tx-any-node": {
Context: runCtx.WithCurrentNetwork(0),
Steps: []*scenarios.Step{
{Text: "InitSubscriptions", Args: []any{[]requests.SubMethod{requests.Methods.ETHNewHeads}}},
{Text: "PingErigonRpc"},
{Text: "CheckTxPoolContent", Args: []any{0, 0, 0}},
{Text: "SendTxWithDynamicFee", Args: []any{recipientAddress, accounts.DevAddress, sendValue}},
{Text: "AwaitBlocks", Args: []any{2 * time.Second}},
},
},
"call-contract": {
Context: runCtx.WithCurrentNetwork(0),
Steps: []*scenarios.Step{
{Text: "InitSubscriptions", Args: []any{[]requests.SubMethod{requests.Methods.ETHNewHeads}}},
{Text: "DeployAndCallLogSubscriber", Args: []any{accounts.DevAddress}},
},
},
"state-sync": {
Steps: []*scenarios.Step{
{Text: "InitSubscriptions", Args: []any{[]requests.SubMethod{requests.Methods.ETHNewHeads}}},
{Text: "CreateAccountWithFunds", Args: []any{networkname.DevChainName, "root-funder", 200.0}},
{Text: "CreateAccountWithFunds", Args: []any{networkname.BorDevnetChainName, "child-funder", 200.0}},
{Text: "DeployChildChainReceiver", Args: []any{"child-funder"}},
{Text: "DeployRootChainSender", Args: []any{"root-funder"}},
{Text: "GenerateSyncEvents", Args: []any{"root-funder", 10, 2, 2}},
{Text: "ProcessRootTransfers", Args: []any{"root-funder", 10, 2, 2}},
{Text: "BatchProcessRootTransfers", Args: []any{"root-funder", 1, 10, 2, 2}},
},
},
"child-chain-exit": {
Steps: []*scenarios.Step{
{Text: "CreateAccountWithFunds", Args: []any{networkname.DevChainName, "root-funder", 200.0}},
{Text: "CreateAccountWithFunds", Args: []any{networkname.BorDevnetChainName, "child-funder", 200.0}},
{Text: "DeployRootChainReceiver", Args: []any{"root-funder"}},
{Text: "DeployChildChainSender", Args: []any{"child-funder"}},
{Text: "ProcessChildTransfers", Args: []any{"child-funder", 1, 2, 2}},
//{Text: "BatchProcessTransfers", Args: []any{"child-funder", 1, 10, 2, 2}},
},
},
}.Run(runCtx, strings.Split(ctx.String("scenarios"), ",")...)
if ctx.Bool("wait") || (metrics && len(diagnosticsUrl) > 0) {
logger.Info("Waiting")
network.Wait()
} else {
logger.Info("Stopping Networks")
network.Stop()
}
return nil
}
func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
dataDir := ctx.String(DataDirFlag.Name)
chain := ctx.String(ChainFlag.Name)
baseRpcHost := ctx.String(BaseRpcHostFlag.Name)
baseRpcPort := ctx.Int(BaseRpcPortFlag.Name)
faucetSource := accounts.NewAccount("faucet-source")
switch chain {
case networkname.BorDevnetChainName:
if ctx.Bool(WithoutHeimdallFlag.Name) {
return []*devnet.Network{
{
DataDir: dataDir,
Chain: networkname.BorDevnetChainName,
Logger: logger,
BasePort: 30303,
BasePrivateApiAddr: "localhost:10090",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
//Snapshots: true,
Alloc: types.GenesisAlloc{
faucetSource.Address: {Balance: accounts.EtherAmount(200_000)},
},
Services: []devnet.Service{
account_services.NewFaucet(networkname.BorDevnetChainName, faucetSource),
},
Nodes: []devnet.Node{
args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
WithoutHeimdall: true,
},
AccountSlots: 200,
},
args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
WithoutHeimdall: true,
},
},
},
}}, nil
} else {
var heimdallGrpc string
var services []devnet.Service
checkpointOwner := accounts.NewAccount("checkpoint-owner")
if ctx.Bool(LocalHeimdallFlag.Name) {
config := *params.BorDevnetChainConfig
if sprintSize := uint64(ctx.Int(BorSprintSizeFlag.Name)); sprintSize > 0 {
config.Bor.Sprint = map[string]uint64{"0": sprintSize}
}
services = append(services, polygon.NewHeimdall(&config,
&polygon.CheckpointConfig{
CheckpointBufferTime: 60 * time.Second,
CheckpointAccount: checkpointOwner,
},
logger))
heimdallGrpc = polygon.HeimdallGRpc(devnet.WithCliContext(context.Background(), ctx))
}
return []*devnet.Network{
{
DataDir: dataDir,
Chain: networkname.BorDevnetChainName,
Logger: logger,
BasePort: 30303,
BasePrivateApiAddr: "localhost:10090",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
BorStateSyncDelay: 5 * time.Second,
Services: append(services, account_services.NewFaucet(networkname.BorDevnetChainName, faucetSource)),
Alloc: types.GenesisAlloc{
faucetSource.Address: {Balance: accounts.EtherAmount(200_000)},
},
Nodes: []devnet.Node{
args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
HeimdallGRpc: heimdallGrpc,
},
AccountSlots: 200,
},
args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
HeimdallGRpc: heimdallGrpc,
},
AccountSlots: 200,
},
/*args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
HeimdallGRpc: heimdallGrpc,
},
AccountSlots: 200,
},*/
args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
HeimdallGRpc: heimdallGrpc,
},
},
},
},
{
DataDir: dataDir,
Chain: networkname.DevChainName,
Logger: logger,
BasePort: 30403,
BasePrivateApiAddr: "localhost:10190",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
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{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
VMDebug: true,
HttpCorsDomain: "*",
},
DevPeriod: 5,
AccountSlots: 200,
},
args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "3",
},
},
},
}}, nil
}
case networkname.DevChainName:
return []*devnet.Network{
{
DataDir: dataDir,
Chain: networkname.DevChainName,
Logger: logger,
BasePrivateApiAddr: "localhost:10090",
BaseRPCHost: baseRpcHost,
BaseRPCPort: baseRpcPort,
Alloc: types.GenesisAlloc{
faucetSource.Address: {Balance: accounts.EtherAmount(200_000)},
},
Services: []devnet.Service{
account_services.NewFaucet(networkname.DevChainName, faucetSource),
},
Nodes: []devnet.Node{
args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
},
AccountSlots: 200,
},
args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
},
},
},
}}, nil
}
return nil, fmt.Errorf(`Unknown network: "%s"`, chain)
}