Skip to content

Commit bf5d2a4

Browse files
committed
[GH-846] Added bootstrap-url parameter for simple initialization of archive node.
1 parent 3943017 commit bf5d2a4

30 files changed

+366
-66
lines changed

nil/client/client.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/NilFoundation/nil/nil/internal/contracts"
1515
"github.com/NilFoundation/nil/nil/internal/types"
1616
"github.com/NilFoundation/nil/nil/services/rpc/jsonrpc"
17+
rpctypes "github.com/NilFoundation/nil/nil/services/rpc/types"
1718
"github.com/NilFoundation/nil/nil/services/txnpool"
1819
)
1920

@@ -132,6 +133,8 @@ type Client interface {
132133

133134
// GetDebugContract retrieves smart contract with its data, such as code, storage and proof
134135
GetDebugContract(ctx context.Context, contractAddr types.Address, blockId any) (*jsonrpc.DebugRPCContract, error)
136+
137+
GetBootstrapConfig(ctx context.Context) (*rpctypes.BootstrapConfig, error)
135138
}
136139

137140
func EstimateFeeExternal(

nil/client/direct_client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/NilFoundation/nil/nil/services/rpc/jsonrpc"
1616
"github.com/NilFoundation/nil/nil/services/rpc/rawapi"
1717
"github.com/NilFoundation/nil/nil/services/rpc/transport"
18+
rpctypes "github.com/NilFoundation/nil/nil/services/rpc/types"
1819
)
1920

2021
// DirectClient is a client that interacts with the end api directly, without using the rpc server.
@@ -438,3 +439,7 @@ func (c *DirectClient) GetTxpoolStatus(ctx context.Context, shardId types.ShardI
438439
func (c *DirectClient) GetTxpoolContent(ctx context.Context, shardId types.ShardId) (jsonrpc.TxPoolContent, error) {
439440
return c.txPoolApi.GetTxpoolContent(ctx, shardId)
440441
}
442+
443+
func (c *DirectClient) GetBootstrapConfig(ctx context.Context) (*rpctypes.BootstrapConfig, error) {
444+
return c.debugApi.GetBootstrapConfig(ctx)
445+
}

nil/client/rpc/client.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/NilFoundation/nil/nil/internal/types"
2626
"github.com/NilFoundation/nil/nil/services/rpc/jsonrpc"
2727
"github.com/NilFoundation/nil/nil/services/rpc/transport"
28+
rpctypes "github.com/NilFoundation/nil/nil/services/rpc/types"
2829
)
2930

3031
// CallError represents an error that occurs during a remote procedure call,
@@ -79,6 +80,7 @@ const (
7980
Debug_getBlockByHash = "debug_getBlockByHash"
8081
Debug_getBlockByNumber = "debug_getBlockByNumber"
8182
Debug_getContract = "debug_getContract"
83+
Debug_getBootstrapConfig = "debug_getBootstrapConfig"
8284
Web3_clientVersion = "web3_clientVersion"
8385
Dev_doPanicOnShard = "dev_doPanicOnShard"
8486
Txpool_getTxpoolStatus = "txpool_getTxpoolStatus"
@@ -951,6 +953,18 @@ func (c *Client) DoPanicOnShard(ctx context.Context, shardId types.ShardId) (uin
951953
return 0, err
952954
}
953955

956+
func (c *Client) GetTxpoolStatus(ctx context.Context, shardId types.ShardId) (jsonrpc.TxPoolStatus, error) {
957+
return simpleCall[jsonrpc.TxPoolStatus](ctx, c, Txpool_getTxpoolStatus, shardId)
958+
}
959+
960+
func (c *Client) GetTxpoolContent(ctx context.Context, shardId types.ShardId) (jsonrpc.TxPoolContent, error) {
961+
return simpleCall[jsonrpc.TxPoolContent](ctx, c, Txpool_getTxpoolContent, shardId)
962+
}
963+
964+
func (c *Client) GetBootstrapConfig(ctx context.Context) (*rpctypes.BootstrapConfig, error) {
965+
return simpleCall[*rpctypes.BootstrapConfig](ctx, c, Debug_getBootstrapConfig)
966+
}
967+
954968
func simpleCall[ReturnType any](ctx context.Context, c *Client, method string, params ...any) (ReturnType, error) {
955969
res, err := c.call(ctx, method, params...)
956970
var result ReturnType
@@ -977,11 +991,3 @@ func simpleCallUint64[ReturnType ~uint64](
977991
}
978992
return ReturnType(result), err
979993
}
980-
981-
func (c *Client) GetTxpoolStatus(ctx context.Context, shardId types.ShardId) (jsonrpc.TxPoolStatus, error) {
982-
return simpleCall[jsonrpc.TxPoolStatus](ctx, c, Txpool_getTxpoolStatus, shardId)
983-
}
984-
985-
func (c *Client) GetTxpoolContent(ctx context.Context, shardId types.ShardId) (jsonrpc.TxPoolContent, error) {
986-
return simpleCall[jsonrpc.TxPoolContent](ctx, c, Txpool_getTxpoolContent, shardId)
987-
}

nil/cmd/nild/main.go

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"errors"
66
"os"
7+
"time"
78

9+
rpc_client "github.com/NilFoundation/nil/nil/client/rpc"
810
"github.com/NilFoundation/nil/nil/cmd/nild/nildconfig"
911
"github.com/NilFoundation/nil/nil/common/check"
1012
"github.com/NilFoundation/nil/nil/common/concurrent"
@@ -32,7 +34,7 @@ var logFilter string
3234
func main() {
3335
logger := logging.NewLogger("nild")
3436

35-
cfg := parseArgs()
37+
cfg := parseArgs(logger)
3638

3739
logging.ApplyComponentsFilter(logFilter)
3840

@@ -100,7 +102,43 @@ func addBasicFlags(fset *pflag.FlagSet, cfg *nildconfig.Config) {
100102
&cfg.CollatorTickPeriodMs, "collator-tick-ms", cfg.CollatorTickPeriodMs, "collator tick period in milliseconds")
101103
}
102104

103-
func parseArgs() *nildconfig.Config {
105+
func doBootstrapRequestAndPatchConfig(
106+
ctx context.Context,
107+
bootstrapUrl string,
108+
cfg *nildconfig.Config,
109+
logger logging.Logger,
110+
) error {
111+
ctx, cancel := context.WithTimeout(ctx, 15*time.Second)
112+
defer cancel()
113+
114+
bootstrapConfig, err := rpc_client.NewClient(bootstrapUrl, logger).GetBootstrapConfig(ctx)
115+
if err != nil {
116+
return err
117+
}
118+
119+
// Patch config
120+
cfg.NShards = bootstrapConfig.NShards
121+
122+
if cfg.ZeroState != nil {
123+
logger.Warn().Msg("overriding zero state config")
124+
}
125+
cfg.ZeroState = bootstrapConfig.ZeroStateConfig
126+
127+
if cfg.Network.TcpPort == 0 {
128+
tcpPort := 3000 // Some port to work through libp2p
129+
logger.Info().Msgf("setting TCP port to %d", tcpPort)
130+
cfg.Network.TcpPort = tcpPort
131+
}
132+
133+
cfg.BootstrapPeers = append(cfg.BootstrapPeers, bootstrapConfig.BootstrapPeers...)
134+
135+
cfg.Network.DHTBootstrapPeers = append(cfg.Network.DHTBootstrapPeers, bootstrapConfig.DhtBootstrapPeers...)
136+
cfg.Network.DHTEnabled = true
137+
138+
return nil
139+
}
140+
141+
func parseArgs(logger logging.Logger) *nildconfig.Config {
104142
cfg, err := loadConfig()
105143
check.PanicIfErr(err)
106144

@@ -179,17 +217,30 @@ func parseArgs() *nildconfig.Config {
179217
replayCmd.Flags().Var(&cfg.Replay.BlockIdLast, "last-block", "last block id to replay")
180218
replayCmd.Flags().Var(&cfg.Replay.ShardId, "shard-id", "shard id to replay block from")
181219

220+
var bootstrapUrl string
182221
archiveCmd := &cobra.Command{
183222
Use: "archive",
184223
Short: "Run nil archive node",
185-
Run: func(cmd *cobra.Command, args []string) {
224+
RunE: func(cmd *cobra.Command, args []string) error {
186225
cfg.RunMode = nilservice.ArchiveRunMode
226+
227+
if bootstrapUrl != "" {
228+
return doBootstrapRequestAndPatchConfig(cmd.Context(), bootstrapUrl, cfg, logger)
229+
}
230+
231+
return nil
187232
},
188233
}
189234

190235
addBasicFlags(archiveCmd.Flags(), cfg)
191236
cmdflags.AddNetwork(archiveCmd.Flags(), cfg.Network)
192237
cmdflags.AddTelemetry(archiveCmd.Flags(), cfg.Telemetry)
238+
// N.B. Despite the fact that we override the config with the loaded configuration,
239+
// we still handle this flag in the usual way rather than resorting to a hack like GetConfigNameFromArgs.
240+
// Network-supplied values should win by default, but because we merge only a small, well-defined
241+
// subset of settings, any rare conflicts can be resolved explicitly and safely.
242+
archiveCmd.Flags().StringVar(
243+
&bootstrapUrl, "bootstrap-url", "", "url to fetch initial configuration from (genesis config etc.")
193244

194245
rpcCmd := &cobra.Command{
195246
Use: "rpc",

nil/internal/cobrax/cmdflags/network.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ func AddNetwork(fset *pflag.FlagSet, cfg *network.Config) {
1515

1616
fset.BoolVar(&cfg.ServeRelay, "serve-relay", cfg.ServeRelay, "enable relay")
1717
fset.Var(&cfg.Relays, "relays", "relay peers")
18+
fset.Var(&cfg.RelayPublicAddress, "relay-public-address", "public address of relay")
1819

1920
fset.BoolVar(&cfg.DHTEnabled, "with-discovery", cfg.DHTEnabled, "enable discovery (with Kademlia DHT)")
2021
fset.Var(&cfg.DHTBootstrapPeers, "discovery-bootstrap-peers", "bootstrap peers for discovery")

nil/internal/execution/zerostate.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import (
1616
)
1717

1818
type ContractDescr struct {
19-
Name string `yaml:"name"`
20-
Address types.Address `yaml:"address,omitempty"`
21-
Value types.Value `yaml:"value"`
22-
Shard types.ShardId `yaml:"shard,omitempty"`
23-
Contract string `yaml:"contract"`
24-
CtorArgs []any `yaml:"ctorArgs,omitempty"`
19+
Name string `yaml:"name" json:"name"`
20+
Address types.Address `yaml:"address,omitempty" json:"address,omitempty"`
21+
Value types.Value `yaml:"value" json:"value"`
22+
Shard types.ShardId `yaml:"shard,omitempty" json:"shard,omitempty"`
23+
Contract string `yaml:"contract" json:"contract"`
24+
CtorArgs []any `yaml:"ctorArgs,omitempty" json:"ctorArgs,omitempty"`
2525
}
2626

2727
type MainKeys struct {
@@ -30,13 +30,13 @@ type MainKeys struct {
3030
}
3131

3232
type ConfigParams struct {
33-
Validators config.ParamValidators `yaml:"validators,omitempty"`
34-
GasPrice config.ParamGasPrice `yaml:"gasPrice"`
33+
Validators config.ParamValidators `yaml:"validators,omitempty" json:"validators,omitempty"`
34+
GasPrice config.ParamGasPrice `yaml:"gasPrice" json:"gasPrice"`
3535
}
3636

3737
type ZeroStateConfig struct {
38-
ConfigParams ConfigParams `yaml:"config,omitempty"`
39-
Contracts []*ContractDescr `yaml:"contracts"`
38+
ConfigParams ConfigParams `yaml:"config,omitempty" json:"config,omitempty"`
39+
Contracts []*ContractDescr `yaml:"contracts" json:"contracts"`
4040
}
4141

4242
func CreateDefaultZeroStateConfig(mainPublicKey []byte) (*ZeroStateConfig, error) {

nil/internal/network/addr_info.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
package network
22

33
import (
4+
"errors"
5+
46
"github.com/NilFoundation/nil/nil/common"
57
"github.com/libp2p/go-libp2p/core/peer"
8+
"gopkg.in/yaml.v3"
69
)
710

811
type AddrInfo peer.AddrInfo
912

13+
func (a AddrInfo) Empty() bool {
14+
return errors.Is(a.ID.Validate(), peer.ErrEmptyPeerID)
15+
}
16+
1017
func (a *AddrInfo) Set(value string) error {
1118
addr, err := peer.AddrInfoFromString(value)
1219
if err != nil {
@@ -44,6 +51,14 @@ func (a *AddrInfo) UnmarshalText(text []byte) error {
4451
return a.Set(string(text))
4552
}
4653

54+
func (a *AddrInfo) MarshalYAML() (any, error) {
55+
return a.MarshalText()
56+
}
57+
58+
func (a *AddrInfo) UnmarshalYAML(value *yaml.Node) error {
59+
return a.UnmarshalText([]byte(value.Value))
60+
}
61+
4762
type AddrInfoSlice = common.PValueSlice[*AddrInfo, AddrInfo]
4863

4964
func ToLibP2pAddrInfoSlice(s AddrInfoSlice) []peer.AddrInfo {

nil/internal/network/addr_info_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,17 @@ func TestConfigYamlSerialization(t *testing.T) {
2525
addrInfo1,
2626
addrInfo2,
2727
},
28+
RelayPublicAddress: addrInfo1,
2829
}
2930

3031
data, err := yaml.Marshal(config)
3132
require.NoError(t, err)
3233

33-
expectedYaml := `dhtBootstrapPeers:
34-
- /ip4/127.0.0.1/tcp/1500/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf
35-
- /ip4/192.168.1.1/tcp/4002/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yg
34+
expectedYaml := `---
35+
dhtBootstrapPeers:
36+
- /ip4/127.0.0.1/tcp/1500/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf
37+
- /ip4/192.168.1.1/tcp/4002/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yg
38+
relayPublicAddress: /ip4/127.0.0.1/tcp/1500/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf
3639
`
3740
require.YAMLEq(t, expectedYaml, string(data))
3841

@@ -53,15 +56,14 @@ func TestAddrInfoStringRepresentation(t *testing.T) {
5356
"/ip4/192.168.0.10/tcp/4002/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf")
5457
require.NoError(t, err)
5558

56-
_, id := peer.SplitAddr(addr1)
59+
transport1, id := peer.SplitAddr(addr1)
60+
transport2, _ := peer.SplitAddr(addr2)
5761
addrInfo := AddrInfo{
5862
ID: id,
59-
Addrs: []ma.Multiaddr{addr1, addr2},
63+
Addrs: []ma.Multiaddr{transport1, transport2},
6064
}
6165

62-
expectedString := "/ip4/127.0.0.1/tcp/1500/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf" +
63-
"/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf," +
64-
"/ip4/192.168.0.10/tcp/4002/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf" +
65-
"/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf"
66+
expectedString := "/ip4/127.0.0.1/tcp/1500/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf," +
67+
"/ip4/192.168.0.10/tcp/4002/p2p/16Uiu2HAmQctkUi9y7WfUtYa9rPon1m5TRBtXSvUwi2VtpbWZj4yf"
6668
require.Equal(t, expectedString, addrInfo.String())
6769
}

nil/internal/network/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type Config struct {
2323

2424
ServeRelay bool `yaml:"serveRelay,omitempty"`
2525
Relays AddrInfoSlice `yaml:"relays,omitempty"`
26+
// RelayPublicAddress is the public relay address used to wrap the node's own address in BootstrapConfig if set.
27+
RelayPublicAddress AddrInfo `yaml:"relayPublicAddress,omitempty"`
2628

2729
DHTEnabled bool `yaml:"dhtEnabled,omitempty"`
2830
DHTBootstrapPeers AddrInfoSlice `yaml:"dhtBootstrapPeers,omitempty"`

nil/internal/network/host.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/libp2p/go-libp2p/core/network"
1515
"github.com/libp2p/go-libp2p/core/peer"
1616
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
17+
"github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
1718
"github.com/libp2p/go-libp2p/p2p/security/noise"
1819
quic "github.com/libp2p/go-libp2p/p2p/transport/quic"
1920
"github.com/libp2p/go-libp2p/p2p/transport/tcp"
@@ -88,7 +89,11 @@ func newHost(ctx context.Context, conf *Config) (Host, logging.Logger, error) {
8889
}
8990

9091
if conf.ServeRelay {
91-
options = append(options, libp2p.EnableRelayService())
92+
options = append(options, libp2p.EnableRelayService(
93+
// We temporarily disable the limitation because proxying traffic through a relay is the simplest
94+
// way to make the whole system work. Later we should study EnableAutoNATv2 and
95+
// EnableHolePunching more carefully to move to direct peer-to-peer connections.
96+
relay.WithInfiniteLimits()))
9297

9398
// todo: remove it after relay is tested
9499
// this is to make sure that the relay is not disabled

nil/internal/network/relay_test.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package network
22

33
import (
4+
"context"
45
"testing"
56

67
"github.com/libp2p/go-libp2p/core/network"
7-
"github.com/libp2p/go-libp2p/core/peer"
88
"github.com/stretchr/testify/suite"
99
)
1010

@@ -29,6 +29,9 @@ func (s *RelayTestSuite) TestRelay() {
2929
Relays: []AddrInfo{CalcAddress(relay)},
3030
Reachability: network.ReachabilityPrivate,
3131
})
32+
private.SetRequestHandler(s.context, "/hello", func(context.Context, []byte) ([]byte, error) {
33+
return []byte("world"), nil
34+
})
3235
defer private.Close()
3336

3437
// Connect the private node to the relay (avoiding discovery)
@@ -38,12 +41,15 @@ func (s *RelayTestSuite) TestRelay() {
3841
client := s.newManager()
3942
defer client.Close()
4043

41-
relayedAddr, err := peer.AddrInfoFromString(hostAddress(relay) + "/p2p-circuit/p2p/" + private.host.ID().String())
44+
relayedAddr := RelayedAddress(private.host.ID(), CalcAddress(relay))
45+
id, err := client.Connect(s.context, relayedAddr)
4246
s.Require().NoError(err)
47+
s.Require().Equal(private.host.ID(), id)
4348

44-
id, err := client.Connect(s.context, AddrInfo(*relayedAddr))
49+
resp, err := client.SendRequestAndGetResponse(
50+
network.WithAllowLimitedConn(s.context, "relay"), id, "/hello", []byte("hello"))
4551
s.Require().NoError(err)
46-
s.Require().Equal(private.host.ID(), id)
52+
s.Require().Equal("world", string(resp))
4753
}
4854

4955
func TestRelay(t *testing.T) {

nil/internal/network/testaide.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,14 @@ import (
1111
"time"
1212

1313
"github.com/NilFoundation/nil/nil/common"
14-
"github.com/NilFoundation/nil/nil/common/check"
1514
"github.com/libp2p/go-libp2p/core/peer"
1615
"github.com/stretchr/testify/require"
1716
)
1817

19-
func CalcAddress(m Manager) AddrInfo {
20-
addr, err := peer.AddrInfoFromString(hostAddress(m))
21-
check.PanicIfErr(err)
22-
return AddrInfo(*addr)
23-
}
24-
25-
func hostAddress(m Manager) string {
26-
return m.getHost().Addrs()[0].String() + "/p2p/" + m.getHost().ID().String()
27-
}
28-
2918
func NewTestManagerWithBaseConfig(ctx context.Context, t *testing.T, conf *Config) *BasicManager {
3019
t.Helper()
3120

3221
conf = common.CopyPtr(conf)
33-
if conf.IPV4Address == "" {
34-
conf.IPV4Address = "127.0.0.1"
35-
}
3622
if conf.PrivateKey == nil {
3723
privateKey, err := GeneratePrivateKey()
3824
require.NoError(t, err)

0 commit comments

Comments
 (0)