Commit d566acb3 authored by acud's avatar acud Committed by GitHub

kademlia, cmd, node: add bootnode-mode flag (#1399)

parent 037d5527
...@@ -42,6 +42,7 @@ const ( ...@@ -42,6 +42,7 @@ const (
optionNamePaymentTolerance = "payment-tolerance" optionNamePaymentTolerance = "payment-tolerance"
optionNamePaymentEarly = "payment-early" optionNamePaymentEarly = "payment-early"
optionNameResolverEndpoints = "resolver-options" optionNameResolverEndpoints = "resolver-options"
optionNameBootnodeMode = "bootnode-mode"
optionNameGatewayMode = "gateway-mode" optionNameGatewayMode = "gateway-mode"
optionNameClefSignerEnable = "clef-signer-enable" optionNameClefSignerEnable = "clef-signer-enable"
optionNameClefSignerEndpoint = "clef-signer-endpoint" optionNameClefSignerEndpoint = "clef-signer-endpoint"
...@@ -203,6 +204,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) { ...@@ -203,6 +204,7 @@ func (c *command) setAllFlags(cmd *cobra.Command) {
cmd.Flags().String(optionNamePaymentEarly, "1000000000000", "amount in BZZ below the peers payment threshold when we initiate settlement") cmd.Flags().String(optionNamePaymentEarly, "1000000000000", "amount in BZZ below the peers payment threshold when we initiate settlement")
cmd.Flags().StringSlice(optionNameResolverEndpoints, []string{}, "ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url") cmd.Flags().StringSlice(optionNameResolverEndpoints, []string{}, "ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url")
cmd.Flags().Bool(optionNameGatewayMode, false, "disable a set of sensitive features in the api") cmd.Flags().Bool(optionNameGatewayMode, false, "disable a set of sensitive features in the api")
cmd.Flags().Bool(optionNameBootnodeMode, false, "cause the node to always accept incoming connections")
cmd.Flags().Bool(optionNameClefSignerEnable, false, "enable clef signer") cmd.Flags().Bool(optionNameClefSignerEnable, false, "enable clef signer")
cmd.Flags().String(optionNameClefSignerEndpoint, "", "clef signer endpoint") cmd.Flags().String(optionNameClefSignerEndpoint, "", "clef signer endpoint")
cmd.Flags().String(optionNameClefSignerEthereumAddress, "", "ethereum address to use from clef signer") cmd.Flags().String(optionNameClefSignerEthereumAddress, "", "ethereum address to use from clef signer")
......
...@@ -143,6 +143,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz ...@@ -143,6 +143,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
PaymentEarly: c.config.GetString(optionNamePaymentEarly), PaymentEarly: c.config.GetString(optionNamePaymentEarly),
ResolverConnectionCfgs: resolverCfgs, ResolverConnectionCfgs: resolverCfgs,
GatewayMode: c.config.GetBool(optionNameGatewayMode), GatewayMode: c.config.GetBool(optionNameGatewayMode),
BootnodeMode: c.config.GetBool(optionNameBootnodeMode),
SwapEndpoint: c.config.GetString(optionNameSwapEndpoint), SwapEndpoint: c.config.GetString(optionNameSwapEndpoint),
SwapFactoryAddress: c.config.GetString(optionNameSwapFactoryAddress), SwapFactoryAddress: c.config.GetString(optionNameSwapFactoryAddress),
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit), SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
......
...@@ -43,7 +43,8 @@ type binSaturationFunc func(bin uint8, peers, connected *pslice.PSlice) (saturat ...@@ -43,7 +43,8 @@ type binSaturationFunc func(bin uint8, peers, connected *pslice.PSlice) (saturat
type Options struct { type Options struct {
SaturationFunc binSaturationFunc SaturationFunc binSaturationFunc
Bootnodes []ma.Multiaddr Bootnodes []ma.Multiaddr
Standalone bool StandaloneMode bool
BootnodeMode bool
} }
// Kad is the Swarm forwarding kademlia implementation. // Kad is the Swarm forwarding kademlia implementation.
...@@ -64,9 +65,10 @@ type Kad struct { ...@@ -64,9 +65,10 @@ type Kad struct {
peerSig []chan struct{} peerSig []chan struct{}
peerSigMtx sync.Mutex peerSigMtx sync.Mutex
logger logging.Logger // logger logger logging.Logger // logger
standalone bool standalone bool // indicates whether the node is working in standalone mode
quit chan struct{} // quit channel bootnode bool // indicates whether the node is working in bootnode mode
done chan struct{} // signal that `manage` has quit quit chan struct{} // quit channel
done chan struct{} // signal that `manage` has quit
wg sync.WaitGroup wg sync.WaitGroup
} }
...@@ -93,7 +95,8 @@ func New(base swarm.Address, addressbook addressbook.Interface, discovery discov ...@@ -93,7 +95,8 @@ func New(base swarm.Address, addressbook addressbook.Interface, discovery discov
manageC: make(chan struct{}, 1), manageC: make(chan struct{}, 1),
waitNext: make(map[string]retryInfo), waitNext: make(map[string]retryInfo),
logger: logger, logger: logger,
standalone: o.Standalone, standalone: o.StandaloneMode,
bootnode: o.BootnodeMode,
quit: make(chan struct{}), quit: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
wg: sync.WaitGroup{}, wg: sync.WaitGroup{},
...@@ -453,6 +456,11 @@ func (k *Kad) AddPeers(ctx context.Context, addrs ...swarm.Address) error { ...@@ -453,6 +456,11 @@ func (k *Kad) AddPeers(ctx context.Context, addrs ...swarm.Address) error {
} }
func (k *Kad) Pick(peer p2p.Peer) bool { func (k *Kad) Pick(peer p2p.Peer) bool {
if k.bootnode {
// shortcircuit for bootnode mode - always accept connections,
// at least until we find a better solution.
return true
}
po := swarm.Proximity(k.base.Bytes(), peer.Address.Bytes()) po := swarm.Proximity(k.base.Bytes(), peer.Address.Bytes())
_, oversaturated := k.saturationFunc(po, k.knownPeers, k.connectedPeers) _, oversaturated := k.saturationFunc(po, k.knownPeers, k.connectedPeers)
// pick the peer if we are not oversaturated // pick the peer if we are not oversaturated
...@@ -461,9 +469,12 @@ func (k *Kad) Pick(peer p2p.Peer) bool { ...@@ -461,9 +469,12 @@ func (k *Kad) Pick(peer p2p.Peer) bool {
// Connected is called when a peer has dialed in. // Connected is called when a peer has dialed in.
func (k *Kad) Connected(ctx context.Context, peer p2p.Peer) error { func (k *Kad) Connected(ctx context.Context, peer p2p.Peer) error {
po := swarm.Proximity(k.base.Bytes(), peer.Address.Bytes()) if !k.bootnode {
if _, overSaturated := k.saturationFunc(po, k.knownPeers, k.connectedPeers); overSaturated { // don't run this check if we're a bootnode
return topology.ErrOversaturated po := swarm.Proximity(k.base.Bytes(), peer.Address.Bytes())
if _, overSaturated := k.saturationFunc(po, k.knownPeers, k.connectedPeers); overSaturated {
return topology.ErrOversaturated
}
} }
if err := k.connected(ctx, peer.Address); err != nil { if err := k.connected(ctx, peer.Address); err != nil {
......
...@@ -46,7 +46,7 @@ var nonConnectableAddress, _ = ma.NewMultiaddr(underlayBase + "16Uiu2HAkx8ULY8cT ...@@ -46,7 +46,7 @@ var nonConnectableAddress, _ = ma.NewMultiaddr(underlayBase + "16Uiu2HAkx8ULY8cT
func TestNeighborhoodDepth(t *testing.T) { func TestNeighborhoodDepth(t *testing.T) {
var ( var (
conns int32 // how many connect calls were made to the p2p mock conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, nil, nil) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
peers []swarm.Address peers []swarm.Address
binEight []swarm.Address binEight []swarm.Address
) )
...@@ -167,7 +167,7 @@ func TestManage(t *testing.T) { ...@@ -167,7 +167,7 @@ func TestManage(t *testing.T) {
saturationFunc = func(bin uint8, peers, connected *pslice.PSlice) (bool, bool) { saturationFunc = func(bin uint8, peers, connected *pslice.PSlice) (bool, bool) {
return saturationVal, overSaturationVal return saturationVal, overSaturationVal
} }
base, kad, ab, _, signer = newTestKademlia(&conns, nil, saturationFunc, nil) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{SaturationFunc: saturationFunc})
) )
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -216,7 +216,7 @@ func TestBinSaturation(t *testing.T) { ...@@ -216,7 +216,7 @@ func TestBinSaturation(t *testing.T) {
var ( var (
conns int32 // how many connect calls were made to the p2p mock conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, nil, nil) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
peers []swarm.Address peers []swarm.Address
) )
...@@ -271,7 +271,7 @@ func TestOversaturation(t *testing.T) { ...@@ -271,7 +271,7 @@ func TestOversaturation(t *testing.T) {
var ( var (
conns int32 // how many connect calls were made to the p2p mock conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, nil, nil) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
) )
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -318,11 +318,66 @@ func TestOversaturation(t *testing.T) { ...@@ -318,11 +318,66 @@ func TestOversaturation(t *testing.T) {
} }
} }
func TestOversaturationBootnode(t *testing.T) {
defer func(p int) {
*kademlia.OverSaturationPeers = p
}(*kademlia.OverSaturationPeers)
*kademlia.OverSaturationPeers = 4
var (
conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{BootnodeMode: true})
)
if err := kad.Start(context.Background()); err != nil {
t.Fatal(err)
}
defer kad.Close()
// Add maximum accepted number of peers up until bin 5 without problems
for i := 0; i < 6; i++ {
for j := 0; j < *kademlia.OverSaturationPeers; j++ {
addr := test.RandomAddressAt(base, i)
// if error is not nil as specified, connectOne goes fatal
connectOne(t, signer, kad, ab, addr, nil)
}
// see depth is limited to currently added peers proximity
kDepth(t, kad, i)
}
// see depth is 5
kDepth(t, kad, 5)
for k := 0; k < 5; k++ {
// further connections should succeed outside of depth
for l := 0; l < 3; l++ {
addr := test.RandomAddressAt(base, k)
// if error is not as specified, connectOne goes fatal
connectOne(t, signer, kad, ab, addr, nil)
// check that pick works correctly
if !kad.Pick(p2p.Peer{Address: addr}) {
t.Fatal("should pick the peer but didnt")
}
}
// see depth is still as expected
kDepth(t, kad, 5)
}
// see we can still add / not limiting more peers in neighborhood depth
for m := 0; m < 12; m++ {
addr := test.RandomAddressAt(base, 5)
// if error is not nil as specified, connectOne goes fatal
connectOne(t, signer, kad, ab, addr, nil)
// see depth is still as expected
kDepth(t, kad, 5)
}
}
// TestNotifierHooks tests that the Connected/Disconnected hooks // TestNotifierHooks tests that the Connected/Disconnected hooks
// result in the correct behavior once called. // result in the correct behavior once called.
func TestNotifierHooks(t *testing.T) { func TestNotifierHooks(t *testing.T) {
var ( var (
base, kad, ab, _, signer = newTestKademlia(nil, nil, nil, nil) base, kad, ab, _, signer = newTestKademlia(nil, nil, kademlia.Options{})
peer = test.RandomAddressAt(base, 3) peer = test.RandomAddressAt(base, 3)
addr = test.RandomAddressAt(peer, 4) // address which is closer to peer addr = test.RandomAddressAt(peer, 4) // address which is closer to peer
) )
...@@ -357,7 +412,7 @@ func TestNotifierHooks(t *testing.T) { ...@@ -357,7 +412,7 @@ func TestNotifierHooks(t *testing.T) {
func TestDiscoveryHooks(t *testing.T) { func TestDiscoveryHooks(t *testing.T) {
var ( var (
conns int32 conns int32
_, kad, ab, disc, signer = newTestKademlia(&conns, nil, nil, nil) _, kad, ab, disc, signer = newTestKademlia(&conns, nil, kademlia.Options{})
p1, p2, p3 = test.RandomAddress(), test.RandomAddress(), test.RandomAddress() p1, p2, p3 = test.RandomAddress(), test.RandomAddress(), test.RandomAddress()
) )
...@@ -396,7 +451,7 @@ func TestBackoff(t *testing.T) { ...@@ -396,7 +451,7 @@ func TestBackoff(t *testing.T) {
var ( var (
conns int32 // how many connect calls were made to the p2p mock conns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, nil, nil, nil) base, kad, ab, _, signer = newTestKademlia(&conns, nil, kademlia.Options{})
) )
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -439,7 +494,7 @@ func TestAddressBookPrune(t *testing.T) { ...@@ -439,7 +494,7 @@ func TestAddressBookPrune(t *testing.T) {
var ( var (
conns, failedConns int32 // how many connect calls were made to the p2p mock conns, failedConns int32 // how many connect calls were made to the p2p mock
base, kad, ab, _, signer = newTestKademlia(&conns, &failedConns, nil, nil) base, kad, ab, _, signer = newTestKademlia(&conns, &failedConns, kademlia.Options{})
) )
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -611,7 +666,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -611,7 +666,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
} }
t.Run("single subscription", func(t *testing.T) { t.Run("single subscription", func(t *testing.T) {
base, kad, ab, _, sg := newTestKademlia(nil, nil, nil, nil) base, kad, ab, _, sg := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -627,7 +682,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -627,7 +682,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
}) })
t.Run("single subscription, remove peer", func(t *testing.T) { t.Run("single subscription, remove peer", func(t *testing.T) {
base, kad, ab, _, sg := newTestKademlia(nil, nil, nil, nil) base, kad, ab, _, sg := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -646,7 +701,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -646,7 +701,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
}) })
t.Run("multiple subscriptions", func(t *testing.T) { t.Run("multiple subscriptions", func(t *testing.T) {
base, kad, ab, _, sg := newTestKademlia(nil, nil, nil, nil) base, kad, ab, _, sg := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -667,7 +722,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -667,7 +722,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
}) })
t.Run("multiple changes", func(t *testing.T) { t.Run("multiple changes", func(t *testing.T) {
base, kad, ab, _, sg := newTestKademlia(nil, nil, nil, nil) base, kad, ab, _, sg := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -692,7 +747,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -692,7 +747,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
}) })
t.Run("no depth change", func(t *testing.T) { t.Run("no depth change", func(t *testing.T) {
_, kad, _, _, _ := newTestKademlia(nil, nil, nil, nil) _, kad, _, _, _ := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -714,7 +769,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) { ...@@ -714,7 +769,7 @@ func TestKademlia_SubscribePeersChange(t *testing.T) {
} }
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {
_, kad, ab, _, signer := newTestKademlia(nil, nil, nil, nil) _, kad, ab, _, signer := newTestKademlia(nil, nil, kademlia.Options{})
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -741,7 +796,7 @@ func TestStart(t *testing.T) { ...@@ -741,7 +796,7 @@ func TestStart(t *testing.T) {
t.Run("non-empty addressbook", func(t *testing.T) { t.Run("non-empty addressbook", func(t *testing.T) {
var conns, failedConns int32 // how many connect calls were made to the p2p mock var conns, failedConns int32 // how many connect calls were made to the p2p mock
_, kad, ab, _, signer := newTestKademlia(&conns, &failedConns, nil, bootnodes) _, kad, ab, _, signer := newTestKademlia(&conns, &failedConns, kademlia.Options{Bootnodes: bootnodes})
defer kad.Close() defer kad.Close()
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
...@@ -769,7 +824,7 @@ func TestStart(t *testing.T) { ...@@ -769,7 +824,7 @@ func TestStart(t *testing.T) {
t.Run("empty addressbook", func(t *testing.T) { t.Run("empty addressbook", func(t *testing.T) {
var conns, failedConns int32 // how many connect calls were made to the p2p mock var conns, failedConns int32 // how many connect calls were made to the p2p mock
_, kad, _, _, _ := newTestKademlia(&conns, &failedConns, nil, bootnodes) _, kad, _, _, _ := newTestKademlia(&conns, &failedConns, kademlia.Options{Bootnodes: bootnodes})
defer kad.Close() defer kad.Close()
if err := kad.Start(context.Background()); err != nil { if err := kad.Start(context.Background()); err != nil {
...@@ -781,17 +836,16 @@ func TestStart(t *testing.T) { ...@@ -781,17 +836,16 @@ func TestStart(t *testing.T) {
}) })
} }
func newTestKademlia(connCounter, failedConnCounter *int32, f func(bin uint8, peers, connected *pslice.PSlice) (bool, bool), bootnodes []ma.Multiaddr) (swarm.Address, *kademlia.Kad, addressbook.Interface, *mock.Discovery, beeCrypto.Signer) { func newTestKademlia(connCounter, failedConnCounter *int32, kadOpts kademlia.Options) (swarm.Address, *kademlia.Kad, addressbook.Interface, *mock.Discovery, beeCrypto.Signer) {
pk, _ := crypto.GenerateSecp256k1Key()
var ( var (
signer = beeCrypto.NewDefaultSigner(pk) pk, _ = crypto.GenerateSecp256k1Key() // random private key
base = test.RandomAddress() // base address signer = beeCrypto.NewDefaultSigner(pk) // signer
ab = addressbook.New(mockstate.NewStateStore()) // address book base = test.RandomAddress() // base address
p2p = p2pMock(ab, signer, connCounter, failedConnCounter) ab = addressbook.New(mockstate.NewStateStore()) // address book
logger = logging.New(ioutil.Discard, 0) // logger p2p = p2pMock(ab, signer, connCounter, failedConnCounter) // p2p mock
disc = mock.NewDiscovery() logger = logging.New(ioutil.Discard, 0) // logger
kad = kademlia.New(base, ab, disc, p2p, logger, kademlia.Options{SaturationFunc: f, Bootnodes: bootnodes}) // kademlia instance disc = mock.NewDiscovery() // mock discovery protocol
kad = kademlia.New(base, ab, disc, p2p, logger, kadOpts) // kademlia instance
) )
return base, kad, ab, disc, signer return base, kad, ab, disc, signer
......
...@@ -104,6 +104,7 @@ type Options struct { ...@@ -104,6 +104,7 @@ type Options struct {
PaymentEarly string PaymentEarly string
ResolverConnectionCfgs []multiresolver.ConnectionConfig ResolverConnectionCfgs []multiresolver.ConnectionConfig
GatewayMode bool GatewayMode bool
BootnodeMode bool
SwapEndpoint string SwapEndpoint string
SwapFactoryAddress string SwapFactoryAddress string
SwapInitialDeposit string SwapInitialDeposit string
...@@ -361,7 +362,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey, ...@@ -361,7 +362,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
settlement.SetNotifyPaymentFunc(acc.AsyncNotifyPayment) settlement.SetNotifyPaymentFunc(acc.AsyncNotifyPayment)
pricing.SetPaymentThresholdObserver(acc) pricing.SetPaymentThresholdObserver(acc)
kad := kademlia.New(swarmAddress, addressbook, hive, p2ps, logger, kademlia.Options{Bootnodes: bootnodes, Standalone: o.Standalone}) kad := kademlia.New(swarmAddress, addressbook, hive, p2ps, logger, kademlia.Options{Bootnodes: bootnodes, StandaloneMode: o.Standalone, BootnodeMode: o.BootnodeMode})
b.topologyCloser = kad b.topologyCloser = kad
hive.SetAddPeersHandler(kad.AddPeers) hive.SetAddPeersHandler(kad.AddPeers)
p2ps.SetPickyNotifier(kad) p2ps.SetPickyNotifier(kad)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment