mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-21 19:20:39 +00:00
p2p: silkworm sentry (#8527)
This commit is contained in:
parent
329d18ef6f
commit
d92898a508
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon/consensus/bor/heimdallgrpc"
|
"github.com/ledgerwatch/erigon/consensus/bor/heimdallgrpc"
|
||||||
"github.com/ledgerwatch/erigon/core/rawdb/blockio"
|
"github.com/ledgerwatch/erigon/core/rawdb/blockio"
|
||||||
"github.com/ledgerwatch/erigon/node/nodecfg"
|
"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/builder"
|
||||||
"github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks"
|
"github.com/ledgerwatch/erigon/turbo/snapshotsync/freezeblocks"
|
||||||
"github.com/ledgerwatch/log/v3"
|
"github.com/ledgerwatch/log/v3"
|
||||||
@ -33,7 +34,6 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon-lib/kv/rawdbv3"
|
"github.com/ledgerwatch/erigon-lib/kv/rawdbv3"
|
||||||
libstate "github.com/ledgerwatch/erigon-lib/state"
|
libstate "github.com/ledgerwatch/erigon-lib/state"
|
||||||
"github.com/ledgerwatch/erigon/cmd/hack/tool/fromdb"
|
"github.com/ledgerwatch/erigon/cmd/hack/tool/fromdb"
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/consensus"
|
"github.com/ledgerwatch/erigon/consensus"
|
||||||
"github.com/ledgerwatch/erigon/core"
|
"github.com/ledgerwatch/erigon/core"
|
||||||
"github.com/ledgerwatch/erigon/core/rawdb"
|
"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 }
|
maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 }
|
||||||
|
|
||||||
sentryControlServer, err := sentry.NewMultiClient(
|
sentryControlServer, err := sentry_multi_client.NewMultiClient(
|
||||||
db,
|
db,
|
||||||
"",
|
"",
|
||||||
chainConfig,
|
chainConfig,
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
"github.com/ledgerwatch/erigon-lib/common/datadir"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/cmd/utils"
|
"github.com/ledgerwatch/erigon/cmd/utils"
|
||||||
"github.com/ledgerwatch/erigon/common/paths"
|
"github.com/ledgerwatch/erigon/common/paths"
|
||||||
|
"github.com/ledgerwatch/erigon/p2p/sentry"
|
||||||
"github.com/ledgerwatch/erigon/turbo/debug"
|
"github.com/ledgerwatch/erigon/turbo/debug"
|
||||||
"github.com/ledgerwatch/erigon/turbo/logging"
|
"github.com/ledgerwatch/erigon/turbo/logging"
|
||||||
node2 "github.com/ledgerwatch/erigon/turbo/node"
|
node2 "github.com/ledgerwatch/erigon/turbo/node"
|
||||||
|
@ -838,9 +838,21 @@ var (
|
|||||||
|
|
||||||
SilkwormPathFlag = cli.StringFlag{
|
SilkwormPathFlag = cli.StringFlag{
|
||||||
Name: "silkworm.path",
|
Name: "silkworm.path",
|
||||||
Usage: "Path to the silkworm_api library (enables embedded Silkworm execution)",
|
Usage: "Path to the Silkworm library",
|
||||||
Value: "",
|
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}
|
var MetricFlags = []cli.Flag{&MetricsEnabledFlag, &MetricsHTTPFlag, &MetricsPortFlag}
|
||||||
@ -1031,6 +1043,7 @@ func NewP2PConfig(
|
|||||||
return nil, fmt.Errorf("invalid nat option %s: %w", natSetting, err)
|
return nil, fmt.Errorf("invalid nat option %s: %w", natSetting, err)
|
||||||
}
|
}
|
||||||
cfg.NAT = natif
|
cfg.NAT = natif
|
||||||
|
cfg.NATSpec = natSetting
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1079,11 +1092,13 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
|
|||||||
// setNAT creates a port mapper from command line flags.
|
// setNAT creates a port mapper from command line flags.
|
||||||
func setNAT(ctx *cli.Context, cfg *p2p.Config) {
|
func setNAT(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
if ctx.IsSet(NATFlag.Name) {
|
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 {
|
if err != nil {
|
||||||
Fatalf("Option %s: %v", NATFlag.Name, err)
|
Fatalf("Option %s: %v", NATFlag.Name, err)
|
||||||
}
|
}
|
||||||
cfg.NAT = natif
|
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
|
ethPeers := cfg.MaxPeers
|
||||||
cfg.Name = nodeName
|
|
||||||
logger.Info("Maximum peer count", "ETH", ethPeers, "total", cfg.MaxPeers)
|
logger.Info("Maximum peer count", "ETH", ethPeers, "total", cfg.MaxPeers)
|
||||||
|
|
||||||
if netrestrict := ctx.String(NetrestrictFlag.Name); netrestrict != "" {
|
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) {
|
func setSilkworm(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
cfg.SilkwormEnabled = ctx.IsSet(SilkwormPathFlag.Name)
|
cfg.SilkwormPath = ctx.String(SilkwormPathFlag.Name)
|
||||||
if cfg.SilkwormEnabled {
|
if ctx.IsSet(SilkwormExecutionFlag.Name) {
|
||||||
cfg.SilkwormPath = ctx.String(SilkwormPathFlag.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
|
// 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)
|
setSilkworm(ctx, cfg)
|
||||||
|
|
||||||
cfg.Ethstats = ctx.String(EthStatsURLFlag.Name)
|
cfg.Ethstats = ctx.String(EthStatsURLFlag.Name)
|
||||||
cfg.P2PEnabled = len(nodeConfig.P2P.SentryAddr) == 0
|
|
||||||
cfg.HistoryV3 = ctx.Bool(HistoryV3Flag.Name)
|
cfg.HistoryV3 = ctx.Bool(HistoryV3Flag.Name)
|
||||||
if ctx.IsSet(NetworkIdFlag.Name) {
|
if ctx.IsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkID = ctx.Uint64(NetworkIdFlag.Name)
|
cfg.NetworkID = ctx.Uint64(NetworkIdFlag.Name)
|
||||||
|
112
eth/backend.go
112
eth/backend.go
@ -47,6 +47,8 @@ import (
|
|||||||
|
|
||||||
"github.com/ledgerwatch/erigon/core/rawdb/blockio"
|
"github.com/ledgerwatch/erigon/core/rawdb/blockio"
|
||||||
"github.com/ledgerwatch/erigon/ethdb/prune"
|
"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/builder"
|
||||||
"github.com/ledgerwatch/erigon/turbo/engineapi"
|
"github.com/ledgerwatch/erigon/turbo/engineapi"
|
||||||
"github.com/ledgerwatch/erigon/turbo/engineapi/engine_block_downloader"
|
"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/caplin/caplin1"
|
||||||
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli"
|
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/cli"
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/common/debug"
|
"github.com/ledgerwatch/erigon/common/debug"
|
||||||
|
|
||||||
rpcsentinel "github.com/ledgerwatch/erigon-lib/gointerfaces/sentinel"
|
rpcsentinel "github.com/ledgerwatch/erigon-lib/gointerfaces/sentinel"
|
||||||
@ -165,7 +166,7 @@ type Ethereum struct {
|
|||||||
// downloader fields
|
// downloader fields
|
||||||
sentryCtx context.Context
|
sentryCtx context.Context
|
||||||
sentryCancel context.CancelFunc
|
sentryCancel context.CancelFunc
|
||||||
sentriesClient *sentry.MultiClient
|
sentriesClient *sentry_multi_client.MultiClient
|
||||||
sentryServers []*sentry.GrpcServer
|
sentryServers []*sentry.GrpcServer
|
||||||
|
|
||||||
stagedSync *stagedsync.Sync
|
stagedSync *stagedsync.Sync
|
||||||
@ -200,7 +201,10 @@ type Ethereum struct {
|
|||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
sentinel rpcsentinel.SentinelClient
|
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) {
|
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)
|
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
|
var sentries []direct.SentryClient
|
||||||
if len(stack.Config().P2P.SentryAddr) > 0 {
|
if len(stack.Config().P2P.SentryAddr) > 0 {
|
||||||
for _, addr := range stack.Config().P2P.SentryAddr {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sentries = append(sentries, sentryClient)
|
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 {
|
} else {
|
||||||
var readNodeInfo = func() *eth.NodeInfo {
|
var readNodeInfo = func() *eth.NodeInfo {
|
||||||
var res *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)
|
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,
|
inMemoryExecution := func(batch kv.RwTx, header *types.Header, body *types.RawBody, unwindPoint uint64, headersChain []*types.Header, bodiesChain []*types.RawBody,
|
||||||
notifications *shards.Notifications) error {
|
notifications *shards.Notifications) error {
|
||||||
terseLogger := log.New()
|
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,
|
chainKv,
|
||||||
stack.Config().NodeName(),
|
stack.Config().NodeName(),
|
||||||
chainConfig,
|
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)
|
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 {
|
if config.SilkwormRpcDaemon && httpRpcCfg.Enabled {
|
||||||
go func() {
|
silkwormRPCDaemonService := s.silkworm.NewRpcDaemonService(chainKv)
|
||||||
<-ctx.Done()
|
s.silkwormRPCDaemonService = &silkwormRPCDaemonService
|
||||||
s.silkworm.StopRpcDaemon()
|
} else {
|
||||||
}()
|
go func() {
|
||||||
err = s.silkworm.StartRpcDaemon(chainKv)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error(err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := cli.StartRpcServer(ctx, httpRpcCfg, s.apiList, s.logger); err != nil {
|
if err := cli.StartRpcServer(ctx, httpRpcCfg, s.apiList, s.logger); err != nil {
|
||||||
s.logger.Error(err.Error())
|
s.logger.Error("cli.StartRpcServer error", "err", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}()
|
||||||
}()
|
}
|
||||||
|
|
||||||
go s.engineBackendRPC.Start(httpRpcCfg, s.chainDB, s.blockReader, ff, stateCache, s.agg, s.engine, ethRpcClient, txPoolRpcClient, miningRpcClient)
|
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)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1310,7 +1352,17 @@ func (s *Ethereum) Stop() error {
|
|||||||
}
|
}
|
||||||
s.chainDB.Close()
|
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()
|
s.silkworm.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1337,7 +1389,7 @@ func (s *Ethereum) SentryCtx() context.Context {
|
|||||||
return s.sentryCtx
|
return s.sentryCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) SentryControlServer() *sentry.MultiClient {
|
func (s *Ethereum) SentryControlServer() *sentry_multi_client.MultiClient {
|
||||||
return s.sentriesClient
|
return s.sentriesClient
|
||||||
}
|
}
|
||||||
func (s *Ethereum) BlockIO() (services.FullBlockReader, *blockio.BlockWriter) {
|
func (s *Ethereum) BlockIO() (services.FullBlockReader, *blockio.BlockWriter) {
|
||||||
|
@ -102,7 +102,8 @@ var Defaults = Config{
|
|||||||
Produce: true,
|
Produce: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
SilkwormEnabled: false,
|
// applies if SilkwormPath is set
|
||||||
|
SilkwormExecution: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -178,8 +179,6 @@ type Config struct {
|
|||||||
// for nodes to connect to.
|
// for nodes to connect to.
|
||||||
EthDiscoveryURLs []string
|
EthDiscoveryURLs []string
|
||||||
|
|
||||||
P2PEnabled bool
|
|
||||||
|
|
||||||
Prune prune.Mode
|
Prune prune.Mode
|
||||||
BatchSize datasize.ByteSize // Batch size for execution stage
|
BatchSize datasize.ByteSize // Batch size for execution stage
|
||||||
|
|
||||||
@ -253,8 +252,10 @@ type Config struct {
|
|||||||
ForcePartialCommit bool
|
ForcePartialCommit bool
|
||||||
|
|
||||||
// Embedded Silkworm support
|
// Embedded Silkworm support
|
||||||
SilkwormEnabled bool
|
SilkwormPath string
|
||||||
SilkwormPath string
|
SilkwormExecution bool
|
||||||
|
SilkwormRpcDaemon bool
|
||||||
|
SilkwormSentry bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sync struct {
|
type Sync struct {
|
||||||
|
@ -22,7 +22,6 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
Genesis *types.Genesis `toml:",omitempty"`
|
Genesis *types.Genesis `toml:",omitempty"`
|
||||||
NetworkID uint64
|
NetworkID uint64
|
||||||
EthDiscoveryURLs []string
|
EthDiscoveryURLs []string
|
||||||
P2PEnabled bool
|
|
||||||
Prune prune.Mode
|
Prune prune.Mode
|
||||||
BatchSize datasize.ByteSize
|
BatchSize datasize.ByteSize
|
||||||
ImportMode bool
|
ImportMode bool
|
||||||
@ -47,7 +46,6 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
|||||||
enc.Genesis = c.Genesis
|
enc.Genesis = c.Genesis
|
||||||
enc.NetworkID = c.NetworkID
|
enc.NetworkID = c.NetworkID
|
||||||
enc.EthDiscoveryURLs = c.EthDiscoveryURLs
|
enc.EthDiscoveryURLs = c.EthDiscoveryURLs
|
||||||
enc.P2PEnabled = c.P2PEnabled
|
|
||||||
enc.Prune = c.Prune
|
enc.Prune = c.Prune
|
||||||
enc.BatchSize = c.BatchSize
|
enc.BatchSize = c.BatchSize
|
||||||
enc.ImportMode = c.ImportMode
|
enc.ImportMode = c.ImportMode
|
||||||
@ -73,7 +71,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
Genesis *types.Genesis `toml:",omitempty"`
|
Genesis *types.Genesis `toml:",omitempty"`
|
||||||
NetworkID *uint64
|
NetworkID *uint64
|
||||||
EthDiscoveryURLs []string
|
EthDiscoveryURLs []string
|
||||||
P2PEnabled *bool
|
|
||||||
Prune *prune.Mode
|
Prune *prune.Mode
|
||||||
BatchSize *datasize.ByteSize
|
BatchSize *datasize.ByteSize
|
||||||
ImportMode *bool
|
ImportMode *bool
|
||||||
@ -107,9 +104,6 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
|||||||
if dec.EthDiscoveryURLs != nil {
|
if dec.EthDiscoveryURLs != nil {
|
||||||
c.EthDiscoveryURLs = dec.EthDiscoveryURLs
|
c.EthDiscoveryURLs = dec.EthDiscoveryURLs
|
||||||
}
|
}
|
||||||
if dec.P2PEnabled != nil {
|
|
||||||
c.P2PEnabled = *dec.P2PEnabled
|
|
||||||
}
|
|
||||||
if dec.Prune != nil {
|
if dec.Prune != nil {
|
||||||
c.Prune = *dec.Prune
|
c.Prune = *dec.Prune
|
||||||
}
|
}
|
||||||
|
@ -37,12 +37,12 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon/turbo/services"
|
"github.com/ledgerwatch/erigon/turbo/services"
|
||||||
"github.com/ledgerwatch/log/v3"
|
"github.com/ledgerwatch/log/v3"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/consensus"
|
"github.com/ledgerwatch/erigon/consensus"
|
||||||
"github.com/ledgerwatch/erigon/core/rawdb"
|
"github.com/ledgerwatch/erigon/core/rawdb"
|
||||||
"github.com/ledgerwatch/erigon/core/types"
|
"github.com/ledgerwatch/erigon/core/types"
|
||||||
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
||||||
"github.com/ledgerwatch/erigon/node"
|
"github.com/ledgerwatch/erigon/node"
|
||||||
|
"github.com/ledgerwatch/erigon/p2p/sentry"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package sentry
|
package sentry_multi_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@ -1,7 +1,8 @@
|
|||||||
package sentry
|
package sentry_multi_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/ledgerwatch/erigon/p2p/sentry"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/holiman/uint256"
|
"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 {
|
if sentPeers == nil || len(sentPeers.Peers) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return ConvertH512ToPeerID(sentPeers.Peers[0]), true
|
return sentry.ConvertH512ToPeerID(sentPeers.Peers[0]), true
|
||||||
}
|
}
|
||||||
return [64]byte{}, false
|
return [64]byte{}, false
|
||||||
}
|
}
|
||||||
@ -119,7 +120,7 @@ func (cs *MultiClient) SendHeaderRequest(ctx context.Context, req *headerdownloa
|
|||||||
if sentPeers == nil || len(sentPeers.Peers) == 0 {
|
if sentPeers == nil || len(sentPeers.Peers) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return ConvertH512ToPeerID(sentPeers.Peers[0]), true
|
return sentry.ConvertH512ToPeerID(sentPeers.Peers[0]), true
|
||||||
}
|
}
|
||||||
return [64]byte{}, false
|
return [64]byte{}, false
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package sentry
|
package sentry_multi_client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
sentry2 "github.com/ledgerwatch/erigon/p2p/sentry"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
@ -441,7 +442,7 @@ func (cs *MultiClient) blockHeaders(ctx context.Context, pkt eth.BlockHeadersPac
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -450,7 +451,7 @@ func (cs *MultiClient) blockHeaders(ctx context.Context, pkt eth.BlockHeadersPac
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sort.Sort(headerdownload.HeadersSort(csHeaders)) // Sorting by order of block heights
|
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 {
|
if canRequestMore {
|
||||||
currentTime := time.Now()
|
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 {
|
} else {
|
||||||
outreq := proto_sentry.PenalizePeerRequest{
|
outreq := proto_sentry.PenalizePeerRequest{
|
||||||
PeerId: inreq.PeerId,
|
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 {
|
if _, err1 := sentry.PeerMinBlock(ctx, &outreq, &grpc.EmptyCallOption{}); err1 != nil {
|
||||||
cs.logger.Error("Could not send min block for peer", "err", err1)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,7 +561,7 @@ func (cs *MultiClient) blockBodies66(ctx context.Context, inreq *proto_sentry.In
|
|||||||
// No point processing empty response
|
// No point processing empty response
|
||||||
return nil
|
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
|
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 {
|
func (cs *MultiClient) HandlePeerEvent(ctx context.Context, event *proto_sentry.PeerEvent, sentry direct.SentryClient) error {
|
||||||
eventID := event.EventId.String()
|
eventID := event.EventId.String()
|
||||||
peerID := ConvertH512ToPeerID(event.PeerId)
|
peerID := sentry2.ConvertH512ToPeerID(event.PeerId)
|
||||||
peerIDStr := hex.EncodeToString(peerID[:])
|
peerIDStr := hex.EncodeToString(peerID[:])
|
||||||
|
|
||||||
if !cs.logPeerInfo {
|
if !cs.logPeerInfo {
|
@ -156,6 +156,9 @@ type Config struct {
|
|||||||
// Internet.
|
// Internet.
|
||||||
NAT nat.Interface `toml:",omitempty"`
|
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
|
// If Dialer is set to a non-nil value, the given Dialer
|
||||||
// is used to dial outbound peer connections.
|
// is used to dial outbound peer connections.
|
||||||
Dialer NodeDialer `toml:"-"`
|
Dialer NodeDialer `toml:"-"`
|
||||||
|
@ -142,7 +142,6 @@ func InitMiner(ctx context.Context, genesis *types.Genesis, privKey *ecdsa.Priva
|
|||||||
RPCGasCap: 50000000,
|
RPCGasCap: 50000000,
|
||||||
RPCTxFeeCap: 1, // 1 ether
|
RPCTxFeeCap: 1, // 1 ether
|
||||||
Snapshot: ethconfig.BlocksFreezing{NoDownloader: true},
|
Snapshot: ethconfig.BlocksFreezing{NoDownloader: true},
|
||||||
P2PEnabled: true,
|
|
||||||
StateStream: true,
|
StateStream: true,
|
||||||
}
|
}
|
||||||
ethCfg.TxPool.DBDir = nodeCfg.Dirs.TxPool
|
ethCfg.TxPool.DBDir = nodeCfg.Dirs.TxPool
|
||||||
|
@ -165,6 +165,9 @@ var DefaultFlags = []cli.Flag{
|
|||||||
&utils.OtsSearchMaxCapFlag,
|
&utils.OtsSearchMaxCapFlag,
|
||||||
|
|
||||||
&utils.SilkwormPathFlag,
|
&utils.SilkwormPathFlag,
|
||||||
|
&utils.SilkwormExecutionFlag,
|
||||||
|
&utils.SilkwormRpcDaemonFlag,
|
||||||
|
&utils.SilkwormSentryFlag,
|
||||||
|
|
||||||
&utils.TrustedSetupFile,
|
&utils.TrustedSetupFile,
|
||||||
}
|
}
|
||||||
|
@ -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")
|
|
||||||
}
|
|
@ -1,3 +1,5 @@
|
|||||||
|
//go:build unix
|
||||||
|
|
||||||
package silkworm
|
package silkworm
|
||||||
|
|
||||||
/*
|
/*
|
16
turbo/silkworm/load_windows.go
Normal file
16
turbo/silkworm/load_windows.go
Normal file
@ -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")
|
||||||
|
}
|
@ -1,156 +1,28 @@
|
|||||||
package silkworm
|
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 <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "silkworm_api_bridge.h"
|
||||||
|
|
||||||
#if defined _MSC_VER
|
static bool go_string_copy(_GoString_ s, char *dest, size_t size) {
|
||||||
#define SILKWORM_EXPORT __declspec(dllexport)
|
size_t len = _GoStringLen(s);
|
||||||
#else
|
if (len >= size) return false;
|
||||||
#define SILKWORM_EXPORT __attribute__((visibility("default")))
|
const char *src = _GoStringPtr(s);
|
||||||
#endif
|
strncpy(dest, src, len);
|
||||||
|
dest[len] = '\0';
|
||||||
#if __cplusplus
|
return true;
|
||||||
#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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon-lib/kv"
|
"github.com/ledgerwatch/erigon-lib/kv"
|
||||||
@ -158,22 +30,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SILKWORM_OK = iota
|
SILKWORM_OK = C.SILKWORM_OK
|
||||||
SILKWORM_INTERNAL_ERROR
|
SILKWORM_INTERNAL_ERROR = C.SILKWORM_INTERNAL_ERROR
|
||||||
SILKWORM_UNKNOWN_ERROR
|
SILKWORM_UNKNOWN_ERROR = C.SILKWORM_UNKNOWN_ERROR
|
||||||
SILKWORM_INVALID_HANDLE
|
SILKWORM_INVALID_HANDLE = C.SILKWORM_INVALID_HANDLE
|
||||||
SILKWORM_INVALID_PATH
|
SILKWORM_INVALID_PATH = C.SILKWORM_INVALID_PATH
|
||||||
SILKWORM_INVALID_SNAPSHOT
|
SILKWORM_INVALID_SNAPSHOT = C.SILKWORM_INVALID_SNAPSHOT
|
||||||
SILKWORM_INVALID_MDBX_TXN
|
SILKWORM_INVALID_MDBX_TXN = C.SILKWORM_INVALID_MDBX_TXN
|
||||||
SILKWORM_INVALID_BLOCK_RANGE
|
SILKWORM_INVALID_BLOCK_RANGE = C.SILKWORM_INVALID_BLOCK_RANGE
|
||||||
SILKWORM_BLOCK_NOT_FOUND
|
SILKWORM_BLOCK_NOT_FOUND = C.SILKWORM_BLOCK_NOT_FOUND
|
||||||
SILKWORM_UNKNOWN_CHAIN_ID
|
SILKWORM_UNKNOWN_CHAIN_ID = C.SILKWORM_UNKNOWN_CHAIN_ID
|
||||||
SILKWORM_MDBX_ERROR
|
SILKWORM_MDBX_ERROR = C.SILKWORM_MDBX_ERROR
|
||||||
SILKWORM_INVALID_BLOCK
|
SILKWORM_INVALID_BLOCK = C.SILKWORM_INVALID_BLOCK
|
||||||
SILKWORM_DECODING_ERROR
|
SILKWORM_DECODING_ERROR = C.SILKWORM_DECODING_ERROR
|
||||||
SILKWORM_TOO_MANY_INSTANCES
|
SILKWORM_TOO_MANY_INSTANCES = C.SILKWORM_TOO_MANY_INSTANCES
|
||||||
SILKWORM_INSTANCE_NOT_FOUND
|
SILKWORM_INVALID_SETTINGS = C.SILKWORM_INVALID_SETTINGS
|
||||||
SILKWORM_TERMINATION_SIGNAL
|
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.
|
// ErrInterrupted is the error returned by Silkworm APIs when stopped by any termination signal.
|
||||||
@ -187,10 +60,12 @@ type Silkworm struct {
|
|||||||
addSnapshot unsafe.Pointer
|
addSnapshot unsafe.Pointer
|
||||||
startRpcDaemon unsafe.Pointer
|
startRpcDaemon unsafe.Pointer
|
||||||
stopRpcDaemon unsafe.Pointer
|
stopRpcDaemon unsafe.Pointer
|
||||||
|
sentryStart unsafe.Pointer
|
||||||
|
sentryStop unsafe.Pointer
|
||||||
executeBlocks unsafe.Pointer
|
executeBlocks unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(dllPath string) (*Silkworm, error) {
|
func New(dllPath string, dataDirPath string) (*Silkworm, error) {
|
||||||
dllHandle, err := OpenLibrary(dllPath)
|
dllHandle, err := OpenLibrary(dllPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load silkworm library from path %s: %w", dllPath, err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load silkworm function silkworm_stop_rpcdaemon: %w", err)
|
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")
|
executeBlocks, err := LoadFunction(dllHandle, "silkworm_execute_blocks")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to load silkworm function silkworm_execute_blocks: %w", err)
|
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{
|
silkworm := &Silkworm{
|
||||||
dllHandle: dllHandle,
|
dllHandle: dllHandle,
|
||||||
|
instance: nil,
|
||||||
initFunc: initFunc,
|
initFunc: initFunc,
|
||||||
finiFunc: finiFunc,
|
finiFunc: finiFunc,
|
||||||
addSnapshot: addSnapshot,
|
addSnapshot: addSnapshot,
|
||||||
startRpcDaemon: startRpcDaemon,
|
startRpcDaemon: startRpcDaemon,
|
||||||
stopRpcDaemon: stopRpcDaemon,
|
stopRpcDaemon: stopRpcDaemon,
|
||||||
|
sentryStart: sentryStart,
|
||||||
|
sentryStop: sentryStop,
|
||||||
executeBlocks: executeBlocks,
|
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 {
|
if status == SILKWORM_OK {
|
||||||
return silkworm, nil
|
return silkworm, nil
|
||||||
}
|
}
|
||||||
@ -333,7 +226,129 @@ func (s *Silkworm) StopRpcDaemon() error {
|
|||||||
return fmt.Errorf("silkworm_stop_rpcdaemon error %d", status)
|
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) {
|
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())
|
cTxn := (*C.MDBX_txn)(txn.CHandle())
|
||||||
cChainId := C.uint64_t(chainID.Uint64())
|
cChainId := C.uint64_t(chainID.Uint64())
|
||||||
cStartBlock := C.uint64_t(startBlock)
|
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 {
|
if status == SILKWORM_OK {
|
||||||
return lastExecutedBlock, nil
|
return lastExecutedBlock, nil
|
||||||
}
|
}
|
||||||
// Handle special erros
|
// Handle special errors
|
||||||
if status == SILKWORM_INVALID_BLOCK {
|
if status == SILKWORM_INVALID_BLOCK {
|
||||||
return lastExecutedBlock, consensus.ErrInvalidBlock
|
return lastExecutedBlock, consensus.ErrInvalidBlock
|
||||||
}
|
}
|
||||||
|
206
turbo/silkworm/silkworm_api.h
Normal file
206
turbo/silkworm/silkworm_api.h
Normal file
@ -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 <stdbool.h> // NOLINT(*-deprecated-headers)
|
||||||
|
#include <stddef.h> // NOLINT(*-deprecated-headers)
|
||||||
|
#include <stdint.h> // 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_
|
75
turbo/silkworm/silkworm_api_bridge.h
Normal file
75
turbo/silkworm/silkworm_api_bridge.h
Normal file
@ -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_
|
@ -33,7 +33,6 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon-lib/txpool/txpoolcfg"
|
"github.com/ledgerwatch/erigon-lib/txpool/txpoolcfg"
|
||||||
types2 "github.com/ledgerwatch/erigon-lib/types"
|
types2 "github.com/ledgerwatch/erigon-lib/types"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/consensus"
|
"github.com/ledgerwatch/erigon/consensus"
|
||||||
"github.com/ledgerwatch/erigon/consensus/bor"
|
"github.com/ledgerwatch/erigon/consensus/bor"
|
||||||
"github.com/ledgerwatch/erigon/consensus/ethash"
|
"github.com/ledgerwatch/erigon/consensus/ethash"
|
||||||
@ -51,6 +50,7 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
||||||
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
||||||
"github.com/ledgerwatch/erigon/ethdb/prune"
|
"github.com/ledgerwatch/erigon/ethdb/prune"
|
||||||
|
"github.com/ledgerwatch/erigon/p2p/sentry/sentry_multi_client"
|
||||||
"github.com/ledgerwatch/erigon/params"
|
"github.com/ledgerwatch/erigon/params"
|
||||||
"github.com/ledgerwatch/erigon/rlp"
|
"github.com/ledgerwatch/erigon/rlp"
|
||||||
"github.com/ledgerwatch/erigon/turbo/builder"
|
"github.com/ledgerwatch/erigon/turbo/builder"
|
||||||
@ -84,7 +84,7 @@ type MockSentry struct {
|
|||||||
MiningSync *stagedsync.Sync
|
MiningSync *stagedsync.Sync
|
||||||
PendingBlocks chan *types.Block
|
PendingBlocks chan *types.Block
|
||||||
MinedBlocks chan *types.Block
|
MinedBlocks chan *types.Block
|
||||||
sentriesClient *sentry.MultiClient
|
sentriesClient *sentry_multi_client.MultiClient
|
||||||
Key *ecdsa.PrivateKey
|
Key *ecdsa.PrivateKey
|
||||||
Genesis *types.Block
|
Genesis *types.Block
|
||||||
SentryClient direct.SentryClient
|
SentryClient direct.SentryClient
|
||||||
@ -367,7 +367,7 @@ func MockWithEverything(tb testing.TB, gspec *types.Genesis, key *ecdsa.PrivateK
|
|||||||
networkID := uint64(1)
|
networkID := uint64(1)
|
||||||
maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 }
|
maxBlockBroadcastPeers := func(header *types.Header) uint { return 0 }
|
||||||
|
|
||||||
mock.sentriesClient, err = sentry.NewMultiClient(
|
mock.sentriesClient, err = sentry_multi_client.NewMultiClient(
|
||||||
mock.DB,
|
mock.DB,
|
||||||
"mock",
|
"mock",
|
||||||
mock.ChainConfig,
|
mock.ChainConfig,
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon-lib/kv/membatchwithdb"
|
"github.com/ledgerwatch/erigon-lib/kv/membatchwithdb"
|
||||||
"github.com/ledgerwatch/erigon-lib/state"
|
"github.com/ledgerwatch/erigon-lib/state"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon/cmd/sentry/sentry"
|
|
||||||
"github.com/ledgerwatch/erigon/consensus"
|
"github.com/ledgerwatch/erigon/consensus"
|
||||||
"github.com/ledgerwatch/erigon/consensus/bor/finality/flags"
|
"github.com/ledgerwatch/erigon/consensus/bor/finality/flags"
|
||||||
"github.com/ledgerwatch/erigon/consensus/bor/heimdall"
|
"github.com/ledgerwatch/erigon/consensus/bor/heimdall"
|
||||||
@ -32,6 +31,7 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
||||||
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
|
||||||
"github.com/ledgerwatch/erigon/p2p"
|
"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/engineapi/engine_helpers"
|
||||||
"github.com/ledgerwatch/erigon/turbo/services"
|
"github.com/ledgerwatch/erigon/turbo/services"
|
||||||
"github.com/ledgerwatch/erigon/turbo/shards"
|
"github.com/ledgerwatch/erigon/turbo/shards"
|
||||||
@ -443,11 +443,18 @@ func StateStep(ctx context.Context, chainReader consensus.ChainReader, engine co
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func silkwormForExecutionStage(silkworm *silkworm.Silkworm, cfg *ethconfig.Config) *silkworm.Silkworm {
|
||||||
|
if cfg.SilkwormExecution {
|
||||||
|
return silkworm
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewDefaultStages(ctx context.Context,
|
func NewDefaultStages(ctx context.Context,
|
||||||
db kv.RwDB,
|
db kv.RwDB,
|
||||||
p2pCfg p2p.Config,
|
p2pCfg p2p.Config,
|
||||||
cfg *ethconfig.Config,
|
cfg *ethconfig.Config,
|
||||||
controlServer *sentry.MultiClient,
|
controlServer *sentry_multi_client.MultiClient,
|
||||||
notifications *shards.Notifications,
|
notifications *shards.Notifications,
|
||||||
snapDownloader proto_downloader.DownloaderClient,
|
snapDownloader proto_downloader.DownloaderClient,
|
||||||
blockReader services.FullBlockReader,
|
blockReader services.FullBlockReader,
|
||||||
@ -496,7 +503,7 @@ func NewDefaultStages(ctx context.Context,
|
|||||||
cfg.Genesis,
|
cfg.Genesis,
|
||||||
cfg.Sync,
|
cfg.Sync,
|
||||||
agg,
|
agg,
|
||||||
silkworm,
|
silkwormForExecutionStage(silkworm, cfg),
|
||||||
),
|
),
|
||||||
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
||||||
stagedsync.StageTrieCfg(db, true, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg),
|
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,
|
func NewPipelineStages(ctx context.Context,
|
||||||
db kv.RwDB,
|
db kv.RwDB,
|
||||||
cfg *ethconfig.Config,
|
cfg *ethconfig.Config,
|
||||||
controlServer *sentry.MultiClient,
|
controlServer *sentry_multi_client.MultiClient,
|
||||||
notifications *shards.Notifications,
|
notifications *shards.Notifications,
|
||||||
snapDownloader proto_downloader.DownloaderClient,
|
snapDownloader proto_downloader.DownloaderClient,
|
||||||
blockReader services.FullBlockReader,
|
blockReader services.FullBlockReader,
|
||||||
@ -551,7 +558,7 @@ func NewPipelineStages(ctx context.Context,
|
|||||||
cfg.Genesis,
|
cfg.Genesis,
|
||||||
cfg.Sync,
|
cfg.Sync,
|
||||||
agg,
|
agg,
|
||||||
silkworm,
|
silkwormForExecutionStage(silkworm, cfg),
|
||||||
),
|
),
|
||||||
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
||||||
stagedsync.StageTrieCfg(db, checkStateRoot, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg),
|
stagedsync.StageTrieCfg(db, checkStateRoot, true, false, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg),
|
||||||
@ -563,7 +570,7 @@ func NewPipelineStages(ctx context.Context,
|
|||||||
runInTestMode)
|
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,
|
dirs datadir.Dirs, notifications *shards.Notifications, blockReader services.FullBlockReader, blockWriter *blockio.BlockWriter, agg *state.AggregatorV3,
|
||||||
silkworm *silkworm.Silkworm, logger log.Logger) *stagedsync.Sync {
|
silkworm *silkworm.Silkworm, logger log.Logger) *stagedsync.Sync {
|
||||||
return stagedsync.New(
|
return stagedsync.New(
|
||||||
@ -590,7 +597,7 @@ func NewInMemoryExecution(ctx context.Context, db kv.RwDB, cfg *ethconfig.Config
|
|||||||
cfg.Genesis,
|
cfg.Genesis,
|
||||||
cfg.Sync,
|
cfg.Sync,
|
||||||
agg,
|
agg,
|
||||||
silkworm,
|
silkwormForExecutionStage(silkworm, cfg),
|
||||||
),
|
),
|
||||||
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
stagedsync.StageHashStateCfg(db, dirs, cfg.HistoryV3),
|
||||||
stagedsync.StageTrieCfg(db, true, true, true, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg)),
|
stagedsync.StageTrieCfg(db, true, true, true, dirs.Tmp, blockReader, controlServer.Hd, cfg.HistoryV3, agg)),
|
||||||
|
Loading…
Reference in New Issue
Block a user