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 (
optionNamePaymentTolerance = "payment-tolerance"
optionNamePaymentEarly = "payment-early"
optionNameResolverEndpoints = "resolver-options"
optionNameBootnodeMode = "bootnode-mode"
optionNameGatewayMode = "gateway-mode"
optionNameClefSignerEnable = "clef-signer-enable"
optionNameClefSignerEndpoint = "clef-signer-endpoint"
......@@ -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().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(optionNameBootnodeMode, false, "cause the node to always accept incoming connections")
cmd.Flags().Bool(optionNameClefSignerEnable, false, "enable clef signer")
cmd.Flags().String(optionNameClefSignerEndpoint, "", "clef signer endpoint")
cmd.Flags().String(optionNameClefSignerEthereumAddress, "", "ethereum address to use from clef signer")
......
......@@ -143,6 +143,7 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
PaymentEarly: c.config.GetString(optionNamePaymentEarly),
ResolverConnectionCfgs: resolverCfgs,
GatewayMode: c.config.GetBool(optionNameGatewayMode),
BootnodeMode: c.config.GetBool(optionNameBootnodeMode),
SwapEndpoint: c.config.GetString(optionNameSwapEndpoint),
SwapFactoryAddress: c.config.GetString(optionNameSwapFactoryAddress),
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
......
......@@ -43,7 +43,8 @@ type binSaturationFunc func(bin uint8, peers, connected *pslice.PSlice) (saturat
type Options struct {
SaturationFunc binSaturationFunc
Bootnodes []ma.Multiaddr
Standalone bool
StandaloneMode bool
BootnodeMode bool
}
// Kad is the Swarm forwarding kademlia implementation.
......@@ -64,7 +65,8 @@ type Kad struct {
peerSig []chan struct{}
peerSigMtx sync.Mutex
logger logging.Logger // logger
standalone bool
standalone bool // indicates whether the node is working in standalone mode
bootnode bool // indicates whether the node is working in bootnode mode
quit chan struct{} // quit channel
done chan struct{} // signal that `manage` has quit
wg sync.WaitGroup
......@@ -93,7 +95,8 @@ func New(base swarm.Address, addressbook addressbook.Interface, discovery discov
manageC: make(chan struct{}, 1),
waitNext: make(map[string]retryInfo),
logger: logger,
standalone: o.Standalone,
standalone: o.StandaloneMode,
bootnode: o.BootnodeMode,
quit: make(chan struct{}),
done: make(chan struct{}),
wg: sync.WaitGroup{},
......@@ -453,6 +456,11 @@ func (k *Kad) AddPeers(ctx context.Context, addrs ...swarm.Address) error {
}
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())
_, oversaturated := k.saturationFunc(po, k.knownPeers, k.connectedPeers)
// pick the peer if we are not oversaturated
......@@ -461,10 +469,13 @@ func (k *Kad) Pick(peer p2p.Peer) bool {
// Connected is called when a peer has dialed in.
func (k *Kad) Connected(ctx context.Context, peer p2p.Peer) error {
if !k.bootnode {
// don't run this check if we're a bootnode
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 {
return err
......
......@@ -46,7 +46,7 @@ var nonConnectableAddress, _ = ma.NewMultiaddr(underlayBase + "16Uiu2HAkx8ULY8cT
func TestNeighborhoodDepth(t *testing.T) {
var (
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
binEight []swarm.Address
)
......@@ -167,7 +167,7 @@ func TestManage(t *testing.T) {
saturationFunc = func(bin uint8, peers, connected *pslice.PSlice) (bool, bool) {
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 {
......@@ -216,7 +216,7 @@ func TestBinSaturation(t *testing.T) {
var (
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
)
......@@ -271,7 +271,7 @@ func TestOversaturation(t *testing.T) {
var (
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 {
......@@ -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
// result in the correct behavior once called.
func TestNotifierHooks(t *testing.T) {
var (
base, kad, ab, _, signer = newTestKademlia(nil, nil, nil, nil)
base, kad, ab, _, signer = newTestKademlia(nil, nil, kademlia.Options{})
peer = test.RandomAddressAt(base, 3)
addr = test.RandomAddressAt(peer, 4) // address which is closer to peer
)
......@@ -357,7 +412,7 @@ func TestNotifierHooks(t *testing.T) {
func TestDiscoveryHooks(t *testing.T) {
var (
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()
)
......@@ -396,7 +451,7 @@ func TestBackoff(t *testing.T) {
var (
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 {
......@@ -439,7 +494,7 @@ func TestAddressBookPrune(t *testing.T) {
var (
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 {
......@@ -611,7 +666,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -627,7 +682,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -646,7 +701,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -667,7 +722,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -692,7 +747,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -714,7 +769,7 @@ func TestKademlia_SubscribePeersChange(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 {
t.Fatal(err)
}
......@@ -741,7 +796,7 @@ func TestStart(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
_, kad, ab, _, signer := newTestKademlia(&conns, &failedConns, nil, bootnodes)
_, kad, ab, _, signer := newTestKademlia(&conns, &failedConns, kademlia.Options{Bootnodes: bootnodes})
defer kad.Close()
for i := 0; i < 3; i++ {
......@@ -769,7 +824,7 @@ func TestStart(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
_, kad, _, _, _ := newTestKademlia(&conns, &failedConns, nil, bootnodes)
_, kad, _, _, _ := newTestKademlia(&conns, &failedConns, kademlia.Options{Bootnodes: bootnodes})
defer kad.Close()
if err := kad.Start(context.Background()); err != nil {
......@@ -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) {
pk, _ := crypto.GenerateSecp256k1Key()
func newTestKademlia(connCounter, failedConnCounter *int32, kadOpts kademlia.Options) (swarm.Address, *kademlia.Kad, addressbook.Interface, *mock.Discovery, beeCrypto.Signer) {
var (
signer = beeCrypto.NewDefaultSigner(pk)
pk, _ = crypto.GenerateSecp256k1Key() // random private key
signer = beeCrypto.NewDefaultSigner(pk) // signer
base = test.RandomAddress() // base address
ab = addressbook.New(mockstate.NewStateStore()) // address book
p2p = p2pMock(ab, signer, connCounter, failedConnCounter)
p2p = p2pMock(ab, signer, connCounter, failedConnCounter) // p2p mock
logger = logging.New(ioutil.Discard, 0) // logger
disc = mock.NewDiscovery()
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
......
......@@ -104,6 +104,7 @@ type Options struct {
PaymentEarly string
ResolverConnectionCfgs []multiresolver.ConnectionConfig
GatewayMode bool
BootnodeMode bool
SwapEndpoint string
SwapFactoryAddress string
SwapInitialDeposit string
......@@ -361,7 +362,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
settlement.SetNotifyPaymentFunc(acc.AsyncNotifyPayment)
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
hive.SetAddPeersHandler(kad.AddPeers)
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