diff --git a/cmd/integration/commands/stages.go b/cmd/integration/commands/stages.go index 8896ae7b2..5ac02ddfb 100644 --- a/cmd/integration/commands/stages.go +++ b/cmd/integration/commands/stages.go @@ -15,6 +15,7 @@ import ( "github.com/ledgerwatch/erigon/consensus/bor/heimdallgrpc" "github.com/ledgerwatch/erigon/core/rawdb/blockio" "github.com/ledgerwatch/erigon/node/nodecfg" + "github.com/ledgerwatch/erigon/p2p/sentry/sentry_multi_client" "github.com/ledgerwatch/erigon/turbo/builder" "github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks" "github.com/ledgerwatch/log/v3" @@ -33,7 +34,6 @@ import ( "github.com/ledgerwatch/erigon-lib/kv/rawdbv3" libstate "github.com/ledgerwatch/erigon-lib/state" "github.com/ledgerwatch/erigon/cmd/hack/tool/fromdb" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/core" "github.com/ledgerwatch/erigon/core/rawdb" @@ -1529,7 +1529,7 @@ func newSync(ctx context.Context, db kv.RwDB, miningConfig *params.MiningConfig, maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 } - sentryControlServer, err := sentry.NewMultiClient( + sentryControlServer, err := sentry_multi_client.NewMultiClient( db, "", chainConfig, diff --git a/cmd/sentry/main.go b/cmd/sentry/main.go index 146ee737b..abd86dca1 100644 --- a/cmd/sentry/main.go +++ b/cmd/sentry/main.go @@ -8,9 +8,9 @@ import ( "github.com/ledgerwatch/erigon-lib/common/datadir" "github.com/spf13/cobra" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/cmd/utils" "github.com/ledgerwatch/erigon/common/paths" + "github.com/ledgerwatch/erigon/p2p/sentry" "github.com/ledgerwatch/erigon/turbo/debug" "github.com/ledgerwatch/erigon/turbo/logging" node2 "github.com/ledgerwatch/erigon/turbo/node" diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 6296546a6..a8236b516 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -838,9 +838,21 @@ var ( SilkwormPathFlag = cli.StringFlag{ Name: "silkworm.path", - Usage: "Path to the silkworm_api library (enables embedded Silkworm execution)", + Usage: "Path to the Silkworm library", Value: "", } + SilkwormExecutionFlag = cli.BoolFlag{ + Name: "silkworm.exec", + Usage: "Enable Silkworm block execution", + } + SilkwormRpcDaemonFlag = cli.BoolFlag{ + Name: "silkworm.rpcd", + Usage: "Enable embedded Silkworm RPC daemon", + } + SilkwormSentryFlag = cli.BoolFlag{ + Name: "silkworm.sentry", + Usage: "Enable embedded Silkworm Sentry service", + } ) var MetricFlags = []cli.Flag{&MetricsEnabledFlag, &MetricsHTTPFlag, &MetricsPortFlag} @@ -1031,6 +1043,7 @@ func NewP2PConfig( return nil, fmt.Errorf("invalid nat option %s: %w", natSetting, err) } cfg.NAT = natif + cfg.NATSpec = natSetting return cfg, nil } @@ -1079,11 +1092,13 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) { // setNAT creates a port mapper from command line flags. func setNAT(ctx *cli.Context, cfg *p2p.Config) { if ctx.IsSet(NATFlag.Name) { - natif, err := nat.Parse(ctx.String(NATFlag.Name)) + natSetting := ctx.String(NATFlag.Name) + natif, err := nat.Parse(natSetting) if err != nil { Fatalf("Option %s: %v", NATFlag.Name, err) } cfg.NAT = natif + cfg.NATSpec = natSetting } } @@ -1161,7 +1176,6 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config, nodeName, datadir string, l } ethPeers := cfg.MaxPeers - cfg.Name = nodeName logger.Info("Maximum peer count", "ETH", ethPeers, "total", cfg.MaxPeers) if netrestrict := ctx.String(NetrestrictFlag.Name); netrestrict != "" { @@ -1460,10 +1474,12 @@ func setWhitelist(ctx *cli.Context, cfg *ethconfig.Config) { } func setSilkworm(ctx *cli.Context, cfg *ethconfig.Config) { - cfg.SilkwormEnabled = ctx.IsSet(SilkwormPathFlag.Name) - if cfg.SilkwormEnabled { - cfg.SilkwormPath = ctx.String(SilkwormPathFlag.Name) + cfg.SilkwormPath = ctx.String(SilkwormPathFlag.Name) + if ctx.IsSet(SilkwormExecutionFlag.Name) { + cfg.SilkwormExecution = ctx.Bool(SilkwormExecutionFlag.Name) } + cfg.SilkwormRpcDaemon = ctx.Bool(SilkwormRpcDaemonFlag.Name) + cfg.SilkwormSentry = ctx.Bool(SilkwormSentryFlag.Name) } // CheckExclusive verifies that only a single instance of the provided flags was @@ -1576,7 +1592,6 @@ func SetEthConfig(ctx *cli.Context, nodeConfig *nodecfg.Config, cfg *ethconfig.C setSilkworm(ctx, cfg) cfg.Ethstats = ctx.String(EthStatsURLFlag.Name) - cfg.P2PEnabled = len(nodeConfig.P2P.SentryAddr) == 0 cfg.HistoryV3 = ctx.Bool(HistoryV3Flag.Name) if ctx.IsSet(NetworkIdFlag.Name) { cfg.NetworkID = ctx.Uint64(NetworkIdFlag.Name) diff --git a/eth/backend.go b/eth/backend.go index 5e5a80624..0cad97d98 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -47,6 +47,8 @@ import ( "github.com/ledgerwatch/erigon/core/rawdb/blockio" "github.com/ledgerwatch/erigon/ethdb/prune" + "github.com/ledgerwatch/erigon/p2p/sentry" + "github.com/ledgerwatch/erigon/p2p/sentry/sentry_multi_client" "github.com/ledgerwatch/erigon/turbo/builder" "github.com/ledgerwatch/erigon/turbo/engineapi" "github.com/ledgerwatch/erigon/turbo/engineapi/engine_block_downloader" @@ -87,7 +89,6 @@ import ( "github.com/ledgerwatch/erigon/cmd/caplin/caplin1" "github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/common/debug" rpcsentinel "github.com/ledgerwatch/erigon-lib/gointerfaces/sentinel" @@ -165,7 +166,7 @@ type Ethereum struct { // downloader fields sentryCtx context.Context sentryCancel context.CancelFunc - sentriesClient *sentry.MultiClient + sentriesClient *sentry_multi_client.MultiClient sentryServers []*sentry.GrpcServer stagedSync *stagedsync.Sync @@ -200,7 +201,10 @@ type Ethereum struct { logger log.Logger sentinel rpcsentinel.SentinelClient - silkworm *silkworm.Silkworm + + silkworm *silkworm.Silkworm + silkwormRPCDaemonService *silkworm.RpcDaemonService + silkwormSentryService *silkworm.SentryService } func splitAddrIntoHostAndPort(addr string) (host string, port int, err error) { @@ -339,15 +343,56 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger backend.gasPrice, _ = uint256.FromBig(config.Miner.GasPrice) + if config.SilkwormPath != "" { + backend.silkworm, err = silkworm.New(config.SilkwormPath, config.Dirs.DataDir) + if err != nil { + return nil, err + } + } + var sentries []direct.SentryClient if len(stack.Config().P2P.SentryAddr) > 0 { for _, addr := range stack.Config().P2P.SentryAddr { - sentryClient, err := sentry.GrpcClient(backend.sentryCtx, addr) + sentryClient, err := sentry_multi_client.GrpcClient(backend.sentryCtx, addr) if err != nil { return nil, err } sentries = append(sentries, sentryClient) } + } else if config.SilkwormSentry { + apiPort := 53774 + apiAddr := fmt.Sprintf("127.0.0.1:%d", apiPort) + p2pConfig := stack.Config().P2P + + collectNodeURLs := func(nodes []*enode.Node) []string { + var urls []string + for _, n := range nodes { + urls = append(urls, n.URLv4()) + } + return urls + } + + settings := silkworm.SentrySettings{ + ClientId: p2pConfig.Name, + ApiPort: apiPort, + Port: p2pConfig.ListenPort(), + Nat: p2pConfig.NATSpec, + NetworkId: config.NetworkID, + NodeKey: crypto.FromECDSA(p2pConfig.PrivateKey), + StaticPeers: collectNodeURLs(p2pConfig.StaticNodes), + Bootnodes: collectNodeURLs(p2pConfig.BootstrapNodes), + NoDiscover: p2pConfig.NoDiscovery, + MaxPeers: p2pConfig.MaxPeers, + } + + silkwormSentryService := backend.silkworm.NewSentryService(settings) + backend.silkwormSentryService = &silkwormSentryService + + sentryClient, err := sentry_multi_client.GrpcClient(backend.sentryCtx, apiAddr) + if err != nil { + return nil, err + } + sentries = append(sentries, sentryClient) } else { var readNodeInfo = func() *eth.NodeInfo { var res *eth.NodeInfo @@ -478,13 +523,6 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger backend.engine = ethconsensusconfig.CreateConsensusEngine(ctx, stack.Config(), chainConfig, consensusConfig, config.Miner.Notify, config.Miner.Noverify, heimdallClient, config.WithoutHeimdall, blockReader, false /* readonly */, logger) - if config.SilkwormEnabled { - backend.silkworm, err = silkworm.New(config.SilkwormPath) - if err != nil { - return nil, err - } - } - inMemoryExecution := func(batch kv.RwTx, header *types.Header, body *types.RawBody, unwindPoint uint64, headersChain []*types.Header, bodiesChain []*types.RawBody, notifications *shards.Notifications) error { terseLogger := log.New() @@ -529,7 +567,7 @@ func New(ctx context.Context, stack *node.Node, config *ethconfig.Config, logger } } - backend.sentriesClient, err = sentry.NewMultiClient( + backend.sentriesClient, err = sentry_multi_client.NewMultiClient( chainKv, stack.Config().NodeName(), chainConfig, @@ -862,24 +900,17 @@ func (s *Ethereum) Init(stack *node.Node, config *ethconfig.Config) error { } s.apiList = jsonrpc.APIList(chainKv, ethRpcClient, txPoolRpcClient, miningRpcClient, ff, stateCache, blockReader, s.agg, httpRpcCfg, s.engine, s.logger) - go func() { - if config.SilkwormEnabled && httpRpcCfg.Enabled { - go func() { - <-ctx.Done() - s.silkworm.StopRpcDaemon() - }() - err = s.silkworm.StartRpcDaemon(chainKv) - if err != nil { - s.logger.Error(err.Error()) - return - } - } else { + + if config.SilkwormRpcDaemon && httpRpcCfg.Enabled { + silkwormRPCDaemonService := s.silkworm.NewRpcDaemonService(chainKv) + s.silkwormRPCDaemonService = &silkwormRPCDaemonService + } else { + go func() { if err := cli.StartRpcServer(ctx, httpRpcCfg, s.apiList, s.logger); err != nil { - s.logger.Error(err.Error()) - return + s.logger.Error("cli.StartRpcServer error", "err", err) } - } - }() + }() + } go s.engineBackendRPC.Start(httpRpcCfg, s.chainDB, s.blockReader, ff, stateCache, s.agg, s.engine, ethRpcClient, txPoolRpcClient, miningRpcClient) @@ -1265,6 +1296,17 @@ func (s *Ethereum) Start() error { s.engine.(*bor.Bor).Start(s.chainDB) } + if s.silkwormRPCDaemonService != nil { + if err := s.silkwormRPCDaemonService.Start(); err != nil { + s.logger.Error("silkworm.StartRpcDaemon error", "err", err) + } + } + if s.silkwormSentryService != nil { + if err := s.silkwormSentryService.Start(); err != nil { + s.logger.Error("silkworm.SentryStart error", "err", err) + } + } + return nil } @@ -1310,7 +1352,17 @@ func (s *Ethereum) Stop() error { } s.chainDB.Close() - if s.config.SilkwormEnabled { + if s.silkwormRPCDaemonService != nil { + if err := s.silkwormRPCDaemonService.Stop(); err != nil { + s.logger.Error("silkworm.StopRpcDaemon error", "err", err) + } + } + if s.silkwormSentryService != nil { + if err := s.silkwormSentryService.Stop(); err != nil { + s.logger.Error("silkworm.SentryStop error", "err", err) + } + } + if s.silkworm != nil { s.silkworm.Close() } @@ -1337,7 +1389,7 @@ func (s *Ethereum) SentryCtx() context.Context { return s.sentryCtx } -func (s *Ethereum) SentryControlServer() *sentry.MultiClient { +func (s *Ethereum) SentryControlServer() *sentry_multi_client.MultiClient { return s.sentriesClient } func (s *Ethereum) BlockIO() (services.FullBlockReader, *blockio.BlockWriter) { diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index 83ea6a406..fe0000506 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -102,7 +102,8 @@ var Defaults = Config{ Produce: true, }, - SilkwormEnabled: false, + // applies if SilkwormPath is set + SilkwormExecution: true, } func init() { @@ -178,8 +179,6 @@ type Config struct { // for nodes to connect to. EthDiscoveryURLs []string - P2PEnabled bool - Prune prune.Mode BatchSize datasize.ByteSize // Batch size for execution stage @@ -253,8 +252,10 @@ type Config struct { ForcePartialCommit bool // Embedded Silkworm support - SilkwormEnabled bool - SilkwormPath string + SilkwormPath string + SilkwormExecution bool + SilkwormRpcDaemon bool + SilkwormSentry bool } type Sync struct { diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go index 5d9db0e26..f5432c115 100644 --- a/eth/ethconfig/gen_config.go +++ b/eth/ethconfig/gen_config.go @@ -22,7 +22,6 @@ func (c Config) MarshalTOML() (interface{}, error) { Genesis *types.Genesis `toml:",omitempty"` NetworkID uint64 EthDiscoveryURLs []string - P2PEnabled bool Prune prune.Mode BatchSize datasize.ByteSize ImportMode bool @@ -47,7 +46,6 @@ func (c Config) MarshalTOML() (interface{}, error) { enc.Genesis = c.Genesis enc.NetworkID = c.NetworkID enc.EthDiscoveryURLs = c.EthDiscoveryURLs - enc.P2PEnabled = c.P2PEnabled enc.Prune = c.Prune enc.BatchSize = c.BatchSize enc.ImportMode = c.ImportMode @@ -73,7 +71,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { Genesis *types.Genesis `toml:",omitempty"` NetworkID *uint64 EthDiscoveryURLs []string - P2PEnabled *bool Prune *prune.Mode BatchSize *datasize.ByteSize ImportMode *bool @@ -107,9 +104,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error { if dec.EthDiscoveryURLs != nil { c.EthDiscoveryURLs = dec.EthDiscoveryURLs } - if dec.P2PEnabled != nil { - c.P2PEnabled = *dec.P2PEnabled - } if dec.Prune != nil { c.Prune = *dec.Prune } diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 45f67ab8d..a2556f9b5 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -37,12 +37,12 @@ import ( "github.com/ledgerwatch/erigon/turbo/services" "github.com/ledgerwatch/log/v3" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/node" + "github.com/ledgerwatch/erigon/p2p/sentry" ) const ( diff --git a/cmd/sentry/sentry/eth_handshake.go b/p2p/sentry/eth_handshake.go similarity index 100% rename from cmd/sentry/sentry/eth_handshake.go rename to p2p/sentry/eth_handshake.go diff --git a/cmd/sentry/sentry/eth_handshake_test.go b/p2p/sentry/eth_handshake_test.go similarity index 100% rename from cmd/sentry/sentry/eth_handshake_test.go rename to p2p/sentry/eth_handshake_test.go diff --git a/cmd/sentry/sentry/sentry_grpc_server.go b/p2p/sentry/sentry_grpc_server.go similarity index 100% rename from cmd/sentry/sentry/sentry_grpc_server.go rename to p2p/sentry/sentry_grpc_server.go diff --git a/cmd/sentry/sentry/sentry_grpc_server_test.go b/p2p/sentry/sentry_grpc_server_test.go similarity index 100% rename from cmd/sentry/sentry/sentry_grpc_server_test.go rename to p2p/sentry/sentry_grpc_server_test.go diff --git a/cmd/sentry/sentry/broadcast.go b/p2p/sentry/sentry_multi_client/broadcast.go similarity index 98% rename from cmd/sentry/sentry/broadcast.go rename to p2p/sentry/sentry_multi_client/broadcast.go index e3f961aaf..f210d8ea5 100644 --- a/cmd/sentry/sentry/broadcast.go +++ b/p2p/sentry/sentry_multi_client/broadcast.go @@ -1,4 +1,4 @@ -package sentry +package sentry_multi_client import ( "context" diff --git a/cmd/sentry/sentry/sentry_api.go b/p2p/sentry/sentry_multi_client/sentry_api.go similarity index 96% rename from cmd/sentry/sentry/sentry_api.go rename to p2p/sentry/sentry_multi_client/sentry_api.go index f850e677e..210e91d1d 100644 --- a/cmd/sentry/sentry/sentry_api.go +++ b/p2p/sentry/sentry_multi_client/sentry_api.go @@ -1,7 +1,8 @@ -package sentry +package sentry_multi_client import ( "context" + "github.com/ledgerwatch/erigon/p2p/sentry" "math/rand" "github.com/holiman/uint256" @@ -72,7 +73,7 @@ func (cs *MultiClient) SendBodyRequest(ctx context.Context, req *bodydownload.Bo if sentPeers == nil || len(sentPeers.Peers) == 0 { continue } - return ConvertH512ToPeerID(sentPeers.Peers[0]), true + return sentry.ConvertH512ToPeerID(sentPeers.Peers[0]), true } return [64]byte{}, false } @@ -119,7 +120,7 @@ func (cs *MultiClient) SendHeaderRequest(ctx context.Context, req *headerdownloa if sentPeers == nil || len(sentPeers.Peers) == 0 { continue } - return ConvertH512ToPeerID(sentPeers.Peers[0]), true + return sentry.ConvertH512ToPeerID(sentPeers.Peers[0]), true } return [64]byte{}, false } diff --git a/cmd/sentry/sentry/sentry_multi_client.go b/p2p/sentry/sentry_multi_client/sentry_multi_client.go similarity index 98% rename from cmd/sentry/sentry/sentry_multi_client.go rename to p2p/sentry/sentry_multi_client/sentry_multi_client.go index f11702011..1766e399e 100644 --- a/cmd/sentry/sentry/sentry_multi_client.go +++ b/p2p/sentry/sentry_multi_client/sentry_multi_client.go @@ -1,4 +1,4 @@ -package sentry +package sentry_multi_client import ( "bytes" @@ -6,6 +6,7 @@ import ( "encoding/hex" "errors" "fmt" + sentry2 "github.com/ledgerwatch/erigon/p2p/sentry" "math/rand" "sort" "sync" @@ -441,7 +442,7 @@ func (cs *MultiClient) blockHeaders(ctx context.Context, pkt eth.BlockHeadersPac return err } defer tx.Rollback() - penalties, err := cs.Hd.ProcessHeadersPOS(csHeaders, tx, ConvertH512ToPeerID(peerID)) + penalties, err := cs.Hd.ProcessHeadersPOS(csHeaders, tx, sentry2.ConvertH512ToPeerID(peerID)) if err != nil { return err } @@ -450,7 +451,7 @@ func (cs *MultiClient) blockHeaders(ctx context.Context, pkt eth.BlockHeadersPac } } else { sort.Sort(headerdownload.HeadersSort(csHeaders)) // Sorting by order of block heights - canRequestMore := cs.Hd.ProcessHeaders(csHeaders, false /* newBlock */, ConvertH512ToPeerID(peerID)) + canRequestMore := cs.Hd.ProcessHeaders(csHeaders, false /* newBlock */, sentry2.ConvertH512ToPeerID(peerID)) if canRequestMore { currentTime := time.Now() @@ -520,7 +521,7 @@ func (cs *MultiClient) newBlock66(ctx context.Context, inreq *proto_sentry.Inbou }) } - cs.Hd.ProcessHeaders(segments, true /* newBlock */, ConvertH512ToPeerID(inreq.PeerId)) // There is only one segment in this case + cs.Hd.ProcessHeaders(segments, true /* newBlock */, sentry2.ConvertH512ToPeerID(inreq.PeerId)) // There is only one segment in this case } else { outreq := proto_sentry.PenalizePeerRequest{ PeerId: inreq.PeerId, @@ -546,7 +547,7 @@ func (cs *MultiClient) newBlock66(ctx context.Context, inreq *proto_sentry.Inbou if _, err1 := sentry.PeerMinBlock(ctx, &outreq, &grpc.EmptyCallOption{}); err1 != nil { cs.logger.Error("Could not send min block for peer", "err", err1) } - cs.logger.Trace(fmt.Sprintf("NewBlockMsg{blockNumber: %d} from [%s]", request.Block.NumberU64(), ConvertH512ToPeerID(inreq.PeerId))) + cs.logger.Trace(fmt.Sprintf("NewBlockMsg{blockNumber: %d} from [%s]", request.Block.NumberU64(), sentry2.ConvertH512ToPeerID(inreq.PeerId))) return nil } @@ -560,7 +561,7 @@ func (cs *MultiClient) blockBodies66(ctx context.Context, inreq *proto_sentry.In // No point processing empty response return nil } - cs.Bd.DeliverBodies(txs, uncles, withdrawals, uint64(len(inreq.Data)), ConvertH512ToPeerID(inreq.PeerId)) + cs.Bd.DeliverBodies(txs, uncles, withdrawals, uint64(len(inreq.Data)), sentry2.ConvertH512ToPeerID(inreq.PeerId)) return nil } @@ -751,7 +752,7 @@ func (cs *MultiClient) handleInboundMessage(ctx context.Context, inreq *proto_se func (cs *MultiClient) HandlePeerEvent(ctx context.Context, event *proto_sentry.PeerEvent, sentry direct.SentryClient) error { eventID := event.EventId.String() - peerID := ConvertH512ToPeerID(event.PeerId) + peerID := sentry2.ConvertH512ToPeerID(event.PeerId) peerIDStr := hex.EncodeToString(peerID[:]) if !cs.logPeerInfo { diff --git a/p2p/server.go b/p2p/server.go index b9b3ed456..4a32e45b3 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -156,6 +156,9 @@ type Config struct { // Internet. NAT nat.Interface `toml:",omitempty"` + // NAT interface description (see NAT.Parse()). + NATSpec string + // If Dialer is set to a non-nil value, the given Dialer // is used to dial outbound peer connections. Dialer NodeDialer `toml:"-"` diff --git a/tests/bor/helper/miner.go b/tests/bor/helper/miner.go index 875ed01d6..fc420100a 100644 --- a/tests/bor/helper/miner.go +++ b/tests/bor/helper/miner.go @@ -142,7 +142,6 @@ func InitMiner(ctx context.Context, genesis *types.Genesis, privKey *ecdsa.Priva RPCGasCap: 50000000, RPCTxFeeCap: 1, // 1 ether Snapshot: ethconfig.BlocksFreezing{NoDownloader: true}, - P2PEnabled: true, StateStream: true, } ethCfg.TxPool.DBDir = nodeCfg.Dirs.TxPool diff --git a/turbo/cli/default_flags.go b/turbo/cli/default_flags.go index 978b96d97..a65180a71 100644 --- a/turbo/cli/default_flags.go +++ b/turbo/cli/default_flags.go @@ -165,6 +165,9 @@ var DefaultFlags = []cli.Flag{ &utils.OtsSearchMaxCapFlag, &utils.SilkwormPathFlag, + &utils.SilkwormExecutionFlag, + &utils.SilkwormRpcDaemonFlag, + &utils.SilkwormSentryFlag, &utils.TrustedSetupFile, } diff --git a/turbo/silkworm/load.go b/turbo/silkworm/load.go deleted file mode 100644 index 4ae890436..000000000 --- a/turbo/silkworm/load.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !linux -// +build !linux - -package silkworm - -import ( - "errors" - "unsafe" -) - -func OpenLibrary(dllPath string) (unsafe.Pointer, error) { - // See https://github.com/golang/go/issues/28024 - return nil, errors.New("Silkworm is only supported on Linux") -} - -func LoadFunction(dllHandle unsafe.Pointer, funcName string) (unsafe.Pointer, error) { - // See https://github.com/golang/go/issues/28024 - return nil, errors.New("Silkworm is only supported on Linux") -} diff --git a/turbo/silkworm/load_linux.go b/turbo/silkworm/load_unix.go similarity index 97% rename from turbo/silkworm/load_linux.go rename to turbo/silkworm/load_unix.go index 5f3113103..11a22c748 100644 --- a/turbo/silkworm/load_linux.go +++ b/turbo/silkworm/load_unix.go @@ -1,3 +1,5 @@ +//go:build unix + package silkworm /* diff --git a/turbo/silkworm/load_windows.go b/turbo/silkworm/load_windows.go new file mode 100644 index 000000000..537411083 --- /dev/null +++ b/turbo/silkworm/load_windows.go @@ -0,0 +1,16 @@ +//go:build windows + +package silkworm + +import ( + "errors" + "unsafe" +) + +func OpenLibrary(dllPath string) (unsafe.Pointer, error) { + return nil, errors.New("not implemented") +} + +func LoadFunction(dllHandle unsafe.Pointer, funcName string) (unsafe.Pointer, error) { + return nil, errors.New("not implemented") +} diff --git a/turbo/silkworm/silkworm.go b/turbo/silkworm/silkworm.go index 1463637af..d27e6a929 100644 --- a/turbo/silkworm/silkworm.go +++ b/turbo/silkworm/silkworm.go @@ -1,156 +1,28 @@ package silkworm /* -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// START silkworm_api.h: C API exported by Silkworm to be used in Erigon. -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -#ifndef SILKWORM_API_H_ -#define SILKWORM_API_H_ - -#include -#include -#include #include +#include +#include "silkworm_api_bridge.h" -#if defined _MSC_VER -#define SILKWORM_EXPORT __declspec(dllexport) -#else -#define SILKWORM_EXPORT __attribute__((visibility("default"))) -#endif - -#if __cplusplus -#define SILKWORM_NOEXCEPT noexcept -#else -#define SILKWORM_NOEXCEPT -#endif - -#if __cplusplus -extern "C" { -#endif - -typedef struct MDBX_env MDBX_env; -typedef struct MDBX_txn MDBX_txn; - -#define SILKWORM_OK 0 -#define SILKWORM_INTERNAL_ERROR 1 -#define SILKWORM_UNKNOWN_ERROR 2 -#define SILKWORM_INVALID_HANDLE 3 -#define SILKWORM_INVALID_PATH 4 -#define SILKWORM_INVALID_SNAPSHOT 5 -#define SILKWORM_INVALID_MDBX_TXN 6 -#define SILKWORM_INVALID_BLOCK_RANGE 7 -#define SILKWORM_BLOCK_NOT_FOUND 8 -#define SILKWORM_UNKNOWN_CHAIN_ID 9 -#define SILKWORM_MDBX_ERROR 10 -#define SILKWORM_INVALID_BLOCK 11 -#define SILKWORM_DECODING_ERROR 12 -#define SILKWORM_TOO_MANY_INSTANCES 13 -#define SILKWORM_INSTANCE_NOT_FOUND 14 -#define SILKWORM_TERMINATION_SIGNAL 15 - -typedef struct SilkwormHandle SilkwormHandle; - -SILKWORM_EXPORT int silkworm_init(SilkwormHandle** handle) SILKWORM_NOEXCEPT; - -struct SilkwormMemoryMappedFile { - const char* file_path; - uint8_t* memory_address; - uint64_t memory_length; -}; - -struct SilkwormHeadersSnapshot { - struct SilkwormMemoryMappedFile segment; - struct SilkwormMemoryMappedFile header_hash_index; -}; - -struct SilkwormBodiesSnapshot { - struct SilkwormMemoryMappedFile segment; - struct SilkwormMemoryMappedFile block_num_index; -}; - -struct SilkwormTransactionsSnapshot { - struct SilkwormMemoryMappedFile segment; - struct SilkwormMemoryMappedFile tx_hash_index; - struct SilkwormMemoryMappedFile tx_hash_2_block_index; -}; - -struct SilkwormChainSnapshot { - struct SilkwormHeadersSnapshot headers; - struct SilkwormBodiesSnapshot bodies; - struct SilkwormTransactionsSnapshot transactions; -}; - -SILKWORM_EXPORT int silkworm_add_snapshot(SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot) SILKWORM_NOEXCEPT; - -SILKWORM_EXPORT int silkworm_start_rpcdaemon(SilkwormHandle* handle, MDBX_env* env) SILKWORM_NOEXCEPT; - -SILKWORM_EXPORT int silkworm_stop_rpcdaemon(SilkwormHandle* handle) SILKWORM_NOEXCEPT; - -SILKWORM_EXPORT int silkworm_execute_blocks( - SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, uint64_t max_block, - uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, - uint64_t* last_executed_block, int* mdbx_error_code) SILKWORM_NOEXCEPT; - -SILKWORM_EXPORT int silkworm_fini(SilkwormHandle* handle) SILKWORM_NOEXCEPT; - -#if __cplusplus -} -#endif - -#endif // SILKWORM_API_H_ - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// END silkworm_api.h: C API exported by Silkworm to be used in Erigon. -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef int (*silkworm_init_func)(SilkwormHandle** handle); - -int call_silkworm_init_func(void* func_ptr, SilkwormHandle** handle) { - return ((silkworm_init_func)func_ptr)(handle); -} - -typedef int (*silkworm_add_snapshot_func)(SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot); - -int call_silkworm_add_snapshot_func(void* func_ptr, SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot) { - return ((silkworm_add_snapshot_func)func_ptr)(handle, snapshot); -} - -typedef int (*silkworm_start_rpcdaemon_func)(SilkwormHandle* handle, MDBX_env* env); - -int call_silkworm_start_rpcdaemon_func(void* func_ptr, SilkwormHandle* handle, MDBX_env* env) { - return ((silkworm_start_rpcdaemon_func)func_ptr)(handle, env); -} - -typedef int (*silkworm_stop_rpcdaemon_func)(SilkwormHandle* handle); - -int call_silkworm_stop_rpcdaemon_func(void* func_ptr, SilkwormHandle* handle) { - return ((silkworm_stop_rpcdaemon_func)func_ptr)(handle); -} - -typedef int (*silkworm_execute_blocks_func)(SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, - uint64_t max_block, uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, - uint64_t* last_executed_block, int* mdbx_error_code); - -int call_silkworm_execute_blocks_func(void* func_ptr, SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, - uint64_t max_block, uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, - uint64_t* last_executed_block, int* mdbx_error_code) { - return ((silkworm_execute_blocks_func)func_ptr)(handle, txn, chain_id, start_block, max_block, batch_size, write_change_sets, - write_receipts, write_call_traces, last_executed_block, mdbx_error_code); -} - -typedef int (*silkworm_fini_func)(SilkwormHandle* handle); - -int call_silkworm_fini_func(void* func_ptr, SilkwormHandle* handle) { - return ((silkworm_fini_func)func_ptr)(handle); +static bool go_string_copy(_GoString_ s, char *dest, size_t size) { + size_t len = _GoStringLen(s); + if (len >= size) return false; + const char *src = _GoStringPtr(s); + strncpy(dest, src, len); + dest[len] = '\0'; + return true; } */ import "C" + import ( "errors" "fmt" "math/big" + "runtime" "unsafe" "github.com/ledgerwatch/erigon-lib/kv" @@ -158,22 +30,23 @@ import ( ) const ( - SILKWORM_OK = iota - SILKWORM_INTERNAL_ERROR - SILKWORM_UNKNOWN_ERROR - SILKWORM_INVALID_HANDLE - SILKWORM_INVALID_PATH - SILKWORM_INVALID_SNAPSHOT - SILKWORM_INVALID_MDBX_TXN - SILKWORM_INVALID_BLOCK_RANGE - SILKWORM_BLOCK_NOT_FOUND - SILKWORM_UNKNOWN_CHAIN_ID - SILKWORM_MDBX_ERROR - SILKWORM_INVALID_BLOCK - SILKWORM_DECODING_ERROR - SILKWORM_TOO_MANY_INSTANCES - SILKWORM_INSTANCE_NOT_FOUND - SILKWORM_TERMINATION_SIGNAL + SILKWORM_OK = C.SILKWORM_OK + SILKWORM_INTERNAL_ERROR = C.SILKWORM_INTERNAL_ERROR + SILKWORM_UNKNOWN_ERROR = C.SILKWORM_UNKNOWN_ERROR + SILKWORM_INVALID_HANDLE = C.SILKWORM_INVALID_HANDLE + SILKWORM_INVALID_PATH = C.SILKWORM_INVALID_PATH + SILKWORM_INVALID_SNAPSHOT = C.SILKWORM_INVALID_SNAPSHOT + SILKWORM_INVALID_MDBX_TXN = C.SILKWORM_INVALID_MDBX_TXN + SILKWORM_INVALID_BLOCK_RANGE = C.SILKWORM_INVALID_BLOCK_RANGE + SILKWORM_BLOCK_NOT_FOUND = C.SILKWORM_BLOCK_NOT_FOUND + SILKWORM_UNKNOWN_CHAIN_ID = C.SILKWORM_UNKNOWN_CHAIN_ID + SILKWORM_MDBX_ERROR = C.SILKWORM_MDBX_ERROR + SILKWORM_INVALID_BLOCK = C.SILKWORM_INVALID_BLOCK + SILKWORM_DECODING_ERROR = C.SILKWORM_DECODING_ERROR + SILKWORM_TOO_MANY_INSTANCES = C.SILKWORM_TOO_MANY_INSTANCES + SILKWORM_INVALID_SETTINGS = C.SILKWORM_INVALID_SETTINGS + SILKWORM_TERMINATION_SIGNAL = C.SILKWORM_TERMINATION_SIGNAL + SILKWORM_SERVICE_ALREADY_STARTED = C.SILKWORM_SERVICE_ALREADY_STARTED ) // ErrInterrupted is the error returned by Silkworm APIs when stopped by any termination signal. @@ -187,10 +60,12 @@ type Silkworm struct { addSnapshot unsafe.Pointer startRpcDaemon unsafe.Pointer stopRpcDaemon unsafe.Pointer + sentryStart unsafe.Pointer + sentryStop unsafe.Pointer executeBlocks unsafe.Pointer } -func New(dllPath string) (*Silkworm, error) { +func New(dllPath string, dataDirPath string) (*Silkworm, error) { dllHandle, err := OpenLibrary(dllPath) if err != nil { return nil, fmt.Errorf("failed to load silkworm library from path %s: %w", dllPath, err) @@ -216,6 +91,14 @@ func New(dllPath string) (*Silkworm, error) { if err != nil { return nil, fmt.Errorf("failed to load silkworm function silkworm_stop_rpcdaemon: %w", err) } + sentryStart, err := LoadFunction(dllHandle, "silkworm_sentry_start") + if err != nil { + return nil, fmt.Errorf("failed to load silkworm function silkworm_sentry_start: %w", err) + } + sentryStop, err := LoadFunction(dllHandle, "silkworm_sentry_stop") + if err != nil { + return nil, fmt.Errorf("failed to load silkworm function silkworm_sentry_stop: %w", err) + } executeBlocks, err := LoadFunction(dllHandle, "silkworm_execute_blocks") if err != nil { return nil, fmt.Errorf("failed to load silkworm function silkworm_execute_blocks: %w", err) @@ -223,14 +106,24 @@ func New(dllPath string) (*Silkworm, error) { silkworm := &Silkworm{ dllHandle: dllHandle, + instance: nil, initFunc: initFunc, finiFunc: finiFunc, addSnapshot: addSnapshot, startRpcDaemon: startRpcDaemon, stopRpcDaemon: stopRpcDaemon, + sentryStart: sentryStart, + sentryStop: sentryStop, executeBlocks: executeBlocks, } - status := C.call_silkworm_init_func(silkworm.initFunc, &silkworm.instance) //nolint:gocritic + + settings := &C.struct_SilkwormSettings{} + + if !C.go_string_copy(dataDirPath, &settings.data_dir_path[0], C.SILKWORM_PATH_SIZE) { + return nil, errors.New("silkworm.New failed to copy dataDirPath") + } + + status := C.call_silkworm_init_func(silkworm.initFunc, &silkworm.instance, settings) //nolint:gocritic if status == SILKWORM_OK { return silkworm, nil } @@ -333,7 +226,129 @@ func (s *Silkworm) StopRpcDaemon() error { return fmt.Errorf("silkworm_stop_rpcdaemon error %d", status) } +type RpcDaemonService struct { + silkworm *Silkworm + db kv.RoDB +} + +func (s *Silkworm) NewRpcDaemonService(db kv.RoDB) RpcDaemonService { + return RpcDaemonService{ + silkworm: s, + db: db, + } +} + +func (service RpcDaemonService) Start() error { + return service.silkworm.StartRpcDaemon(service.db) +} + +func (service RpcDaemonService) Stop() error { + return service.silkworm.StopRpcDaemon() +} + +type SentrySettings struct { + ClientId string + ApiPort int + Port int + Nat string + NetworkId uint64 + NodeKey []byte + StaticPeers []string + Bootnodes []string + NoDiscover bool + MaxPeers int +} + +func copyPeerURLs(list []string, cList *[C.SILKWORM_SENTRY_SETTINGS_PEERS_MAX][C.SILKWORM_SENTRY_SETTINGS_PEER_URL_SIZE]C.char) error { + listLen := len(list) + if listLen > C.SILKWORM_SENTRY_SETTINGS_PEERS_MAX { + return errors.New("copyPeerURLs: peers URL list has too many items") + } + // mark the list end with an empty string + if listLen < C.SILKWORM_SENTRY_SETTINGS_PEERS_MAX { + cList[listLen][0] = 0 + } + for i, url := range list { + if !C.go_string_copy(url, &cList[i][0], C.SILKWORM_SENTRY_SETTINGS_PEER_URL_SIZE) { + return fmt.Errorf("copyPeerURLs: failed to copy peer URL %d", i) + } + } + return nil +} + +func makeCSentrySettings(settings SentrySettings) (*C.struct_SilkwormSentrySettings, error) { + cSettings := &C.struct_SilkwormSentrySettings{ + api_port: C.uint16_t(settings.ApiPort), + port: C.uint16_t(settings.Port), + network_id: C.uint64_t(settings.NetworkId), + no_discover: C.bool(settings.NoDiscover), + max_peers: C.size_t(settings.MaxPeers), + } + if !C.go_string_copy(settings.ClientId, &cSettings.client_id[0], C.SILKWORM_SENTRY_SETTINGS_CLIENT_ID_SIZE) { + return nil, errors.New("makeCSentrySettings failed to copy ClientId") + } + if !C.go_string_copy(settings.Nat, &cSettings.nat[0], C.SILKWORM_SENTRY_SETTINGS_NAT_SIZE) { + return nil, errors.New("makeCSentrySettings failed to copy Nat") + } + if len(settings.NodeKey) == C.SILKWORM_SENTRY_SETTINGS_NODE_KEY_SIZE { + C.memcpy(unsafe.Pointer(&cSettings.node_key[0]), unsafe.Pointer(&settings.NodeKey[0]), C.SILKWORM_SENTRY_SETTINGS_NODE_KEY_SIZE) //nolint:gocritic + } else { + return nil, errors.New("makeCSentrySettings failed to copy NodeKey") + } + if err := copyPeerURLs(settings.StaticPeers, &cSettings.static_peers); err != nil { + return nil, fmt.Errorf("copyPeerURLs failed to copy StaticPeers: %w", err) + } + if err := copyPeerURLs(settings.Bootnodes, &cSettings.bootnodes); err != nil { + return nil, fmt.Errorf("copyPeerURLs failed to copy Bootnodes: %w", err) + } + return cSettings, nil +} + +func (s *Silkworm) SentryStart(settings SentrySettings) error { + cSettings, err := makeCSentrySettings(settings) + if err != nil { + return err + } + status := C.call_silkworm_sentry_start_func(s.sentryStart, s.instance, cSettings) + if status == SILKWORM_OK { + return nil + } + return fmt.Errorf("silkworm_sentry_start error %d", status) +} + +func (s *Silkworm) SentryStop() error { + status := C.call_silkworm_stop_rpcdaemon_func(s.sentryStop, s.instance) + if status == SILKWORM_OK { + return nil + } + return fmt.Errorf("silkworm_sentry_stop error %d", status) +} + +type SentryService struct { + silkworm *Silkworm + settings SentrySettings +} + +func (s *Silkworm) NewSentryService(settings SentrySettings) SentryService { + return SentryService{ + silkworm: s, + settings: settings, + } +} + +func (service SentryService) Start() error { + return service.silkworm.SentryStart(service.settings) +} + +func (service SentryService) Stop() error { + return service.silkworm.SentryStop() +} + func (s *Silkworm) ExecuteBlocks(txn kv.Tx, chainID *big.Int, startBlock uint64, maxBlock uint64, batchSize uint64, writeChangeSets, writeReceipts, writeCallTraces bool) (lastExecutedBlock uint64, err error) { + if runtime.GOOS == "darwin" { + return 0, errors.New("silkworm execution is incompatible with Go runtime on macOS due to stack size mismatch (see https://github.com/golang/go/issues/28024)") + } + cTxn := (*C.MDBX_txn)(txn.CHandle()) cChainId := C.uint64_t(chainID.Uint64()) cStartBlock := C.uint64_t(startBlock) @@ -351,7 +366,7 @@ func (s *Silkworm) ExecuteBlocks(txn kv.Tx, chainID *big.Int, startBlock uint64, if status == SILKWORM_OK { return lastExecutedBlock, nil } - // Handle special erros + // Handle special errors if status == SILKWORM_INVALID_BLOCK { return lastExecutedBlock, consensus.ErrInvalidBlock } diff --git a/turbo/silkworm/silkworm_api.h b/turbo/silkworm/silkworm_api.h new file mode 100644 index 000000000..91ea51955 --- /dev/null +++ b/turbo/silkworm/silkworm_api.h @@ -0,0 +1,206 @@ +/* + Copyright 2023 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SILKWORM_API_H_ +#define SILKWORM_API_H_ + +// C API exported by Silkworm to be used in Erigon. + +#include // NOLINT(*-deprecated-headers) +#include // NOLINT(*-deprecated-headers) +#include // NOLINT(*-deprecated-headers) + +#if defined _MSC_VER +#define SILKWORM_EXPORT __declspec(dllexport) +#else +#define SILKWORM_EXPORT __attribute__((visibility("default"))) +#endif + +#if __cplusplus +#define SILKWORM_NOEXCEPT noexcept +#else +#define SILKWORM_NOEXCEPT +#endif + +#if __cplusplus +extern "C" { +#endif + +// Silkworm library error codes (SILKWORM_OK indicates no error, i.e. success) + +#define SILKWORM_OK 0 +#define SILKWORM_INTERNAL_ERROR 1 +#define SILKWORM_UNKNOWN_ERROR 2 +#define SILKWORM_INVALID_HANDLE 3 +#define SILKWORM_INVALID_PATH 4 +#define SILKWORM_INVALID_SNAPSHOT 5 +#define SILKWORM_INVALID_MDBX_TXN 6 +#define SILKWORM_INVALID_BLOCK_RANGE 7 +#define SILKWORM_BLOCK_NOT_FOUND 8 +#define SILKWORM_UNKNOWN_CHAIN_ID 9 +#define SILKWORM_MDBX_ERROR 10 +#define SILKWORM_INVALID_BLOCK 11 +#define SILKWORM_DECODING_ERROR 12 +#define SILKWORM_TOO_MANY_INSTANCES 13 +#define SILKWORM_INVALID_SETTINGS 14 +#define SILKWORM_TERMINATION_SIGNAL 15 +#define SILKWORM_SERVICE_ALREADY_STARTED 16 + +typedef struct MDBX_env MDBX_env; +typedef struct MDBX_txn MDBX_txn; +typedef struct SilkwormHandle SilkwormHandle; + +struct SilkwormMemoryMappedFile { + const char* file_path; + uint8_t* memory_address; + uint64_t memory_length; +}; + +struct SilkwormHeadersSnapshot { + struct SilkwormMemoryMappedFile segment; + struct SilkwormMemoryMappedFile header_hash_index; +}; + +struct SilkwormBodiesSnapshot { + struct SilkwormMemoryMappedFile segment; + struct SilkwormMemoryMappedFile block_num_index; +}; + +struct SilkwormTransactionsSnapshot { + struct SilkwormMemoryMappedFile segment; + struct SilkwormMemoryMappedFile tx_hash_index; + struct SilkwormMemoryMappedFile tx_hash_2_block_index; +}; + +struct SilkwormChainSnapshot { + struct SilkwormHeadersSnapshot headers; + struct SilkwormBodiesSnapshot bodies; + struct SilkwormTransactionsSnapshot transactions; +}; + +#define SILKWORM_PATH_SIZE 260 + +struct SilkwormSettings { + //! Data directory path in UTF-8. + char data_dir_path[SILKWORM_PATH_SIZE]; +}; + +/** + * \brief Initialize the Silkworm C API library. + * \param[in,out] handle Silkworm instance handle returned on successful initialization. + * \param[in] settings General Silkworm settings. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + */ +SILKWORM_EXPORT int silkworm_init( + SilkwormHandle** handle, + const struct SilkwormSettings* settings) SILKWORM_NOEXCEPT; + +/** + * \brief Build a set of indexes for the given snapshots. + * \param[in] handle A valid Silkworm instance handle, got with silkworm_init. + * \param[in] snapshots An array of snapshots to index. + * \param[in] indexPaths An array of paths to write indexes to. + * Note that the name of the index is a part of the path and it is used to determine the index type. + * \param[in] len The number of snapshots and paths. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure on some or all indexes. + */ +SILKWORM_EXPORT int silkworm_build_recsplit_indexes(SilkwormHandle* handle, struct SilkwormMemoryMappedFile* snapshots[], int len) SILKWORM_NOEXCEPT; + +/** + * \brief Notify Silkworm about a new snapshot to use. + * \param[in] handle A valid Silkworm instance handle, got with silkworm_init. + * \param[in] snapshot A snapshot to use. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + */ +SILKWORM_EXPORT int silkworm_add_snapshot(SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot) SILKWORM_NOEXCEPT; + +/** + * \brief Start Silkworm RPC daemon. + * \param[in] handle A valid Silkworm instance handle, got with silkworm_init.Must not be zero. + * \param[in] env An valid MDBX environment. Must not be zero. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + */ +SILKWORM_EXPORT int silkworm_start_rpcdaemon(SilkwormHandle* handle, MDBX_env* env) SILKWORM_NOEXCEPT; + +/** + * \brief Stop Silkworm RPC daemon and wait for its termination. + * \param[in] handle A valid Silkworm instance handle, got with silkworm_init. Must not be zero. + * \param[in] snapshot A snapshot to use. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + */ +SILKWORM_EXPORT int silkworm_stop_rpcdaemon(SilkwormHandle* handle) SILKWORM_NOEXCEPT; + +#define SILKWORM_SENTRY_SETTINGS_CLIENT_ID_SIZE 128 +#define SILKWORM_SENTRY_SETTINGS_NAT_SIZE 50 +#define SILKWORM_SENTRY_SETTINGS_NODE_KEY_SIZE 32 +#define SILKWORM_SENTRY_SETTINGS_PEERS_MAX 128 +#define SILKWORM_SENTRY_SETTINGS_PEER_URL_SIZE 200 + +struct SilkwormSentrySettings { + char client_id[SILKWORM_SENTRY_SETTINGS_CLIENT_ID_SIZE]; + uint16_t api_port; + uint16_t port; + char nat[SILKWORM_SENTRY_SETTINGS_NAT_SIZE]; + uint64_t network_id; + uint8_t node_key[SILKWORM_SENTRY_SETTINGS_NODE_KEY_SIZE]; + char static_peers[SILKWORM_SENTRY_SETTINGS_PEERS_MAX][SILKWORM_SENTRY_SETTINGS_PEER_URL_SIZE]; + char bootnodes[SILKWORM_SENTRY_SETTINGS_PEERS_MAX][SILKWORM_SENTRY_SETTINGS_PEER_URL_SIZE]; + bool no_discover; + size_t max_peers; +}; + +SILKWORM_EXPORT int silkworm_sentry_start(SilkwormHandle* handle, const struct SilkwormSentrySettings* settings) SILKWORM_NOEXCEPT; +SILKWORM_EXPORT int silkworm_sentry_stop(SilkwormHandle* handle) SILKWORM_NOEXCEPT; + +/** + * \brief Execute a batch of blocks and write resulting changes into the database. + * \param[in] handle A valid Silkworm instance handle, got with silkworm_init. + * \param[in] txn A valid read-write MDBX transaction. Must not be zero. + * This function does not commit nor abort the transaction. + * \param[in] chain_id EIP-155 chain ID. SILKWORM_UNKNOWN_CHAIN_ID is returned in case of an unknown or unsupported chain. + * \param[in] start_block The block height to start the execution from. + * \param[in] max_block Do not execute after this block. + * max_block may be executed, or the execution may stop earlier if the batch is full. + * \param[in] batch_size The size of DB changes to accumulate before returning from this method. + * Pass 0 if you want to execute just 1 block. + * \param[in] write_change_sets Whether to write state changes into the DB. + * \param[in] write_receipts Whether to write CBOR-encoded receipts into the DB. + * \param[in] write_call_traces Whether to write call traces into the DB. + * \param[out] last_executed_block The height of the last successfully executed block. + * Not written to if no blocks were executed, otherwise *last_executed_block ≤ max_block. + * \param[out] mdbx_error_code If an MDBX error occurs (this function returns kSilkwormMdbxError) + * and mdbx_error_code isn't NULL, it's populated with the relevant MDBX error code. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + * SILKWORM_BLOCK_NOT_FOUND is probably OK: it simply means that the execution reached the end of the chain + * (blocks up to and incl. last_executed_block were still executed). + */ +SILKWORM_EXPORT int silkworm_execute_blocks( + SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, uint64_t max_block, + uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, + uint64_t* last_executed_block, int* mdbx_error_code) SILKWORM_NOEXCEPT; + +/** + * \brief Finalize the Silkworm C API library. + * \param[in] handle A valid Silkworm instance handle got with silkworm_init. + * \return SILKWORM_OK (=0) on success, a non-zero error value on failure. + */ +SILKWORM_EXPORT int silkworm_fini(SilkwormHandle* handle) SILKWORM_NOEXCEPT; + +#if __cplusplus +} +#endif + +#endif // SILKWORM_API_H_ diff --git a/turbo/silkworm/silkworm_api_bridge.h b/turbo/silkworm/silkworm_api_bridge.h new file mode 100644 index 000000000..2969ca62e --- /dev/null +++ b/turbo/silkworm/silkworm_api_bridge.h @@ -0,0 +1,75 @@ +/* + Copyright 2023 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SILKWORM_API_FUNC_H_ +#define SILKWORM_API_FUNC_H_ + +#include "silkworm_api.h" + +typedef int (*silkworm_init_func)(SilkwormHandle** handle, const struct SilkwormSettings* settings); + +int call_silkworm_init_func(void* func_ptr, SilkwormHandle** handle, const struct SilkwormSettings* settings) { + return ((silkworm_init_func)func_ptr)(handle, settings); +} + +typedef int (*silkworm_add_snapshot_func)(SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot); + +int call_silkworm_add_snapshot_func(void* func_ptr, SilkwormHandle* handle, struct SilkwormChainSnapshot* snapshot) { + return ((silkworm_add_snapshot_func)func_ptr)(handle, snapshot); +} + +typedef int (*silkworm_start_rpcdaemon_func)(SilkwormHandle* handle, MDBX_env* env); + +int call_silkworm_start_rpcdaemon_func(void* func_ptr, SilkwormHandle* handle, MDBX_env* env) { + return ((silkworm_start_rpcdaemon_func)func_ptr)(handle, env); +} + +typedef int (*silkworm_stop_rpcdaemon_func)(SilkwormHandle* handle); + +int call_silkworm_stop_rpcdaemon_func(void* func_ptr, SilkwormHandle* handle) { + return ((silkworm_stop_rpcdaemon_func)func_ptr)(handle); +} + +typedef int (*silkworm_sentry_start_func)(SilkwormHandle* handle, const struct SilkwormSentrySettings* settings); + +int call_silkworm_sentry_start_func(void* func_ptr, SilkwormHandle* handle, const struct SilkwormSentrySettings* settings) { + return ((silkworm_sentry_start_func)func_ptr)(handle, settings); +} + +typedef int (*silkworm_sentry_stop_func)(SilkwormHandle* handle); + +int call_silkworm_sentry_stop_func(void* func_ptr, SilkwormHandle* handle) { + return ((silkworm_sentry_stop_func)func_ptr)(handle); +} + +typedef int (*silkworm_execute_blocks_func)(SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, + uint64_t max_block, uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, + uint64_t* last_executed_block, int* mdbx_error_code); + +int call_silkworm_execute_blocks_func(void* func_ptr, SilkwormHandle* handle, MDBX_txn* txn, uint64_t chain_id, uint64_t start_block, + uint64_t max_block, uint64_t batch_size, bool write_change_sets, bool write_receipts, bool write_call_traces, + uint64_t* last_executed_block, int* mdbx_error_code) { + return ((silkworm_execute_blocks_func)func_ptr)(handle, txn, chain_id, start_block, max_block, batch_size, write_change_sets, + write_receipts, write_call_traces, last_executed_block, mdbx_error_code); +} + +typedef int (*silkworm_fini_func)(SilkwormHandle* handle); + +int call_silkworm_fini_func(void* func_ptr, SilkwormHandle* handle) { + return ((silkworm_fini_func)func_ptr)(handle); +} + +#endif // SILKWORM_API_FUNC_H_ diff --git a/turbo/stages/mock/mock_sentry.go b/turbo/stages/mock/mock_sentry.go index df9e7df5f..de6ba517f 100644 --- a/turbo/stages/mock/mock_sentry.go +++ b/turbo/stages/mock/mock_sentry.go @@ -33,7 +33,6 @@ import ( "github.com/ledgerwatch/erigon-lib/txpool/txpoolcfg" types2 "github.com/ledgerwatch/erigon-lib/types" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/consensus/bor" "github.com/ledgerwatch/erigon/consensus/ethash" @@ -51,6 +50,7 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/ethdb/prune" + "github.com/ledgerwatch/erigon/p2p/sentry/sentry_multi_client" "github.com/ledgerwatch/erigon/params" "github.com/ledgerwatch/erigon/rlp" "github.com/ledgerwatch/erigon/turbo/builder" @@ -84,7 +84,7 @@ type MockSentry struct { MiningSync *stagedsync.Sync PendingBlocks chan *types.Block MinedBlocks chan *types.Block - sentriesClient *sentry.MultiClient + sentriesClient *sentry_multi_client.MultiClient Key *ecdsa.PrivateKey Genesis *types.Block SentryClient direct.SentryClient @@ -367,7 +367,7 @@ func MockWithEverything(tb testing.TB, gspec *types.Genesis, key *ecdsa.PrivateK networkID := uint64(1) maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 } - mock.sentriesClient, err = sentry.NewMultiClient( + mock.sentriesClient, err = sentry_multi_client.NewMultiClient( mock.DB, "mock", mock.ChainConfig, diff --git a/turbo/stages/stageloop.go b/turbo/stages/stageloop.go index 3ee7d432f..56a1bbd60 100644 --- a/turbo/stages/stageloop.go +++ b/turbo/stages/stageloop.go @@ -19,7 +19,6 @@ import ( "github.com/ledgerwatch/erigon-lib/kv/membatchwithdb" "github.com/ledgerwatch/erigon-lib/state" - "github.com/ledgerwatch/erigon/cmd/sentry/sentry" "github.com/ledgerwatch/erigon/consensus" "github.com/ledgerwatch/erigon/consensus/bor/finality/flags" "github.com/ledgerwatch/erigon/consensus/bor/heimdall" @@ -32,6 +31,7 @@ import ( "github.com/ledgerwatch/erigon/eth/stagedsync" "github.com/ledgerwatch/erigon/eth/stagedsync/stages" "github.com/ledgerwatch/erigon/p2p" + "github.com/ledgerwatch/erigon/p2p/sentry/sentry_multi_client" "github.com/ledgerwatch/erigon/turbo/engineapi/engine_helpers" "github.com/ledgerwatch/erigon/turbo/services" "github.com/ledgerwatch/erigon/turbo/shards" @@ -443,11 +443,18 @@ func StateStep(ctx context.Context, chainReader consensus.ChainReader, engine co return nil } +func silkwormForExecutionStage(silkworm *silkworm.Silkworm, cfg *ethconfig.Config) *silkworm.Silkworm { + if cfg.SilkwormExecution { + return silkworm + } + return nil +} + func NewDefaultStages(ctx context.Context, db kv.RwDB, p2pCfg p2p.Config, cfg *ethconfig.Config, - controlServer *sentry.MultiClient, + controlServer *sentry_multi_client.MultiClient, notifications *shards.Notifications, snapDownloader proto_downloader.DownloaderClient, blockReader services.FullBlockReader, @@ -496,7 +503,7 @@ func NewDefaultStages(ctx context.Context, cfg.Genesis, cfg.Sync, agg, - silkworm, + silkwormForExecutionStage(silkworm, cfg), ), stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, true, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg), @@ -511,7 +518,7 @@ func NewDefaultStages(ctx context.Context, func NewPipelineStages(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config, - controlServer *sentry.MultiClient, + controlServer *sentry_multi_client.MultiClient, notifications *shards.Notifications, snapDownloader proto_downloader.DownloaderClient, blockReader services.FullBlockReader, @@ -551,7 +558,7 @@ func NewPipelineStages(ctx context.Context, cfg.Genesis, cfg.Sync, agg, - silkworm, + silkwormForExecutionStage(silkworm, cfg), ), stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, checkStateRoot, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg), @@ -563,7 +570,7 @@ func NewPipelineStages(ctx context.Context, runInTestMode) } -func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config, controlServer *sentry.MultiClient, +func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config, controlServer *sentry_multi_client.MultiClient, dirs datadir.Dirs, notifications *shards.Notifications, blockReader services.FullBlockReader, blockWriter *blockio.BlockWriter, agg *state.AggregatorV3, silkworm *silkworm.Silkworm, logger log.Logger) *stagedsync.Sync { return stagedsync.New( @@ -590,7 +597,7 @@ func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config cfg.Genesis, cfg.Sync, agg, - silkworm, + silkwormForExecutionStage(silkworm, cfg), ), stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3), stagedsync.StageTrieCfg(db, true, true, true, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg)),