Merge branch 'master' into master

This commit is contained in:
weimumu 2018-12-28 18:31:33 +08:00 committed by GitHub
commit 9dacf4b9e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 2305 additions and 1385 deletions

View file

@ -82,7 +82,7 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
// we need to decide whether we're calling a method or an event // we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
if len(output)%32 != 0 { if len(output)%32 != 0 {
return fmt.Errorf("abi: improperly formatted output") return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(output), output)
} }
return method.Outputs.Unpack(v, output) return method.Outputs.Unpack(v, output)
} else if event, ok := abi.Events[name]; ok { } else if event, ok := abi.Events[name]; ok {

View file

@ -77,6 +77,8 @@ func set(dst, src reflect.Value, output Argument) error {
switch { switch {
case dstType.AssignableTo(srcType): case dstType.AssignableTo(srcType):
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
return setSlice(dst, src, output)
case dstType.Kind() == reflect.Interface: case dstType.Kind() == reflect.Interface:
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Ptr: case dstType.Kind() == reflect.Ptr:
@ -87,6 +89,19 @@ func set(dst, src reflect.Value, output Argument) error {
return nil return nil
} }
// setSlice attempts to assign src to dst when slices are not assignable by default
// e.g. src: [][]byte -> dst: [][15]byte
func setSlice(dst, src reflect.Value, output Argument) error {
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ {
v := src.Index(i)
reflect.Copy(slice.Index(i), v)
}
dst.Set(slice)
return nil
}
// requireAssignable assures that `dest` is a pointer and it's not an interface. // requireAssignable assures that `dest` is a pointer and it's not an interface.
func requireAssignable(dst, src reflect.Value) error { func requireAssignable(dst, src reflect.Value) error {
if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface { if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {

View file

@ -384,6 +384,55 @@ func TestUnpack(t *testing.T) {
} }
} }
func TestUnpackSetDynamicArrayOutput(t *testing.T) {
abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`))
if err != nil {
t.Fatal(err)
}
var (
marshalledReturn32 = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000230783132333435363738393000000000000000000000000000000000000000003078303938373635343332310000000000000000000000000000000000000000")
marshalledReturn15 = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000230783031323334350000000000000000000000000000000000000000000000003078393837363534000000000000000000000000000000000000000000000000")
out32 [][32]byte
out15 [][15]byte
)
// test 32
err = abi.Unpack(&out32, "testDynamicFixedBytes32", marshalledReturn32)
if err != nil {
t.Fatal(err)
}
if len(out32) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out32))
}
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
if !bytes.Equal(out32[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[0])
}
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
if !bytes.Equal(out32[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[1])
}
// test 15
err = abi.Unpack(&out15, "testDynamicFixedBytes32", marshalledReturn15)
if err != nil {
t.Fatal(err)
}
if len(out15) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out15))
}
expected = common.Hex2Bytes("307830313233343500000000000000")
if !bytes.Equal(out15[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[0])
}
expected = common.Hex2Bytes("307839383736353400000000000000")
if !bytes.Equal(out15[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[1])
}
}
type methodMultiOutput struct { type methodMultiOutput struct {
Int *big.Int Int *big.Int
String string String string

View file

@ -164,10 +164,6 @@ var (
Name: "topic", Name: "topic",
Usage: "User-defined topic this feed is tracking, hex encoded. Limited to 64 hexadecimal characters", Usage: "User-defined topic this feed is tracking, hex encoded. Limited to 64 hexadecimal characters",
} }
SwarmFeedDataOnCreateFlag = cli.StringFlag{
Name: "data",
Usage: "Initializes the feed with the given hex-encoded data. Data must be prefixed by 0x",
}
SwarmFeedManifestFlag = cli.StringFlag{ SwarmFeedManifestFlag = cli.StringFlag{
Name: "manifest", Name: "manifest",
Usage: "Refers to the feed through a manifest", Usage: "Refers to the feed through a manifest",

View file

@ -172,6 +172,26 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump) log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
conf.PriceBump = DefaultTxPoolConfig.PriceBump conf.PriceBump = DefaultTxPoolConfig.PriceBump
} }
if conf.AccountSlots < 1 {
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
}
if conf.GlobalSlots < 1 {
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
}
if conf.AccountQueue < 1 {
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
}
if conf.GlobalQueue < 1 {
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
}
if conf.Lifetime < 1 {
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
conf.Lifetime = DefaultTxPoolConfig.Lifetime
}
return conf return conf
} }

View file

@ -1095,7 +1095,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
config := testTxPoolConfig config := testTxPoolConfig
config.GlobalSlots = 0 config.GlobalSlots = 1
pool := NewTxPool(config, params.TestChainConfig, blockchain) pool := NewTxPool(config, params.TestChainConfig, blockchain)
defer pool.Stop() defer pool.Stop()

View file

@ -1488,7 +1488,15 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles) blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
} }
if index, err := d.blockchain.InsertChain(blocks); err != nil { if index, err := d.blockchain.InsertChain(blocks); err != nil {
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err) if index < len(results) {
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
} else {
// The InsertChain method in blockchain.go will sometimes return an out-of-bounds index,
// when it needs to preprocess blocks to import a sidechain.
// The importer will put together a new list of blocks to import, which is a superset
// of the blocks delivered from the downloader, and the indexing will be off.
log.Debug("Downloaded item processing failed on sidechain import", "index", index, "err", err)
}
return errInvalidChain return errInvalidChain
} }
return nil return nil

View file

@ -152,7 +152,7 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
finished = append(finished, req) finished = append(finished, req)
delete(active, pack.PeerId()) delete(active, pack.PeerId())
// Handle dropped peer connections: // Handle dropped peer connections:
case p := <-peerDrop: case p := <-peerDrop:
// Skip if no request is currently pending // Skip if no request is currently pending
req := active[p.id] req := active[p.id]
@ -398,9 +398,8 @@ func (s *stateSync) fillTasks(n int, req *stateReq) {
// process iterates over a batch of delivered state data, injecting each item // process iterates over a batch of delivered state data, injecting each item
// into a running state sync, re-queuing any items that were requested but not // into a running state sync, re-queuing any items that were requested but not
// delivered. // delivered. Returns whether the peer actually managed to deliver anything of
// Returns whether the peer actually managed to deliver anything of value, // value, and any error that occurred.
// and any error that occurred
func (s *stateSync) process(req *stateReq) (int, error) { func (s *stateSync) process(req *stateReq) (int, error) {
// Collect processing stats and update progress if valid data was received // Collect processing stats and update progress if valid data was received
duplicate, unexpected, successful := 0, 0, 0 duplicate, unexpected, successful := 0, 0, 0
@ -412,14 +411,12 @@ func (s *stateSync) process(req *stateReq) (int, error) {
}(time.Now()) }(time.Now())
// Iterate over all the delivered data and inject one-by-one into the trie // Iterate over all the delivered data and inject one-by-one into the trie
progress := false
for _, blob := range req.response { for _, blob := range req.response {
prog, hash, err := s.processNodeData(blob) _, hash, err := s.processNodeData(blob)
switch err { switch err {
case nil: case nil:
s.numUncommitted++ s.numUncommitted++
s.bytesUncommitted += len(blob) s.bytesUncommitted += len(blob)
progress = progress || prog
successful++ successful++
case trie.ErrNotRequested: case trie.ErrNotRequested:
unexpected++ unexpected++

View file

@ -18,6 +18,7 @@
package web3ext package web3ext
var Modules = map[string]string{ var Modules = map[string]string{
"accounting": Accounting_JS,
"admin": Admin_JS, "admin": Admin_JS,
"chequebook": Chequebook_JS, "chequebook": Chequebook_JS,
"clique": Clique_JS, "clique": Clique_JS,
@ -704,3 +705,47 @@ web3._extend({
] ]
}); });
` `
const Accounting_JS = `
web3._extend({
property: 'accounting',
methods: [
new web3._extend.Property({
name: 'balance',
getter: 'account_balance'
}),
new web3._extend.Property({
name: 'balanceCredit',
getter: 'account_balanceCredit'
}),
new web3._extend.Property({
name: 'balanceDebit',
getter: 'account_balanceDebit'
}),
new web3._extend.Property({
name: 'bytesCredit',
getter: 'account_bytesCredit'
}),
new web3._extend.Property({
name: 'bytesDebit',
getter: 'account_bytesDebit'
}),
new web3._extend.Property({
name: 'msgCredit',
getter: 'account_msgCredit'
}),
new web3._extend.Property({
name: 'msgDebit',
getter: 'account_msgDebit'
}),
new web3._extend.Property({
name: 'peerDrops',
getter: 'account_peerDrops'
}),
new web3._extend.Property({
name: 'selfDrops',
getter: 'account_selfDrops'
}),
]
});
`

View file

@ -0,0 +1,94 @@
package protocols
import (
"errors"
)
// Textual version number of accounting API
const AccountingVersion = "1.0"
var errNoAccountingMetrics = errors.New("accounting metrics not enabled")
// AccountingApi provides an API to access account related information
type AccountingApi struct {
metrics *AccountingMetrics
}
// NewAccountingApi creates a new AccountingApi
// m will be used to check if accounting metrics are enabled
func NewAccountingApi(m *AccountingMetrics) *AccountingApi {
return &AccountingApi{m}
}
// Balance returns local node balance (units credited - units debited)
func (self *AccountingApi) Balance() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
balance := mBalanceCredit.Count() - mBalanceDebit.Count()
return balance, nil
}
// BalanceCredit returns total amount of units credited by local node
func (self *AccountingApi) BalanceCredit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mBalanceCredit.Count(), nil
}
// BalanceCredit returns total amount of units debited by local node
func (self *AccountingApi) BalanceDebit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mBalanceDebit.Count(), nil
}
// BytesCredit returns total amount of bytes credited by local node
func (self *AccountingApi) BytesCredit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mBytesCredit.Count(), nil
}
// BalanceCredit returns total amount of bytes debited by local node
func (self *AccountingApi) BytesDebit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mBytesDebit.Count(), nil
}
// MsgCredit returns total amount of messages credited by local node
func (self *AccountingApi) MsgCredit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mMsgCredit.Count(), nil
}
// MsgDebit returns total amount of messages debited by local node
func (self *AccountingApi) MsgDebit() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mMsgDebit.Count(), nil
}
// PeerDrops returns number of times when local node had to drop remote peers
func (self *AccountingApi) PeerDrops() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mPeerDrops.Count(), nil
}
// SelfDrops returns number of times when local node was overdrafted and dropped
func (self *AccountingApi) SelfDrops() (int64, error) {
if self.metrics == nil {
return 0, errNoAccountingMetrics
}
return mSelfDrops.Count(), nil
}

View file

@ -14,65 +14,69 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package simulation package simulations
import ( import (
"errors"
"strings" "strings"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
) )
var (
ErrNodeNotFound = errors.New("node not found")
ErrNoPivotNode = errors.New("no pivot node set")
)
// ConnectToPivotNode connects the node with provided NodeID // ConnectToPivotNode connects the node with provided NodeID
// to the pivot node, already set by Simulation.SetPivotNode method. // to the pivot node, already set by Network.SetPivotNode method.
// It is useful when constructing a star network topology // It is useful when constructing a star network topology
// when simulation adds and removes nodes dynamically. // when Network adds and removes nodes dynamically.
func (s *Simulation) ConnectToPivotNode(id enode.ID) (err error) { func (net *Network) ConnectToPivotNode(id enode.ID) (err error) {
pid := s.PivotNodeID() pivot := net.GetPivotNode()
if pid == nil { if pivot == nil {
return ErrNoPivotNode return ErrNoPivotNode
} }
return s.connect(*pid, id) return net.connect(pivot.ID(), id)
} }
// ConnectToLastNode connects the node with provided NodeID // ConnectToLastNode connects the node with provided NodeID
// to the last node that is up, and avoiding connection to self. // to the last node that is up, and avoiding connection to self.
// It is useful when constructing a chain network topology // It is useful when constructing a chain network topology
// when simulation adds and removes nodes dynamically. // when Network adds and removes nodes dynamically.
func (s *Simulation) ConnectToLastNode(id enode.ID) (err error) { func (net *Network) ConnectToLastNode(id enode.ID) (err error) {
ids := s.UpNodeIDs() ids := net.getUpNodeIDs()
l := len(ids) l := len(ids)
if l < 2 { if l < 2 {
return nil return nil
} }
lid := ids[l-1] last := ids[l-1]
if lid == id { if last == id {
lid = ids[l-2] last = ids[l-2]
} }
return s.connect(lid, id) return net.connect(last, id)
} }
// ConnectToRandomNode connects the node with provieded NodeID // ConnectToRandomNode connects the node with provided NodeID
// to a random node that is up. // to a random node that is up.
func (s *Simulation) ConnectToRandomNode(id enode.ID) (err error) { func (net *Network) ConnectToRandomNode(id enode.ID) (err error) {
n := s.RandomUpNode(id) selected := net.GetRandomUpNode(id)
if n == nil { if selected == nil {
return ErrNodeNotFound return ErrNodeNotFound
} }
return s.connect(n.ID, id) return net.connect(selected.ID(), id)
} }
// ConnectNodesFull connects all nodes one to another. // ConnectNodesFull connects all nodes one to another.
// It provides a complete connectivity in the network // It provides a complete connectivity in the network
// which should be rarely needed. // which should be rarely needed.
func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) { func (net *Network) ConnectNodesFull(ids []enode.ID) (err error) {
if ids == nil { if ids == nil {
ids = s.UpNodeIDs() ids = net.getUpNodeIDs()
} }
l := len(ids) for i, lid := range ids {
for i := 0; i < l; i++ { for _, rid := range ids[i+1:] {
for j := i + 1; j < l; j++ { if err = net.connect(lid, rid); err != nil {
err = s.connect(ids[i], ids[j])
if err != nil {
return err return err
} }
} }
@ -82,14 +86,13 @@ func (s *Simulation) ConnectNodesFull(ids []enode.ID) (err error) {
// ConnectNodesChain connects all nodes in a chain topology. // ConnectNodesChain connects all nodes in a chain topology.
// If ids argument is nil, all nodes that are up will be connected. // If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) { func (net *Network) ConnectNodesChain(ids []enode.ID) (err error) {
if ids == nil { if ids == nil {
ids = s.UpNodeIDs() ids = net.getUpNodeIDs()
} }
l := len(ids) l := len(ids)
for i := 0; i < l-1; i++ { for i := 0; i < l-1; i++ {
err = s.connect(ids[i], ids[i+1]) if err := net.connect(ids[i], ids[i+1]); err != nil {
if err != nil {
return err return err
} }
} }
@ -98,37 +101,32 @@ func (s *Simulation) ConnectNodesChain(ids []enode.ID) (err error) {
// ConnectNodesRing connects all nodes in a ring topology. // ConnectNodesRing connects all nodes in a ring topology.
// If ids argument is nil, all nodes that are up will be connected. // If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesRing(ids []enode.ID) (err error) { func (net *Network) ConnectNodesRing(ids []enode.ID) (err error) {
if ids == nil { if ids == nil {
ids = s.UpNodeIDs() ids = net.getUpNodeIDs()
} }
l := len(ids) l := len(ids)
if l < 2 { if l < 2 {
return nil return nil
} }
for i := 0; i < l-1; i++ { if err := net.ConnectNodesChain(ids); err != nil {
err = s.connect(ids[i], ids[i+1]) return err
if err != nil {
return err
}
} }
return s.connect(ids[l-1], ids[0]) return net.connect(ids[l-1], ids[0])
} }
// ConnectNodesStar connects all nodes in a star topology // ConnectNodesStar connects all nodes in a star topology
// with the center at provided NodeID. // with the center at provided NodeID.
// If ids argument is nil, all nodes that are up will be connected. // If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) { func (net *Network) ConnectNodesStar(pivot enode.ID, ids []enode.ID) (err error) {
if ids == nil { if ids == nil {
ids = s.UpNodeIDs() ids = net.getUpNodeIDs()
} }
l := len(ids) for _, id := range ids {
for i := 0; i < l; i++ { if pivot == id {
if id == ids[i] {
continue continue
} }
err = s.connect(id, ids[i]) if err := net.connect(pivot, id); err != nil {
if err != nil {
return err return err
} }
} }
@ -138,17 +136,17 @@ func (s *Simulation) ConnectNodesStar(id enode.ID, ids []enode.ID) (err error) {
// ConnectNodesStarPivot connects all nodes in a star topology // ConnectNodesStarPivot connects all nodes in a star topology
// with the center at already set pivot node. // with the center at already set pivot node.
// If ids argument is nil, all nodes that are up will be connected. // If ids argument is nil, all nodes that are up will be connected.
func (s *Simulation) ConnectNodesStarPivot(ids []enode.ID) (err error) { func (net *Network) ConnectNodesStarPivot(ids []enode.ID) (err error) {
id := s.PivotNodeID() pivot := net.GetPivotNode()
if id == nil { if pivot == nil {
return ErrNoPivotNode return ErrNoPivotNode
} }
return s.ConnectNodesStar(*id, ids) return net.ConnectNodesStar(pivot.ID(), ids)
} }
// connect connects two nodes but ignores already connected error. // connect connects two nodes but ignores already connected error.
func (s *Simulation) connect(oneID, otherID enode.ID) error { func (net *Network) connect(oneID, otherID enode.ID) error {
return ignoreAlreadyConnectedErr(s.Net.Connect(oneID, otherID)) return ignoreAlreadyConnectedErr(net.Connect(oneID, otherID))
} }
func ignoreAlreadyConnectedErr(err error) error { func ignoreAlreadyConnectedErr(err error) error {
@ -157,3 +155,22 @@ func ignoreAlreadyConnectedErr(err error) error {
} }
return err return err
} }
// SetPivotNode sets the NodeID of the network's pivot node.
// Pivot node is just a specific node that should be treated
// differently then other nodes in test. SetPivotNode and
// GetPivotNode are just a convenient functions to set and
// retrieve it.
func (net *Network) SetPivotNode(id enode.ID) {
net.lock.Lock()
defer net.lock.Unlock()
net.pivotNodeID = id
}
// GetPivotNode returns NodeID of the pivot node set by
// Network.SetPivotNode method.
func (net *Network) GetPivotNode() (node *Node) {
net.lock.RLock()
defer net.lock.RUnlock()
return net.getNode(net.pivotNodeID)
}

View file

@ -0,0 +1,190 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package simulations
import (
"testing"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
)
func newTestNetwork(t *testing.T, nodeCount int) (*Network, []enode.ID) {
adapter := adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
return NewNoopService(nil), nil
},
})
// create network
network := NewNetwork(adapter, &NetworkConfig{
DefaultService: "noopwoop",
})
// create and start nodes
ids := make([]enode.ID, nodeCount)
for i := range ids {
conf := adapters.RandomNodeConfig()
node, err := network.NewNodeWithConfig(conf)
if err != nil {
t.Fatalf("error creating node: %s", err)
}
if err := network.Start(node.ID()); err != nil {
t.Fatalf("error starting node: %s", err)
}
ids[i] = node.ID()
}
if len(network.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
return network, ids
}
func TestConnectToPivotNode(t *testing.T) {
net, ids := newTestNetwork(t, 2)
defer net.Shutdown()
pivot := ids[0]
net.SetPivotNode(pivot)
other := ids[1]
err := net.ConnectToPivotNode(other)
if err != nil {
t.Fatal(err)
}
if net.GetConn(pivot, other) == nil {
t.Error("pivot and the other node are not connected")
}
}
func TestConnectToLastNode(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
first := ids[0]
if err := net.ConnectToLastNode(first); err != nil {
t.Fatal(err)
}
last := ids[len(ids)-1]
for i, id := range ids {
if id == first || id == last {
continue
}
if net.GetConn(first, id) != nil {
t.Errorf("connection must not exist with node(ind: %v, id: %v)", i, id)
}
}
if net.GetConn(first, last) == nil {
t.Error("first and last node must be connected")
}
}
func TestConnectToRandomNode(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
err := net.ConnectToRandomNode(ids[0])
if err != nil {
t.Fatal(err)
}
var cc int
for i, a := range ids {
for _, b := range ids[i:] {
if net.GetConn(a, b) != nil {
cc++
}
}
}
if cc != 1 {
t.Errorf("expected one connection, got %v", cc)
}
}
func TestConnectNodesFull(t *testing.T) {
net, ids := newTestNetwork(t, 12)
defer net.Shutdown()
err := net.ConnectNodesFull(ids)
if err != nil {
t.Fatal(err)
}
VerifyFull(t, net, ids)
}
func TestConnectNodesChain(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
err := net.ConnectNodesChain(ids)
if err != nil {
t.Fatal(err)
}
VerifyChain(t, net, ids)
}
func TestConnectNodesRing(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
err := net.ConnectNodesRing(ids)
if err != nil {
t.Fatal(err)
}
VerifyRing(t, net, ids)
}
func TestConnectNodesStar(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
pivotIndex := 2
err := net.ConnectNodesStar(ids[pivotIndex], ids)
if err != nil {
t.Fatal(err)
}
VerifyStar(t, net, ids, pivotIndex)
}
func TestConnectNodesStarPivot(t *testing.T) {
net, ids := newTestNetwork(t, 10)
defer net.Shutdown()
pivotIndex := 4
net.SetPivotNode(ids[pivotIndex])
err := net.ConnectNodesStarPivot(ids)
if err != nil {
t.Fatal(err)
}
VerifyStar(t, net, ids, pivotIndex)
}

View file

@ -22,6 +22,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"sync" "sync"
"time" "time"
@ -57,6 +58,8 @@ type Network struct {
Conns []*Conn `json:"conns"` Conns []*Conn `json:"conns"`
connMap map[string]int connMap map[string]int
pivotNodeID enode.ID
nodeAdapter adapters.NodeAdapter nodeAdapter adapters.NodeAdapter
events event.Feed events event.Feed
lock sync.RWMutex lock sync.RWMutex
@ -370,23 +373,32 @@ func (net *Network) DidReceive(sender, receiver enode.ID, proto string, code uin
// GetNode gets the node with the given ID, returning nil if the node does not // GetNode gets the node with the given ID, returning nil if the node does not
// exist // exist
func (net *Network) GetNode(id enode.ID) *Node { func (net *Network) GetNode(id enode.ID) *Node {
net.lock.Lock() net.lock.RLock()
defer net.lock.Unlock() defer net.lock.RUnlock()
return net.getNode(id) return net.getNode(id)
} }
// GetNode gets the node with the given name, returning nil if the node does // GetNode gets the node with the given name, returning nil if the node does
// not exist // not exist
func (net *Network) GetNodeByName(name string) *Node { func (net *Network) GetNodeByName(name string) *Node {
net.lock.Lock() net.lock.RLock()
defer net.lock.Unlock() defer net.lock.RUnlock()
return net.getNodeByName(name) return net.getNodeByName(name)
} }
func (net *Network) getNodeByName(name string) *Node {
for _, node := range net.Nodes {
if node.Config.Name == name {
return node
}
}
return nil
}
// GetNodes returns the existing nodes // GetNodes returns the existing nodes
func (net *Network) GetNodes() (nodes []*Node) { func (net *Network) GetNodes() (nodes []*Node) {
net.lock.Lock() net.lock.RLock()
defer net.lock.Unlock() defer net.lock.RUnlock()
nodes = append(nodes, net.Nodes...) nodes = append(nodes, net.Nodes...)
return nodes return nodes
@ -400,20 +412,67 @@ func (net *Network) getNode(id enode.ID) *Node {
return net.Nodes[i] return net.Nodes[i]
} }
func (net *Network) getNodeByName(name string) *Node { // GetRandomUpNode returns a random node on the network, which is running.
func (net *Network) GetRandomUpNode(excludeIDs ...enode.ID) *Node {
net.lock.RLock()
defer net.lock.RUnlock()
return net.getRandomNode(net.getUpNodeIDs(), excludeIDs)
}
func (net *Network) getUpNodeIDs() (ids []enode.ID) {
for _, node := range net.Nodes { for _, node := range net.Nodes {
if node.Config.Name == name { if node.Up {
return node ids = append(ids, node.ID())
} }
} }
return nil return ids
}
// GetRandomDownNode returns a random node on the network, which is stopped.
func (net *Network) GetRandomDownNode(excludeIDs ...enode.ID) *Node {
net.lock.RLock()
defer net.lock.RUnlock()
return net.getRandomNode(net.getDownNodeIDs(), excludeIDs)
}
func (net *Network) getDownNodeIDs() (ids []enode.ID) {
for _, node := range net.GetNodes() {
if !node.Up {
ids = append(ids, node.ID())
}
}
return ids
}
func (net *Network) getRandomNode(ids []enode.ID, excludeIDs []enode.ID) *Node {
filtered := filterIDs(ids, excludeIDs)
l := len(filtered)
if l == 0 {
return nil
}
return net.GetNode(filtered[rand.Intn(l)])
}
func filterIDs(ids []enode.ID, excludeIDs []enode.ID) []enode.ID {
exclude := make(map[enode.ID]bool)
for _, id := range excludeIDs {
exclude[id] = true
}
var filtered []enode.ID
for _, id := range ids {
if _, found := exclude[id]; !found {
filtered = append(filtered, id)
}
}
return filtered
} }
// GetConn returns the connection which exists between "one" and "other" // GetConn returns the connection which exists between "one" and "other"
// regardless of which node initiated the connection // regardless of which node initiated the connection
func (net *Network) GetConn(oneID, otherID enode.ID) *Conn { func (net *Network) GetConn(oneID, otherID enode.ID) *Conn {
net.lock.Lock() net.lock.RLock()
defer net.lock.Unlock() defer net.lock.RUnlock()
return net.getConn(oneID, otherID) return net.getConn(oneID, otherID)
} }

View file

@ -18,14 +18,266 @@ package simulations
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"strconv"
"strings"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
) )
// Tests that a created snapshot with a minimal service only contains the expected connections
// and that a network when loaded with this snapshot only contains those same connections
func TestSnapshot(t *testing.T) {
// PART I
// create snapshot from ring network
// this is a minimal service, whose protocol will take exactly one message OR close of connection before quitting
adapter := adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
return NewNoopService(nil), nil
},
})
// create network
network := NewNetwork(adapter, &NetworkConfig{
DefaultService: "noopwoop",
})
// \todo consider making a member of network, set to true threadsafe when shutdown
runningOne := true
defer func() {
if runningOne {
network.Shutdown()
}
}()
// create and start nodes
nodeCount := 20
ids := make([]enode.ID, nodeCount)
for i := 0; i < nodeCount; i++ {
conf := adapters.RandomNodeConfig()
node, err := network.NewNodeWithConfig(conf)
if err != nil {
t.Fatalf("error creating node: %s", err)
}
if err := network.Start(node.ID()); err != nil {
t.Fatalf("error starting node: %s", err)
}
ids[i] = node.ID()
}
// subscribe to peer events
evC := make(chan *Event)
sub := network.Events().Subscribe(evC)
defer sub.Unsubscribe()
// connect nodes in a ring
// spawn separate thread to avoid deadlock in the event listeners
go func() {
for i, id := range ids {
peerID := ids[(i+1)%len(ids)]
if err := network.Connect(id, peerID); err != nil {
t.Fatal(err)
}
}
}()
// collect connection events up to expected number
ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
checkIds := make(map[enode.ID][]enode.ID)
connEventCount := nodeCount
OUTER:
for {
select {
case <-ctx.Done():
t.Fatal(ctx.Err())
case ev := <-evC:
if ev.Type == EventTypeConn && !ev.Control {
// fail on any disconnect
if !ev.Conn.Up {
t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
}
checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
connEventCount--
log.Debug("ev", "count", connEventCount)
if connEventCount == 0 {
break OUTER
}
}
}
}
// create snapshot of current network
snap, err := network.Snapshot()
if err != nil {
t.Fatal(err)
}
j, err := json.Marshal(snap)
if err != nil {
t.Fatal(err)
}
log.Debug("snapshot taken", "nodes", len(snap.Nodes), "conns", len(snap.Conns), "json", string(j))
// verify that the snap element numbers check out
if len(checkIds) != len(snap.Conns) || len(checkIds) != len(snap.Nodes) {
t.Fatalf("snapshot wrong node,conn counts %d,%d != %d", len(snap.Nodes), len(snap.Conns), len(checkIds))
}
// shut down sim network
runningOne = false
sub.Unsubscribe()
network.Shutdown()
// check that we have all the expected connections in the snapshot
for nodid, nodConns := range checkIds {
for _, nodConn := range nodConns {
var match bool
for _, snapConn := range snap.Conns {
if snapConn.One == nodid && snapConn.Other == nodConn {
match = true
break
} else if snapConn.Other == nodid && snapConn.One == nodConn {
match = true
break
}
}
if !match {
t.Fatalf("snapshot missing conn %v -> %v", nodid, nodConn)
}
}
}
log.Info("snapshot checked")
// PART II
// load snapshot and verify that exactly same connections are formed
adapter = adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
return NewNoopService(nil), nil
},
})
network = NewNetwork(adapter, &NetworkConfig{
DefaultService: "noopwoop",
})
defer func() {
network.Shutdown()
}()
// subscribe to peer events
// every node up and conn up event will generate one additional control event
// therefore multiply the count by two
evC = make(chan *Event, (len(snap.Conns)*2)+(len(snap.Nodes)*2))
sub = network.Events().Subscribe(evC)
defer sub.Unsubscribe()
// load the snapshot
// spawn separate thread to avoid deadlock in the event listeners
err = network.Load(snap)
if err != nil {
t.Fatal(err)
}
// collect connection events up to expected number
ctx, cancel = context.WithTimeout(context.TODO(), time.Second*3)
defer cancel()
connEventCount = nodeCount
OUTER_TWO:
for {
select {
case <-ctx.Done():
t.Fatal(ctx.Err())
case ev := <-evC:
if ev.Type == EventTypeConn && !ev.Control {
// fail on any disconnect
if !ev.Conn.Up {
t.Fatalf("unexpected disconnect: %v -> %v", ev.Conn.One, ev.Conn.Other)
}
log.Debug("conn", "on", ev.Conn.One, "other", ev.Conn.Other)
checkIds[ev.Conn.One] = append(checkIds[ev.Conn.One], ev.Conn.Other)
checkIds[ev.Conn.Other] = append(checkIds[ev.Conn.Other], ev.Conn.One)
connEventCount--
log.Debug("ev", "count", connEventCount)
if connEventCount == 0 {
break OUTER_TWO
}
}
}
}
// check that we have all expected connections in the network
for _, snapConn := range snap.Conns {
var match bool
for nodid, nodConns := range checkIds {
for _, nodConn := range nodConns {
if snapConn.One == nodid && snapConn.Other == nodConn {
match = true
break
} else if snapConn.Other == nodid && snapConn.One == nodConn {
match = true
break
}
}
}
if !match {
t.Fatalf("network missing conn %v -> %v", snapConn.One, snapConn.Other)
}
}
// verify that network didn't generate any other additional connection events after the ones we have collected within a reasonable period of time
ctx, cancel = context.WithTimeout(context.TODO(), time.Second)
defer cancel()
select {
case <-ctx.Done():
case ev := <-evC:
if ev.Type == EventTypeConn {
t.Fatalf("Superfluous conn found %v -> %v", ev.Conn.One, ev.Conn.Other)
}
}
// This test validates if all connections from the snapshot
// are created in the network.
t.Run("conns after load", func(t *testing.T) {
// Create new network.
n := NewNetwork(
adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
return NewNoopService(nil), nil
},
}),
&NetworkConfig{
DefaultService: "noopwoop",
},
)
defer n.Shutdown()
// Load the same snapshot.
err := n.Load(snap)
if err != nil {
t.Fatal(err)
}
// Check every connection from the snapshot
// if it is in the network, too.
for _, c := range snap.Conns {
if n.GetConn(c.One, c.Other) == nil {
t.Errorf("missing connection: %s -> %s", c.One, c.Other)
}
}
})
}
// TestNetworkSimulation creates a multi-node simulation network with each node // TestNetworkSimulation creates a multi-node simulation network with each node
// connected in a ring topology, checks that all nodes successfully handshake // connected in a ring topology, checks that all nodes successfully handshake
// with each other and that a snapshot fully represents the desired topology // with each other and that a snapshot fully represents the desired topology
@ -158,3 +410,78 @@ func triggerChecks(ctx context.Context, ids []enode.ID, trigger chan enode.ID, i
} }
} }
} }
// \todo: refactor to implement shapshots
// and connect configuration methods once these are moved from
// swarm/network/simulations/connect.go
func BenchmarkMinimalService(b *testing.B) {
b.Run("ring/32", benchmarkMinimalServiceTmp)
}
func benchmarkMinimalServiceTmp(b *testing.B) {
// stop timer to discard setup time pollution
args := strings.Split(b.Name(), "/")
nodeCount, err := strconv.ParseInt(args[2], 10, 16)
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
// this is a minimal service, whose protocol will close a channel upon run of protocol
// making it possible to bench the time it takes for the service to start and protocol actually to be run
protoCMap := make(map[enode.ID]map[enode.ID]chan struct{})
adapter := adapters.NewSimAdapter(adapters.Services{
"noopwoop": func(ctx *adapters.ServiceContext) (node.Service, error) {
protoCMap[ctx.Config.ID] = make(map[enode.ID]chan struct{})
svc := NewNoopService(protoCMap[ctx.Config.ID])
return svc, nil
},
})
// create network
network := NewNetwork(adapter, &NetworkConfig{
DefaultService: "noopwoop",
})
defer network.Shutdown()
// create and start nodes
ids := make([]enode.ID, nodeCount)
for i := 0; i < int(nodeCount); i++ {
conf := adapters.RandomNodeConfig()
node, err := network.NewNodeWithConfig(conf)
if err != nil {
b.Fatalf("error creating node: %s", err)
}
if err := network.Start(node.ID()); err != nil {
b.Fatalf("error starting node: %s", err)
}
ids[i] = node.ID()
}
// ready, set, go
b.ResetTimer()
// connect nodes in a ring
for i, id := range ids {
peerID := ids[(i+1)%len(ids)]
if err := network.Connect(id, peerID); err != nil {
b.Fatal(err)
}
}
// wait for all protocols to signal to close down
ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
for nodid, peers := range protoCMap {
for peerid, peerC := range peers {
log.Debug("getting ", "node", nodid, "peer", peerid)
select {
case <-ctx.Done():
b.Fatal(ctx.Err())
case <-peerC:
}
}
}
}
}

134
p2p/simulations/test.go Normal file
View file

@ -0,0 +1,134 @@
package simulations
import (
"testing"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rpc"
)
// NoopService is the service that does not do anything
// but implements node.Service interface.
type NoopService struct {
c map[enode.ID]chan struct{}
}
func NewNoopService(ackC map[enode.ID]chan struct{}) *NoopService {
return &NoopService{
c: ackC,
}
}
func (t *NoopService) Protocols() []p2p.Protocol {
return []p2p.Protocol{
{
Name: "noop",
Version: 666,
Length: 0,
Run: func(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
if t.c != nil {
t.c[peer.ID()] = make(chan struct{})
close(t.c[peer.ID()])
}
rw.ReadMsg()
return nil
},
NodeInfo: func() interface{} {
return struct{}{}
},
PeerInfo: func(id enode.ID) interface{} {
return struct{}{}
},
Attributes: []enr.Entry{},
},
}
}
func (t *NoopService) APIs() []rpc.API {
return []rpc.API{}
}
func (t *NoopService) Start(server *p2p.Server) error {
return nil
}
func (t *NoopService) Stop() error {
return nil
}
func VerifyRing(t *testing.T, net *Network, ids []enode.ID) {
t.Helper()
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := net.GetConn(ids[i], ids[j])
if i == j-1 || (i == 0 && j == n-1) {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}
func VerifyChain(t *testing.T, net *Network, ids []enode.ID) {
t.Helper()
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := net.GetConn(ids[i], ids[j])
if i == j-1 {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}
func VerifyFull(t *testing.T, net *Network, ids []enode.ID) {
t.Helper()
n := len(ids)
var connections int
for i, lid := range ids {
for _, rid := range ids[i+1:] {
if net.GetConn(lid, rid) != nil {
connections++
}
}
}
want := n * (n - 1) / 2
if connections != want {
t.Errorf("wrong number of connections, got: %v, want: %v", connections, want)
}
}
func VerifyStar(t *testing.T, net *Network, ids []enode.ID, centerIndex int) {
t.Helper()
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := net.GetConn(ids[i], ids[j])
if i == centerIndex || j == centerIndex {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}

View file

@ -50,10 +50,6 @@ import (
opentracing "github.com/opentracing/opentracing-go" opentracing "github.com/opentracing/opentracing-go"
) )
var (
ErrNotFound = errors.New("not found")
)
var ( var (
apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil) apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil) apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
@ -136,13 +132,6 @@ func MultiResolverOptionWithResolver(r ResolveValidator, tld string) MultiResolv
} }
} }
// MultiResolverOptionWithNameHash is unused at the time of this writing
func MultiResolverOptionWithNameHash(nameHash func(string) common.Hash) MultiResolverOption {
return func(m *MultiResolver) {
m.nameHash = nameHash
}
}
// NewMultiResolver creates a new instance of MultiResolver. // NewMultiResolver creates a new instance of MultiResolver.
func NewMultiResolver(opts ...MultiResolverOption) (m *MultiResolver) { func NewMultiResolver(opts ...MultiResolverOption) (m *MultiResolver) {
m = &MultiResolver{ m = &MultiResolver{
@ -173,40 +162,6 @@ func (m *MultiResolver) Resolve(addr string) (h common.Hash, err error) {
return return
} }
// ValidateOwner checks the ENS to validate that the owner of the given domain is the given eth address
func (m *MultiResolver) ValidateOwner(name string, address common.Address) (bool, error) {
rs, err := m.getResolveValidator(name)
if err != nil {
return false, err
}
var addr common.Address
for _, r := range rs {
addr, err = r.Owner(m.nameHash(name))
// we hide the error if it is not for the last resolver we check
if err == nil {
return addr == address, nil
}
}
return false, err
}
// HeaderByNumber uses the validator of the given domainname and retrieves the header for the given block number
func (m *MultiResolver) HeaderByNumber(ctx context.Context, name string, blockNr *big.Int) (*types.Header, error) {
rs, err := m.getResolveValidator(name)
if err != nil {
return nil, err
}
for _, r := range rs {
var header *types.Header
header, err = r.HeaderByNumber(ctx, blockNr)
// we hide the error if it is not for the last resolver we check
if err == nil {
return header, nil
}
}
return nil, err
}
// getResolveValidator uses the hostname to retrieve the resolver associated with the top level domain // getResolveValidator uses the hostname to retrieve the resolver associated with the top level domain
func (m *MultiResolver) getResolveValidator(name string) ([]ResolveValidator, error) { func (m *MultiResolver) getResolveValidator(name string) ([]ResolveValidator, error) {
rs := m.resolvers[""] rs := m.resolvers[""]
@ -224,11 +179,6 @@ func (m *MultiResolver) getResolveValidator(name string) ([]ResolveValidator, er
return rs, nil return rs, nil
} }
// SetNameHash sets the hasher function that hashes the domain into a name hash that ENS uses
func (m *MultiResolver) SetNameHash(nameHash func(string) common.Hash) {
m.nameHash = nameHash
}
/* /*
API implements webserver/file system related content storage and retrieval API implements webserver/file system related content storage and retrieval
on top of the FileStore on top of the FileStore
@ -265,9 +215,6 @@ func (a *API) Store(ctx context.Context, data io.Reader, size int64, toEncrypt b
return a.fileStore.Store(ctx, data, size, toEncrypt) return a.fileStore.Store(ctx, data, size, toEncrypt)
} }
// ErrResolve is returned when an URI cannot be resolved from ENS.
type ErrResolve error
// Resolve a name into a content-addressed hash // Resolve a name into a content-addressed hash
// where address could be an ENS name, or a content addressed hash // where address could be an ENS name, or a content addressed hash
func (a *API) Resolve(ctx context.Context, address string) (storage.Address, error) { func (a *API) Resolve(ctx context.Context, address string) (storage.Address, error) {
@ -980,11 +927,6 @@ func (a *API) FeedsUpdate(ctx context.Context, request *feed.Request) (storage.A
return a.feed.Update(ctx, request) return a.feed.Update(ctx, request)
} }
// FeedsHashSize returned the size of the digest produced by Swarm feeds' hashing function
func (a *API) FeedsHashSize() int {
return a.feed.HashSize
}
// ErrCannotLoadFeedManifest is returned when looking up a feeds manifest fails // ErrCannotLoadFeedManifest is returned when looking up a feeds manifest fails
var ErrCannotLoadFeedManifest = errors.New("Cannot load feed manifest") var ErrCannotLoadFeedManifest = errors.New("Cannot load feed manifest")

View file

@ -45,11 +45,6 @@ import (
"github.com/pborman/uuid" "github.com/pborman/uuid"
) )
var (
DefaultGateway = "http://localhost:8500"
DefaultClient = NewClient(DefaultGateway)
)
var ( var (
ErrUnauthorized = errors.New("unauthorized") ErrUnauthorized = errors.New("unauthorized")
) )

View file

@ -83,23 +83,3 @@ func (s *Storage) Get(ctx context.Context, bzzpath string) (*Response, error) {
} }
return &Response{mimeType, status, expsize, string(body[:size])}, err return &Response{mimeType, status, expsize, string(body[:size])}, err
} }
// Modify(rootHash, basePath, contentHash, contentType) takes th e manifest trie rooted in rootHash,
// and merge on to it. creating an entry w conentType (mime)
//
// DEPRECATED: Use the HTTP API instead
func (s *Storage) Modify(ctx context.Context, rootHash, path, contentHash, contentType string) (newRootHash string, err error) {
uri, err := Parse("bzz:/" + rootHash)
if err != nil {
return "", err
}
addr, err := s.api.Resolve(ctx, uri.Addr)
if err != nil {
return "", err
}
addr, err = s.api.Modify(ctx, addr, path, contentHash, contentType)
if err != nil {
return "", err
}
return addr.Hex(), nil
}

View file

@ -29,18 +29,6 @@ func NewControl(api *API, hive *network.Hive) *Control {
return &Control{api, hive} return &Control{api, hive}
} }
//func (self *Control) BlockNetworkRead(on bool) {
// self.hive.BlockNetworkRead(on)
//}
//
//func (self *Control) SyncEnabled(on bool) {
// self.hive.SyncEnabled(on)
//}
//
//func (self *Control) SwapEnabled(on bool) {
// self.hive.SwapEnabled(on)
//}
//
func (c *Control) Hive() string { func (c *Control) Hive() string {
return c.hive.String() return c.hive.String()
} }

View file

@ -26,17 +26,15 @@ import (
func TestParseURI(t *testing.T) { func TestParseURI(t *testing.T) {
type test struct { type test struct {
uri string uri string
expectURI *URI expectURI *URI
expectErr bool expectErr bool
expectRaw bool expectRaw bool
expectImmutable bool expectImmutable bool
expectList bool expectList bool
expectHash bool expectHash bool
expectDeprecatedRaw bool expectValidKey bool
expectDeprecatedImmutable bool expectAddr storage.Address
expectValidKey bool
expectAddr storage.Address
} }
tests := []test{ tests := []test{
{ {

View file

@ -60,7 +60,3 @@ func (bv *BitVector) Set(i int, v bool) {
func (bv *BitVector) Bytes() []byte { func (bv *BitVector) Bytes() []byte {
return bv.b return bv.b
} }
func (bv *BitVector) Length() int {
return bv.len
}

View file

@ -161,7 +161,7 @@ func (d *Peer) handleSubPeersMsg(msg *subPeersMsg) error {
d.setDepth(msg.Depth) d.setDepth(msg.Depth)
var peers []*BzzAddr var peers []*BzzAddr
d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool { d.kad.EachConn(d.Over(), 255, func(p *Peer, po int, isproxbin bool) bool {
if pob, _ := pof(d, d.kad.BaseAddr(), 0); pob > po { if pob, _ := Pof(d, d.kad.BaseAddr(), 0); pob > po {
return false return false
} }
if !d.seen(p.BzzAddr) { if !d.seen(p.BzzAddr) {

View file

@ -49,7 +49,7 @@ a guaranteed constant maximum limit on the number of hops needed to reach one
node from the other. node from the other.
*/ */
var pof = pot.DefaultPof(256) var Pof = pot.DefaultPof(256)
// KadParams holds the config params for Kademlia // KadParams holds the config params for Kademlia
type KadParams struct { type KadParams struct {
@ -62,7 +62,7 @@ type KadParams struct {
RetryExponent int // exponent to multiply retry intervals with RetryExponent int // exponent to multiply retry intervals with
MaxRetries int // maximum number of redial attempts MaxRetries int // maximum number of redial attempts
// function to sanction or prevent suggesting a peer // function to sanction or prevent suggesting a peer
Reachable func(*BzzAddr) bool Reachable func(*BzzAddr) bool `json:"-"`
} }
// NewKadParams returns a params struct with default values // NewKadParams returns a params struct with default values
@ -81,15 +81,14 @@ func NewKadParams() *KadParams {
// Kademlia is a table of live peers and a db of known peers (node records) // Kademlia is a table of live peers and a db of known peers (node records)
type Kademlia struct { type Kademlia struct {
lock sync.RWMutex lock sync.RWMutex
*KadParams // Kademlia configuration parameters *KadParams // Kademlia configuration parameters
base []byte // immutable baseaddress of the table base []byte // immutable baseaddress of the table
addrs *pot.Pot // pots container for known peer addresses addrs *pot.Pot // pots container for known peer addresses
conns *pot.Pot // pots container for live peer connections conns *pot.Pot // pots container for live peer connections
depth uint8 // stores the last current depth of saturation depth uint8 // stores the last current depth of saturation
nDepth int // stores the last neighbourhood depth nDepth int // stores the last neighbourhood depth
nDepthC chan int // returned by DepthC function to signal neighbourhood depth change nDepthC chan int // returned by DepthC function to signal neighbourhood depth change
addrCountC chan int // returned by AddrCountC function to signal peer count change addrCountC chan int // returned by AddrCountC function to signal peer count change
Pof func(pot.Val, pot.Val, int) (int, bool) // function for calculating kademlia routing distance between two addresses
} }
// NewKademlia creates a Kademlia table for base address addr // NewKademlia creates a Kademlia table for base address addr
@ -104,7 +103,6 @@ func NewKademlia(addr []byte, params *KadParams) *Kademlia {
KadParams: params, KadParams: params,
addrs: pot.NewPot(nil, 0), addrs: pot.NewPot(nil, 0),
conns: pot.NewPot(nil, 0), conns: pot.NewPot(nil, 0),
Pof: pof,
} }
} }
@ -147,7 +145,7 @@ func (k *Kademlia) Register(peers ...*BzzAddr) error {
return fmt.Errorf("add peers: %x is self", k.base) return fmt.Errorf("add peers: %x is self", k.base)
} }
var found bool var found bool
k.addrs, _, found, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { k.addrs, _, found, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val {
// if not found // if not found
if v == nil { if v == nil {
// insert new offline peer into conns // insert new offline peer into conns
@ -181,7 +179,7 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) {
// if there is a callable neighbour within the current proxBin, connect // if there is a callable neighbour within the current proxBin, connect
// this makes sure nearest neighbour set is fully connected // this makes sure nearest neighbour set is fully connected
var ppo int var ppo int
k.addrs.EachNeighbour(k.base, pof, func(val pot.Val, po int) bool { k.addrs.EachNeighbour(k.base, Pof, func(val pot.Val, po int) bool {
if po < depth { if po < depth {
return false return false
} }
@ -200,7 +198,7 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) {
var bpo []int var bpo []int
prev := -1 prev := -1
k.conns.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
prev++ prev++
for ; prev < po; prev++ { for ; prev < po; prev++ {
bpo = append(bpo, prev) bpo = append(bpo, prev)
@ -221,7 +219,7 @@ func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) {
// try to select a candidate peer // try to select a candidate peer
// find the first callable peer // find the first callable peer
nxt := bpo[0] nxt := bpo[0]
k.addrs.EachBin(k.base, pof, nxt, func(po, _ int, f func(func(pot.Val, int) bool) bool) bool { k.addrs.EachBin(k.base, Pof, nxt, func(po, _ int, f func(func(pot.Val, int) bool) bool) bool {
// for each bin (up until depth) we find callable candidate peers // for each bin (up until depth) we find callable candidate peers
if po >= depth { if po >= depth {
return false return false
@ -253,7 +251,7 @@ func (k *Kademlia) On(p *Peer) (uint8, bool) {
k.lock.Lock() k.lock.Lock()
defer k.lock.Unlock() defer k.lock.Unlock()
var ins bool var ins bool
k.conns, _, _, _ = pot.Swap(k.conns, p, pof, func(v pot.Val) pot.Val { k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(v pot.Val) pot.Val {
// if not found live // if not found live
if v == nil { if v == nil {
ins = true ins = true
@ -267,7 +265,7 @@ func (k *Kademlia) On(p *Peer) (uint8, bool) {
a := newEntry(p.BzzAddr) a := newEntry(p.BzzAddr)
a.conn = p a.conn = p
// insert new online peer into addrs // insert new online peer into addrs
k.addrs, _, _, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val {
return a return a
}) })
// send new address count value only if the peer is inserted // send new address count value only if the peer is inserted
@ -277,7 +275,7 @@ func (k *Kademlia) On(p *Peer) (uint8, bool) {
} }
log.Trace(k.string()) log.Trace(k.string())
// calculate if depth of saturation changed // calculate if depth of saturation changed
depth := uint8(k.saturation(k.MinBinSize)) depth := uint8(k.saturation())
var changed bool var changed bool
if depth != k.depth { if depth != k.depth {
changed = true changed = true
@ -333,7 +331,7 @@ func (k *Kademlia) Off(p *Peer) {
defer k.lock.Unlock() defer k.lock.Unlock()
var del bool var del bool
if !p.BzzPeer.LightNode { if !p.BzzPeer.LightNode {
k.addrs, _, _, _ = pot.Swap(k.addrs, p, pof, func(v pot.Val) pot.Val { k.addrs, _, _, _ = pot.Swap(k.addrs, p, Pof, func(v pot.Val) pot.Val {
// v cannot be nil, must check otherwise we overwrite entry // v cannot be nil, must check otherwise we overwrite entry
if v == nil { if v == nil {
panic(fmt.Sprintf("connected peer not found %v", p)) panic(fmt.Sprintf("connected peer not found %v", p))
@ -346,7 +344,7 @@ func (k *Kademlia) Off(p *Peer) {
} }
if del { if del {
k.conns, _, _, _ = pot.Swap(k.conns, p, pof, func(_ pot.Val) pot.Val { k.conns, _, _, _ = pot.Swap(k.conns, p, Pof, func(_ pot.Val) pot.Val {
// v cannot be nil, but no need to check // v cannot be nil, but no need to check
return nil return nil
}) })
@ -358,6 +356,10 @@ func (k *Kademlia) Off(p *Peer) {
} }
} }
// EachBin is a two level nested iterator
// The outer iterator returns all bins that have known peers, in order from shallowest to deepest
// The inner iterator returns all peers per bin returned by the outer iterator, in no defined order
// TODO the po returned by the inner iterator is not reliable. However, it is not being used in this method
func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(conn *Peer, po int) bool) { func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(conn *Peer, po int) bool) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
@ -366,7 +368,7 @@ func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(con
var endPo int var endPo int
kadDepth := depthForPot(k.conns, k.MinProxBinSize, k.base) kadDepth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.conns.EachBin(base, pof, o, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.conns.EachBin(base, Pof, o, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
if startPo > 0 && endPo != k.MaxProxDisplay { if startPo > 0 && endPo != k.MaxProxDisplay {
startPo = endPo + 1 startPo = endPo + 1
} }
@ -388,6 +390,7 @@ func (k *Kademlia) EachBin(base []byte, pof pot.Pof, o int, eachBinFunc func(con
// EachConn is an iterator with args (base, po, f) applies f to each live peer // EachConn is an iterator with args (base, po, f) applies f to each live peer
// that has proximity order po or less as measured from the base // that has proximity order po or less as measured from the base
// if base is nil, kademlia base address is used // if base is nil, kademlia base address is used
// It returns peers in order deepest to shallowest
func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int, bool) bool) { func (k *Kademlia) EachConn(base []byte, o int, f func(*Peer, int, bool) bool) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
@ -399,7 +402,7 @@ func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int, bool) bool) {
base = k.base base = k.base
} }
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.conns.EachNeighbour(base, pof, func(val pot.Val, po int) bool { k.conns.EachNeighbour(base, Pof, func(val pot.Val, po int) bool {
if po > o { if po > o {
return true return true
} }
@ -408,8 +411,9 @@ func (k *Kademlia) eachConn(base []byte, o int, f func(*Peer, int, bool) bool) {
} }
// EachAddr called with (base, po, f) is an iterator applying f to each known peer // EachAddr called with (base, po, f) is an iterator applying f to each known peer
// that has proximity order po or less as measured from the base // that has proximity order o or less as measured from the base
// if base is nil, kademlia base address is used // if base is nil, kademlia base address is used
// It returns peers in order deepest to shallowest
func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) { func (k *Kademlia) EachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool) {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
@ -421,7 +425,7 @@ func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int, bool) bool
base = k.base base = k.base
} }
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.addrs.EachNeighbour(base, pof, func(val pot.Val, po int) bool { k.addrs.EachNeighbour(base, Pof, func(val pot.Val, po int) bool {
if po > o { if po > o {
return true return true
} }
@ -447,11 +451,10 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) {
// total number of peers in iteration // total number of peers in iteration
var size int var size int
// true if iteration has all prox peers // determining the depth is a two-step process
var b bool // first we find the proximity bin of the shallowest of the MinProxBinSize peers
// the numeric value of depth cannot be higher than this
// last po recorded in iteration var maxDepth int
var lastPo int
f := func(v pot.Val, i int) bool { f := func(v pot.Val, i int) bool {
// po == 256 means that addr is the pivot address(self) // po == 256 means that addr is the pivot address(self)
@ -463,38 +466,28 @@ func depthForPot(p *pot.Pot, minProxBinSize int, pivotAddr []byte) (depth int) {
// this means we have all nn-peers. // this means we have all nn-peers.
// depth is by default set to the bin of the farthest nn-peer // depth is by default set to the bin of the farthest nn-peer
if size == minProxBinSize { if size == minProxBinSize {
b = true maxDepth = i
depth = i
return true
}
// if there are empty bins between farthest nn and current node,
// the depth should recalculated to be
// the farthest of those empty bins
//
// 0 abac ccde
// 1 2a2a
// 2 589f <--- nearest non-nn
// ============ DEPTH 3 ===========
// 3 <--- don't count as empty bins
// 4 <--- don't count as empty bins
// 5 cbcb cdcd <---- furthest nn
// 6 a1a2 b3c4
if b && i < depth {
depth = i + 1
lastPo = i
return false return false
} }
lastPo = i
return true return true
} }
p.EachNeighbour(pivotAddr, pof, f) p.EachNeighbour(pivotAddr, Pof, f)
// the second step is to test for empty bins in order from shallowest to deepest
// if an empty bin is found, this will be the actual depth
// we stop iterating if we hit the maxDepth determined in the first step
p.EachBin(pivotAddr, Pof, 0, func(po int, _ int, f func(func(pot.Val, int) bool) bool) bool {
if po == depth {
if maxDepth == depth {
return false
}
depth++
return true
}
return false
})
// cover edge case where more than one farthest nn
// AND we only have nn-peers
if lastPo == depth {
depth = 0
}
return depth return depth
} }
@ -556,7 +549,7 @@ func (k *Kademlia) string() string {
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
rest := k.conns.Size() rest := k.conns.Size()
k.conns.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
var rowlen int var rowlen int
if po >= k.MaxProxDisplay { if po >= k.MaxProxDisplay {
po = k.MaxProxDisplay - 1 po = k.MaxProxDisplay - 1
@ -575,7 +568,7 @@ func (k *Kademlia) string() string {
return true return true
}) })
k.addrs.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.addrs.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
var rowlen int var rowlen int
if po >= k.MaxProxDisplay { if po >= k.MaxProxDisplay {
po = k.MaxProxDisplay - 1 po = k.MaxProxDisplay - 1
@ -613,81 +606,74 @@ func (k *Kademlia) string() string {
return "\n" + strings.Join(rows, "\n") return "\n" + strings.Join(rows, "\n")
} }
// PeerPot keeps info about expected nearest neighbours and empty bins // PeerPot keeps info about expected nearest neighbours
// used for testing only // used for testing only
// TODO move to separate testing tools file
type PeerPot struct { type PeerPot struct {
NNSet [][]byte NNSet [][]byte
EmptyBins []int
} }
// NewPeerPotMap creates a map of pot record of *BzzAddr with keys // NewPeerPotMap creates a map of pot record of *BzzAddr with keys
// as hexadecimal representations of the address. // as hexadecimal representations of the address.
// the MinProxBinSize of the passed kademlia is used
// used for testing only // used for testing only
func NewPeerPotMap(kadMinProxSize int, addrs [][]byte) map[string]*PeerPot { // TODO move to separate testing tools file
func NewPeerPotMap(minProxBinSize int, addrs [][]byte) map[string]*PeerPot {
// create a table of all nodes for health check // create a table of all nodes for health check
np := pot.NewPot(nil, 0) np := pot.NewPot(nil, 0)
for _, addr := range addrs { for _, addr := range addrs {
np, _, _ = pot.Add(np, addr, pof) np, _, _ = pot.Add(np, addr, Pof)
} }
ppmap := make(map[string]*PeerPot) ppmap := make(map[string]*PeerPot)
// generate an allknowing source of truth for connections
// for every kademlia passed
for i, a := range addrs { for i, a := range addrs {
// actual kademlia depth // actual kademlia depth
depth := depthForPot(np, kadMinProxSize, a) depth := depthForPot(np, minProxBinSize, a)
// upon entering a new iteration
// this will hold the value the po should be
// if it's one higher than the po in the last iteration
prevPo := 256
// all empty bins which are outside neighbourhood depth
var emptyBins []int
// all nn-peers // all nn-peers
var nns [][]byte var nns [][]byte
np.EachNeighbour(a, pof, func(val pot.Val, po int) bool { // iterate through the neighbours, going from the deepest to the shallowest
np.EachNeighbour(a, Pof, func(val pot.Val, po int) bool {
addr := val.([]byte) addr := val.([]byte)
// po == 256 means that addr is the pivot address(self) // po == 256 means that addr is the pivot address(self)
// we do not include self in the map
if po == 256 { if po == 256 {
return true return true
} }
// append any neighbors found
// iterate through the neighbours, going from the closest to the farthest // a neighbor is any peer in or deeper than the depth
// we calculate the nearest neighbours that should be in the set
// depth in this case equates to:
// 1. Within all bins that are higher or equal than depth there are
// at least minProxBinSize peers connected
// 2. depth-1 bin is not empty
if po >= depth { if po >= depth {
nns = append(nns, addr) nns = append(nns, addr)
prevPo = depth - 1
return true return true
} }
for j := prevPo; j > po; j-- { return false
emptyBins = append(emptyBins, j)
}
prevPo = po - 1
return true
}) })
log.Trace(fmt.Sprintf("%x NNS: %s, emptyBins: %s", addrs[i][:4], LogAddrs(nns), logEmptyBins(emptyBins))) log.Trace(fmt.Sprintf("%x PeerPotMap NNS: %s", addrs[i][:4], LogAddrs(nns)))
ppmap[common.Bytes2Hex(a)] = &PeerPot{nns, emptyBins} ppmap[common.Bytes2Hex(a)] = &PeerPot{
NNSet: nns,
}
} }
return ppmap return ppmap
} }
// saturation returns the lowest proximity order that the bin for that order // saturation iterates through all peers and
// has less than n peers // returns the smallest po value in which the node has less than n peers
// It is used in Healthy function for testing only // if the iterator reaches depth, then value for depth is returned
func (k *Kademlia) saturation(n int) int { // TODO move to separate testing tools file
// TODO this function will stop at the first bin with less than MinBinSize peers, even if there are empty bins between that bin and the depth. This may not be correct behavior
func (k *Kademlia) saturation() int {
prev := -1 prev := -1
k.addrs.EachBin(k.base, pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool { k.addrs.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val, i int) bool) bool) bool {
prev++ prev++
return prev == po && size >= n return prev == po && size >= k.MinBinSize
}) })
// TODO evaluate whether this check cannot just as well be done within the eachbin
depth := depthForPot(k.conns, k.MinProxBinSize, k.base) depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
if depth < prev { if depth < prev {
return depth return depth
@ -695,90 +681,74 @@ func (k *Kademlia) saturation(n int) int {
return prev return prev
} }
// full returns true if all required bins have connected peers. // knowNeighbours tests if all neighbours in the peerpot
// are found among the peers known to the kademlia
// It is used in Healthy function for testing only // It is used in Healthy function for testing only
func (k *Kademlia) full(emptyBins []int) (full bool) { // TODO move to separate testing tools file
prev := 0 func (k *Kademlia) knowNeighbours(addrs [][]byte) (got bool, n int, missing [][]byte) {
e := len(emptyBins)
ok := true
depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.conns.EachBin(k.base, pof, 0, func(po, _ int, _ func(func(val pot.Val, i int) bool) bool) bool {
if po >= depth {
return false
}
if prev == depth+1 {
return true
}
for i := prev; i < po; i++ {
e--
if e < 0 {
ok = false
return false
}
if emptyBins[e] != i {
log.Trace(fmt.Sprintf("%08x po: %d, i: %d, e: %d, emptybins: %v", k.BaseAddr()[:4], po, i, e, logEmptyBins(emptyBins)))
if emptyBins[e] < i {
panic("incorrect peerpot")
}
ok = false
return false
}
}
prev = po + 1
return true
})
if !ok {
return false
}
return e == 0
}
// knowNearestNeighbours tests if all known nearest neighbours given as arguments
// are found in the addressbook
// It is used in Healthy function for testing only
func (k *Kademlia) knowNearestNeighbours(peers [][]byte) bool {
pm := make(map[string]bool) pm := make(map[string]bool)
// create a map with all peers at depth and deeper known in the kademlia
// in order deepest to shallowest compared to the kademlia base address
// all bins (except self) are included (0 <= bin <= 255)
depth := depthForPot(k.addrs, k.MinProxBinSize, k.base)
k.eachAddr(nil, 255, func(p *BzzAddr, po int, nn bool) bool { k.eachAddr(nil, 255, func(p *BzzAddr, po int, nn bool) bool {
if !nn { if po < depth {
return false return false
} }
pk := fmt.Sprintf("%x", p.Address()) pk := common.Bytes2Hex(p.Address())
pm[pk] = true pm[pk] = true
return true return true
}) })
for _, p := range peers {
pk := fmt.Sprintf("%x", p)
if !pm[pk] {
log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", k.BaseAddr()[:4], pk[:8]))
return false
}
}
return true
}
// gotNearestNeighbours tests if all known nearest neighbours given as arguments // iterate through nearest neighbors in the peerpot map
// are connected peers // if we can't find the neighbor in the map we created above
// It is used in Healthy function for testing only // then we don't know all our neighbors
func (k *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool, n int, missing [][]byte) { // (which sadly is all too common in modern society)
pm := make(map[string]bool)
k.eachConn(nil, 255, func(p *Peer, po int, nn bool) bool {
if !nn {
return false
}
pk := fmt.Sprintf("%x", p.Address())
pm[pk] = true
return true
})
var gots int var gots int
var culprits [][]byte var culprits [][]byte
for _, p := range peers { for _, p := range addrs {
pk := fmt.Sprintf("%x", p) pk := common.Bytes2Hex(p)
if pm[pk] { if pm[pk] {
gots++ gots++
} else { } else {
log.Trace(fmt.Sprintf("%08x: ExpNN: %s not found", k.BaseAddr()[:4], pk[:8])) log.Trace(fmt.Sprintf("%08x: known nearest neighbour %s not found", k.base, pk))
culprits = append(culprits, p)
}
}
return gots == len(addrs), gots, culprits
}
// connectedNeighbours tests if all neighbours in the peerpot
// are currently connected in the kademlia
// It is used in Healthy function for testing only
func (k *Kademlia) connectedNeighbours(peers [][]byte) (got bool, n int, missing [][]byte) {
pm := make(map[string]bool)
// create a map with all peers at depth and deeper that are connected in the kademlia
// in order deepest to shallowest compared to the kademlia base address
// all bins (except self) are included (0 <= bin <= 255)
depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
k.eachConn(nil, 255, func(p *Peer, po int, nn bool) bool {
if po < depth {
return false
}
pk := common.Bytes2Hex(p.Address())
pm[pk] = true
return true
})
// iterate through nearest neighbors in the peerpot map
// if we can't find the neighbor in the map we created above
// then we don't know all our neighbors
var gots int
var culprits [][]byte
for _, p := range peers {
pk := common.Bytes2Hex(p)
if pm[pk] {
gots++
} else {
log.Trace(fmt.Sprintf("%08x: ExpNN: %s not found", k.base, pk))
culprits = append(culprits, p) culprits = append(culprits, p)
} }
} }
@ -788,31 +758,40 @@ func (k *Kademlia) gotNearestNeighbours(peers [][]byte) (got bool, n int, missin
// Health state of the Kademlia // Health state of the Kademlia
// used for testing only // used for testing only
type Health struct { type Health struct {
KnowNN bool // whether node knows all its nearest neighbours KnowNN bool // whether node knows all its neighbours
GotNN bool // whether node is connected to all its nearest neighbours CountKnowNN int // amount of neighbors known
CountNN int // amount of nearest neighbors connected to MissingKnowNN [][]byte // which neighbours we should have known but we don't
CulpritsNN [][]byte // which known NNs are missing ConnectNN bool // whether node is connected to all its neighbours
Full bool // whether node has a peer in each kademlia bin (where there is such a peer) CountConnectNN int // amount of neighbours connected to
Hive string MissingConnectNN [][]byte // which neighbours we should have been connected to but we're not
Saturated bool // whether we are connected to all the peers we would have liked to
Hive string
} }
// Healthy reports the health state of the kademlia connectivity // Healthy reports the health state of the kademlia connectivity
// returns a Health struct //
// The PeerPot argument provides an all-knowing view of the network
// The resulting Health object is a result of comparisons between
// what is the actual composition of the kademlia in question (the receiver), and
// what SHOULD it have been when we take all we know about the network into consideration.
//
// used for testing only // used for testing only
func (k *Kademlia) Healthy(pp *PeerPot) *Health { func (k *Kademlia) Healthy(pp *PeerPot) *Health {
k.lock.RLock() k.lock.RLock()
defer k.lock.RUnlock() defer k.lock.RUnlock()
gotnn, countnn, culpritsnn := k.gotNearestNeighbours(pp.NNSet) gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
knownn := k.knowNearestNeighbours(pp.NNSet) knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
full := k.full(pp.EmptyBins) depth := depthForPot(k.conns, k.MinProxBinSize, k.base)
log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, full: %v\n", k.BaseAddr()[:4], knownn, gotnn, full)) saturated := k.saturation() < depth
return &Health{knownn, gotnn, countnn, culpritsnn, full, k.string()} log.Trace(fmt.Sprintf("%08x: healthy: knowNNs: %v, gotNNs: %v, saturated: %v\n", k.base, knownn, gotnn, saturated))
} return &Health{
KnowNN: knownn,
func logEmptyBins(ebs []int) string { CountKnowNN: countknownn,
var ebss []string MissingKnowNN: culpritsknownn,
for _, eb := range ebs { ConnectNN: gotnn,
ebss = append(ebss, fmt.Sprintf("%d", eb)) CountConnectNN: countgotnn,
MissingConnectNN: culpritsgotnn,
Saturated: saturated,
Hive: k.string(),
} }
return strings.Join(ebss, ", ")
} }

View file

@ -41,12 +41,17 @@ func testKadPeerAddr(s string) *BzzAddr {
return &BzzAddr{OAddr: a, UAddr: a} return &BzzAddr{OAddr: a, UAddr: a}
} }
func newTestKademlia(b string) *Kademlia { func newTestKademliaParams() *KadParams {
params := NewKadParams() params := NewKadParams()
// TODO why is this 1?
params.MinBinSize = 1 params.MinBinSize = 1
params.MinProxBinSize = 2 params.MinProxBinSize = 2
return params
}
func newTestKademlia(b string) *Kademlia {
base := pot.NewAddressFromString(b) base := pot.NewAddressFromString(b)
return NewKademlia(base, params) return NewKademlia(base, newTestKademliaParams())
} }
func newTestKadPeer(k *Kademlia, s string, lightNode bool) *Peer { func newTestKadPeer(k *Kademlia, s string, lightNode bool) *Peer {
@ -89,65 +94,165 @@ func TestNeighbourhoodDepth(t *testing.T) {
baseAddress := pot.NewAddressFromBytes(baseAddressBytes) baseAddress := pot.NewAddressFromBytes(baseAddressBytes)
closerAddress := pot.RandomAddressAt(baseAddress, 7) // generate the peers
closerPeer := newTestDiscoveryPeer(closerAddress, kad) var peers []*Peer
kad.On(closerPeer) for i := 0; i < 7; i++ {
addr := pot.RandomAddressAt(baseAddress, i)
peers = append(peers, newTestDiscoveryPeer(addr, kad))
}
var sevenPeers []*Peer
for i := 0; i < 2; i++ {
addr := pot.RandomAddressAt(baseAddress, 7)
sevenPeers = append(sevenPeers, newTestDiscoveryPeer(addr, kad))
}
testNum := 0
// first try with empty kademlia
depth := kad.NeighbourhoodDepth() depth := kad.NeighbourhoodDepth()
if depth != 0 { if depth != 0 {
t.Fatalf("expected depth 0, was %d", depth) t.Fatalf("%d expected depth 0, was %d", testNum, depth)
} }
testNum++
sameAddress := pot.RandomAddressAt(baseAddress, 7) // add one peer on 7
samePeer := newTestDiscoveryPeer(sameAddress, kad) kad.On(sevenPeers[0])
kad.On(samePeer)
depth = kad.NeighbourhoodDepth() depth = kad.NeighbourhoodDepth()
if depth != 0 { if depth != 0 {
t.Fatalf("expected depth 0, was %d", depth) t.Fatalf("%d expected depth 0, was %d", testNum, depth)
} }
testNum++
midAddress := pot.RandomAddressAt(baseAddress, 4) // add a second on 7
midPeer := newTestDiscoveryPeer(midAddress, kad) kad.On(sevenPeers[1])
kad.On(midPeer)
depth = kad.NeighbourhoodDepth()
if depth != 5 {
t.Fatalf("expected depth 5, was %d", depth)
}
kad.Off(midPeer)
depth = kad.NeighbourhoodDepth() depth = kad.NeighbourhoodDepth()
if depth != 0 { if depth != 0 {
t.Fatalf("expected depth 0, was %d", depth) t.Fatalf("%d expected depth 0, was %d", testNum, depth)
} }
testNum++
fartherAddress := pot.RandomAddressAt(baseAddress, 1) // add from 0 to 6
fartherPeer := newTestDiscoveryPeer(fartherAddress, kad) for i, p := range peers {
kad.On(fartherPeer) kad.On(p)
depth = kad.NeighbourhoodDepth() depth = kad.NeighbourhoodDepth()
if depth != 2 { if depth != i+1 {
t.Fatalf("expected depth 2, was %d", depth) t.Fatalf("%d.%d expected depth %d, was %d", i+1, testNum, i, depth)
}
} }
testNum++
midSameAddress := pot.RandomAddressAt(baseAddress, 4) kad.Off(sevenPeers[1])
midSamePeer := newTestDiscoveryPeer(midSameAddress, kad)
kad.Off(closerPeer)
kad.On(midPeer)
kad.On(midSamePeer)
depth = kad.NeighbourhoodDepth() depth = kad.NeighbourhoodDepth()
if depth != 2 { if depth != 6 {
t.Fatalf("expected depth 2, was %d", depth) t.Fatalf("%d expected depth 6, was %d", testNum, depth)
} }
testNum++
kad.Off(fartherPeer) kad.Off(peers[4])
log.Trace(kad.string())
time.Sleep(time.Millisecond)
depth = kad.NeighbourhoodDepth() depth = kad.NeighbourhoodDepth()
if depth != 0 { if depth != 4 {
t.Fatalf("expected depth 0, was %d", depth) t.Fatalf("%d expected depth 4, was %d", testNum, depth)
}
testNum++
kad.Off(peers[3])
depth = kad.NeighbourhoodDepth()
if depth != 3 {
t.Fatalf("%d expected depth 3, was %d", testNum, depth)
}
testNum++
}
// TestHealthStrict tests the simplest definition of health
// Which means whether we are connected to all neighbors we know of
func TestHealthStrict(t *testing.T) {
// base address is all zeros
// no peers
// unhealthy (and lonely)
k := newTestKademlia("11111111")
assertHealth(t, k, false, false)
// know one peer but not connected
// unhealthy
Register(k, "11100000")
log.Trace(k.String())
assertHealth(t, k, false, false)
// know one peer and connected
// healthy
On(k, "11100000")
assertHealth(t, k, true, false)
// know two peers, only one connected
// unhealthy
Register(k, "11111100")
log.Trace(k.String())
assertHealth(t, k, false, false)
// know two peers and connected to both
// healthy
On(k, "11111100")
assertHealth(t, k, true, false)
// know three peers, connected to the two deepest
// healthy
Register(k, "00000000")
log.Trace(k.String())
assertHealth(t, k, true, false)
// know three peers, connected to all three
// healthy
On(k, "00000000")
assertHealth(t, k, true, false)
// add fourth peer deeper than current depth
// unhealthy
Register(k, "11110000")
log.Trace(k.String())
assertHealth(t, k, false, false)
// connected to three deepest peers
// healthy
On(k, "11110000")
assertHealth(t, k, true, false)
// add additional peer in same bin as deepest peer
// unhealthy
Register(k, "11111101")
log.Trace(k.String())
assertHealth(t, k, false, false)
// four deepest of five peers connected
// healthy
On(k, "11111101")
assertHealth(t, k, true, false)
}
func assertHealth(t *testing.T, k *Kademlia, expectHealthy bool, expectSaturation bool) {
t.Helper()
kid := common.Bytes2Hex(k.BaseAddr())
addrs := [][]byte{k.BaseAddr()}
k.EachAddr(nil, 255, func(addr *BzzAddr, po int, _ bool) bool {
addrs = append(addrs, addr.Address())
return true
})
pp := NewPeerPotMap(k.MinProxBinSize, addrs)
healthParams := k.Healthy(pp[kid])
// definition of health, all conditions but be true:
// - we at least know one peer
// - we know all neighbors
// - we are connected to all known neighbors
health := healthParams.KnowNN && healthParams.ConnectNN && healthParams.CountKnowNN > 0
if expectHealthy != health {
t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, k.String())
} }
} }
func testSuggestPeer(k *Kademlia, expAddr string, expPo int, expWant bool) error { func testSuggestPeer(k *Kademlia, expAddr string, expPo int, expWant bool) error {
addr, o, want := k.SuggestPeer() addr, o, want := k.SuggestPeer()
log.Trace("suggestpeer return", "a", addr, "o", o, "want", want)
if binStr(addr) != expAddr { if binStr(addr) != expAddr {
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr)) return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
} }
@ -167,6 +272,7 @@ func binStr(a *BzzAddr) string {
return pot.ToBin(a.Address())[:8] return pot.ToBin(a.Address())[:8]
} }
// TODO explain why this bug occurred and how it should have been mitigated
func TestSuggestPeerBug(t *testing.T) { func TestSuggestPeerBug(t *testing.T) {
// 2 row gap, unsaturated proxbin, no callables -> want PO 0 // 2 row gap, unsaturated proxbin, no callables -> want PO 0
k := newTestKademlia("00000000") k := newTestKademlia("00000000")
@ -186,72 +292,98 @@ func TestSuggestPeerBug(t *testing.T) {
} }
func TestSuggestPeerFindPeers(t *testing.T) { func TestSuggestPeerFindPeers(t *testing.T) {
t.Skip("The SuggestPeers implementation seems to have weaknesses exposed by the change in the new depth calculation. The results are no longer predictable")
testnum := 0
// test 0
// 2 row gap, unsaturated proxbin, no callables -> want PO 0 // 2 row gap, unsaturated proxbin, no callables -> want PO 0
k := newTestKademlia("00000000") k := newTestKademlia("00000000")
On(k, "00100000") On(k, "00100000")
err := testSuggestPeer(k, "<nil>", 0, false) err := testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 1
// 2 row gap, saturated proxbin, no callables -> want PO 0 // 2 row gap, saturated proxbin, no callables -> want PO 0
On(k, "00010000") On(k, "00010000")
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 2
// 1 row gap (1 less), saturated proxbin, no callables -> want PO 1 // 1 row gap (1 less), saturated proxbin, no callables -> want PO 1
On(k, "10000000") On(k, "10000000")
err = testSuggestPeer(k, "<nil>", 1, false) err = testSuggestPeer(k, "<nil>", 1, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 3
// no gap (1 less), saturated proxbin, no callables -> do not want more // no gap (1 less), saturated proxbin, no callables -> do not want more
On(k, "01000000", "00100001") On(k, "01000000", "00100001")
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 4
// oversaturated proxbin, > do not want more // oversaturated proxbin, > do not want more
On(k, "00100001") On(k, "00100001")
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 5
// reintroduce gap, disconnected peer callable // reintroduce gap, disconnected peer callable
Off(k, "01000000") Off(k, "01000000")
log.Trace(k.String())
err = testSuggestPeer(k, "01000000", 0, false) err = testSuggestPeer(k, "01000000", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 6
// second time disconnected peer not callable // second time disconnected peer not callable
// with reasonably set Interval // with reasonably set Interval
err = testSuggestPeer(k, "<nil>", 1, true) log.Trace("foo")
log.Trace(k.String())
err = testSuggestPeer(k, "<nil>", 1, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 6
// on and off again, peer callable again // on and off again, peer callable again
On(k, "01000000") On(k, "01000000")
Off(k, "01000000") Off(k, "01000000")
log.Trace(k.String())
err = testSuggestPeer(k, "01000000", 0, false) err = testSuggestPeer(k, "01000000", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
On(k, "01000000") // test 7
// new closer peer appears, it is immediately wanted // new closer peer appears, it is immediately wanted
On(k, "01000000")
Register(k, "00010001") Register(k, "00010001")
err = testSuggestPeer(k, "00010001", 0, false) err = testSuggestPeer(k, "00010001", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 8
// PO1 disconnects // PO1 disconnects
On(k, "00010001") On(k, "00010001")
log.Info(k.String()) log.Info(k.String())
@ -260,70 +392,94 @@ func TestSuggestPeerFindPeers(t *testing.T) {
// second time, gap filling // second time, gap filling
err = testSuggestPeer(k, "01000000", 0, false) err = testSuggestPeer(k, "01000000", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 9
On(k, "01000000") On(k, "01000000")
log.Info(k.String())
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 10
k.MinBinSize = 2 k.MinBinSize = 2
log.Info(k.String())
err = testSuggestPeer(k, "<nil>", 0, true) err = testSuggestPeer(k, "<nil>", 0, true)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 11
Register(k, "01000001") Register(k, "01000001")
log.Info(k.String())
err = testSuggestPeer(k, "01000001", 0, false) err = testSuggestPeer(k, "01000001", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 12
On(k, "10000001") On(k, "10000001")
log.Trace(fmt.Sprintf("Kad:\n%v", k.String())) log.Trace(fmt.Sprintf("Kad:\n%v", k.String()))
err = testSuggestPeer(k, "<nil>", 1, true) err = testSuggestPeer(k, "<nil>", 1, true)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 13
On(k, "01000001") On(k, "01000001")
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 14
k.MinBinSize = 3 k.MinBinSize = 3
Register(k, "10000010") Register(k, "10000010")
err = testSuggestPeer(k, "10000010", 0, false) err = testSuggestPeer(k, "10000010", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 15
On(k, "10000010") On(k, "10000010")
err = testSuggestPeer(k, "<nil>", 1, false) err = testSuggestPeer(k, "<nil>", 1, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 16
On(k, "01000010") On(k, "01000010")
err = testSuggestPeer(k, "<nil>", 2, false) err = testSuggestPeer(k, "<nil>", 2, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 17
On(k, "00100010") On(k, "00100010")
err = testSuggestPeer(k, "<nil>", 3, false) err = testSuggestPeer(k, "<nil>", 3, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
// test 18
On(k, "00010010") On(k, "00010010")
err = testSuggestPeer(k, "<nil>", 0, false) err = testSuggestPeer(k, "<nil>", 0, false)
if err != nil { if err != nil {
t.Fatal(err.Error()) t.Fatalf("%d %v", testnum, err.Error())
} }
testnum++
} }
@ -459,27 +615,28 @@ func TestKademliaHiveString(t *testing.T) {
// the SuggestPeer and Healthy methods for provided hex-encoded addresses. // the SuggestPeer and Healthy methods for provided hex-encoded addresses.
// Argument pivotAddr is the address of the kademlia. // Argument pivotAddr is the address of the kademlia.
func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) { func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) {
addr := common.FromHex(pivotAddr)
addrs = append(addrs, pivotAddr) t.Skip("this test relies on SuggestPeer which is now not reliable. See description in TestSuggestPeerFindPeers")
addr := common.Hex2Bytes(pivotAddr)
var byteAddrs [][]byte
for _, ahex := range addrs {
byteAddrs = append(byteAddrs, common.Hex2Bytes(ahex))
}
k := NewKademlia(addr, NewKadParams()) k := NewKademlia(addr, NewKadParams())
as := make([][]byte, len(addrs)) // our pivot kademlia is the last one in the array
for i, a := range addrs { for _, a := range byteAddrs {
as[i] = common.FromHex(a)
}
for _, a := range as {
if bytes.Equal(a, addr) { if bytes.Equal(a, addr) {
continue continue
} }
p := &BzzAddr{OAddr: a, UAddr: a} p := &BzzAddr{OAddr: a, UAddr: a}
if err := k.Register(p); err != nil { if err := k.Register(p); err != nil {
t.Fatal(err) t.Fatalf("a %x addr %x: %v", a, addr, err)
} }
} }
ppmap := NewPeerPotMap(2, as) ppmap := NewPeerPotMap(k.MinProxBinSize, byteAddrs)
pp := ppmap[pivotAddr] pp := ppmap[pivotAddr]
@ -492,7 +649,7 @@ func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) {
} }
h := k.Healthy(pp) h := k.Healthy(pp)
if !(h.GotNN && h.KnowNN && h.Full) { if !(h.ConnectNN && h.KnowNN && h.CountKnowNN > 0) {
t.Fatalf("not healthy: %#v\n%v", h, k.String()) t.Fatalf("not healthy: %#v\n%v", h, k.String())
} }
} }

View file

@ -35,8 +35,6 @@ import (
const ( const (
DefaultNetworkID = 3 DefaultNetworkID = 3
// ProtocolMaxMsgSize maximum allowed message size
ProtocolMaxMsgSize = 10 * 1024 * 1024
// timeout for waiting // timeout for waiting
bzzHandshakeTimeout = 3000 * time.Millisecond bzzHandshakeTimeout = 3000 * time.Millisecond
) )
@ -250,11 +248,6 @@ func NewBzzPeer(p *protocols.Peer) *BzzPeer {
return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())} return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())}
} }
// LastActive returns the time the peer was last active
func (p *BzzPeer) LastActive() time.Time {
return p.lastActive
}
// ID returns the peer's underlay node identifier. // ID returns the peer's underlay node identifier.
func (p *BzzPeer) ID() enode.ID { func (p *BzzPeer) ID() enode.ID {
// This is here to resolve a method tie: both protocols.Peer and BzzAddr are embedded // This is here to resolve a method tie: both protocols.Peer and BzzAddr are embedded

View file

@ -20,7 +20,6 @@ import (
"flag" "flag"
"fmt" "fmt"
"os" "os"
"sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
@ -44,31 +43,7 @@ func init() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
} }
type testStore struct {
sync.Mutex
values map[string][]byte
}
func (t *testStore) Load(key string) ([]byte, error) {
t.Lock()
defer t.Unlock()
v, ok := t.values[key]
if !ok {
return nil, fmt.Errorf("key not found: %s", key)
}
return v, nil
}
func (t *testStore) Save(key string, v []byte) error {
t.Lock()
defer t.Unlock()
t.values[key] = v
return nil
}
func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchange { func HandshakeMsgExchange(lhs, rhs *HandshakeMsg, id enode.ID) []p2ptest.Exchange {
return []p2ptest.Exchange{ return []p2ptest.Exchange{
{ {
Expects: []p2ptest.Expect{ Expects: []p2ptest.Expect{

View file

@ -1,306 +0,0 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package simulation
import (
"testing"
"github.com/ethereum/go-ethereum/p2p/enode"
)
func TestConnectToPivotNode(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
pid, err := sim.AddNode()
if err != nil {
t.Fatal(err)
}
sim.SetPivotNode(pid)
id, err := sim.AddNode()
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectToPivotNode(id)
if err != nil {
t.Fatal(err)
}
if sim.Net.GetConn(id, pid) == nil {
t.Error("node did not connect to pivot node")
}
}
func TestConnectToLastNode(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
n := 10
ids, err := sim.AddNodes(n)
if err != nil {
t.Fatal(err)
}
id, err := sim.AddNode()
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectToLastNode(id)
if err != nil {
t.Fatal(err)
}
for _, i := range ids[:n-2] {
if sim.Net.GetConn(id, i) != nil {
t.Error("node connected to the node that is not the last")
}
}
if sim.Net.GetConn(id, ids[n-1]) == nil {
t.Error("node did not connect to the last node")
}
}
func TestConnectToRandomNode(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
n := 10
ids, err := sim.AddNodes(n)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectToRandomNode(ids[0])
if err != nil {
t.Fatal(err)
}
var cc int
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
if sim.Net.GetConn(ids[i], ids[j]) != nil {
cc++
}
}
}
if cc != 1 {
t.Errorf("expected one connection, got %v", cc)
}
}
func TestConnectNodesFull(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
ids, err := sim.AddNodes(12)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectNodesFull(ids)
if err != nil {
t.Fatal(err)
}
testFull(t, sim, ids)
}
func testFull(t *testing.T, sim *Simulation, ids []enode.ID) {
n := len(ids)
var cc int
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
if sim.Net.GetConn(ids[i], ids[j]) != nil {
cc++
}
}
}
want := n * (n - 1) / 2
if cc != want {
t.Errorf("expected %v connection, got %v", want, cc)
}
}
func TestConnectNodesChain(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
ids, err := sim.AddNodes(10)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectNodesChain(ids)
if err != nil {
t.Fatal(err)
}
testChain(t, sim, ids)
}
func testChain(t *testing.T, sim *Simulation, ids []enode.ID) {
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := sim.Net.GetConn(ids[i], ids[j])
if i == j-1 {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}
func TestConnectNodesRing(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
ids, err := sim.AddNodes(10)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
err = sim.ConnectNodesRing(ids)
if err != nil {
t.Fatal(err)
}
testRing(t, sim, ids)
}
func testRing(t *testing.T, sim *Simulation, ids []enode.ID) {
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := sim.Net.GetConn(ids[i], ids[j])
if i == j-1 || (i == 0 && j == n-1) {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}
func TestConnectToNodesStar(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
ids, err := sim.AddNodes(10)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
centerIndex := 2
err = sim.ConnectNodesStar(ids[centerIndex], ids)
if err != nil {
t.Fatal(err)
}
testStar(t, sim, ids, centerIndex)
}
func testStar(t *testing.T, sim *Simulation, ids []enode.ID, centerIndex int) {
n := len(ids)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
c := sim.Net.GetConn(ids[i], ids[j])
if i == centerIndex || j == centerIndex {
if c == nil {
t.Errorf("nodes %v and %v are not connected, but they should be", i, j)
}
} else {
if c != nil {
t.Errorf("nodes %v and %v are connected, but they should not be", i, j)
}
}
}
}
}
func TestConnectToNodesStarPivot(t *testing.T) {
sim := New(noopServiceFuncMap)
defer sim.Close()
ids, err := sim.AddNodes(10)
if err != nil {
t.Fatal(err)
}
if len(sim.Net.Conns) > 0 {
t.Fatal("no connections should exist after just adding nodes")
}
pivotIndex := 4
sim.SetPivotNode(ids[pivotIndex])
err = sim.ConnectNodesStarPivot(ids)
if err != nil {
t.Fatal(err)
}
testStar(t, sim, ids, pivotIndex)
}

View file

@ -59,7 +59,7 @@ func TestPeerEvents(t *testing.T) {
} }
}() }()
err = sim.ConnectNodesChain(sim.NodeIDs()) err = sim.Net.ConnectNodesChain(sim.NodeIDs())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -18,14 +18,8 @@ package simulation_test
import ( import (
"context" "context"
"fmt"
"sync"
"time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/network/simulation" "github.com/ethereum/go-ethereum/swarm/network/simulation"
) )
@ -35,7 +29,8 @@ import (
func ExampleSimulation_WaitTillHealthy() { func ExampleSimulation_WaitTillHealthy() {
log.Error("temporarily disabled as simulations.WaitTillHealthy cannot be trusted") log.Error("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
return
/* Commented out to avoid go vet errors/warnings
sim := simulation.New(map[string]simulation.ServiceFunc{ sim := simulation.New(map[string]simulation.ServiceFunc{
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) { "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
@ -75,6 +70,8 @@ func ExampleSimulation_WaitTillHealthy() {
} }
// continue with the test // continue with the test
*/
} }
// Watch all peer events in the simulation network, buy receiving from a channel. // Watch all peer events in the simulation network, buy receiving from a channel.

View file

@ -39,6 +39,7 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
var ppmap map[string]*network.PeerPot var ppmap map[string]*network.PeerPot
kademlias := s.kademlias() kademlias := s.kademlias()
addrs := make([][]byte, 0, len(kademlias)) addrs := make([][]byte, 0, len(kademlias))
// TODO verify that all kademlias have same params
for _, k := range kademlias { for _, k := range kademlias {
addrs = append(addrs, k.BaseAddr()) addrs = append(addrs, k.BaseAddr())
} }
@ -66,10 +67,10 @@ func (s *Simulation) WaitTillHealthy(ctx context.Context, kadMinProxSize int) (i
h := k.Healthy(pp) h := k.Healthy(pp)
//print info //print info
log.Debug(k.String()) log.Debug(k.String())
log.Debug("kademlia", "empty bins", pp.EmptyBins, "gotNN", h.GotNN, "knowNN", h.KnowNN, "full", h.Full) log.Debug("kademlia", "connectNN", h.ConnectNN, "knowNN", h.KnowNN)
log.Debug("kademlia", "health", h.GotNN && h.KnowNN && h.Full, "addr", hex.EncodeToString(k.BaseAddr()), "node", id) log.Debug("kademlia", "health", h.ConnectNN && h.KnowNN, "addr", hex.EncodeToString(k.BaseAddr()), "node", id)
log.Debug("kademlia", "ill condition", !h.GotNN || !h.Full, "addr", hex.EncodeToString(k.BaseAddr()), "node", id) log.Debug("kademlia", "ill condition", !h.ConnectNN, "addr", hex.EncodeToString(k.BaseAddr()), "node", id)
if !h.GotNN || !h.Full { if !h.ConnectNN {
ill[id] = k ill[id] = k
} }
} }

View file

@ -127,7 +127,7 @@ func (s *Simulation) AddNodesAndConnectFull(count int, opts ...AddNodeOption) (i
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.ConnectNodesFull(ids) err = s.Net.ConnectNodesFull(ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -145,7 +145,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.ConnectToLastNode(id) err = s.Net.ConnectToLastNode(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -154,7 +154,7 @@ func (s *Simulation) AddNodesAndConnectChain(count int, opts ...AddNodeOption) (
return nil, err return nil, err
} }
ids = append([]enode.ID{id}, ids...) ids = append([]enode.ID{id}, ids...)
err = s.ConnectNodesChain(ids) err = s.Net.ConnectNodesChain(ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -171,7 +171,7 @@ func (s *Simulation) AddNodesAndConnectRing(count int, opts ...AddNodeOption) (i
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.ConnectNodesRing(ids) err = s.Net.ConnectNodesRing(ids)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -188,7 +188,7 @@ func (s *Simulation) AddNodesAndConnectStar(count int, opts ...AddNodeOption) (i
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = s.ConnectNodesStar(ids[0], ids[1:]) err = s.Net.ConnectNodesStar(ids[0], ids[1:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -267,27 +267,26 @@ func (s *Simulation) StartNode(id enode.ID) (err error) {
// StartRandomNode starts a random node. // StartRandomNode starts a random node.
func (s *Simulation) StartRandomNode() (id enode.ID, err error) { func (s *Simulation) StartRandomNode() (id enode.ID, err error) {
n := s.randomDownNode() n := s.Net.GetRandomDownNode()
if n == nil { if n == nil {
return id, ErrNodeNotFound return id, ErrNodeNotFound
} }
return n.ID, s.Net.Start(n.ID) return n.ID(), s.Net.Start(n.ID())
} }
// StartRandomNodes starts random nodes. // StartRandomNodes starts random nodes.
func (s *Simulation) StartRandomNodes(count int) (ids []enode.ID, err error) { func (s *Simulation) StartRandomNodes(count int) (ids []enode.ID, err error) {
ids = make([]enode.ID, 0, count) ids = make([]enode.ID, 0, count)
downIDs := s.DownNodeIDs()
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
n := s.randomNode(downIDs, ids...) n := s.Net.GetRandomDownNode()
if n == nil { if n == nil {
return nil, ErrNodeNotFound return nil, ErrNodeNotFound
} }
err = s.Net.Start(n.ID) err = s.Net.Start(n.ID())
if err != nil { if err != nil {
return nil, err return nil, err
} }
ids = append(ids, n.ID) ids = append(ids, n.ID())
} }
return ids, nil return ids, nil
} }
@ -299,27 +298,26 @@ func (s *Simulation) StopNode(id enode.ID) (err error) {
// StopRandomNode stops a random node. // StopRandomNode stops a random node.
func (s *Simulation) StopRandomNode() (id enode.ID, err error) { func (s *Simulation) StopRandomNode() (id enode.ID, err error) {
n := s.RandomUpNode() n := s.Net.GetRandomUpNode()
if n == nil { if n == nil {
return id, ErrNodeNotFound return id, ErrNodeNotFound
} }
return n.ID, s.Net.Stop(n.ID) return n.ID(), s.Net.Stop(n.ID())
} }
// StopRandomNodes stops random nodes. // StopRandomNodes stops random nodes.
func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) { func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) {
ids = make([]enode.ID, 0, count) ids = make([]enode.ID, 0, count)
upIDs := s.UpNodeIDs()
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
n := s.randomNode(upIDs, ids...) n := s.Net.GetRandomUpNode()
if n == nil { if n == nil {
return nil, ErrNodeNotFound return nil, ErrNodeNotFound
} }
err = s.Net.Stop(n.ID) err = s.Net.Stop(n.ID())
if err != nil { if err != nil {
return nil, err return nil, err
} }
ids = append(ids, n.ID) ids = append(ids, n.ID())
} }
return ids, nil return ids, nil
} }
@ -328,35 +326,3 @@ func (s *Simulation) StopRandomNodes(count int) (ids []enode.ID, err error) {
func init() { func init() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
} }
// RandomUpNode returns a random SimNode that is up.
// Arguments are NodeIDs for nodes that should not be returned.
func (s *Simulation) RandomUpNode(exclude ...enode.ID) *adapters.SimNode {
return s.randomNode(s.UpNodeIDs(), exclude...)
}
// randomDownNode returns a random SimNode that is not up.
func (s *Simulation) randomDownNode(exclude ...enode.ID) *adapters.SimNode {
return s.randomNode(s.DownNodeIDs(), exclude...)
}
// randomNode returns a random SimNode from the slice of NodeIDs.
func (s *Simulation) randomNode(ids []enode.ID, exclude ...enode.ID) *adapters.SimNode {
for _, e := range exclude {
var i int
for _, id := range ids {
if id == e {
ids = append(ids[:i], ids[i+1:]...)
} else {
i++
}
}
}
l := len(ids)
if l == 0 {
return nil
}
n := s.Net.GetNode(ids[rand.Intn(l)])
node, _ := n.Node.(*adapters.SimNode)
return node
}

View file

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/network"
) )
@ -228,7 +229,7 @@ func TestAddNodesAndConnectFull(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testFull(t, sim, ids) simulations.VerifyFull(t, sim.Net, ids)
} }
func TestAddNodesAndConnectChain(t *testing.T) { func TestAddNodesAndConnectChain(t *testing.T) {
@ -247,7 +248,7 @@ func TestAddNodesAndConnectChain(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testChain(t, sim, sim.UpNodeIDs()) simulations.VerifyChain(t, sim.Net, sim.UpNodeIDs())
} }
func TestAddNodesAndConnectRing(t *testing.T) { func TestAddNodesAndConnectRing(t *testing.T) {
@ -259,7 +260,7 @@ func TestAddNodesAndConnectRing(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testRing(t, sim, ids) simulations.VerifyRing(t, sim.Net, ids)
} }
func TestAddNodesAndConnectStar(t *testing.T) { func TestAddNodesAndConnectStar(t *testing.T) {
@ -271,7 +272,7 @@ func TestAddNodesAndConnectStar(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
testStar(t, sim, ids, 0) simulations.VerifyStar(t, sim.Net, ids, 0)
} }
//To test that uploading a snapshot works //To test that uploading a snapshot works

View file

@ -39,7 +39,7 @@ func (s *Simulation) Service(name string, id enode.ID) node.Service {
// RandomService returns a single Service by name on a // RandomService returns a single Service by name on a
// randomly chosen node that is up. // randomly chosen node that is up.
func (s *Simulation) RandomService(name string) node.Service { func (s *Simulation) RandomService(name string) node.Service {
n := s.RandomUpNode() n := s.Net.GetRandomUpNode().Node.(*adapters.SimNode)
if n == nil { if n == nil {
return nil return nil
} }

View file

@ -33,7 +33,6 @@ import (
// Common errors that are returned by functions in this package. // Common errors that are returned by functions in this package.
var ( var (
ErrNodeNotFound = errors.New("node not found") ErrNodeNotFound = errors.New("node not found")
ErrNoPivotNode = errors.New("no pivot node set")
) )
// Simulation provides methods on network, nodes and services // Simulation provides methods on network, nodes and services
@ -66,8 +65,7 @@ type Simulation struct {
// after network shutdown. // after network shutdown.
type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) type ServiceFunc func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error)
// New creates a new Simulation instance with new // New creates a new simulation instance
// simulations.Network initialized with provided services.
// Services map must have unique keys as service names and // Services map must have unique keys as service names and
// every ServiceFunc must return a node.Service of the unique type. // every ServiceFunc must return a node.Service of the unique type.
// This restriction is required by node.Node.Start() function // This restriction is required by node.Node.Start() function

View file

@ -26,9 +26,8 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters" "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rpc"
colorable "github.com/mattn/go-colorable" colorable "github.com/mattn/go-colorable"
) )
@ -182,39 +181,23 @@ func noopServiceFunc(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, f
return newNoopService(), nil, nil return newNoopService(), nil, nil
} }
// noopService is the service that does not do anything
// but implements node.Service interface.
type noopService struct{}
func newNoopService() node.Service { func newNoopService() node.Service {
return &noopService{} return &noopService{}
} }
func (t *noopService) Protocols() []p2p.Protocol { // a helper function for most basic Noop service
return []p2p.Protocol{} // of a different type then NoopService to test
}
func (t *noopService) APIs() []rpc.API {
return []rpc.API{}
}
func (t *noopService) Start(server *p2p.Server) error {
return nil
}
func (t *noopService) Stop() error {
return nil
}
// a helper function for most basic noop service
// of a different type then noopService to test
// multiple services on one node. // multiple services on one node.
func noopService2Func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) { func noopService2Func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
return new(noopService2), nil, nil return new(noopService2), nil, nil
} }
// noopService2 is the service that does not do anything // NoopService2 is the service that does not do anything
// but implements node.Service interface. // but implements node.Service interface.
type noopService2 struct { type noopService2 struct {
noopService simulations.NoopService
}
type noopService struct {
simulations.NoopService
} }

View file

@ -31,6 +31,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
@ -156,6 +157,7 @@ func testDiscoverySimulationSimAdapter(t *testing.T, nodes, conns int) {
} }
func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) { func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) {
t.Skip("discovery tests depend on suggestpeer, which is unreliable after kademlia depth change.")
startedAt := time.Now() startedAt := time.Now()
result, err := discoverySimulation(nodes, conns, adapter) result, err := discoverySimulation(nodes, conns, adapter)
if err != nil { if err != nil {
@ -183,6 +185,7 @@ func testDiscoverySimulation(t *testing.T, nodes, conns int, adapter adapters.No
} }
func testDiscoveryPersistenceSimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) map[int][]byte { func testDiscoveryPersistenceSimulation(t *testing.T, nodes, conns int, adapter adapters.NodeAdapter) map[int][]byte {
t.Skip("discovery tests depend on suggestpeer, which is unreliable after kademlia depth change.")
persistenceEnabled = true persistenceEnabled = true
discoveryEnabled = true discoveryEnabled = true
@ -265,7 +268,7 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
wg.Wait() wg.Wait()
log.Debug(fmt.Sprintf("nodes: %v", len(addrs))) log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
// construct the peer pot, so that kademlia health can be checked // construct the peer pot, so that kademlia health can be checked
ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs) ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs)
check := func(ctx context.Context, id enode.ID) (bool, error) { check := func(ctx context.Context, id enode.ID) (bool, error) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@ -281,12 +284,13 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
if err != nil { if err != nil {
return false, fmt.Errorf("error getting node client: %s", err) return false, fmt.Errorf("error getting node client: %s", err)
} }
healthy := &network.Health{} healthy := &network.Health{}
if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil { if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
return false, fmt.Errorf("error getting node health: %s", err) return false, fmt.Errorf("error getting node health: %s", err)
} }
log.Debug(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v\n%v", id, healthy.GotNN, healthy.KnowNN, healthy.Full, healthy.Hive)) log.Info(fmt.Sprintf("node %4s healthy: connected nearest neighbours: %v, know nearest neighbours: %v,\n\n%v", id, healthy.ConnectNN, healthy.KnowNN, healthy.Hive))
return healthy.KnowNN && healthy.GotNN && healthy.Full, nil return healthy.KnowNN && healthy.ConnectNN, nil
} }
// 64 nodes ~ 1min // 64 nodes ~ 1min
@ -371,6 +375,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
if err := triggerChecks(trigger, net, node.ID()); err != nil { if err := triggerChecks(trigger, net, node.ID()); err != nil {
return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err) return nil, fmt.Errorf("error triggering checks for node %s: %s", node.ID().TerminalString(), err)
} }
// TODO we shouldn't be equating underaddr and overaddr like this, as they are not the same in production
ids[i] = node.ID() ids[i] = node.ID()
a := ids[i].Bytes() a := ids[i].Bytes()
@ -379,7 +384,6 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
// run a simulation which connects the 10 nodes in a ring and waits // run a simulation which connects the 10 nodes in a ring and waits
// for full peer discovery // for full peer discovery
ppmap := network.NewPeerPotMap(testMinProxBinSize, addrs)
var restartTime time.Time var restartTime time.Time
@ -400,12 +404,21 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
} }
healthy := &network.Health{} healthy := &network.Health{}
addr := id.String() addr := id.String()
if err := client.Call(&healthy, "hive_healthy", ppmap[addr]); err != nil { ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs)
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
return fmt.Errorf("error getting node health: %s", err) return fmt.Errorf("error getting node health: %s", err)
} }
log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.GotNN && healthy.KnowNN && healthy.Full)) log.Info(fmt.Sprintf("NODE: %s, IS HEALTHY: %t", addr, healthy.ConnectNN && healthy.KnowNN && healthy.CountKnowNN > 0))
if !healthy.GotNN || !healthy.Full { var nodeStr string
if err := client.Call(&nodeStr, "hive_string"); err != nil {
return fmt.Errorf("error getting node string %s", err)
}
log.Info(nodeStr)
for _, a := range addrs {
log.Info(common.Bytes2Hex(a))
}
if !healthy.ConnectNN || healthy.CountKnowNN == 0 {
isHealthy = false isHealthy = false
break break
} }
@ -479,12 +492,14 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
return false, fmt.Errorf("error getting node client: %s", err) return false, fmt.Errorf("error getting node client: %s", err)
} }
healthy := &network.Health{} healthy := &network.Health{}
if err := client.Call(&healthy, "hive_healthy", ppmap[id.String()]); err != nil { ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, addrs)
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
return false, fmt.Errorf("error getting node health: %s", err) return false, fmt.Errorf("error getting node health: %s", err)
} }
log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v, saturated: %v", id, healthy.GotNN, healthy.KnowNN, healthy.Full)) log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v", id, healthy.ConnectNN, healthy.KnowNN))
return healthy.KnowNN && healthy.GotNN && healthy.Full, nil return healthy.KnowNN && healthy.ConnectNN, nil
} }
// 64 nodes ~ 1min // 64 nodes ~ 1min

View file

@ -35,7 +35,6 @@ import (
p2ptest "github.com/ethereum/go-ethereum/p2p/testing" p2ptest "github.com/ethereum/go-ethereum/p2p/testing"
"github.com/ethereum/go-ethereum/swarm/network" "github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/network/simulation" "github.com/ethereum/go-ethereum/swarm/network/simulation"
"github.com/ethereum/go-ethereum/swarm/pot"
"github.com/ethereum/go-ethereum/swarm/state" "github.com/ethereum/go-ethereum/swarm/state"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
"github.com/ethereum/go-ethereum/swarm/testutil" "github.com/ethereum/go-ethereum/swarm/testutil"
@ -57,7 +56,7 @@ var (
bucketKeyRegistry = simulation.BucketKey("registry") bucketKeyRegistry = simulation.BucketKey("registry")
chunkSize = 4096 chunkSize = 4096
pof = pot.DefaultPof(256) pof = network.Pof
) )
func init() { func init() {

View file

@ -453,8 +453,6 @@ func TestDeliveryFromNodes(t *testing.T) {
} }
func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool) { func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
sim := simulation.New(map[string]simulation.ServiceFunc{ sim := simulation.New(map[string]simulation.ServiceFunc{
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
node := ctx.Config.Node() node := ctx.Config.Node()
@ -505,7 +503,8 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
nodeIDs := sim.UpNodeIDs() nodeIDs := sim.UpNodeIDs()
//determine the pivot node to be the first node of the simulation //determine the pivot node to be the first node of the simulation
sim.SetPivotNode(nodeIDs[0]) pivot := nodeIDs[0]
//distribute chunks of a random file into Stores of nodes 1 to nodes //distribute chunks of a random file into Stores of nodes 1 to nodes
//we will do this by creating a file store with an underlying round-robin store: //we will do this by creating a file store with an underlying round-robin store:
//the file store will create a hash for the uploaded file, but every chunk will be //the file store will create a hash for the uploaded file, but every chunk will be
@ -519,7 +518,7 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
//...iterate the buckets... //...iterate the buckets...
for id, bucketVal := range lStores { for id, bucketVal := range lStores {
//...and remove the one which is the pivot node //...and remove the one which is the pivot node
if id == *sim.PivotNodeID() { if id == pivot {
continue continue
} }
//the other ones are added to the array... //the other ones are added to the array...
@ -542,12 +541,13 @@ func testDeliveryFromNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck
} }
log.Debug("Waiting for kademlia") log.Debug("Waiting for kademlia")
// TODO this does not seem to be correct usage of the function, as the simulation may have no kademlias
if _, err := sim.WaitTillHealthy(ctx, 2); err != nil { if _, err := sim.WaitTillHealthy(ctx, 2); err != nil {
return err return err
} }
//get the pivot node's filestore //get the pivot node's filestore
item, ok := sim.NodeItem(*sim.PivotNodeID(), bucketKeyFileStore) item, ok := sim.NodeItem(pivot, bucketKeyFileStore)
if !ok { if !ok {
return fmt.Errorf("No filestore") return fmt.Errorf("No filestore")
} }

View file

@ -17,14 +17,11 @@
package intervals package intervals
import ( import (
"errors"
"testing" "testing"
"github.com/ethereum/go-ethereum/swarm/state" "github.com/ethereum/go-ethereum/swarm/state"
) )
var ErrNotFound = errors.New("not found")
// TestInmemoryStore tests basic functionality of InmemoryStore. // TestInmemoryStore tests basic functionality of InmemoryStore.
func TestInmemoryStore(t *testing.T) { func TestInmemoryStore(t *testing.T) {
testStore(t, state.NewInmemoryStore()) testStore(t, state.NewInmemoryStore())

View file

@ -53,7 +53,6 @@ func TestIntervalsLiveAndHistory(t *testing.T) {
func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) { func testIntervals(t *testing.T, live bool, history *Range, skipCheck bool) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
nodes := 2 nodes := 2
chunkCount := dataChunkCount chunkCount := dataChunkCount
externalStreamName := "externalStream" externalStreamName := "externalStream"

View file

@ -246,7 +246,6 @@ simulation's `action` function.
The snapshot should have 'streamer' in its service list. The snapshot should have 'streamer' in its service list.
*/ */
func runRetrievalTest(chunkCount int, nodeCount int) error { func runRetrievalTest(chunkCount int, nodeCount int) error {
sim := simulation.New(retrievalSimServiceMap) sim := simulation.New(retrievalSimServiceMap)
defer sim.Close() defer sim.Close()
@ -278,13 +277,13 @@ func runRetrievalTest(chunkCount int, nodeCount int) error {
} }
//this is the node selected for upload //this is the node selected for upload
node := sim.RandomUpNode() node := sim.Net.GetRandomUpNode()
item, ok := sim.NodeItem(node.ID, bucketKeyStore) item, ok := sim.NodeItem(node.ID(), bucketKeyStore)
if !ok { if !ok {
return fmt.Errorf("No localstore") return fmt.Errorf("No localstore")
} }
lstore := item.(*storage.LocalStore) lstore := item.(*storage.LocalStore)
conf.hashes, err = uploadFileToSingleNodeStore(node.ID, chunkCount, lstore) conf.hashes, err = uploadFileToSingleNodeStore(node.ID(), chunkCount, lstore)
if err != nil { if err != nil {
return err return err
} }

View file

@ -182,8 +182,6 @@ func streamerFunc(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Servic
} }
func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) { func testSyncingViaGlobalSync(t *testing.T, chunkCount int, nodeCount int) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
sim := simulation.New(simServiceMap) sim := simulation.New(simServiceMap)
defer sim.Close() defer sim.Close()
@ -248,20 +246,20 @@ func runSim(conf *synctestConfig, ctx context.Context, sim *simulation.Simulatio
//get the node at that index //get the node at that index
//this is the node selected for upload //this is the node selected for upload
node := sim.RandomUpNode() node := sim.Net.GetRandomUpNode()
item, ok := sim.NodeItem(node.ID, bucketKeyStore) item, ok := sim.NodeItem(node.ID(), bucketKeyStore)
if !ok { if !ok {
return fmt.Errorf("No localstore") return fmt.Errorf("No localstore")
} }
lstore := item.(*storage.LocalStore) lstore := item.(*storage.LocalStore)
hashes, err := uploadFileToSingleNodeStore(node.ID, chunkCount, lstore) hashes, err := uploadFileToSingleNodeStore(node.ID(), chunkCount, lstore)
if err != nil { if err != nil {
return err return err
} }
for _, h := range hashes { for _, h := range hashes {
evt := &simulations.Event{ evt := &simulations.Event{
Type: EventTypeChunkCreated, Type: EventTypeChunkCreated,
Node: sim.Net.GetNode(node.ID), Node: sim.Net.GetNode(node.ID()),
Data: h.String(), Data: h.String(),
} }
sim.Net.Events().Send(evt) sim.Net.Events().Send(evt)
@ -332,7 +330,6 @@ kademlia network. The snapshot should have 'streamer' in its service list.
*/ */
func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int) error { func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int) error {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
sim := simulation.New(map[string]simulation.ServiceFunc{ sim := simulation.New(map[string]simulation.ServiceFunc{
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
n := ctx.Config.Node() n := ctx.Config.Node()
@ -453,13 +450,13 @@ func testSyncingViaDirectSubscribe(t *testing.T, chunkCount int, nodeCount int)
} }
} }
//select a random node for upload //select a random node for upload
node := sim.RandomUpNode() node := sim.Net.GetRandomUpNode()
item, ok := sim.NodeItem(node.ID, bucketKeyStore) item, ok := sim.NodeItem(node.ID(), bucketKeyStore)
if !ok { if !ok {
return fmt.Errorf("No localstore") return fmt.Errorf("No localstore")
} }
lstore := item.(*storage.LocalStore) lstore := item.(*storage.LocalStore)
hashes, err := uploadFileToSingleNodeStore(node.ID, chunkCount, lstore) hashes, err := uploadFileToSingleNodeStore(node.ID(), chunkCount, lstore)
if err != nil { if err != nil {
return err return err
} }
@ -555,9 +552,7 @@ func mapKeysToNodes(conf *synctestConfig) {
np, _, _ = pot.Add(np, a, pof) np, _, _ = pot.Add(np, a, pof)
} }
var kadMinProxSize = 2 ppmap := network.NewPeerPotMap(network.NewKadParams().MinProxBinSize, conf.addrs)
ppmap := network.NewPeerPotMap(kadMinProxSize, conf.addrs)
//for each address, run EachNeighbour on the chunk hashes pot to identify closest nodes //for each address, run EachNeighbour on the chunk hashes pot to identify closest nodes
log.Trace(fmt.Sprintf("Generated hash chunk(s): %v", conf.hashes)) log.Trace(fmt.Sprintf("Generated hash chunk(s): %v", conf.hashes))

View file

@ -388,14 +388,6 @@ func (r *Registry) Quit(peerId enode.ID, s Stream) error {
return peer.Send(context.TODO(), msg) return peer.Send(context.TODO(), msg)
} }
func (r *Registry) NodeInfo() interface{} {
return nil
}
func (r *Registry) PeerInfo(id enode.ID) interface{} {
return nil
}
func (r *Registry) Close() error { func (r *Registry) Close() error {
return r.intervalsStore.Close() return r.intervalsStore.Close()
} }

View file

@ -127,19 +127,9 @@ func (s *SwarmSyncerServer) SetNextBatch(from, to uint64) ([]byte, uint64, uint6
// SwarmSyncerClient // SwarmSyncerClient
type SwarmSyncerClient struct { type SwarmSyncerClient struct {
sessionAt uint64 store storage.SyncChunkStore
nextC chan struct{} peer *Peer
sessionRoot storage.Address stream Stream
sessionReader storage.LazySectionReader
retrieveC chan *storage.Chunk
storeC chan *storage.Chunk
store storage.SyncChunkStore
// chunker storage.Chunker
currentRoot storage.Address
requestFunc func(chunk *storage.Chunk)
end, start uint64
peer *Peer
stream Stream
} }
// NewSwarmSyncerClient is a contructor for provable data exchange syncer // NewSwarmSyncerClient is a contructor for provable data exchange syncer
@ -209,46 +199,6 @@ func (s *SwarmSyncerClient) BatchDone(stream Stream, from uint64, hashes []byte,
return nil return nil
} }
func (s *SwarmSyncerClient) TakeoverProof(stream Stream, from uint64, hashes []byte, root storage.Address) (*TakeoverProof, error) {
// for provable syncer currentRoot is non-zero length
// TODO: reenable this with putter/getter
// if s.chunker != nil {
// if from > s.sessionAt { // for live syncing currentRoot is always updated
// //expRoot, err := s.chunker.Append(s.currentRoot, bytes.NewReader(hashes), s.retrieveC, s.storeC)
// expRoot, _, err := s.chunker.Append(s.currentRoot, bytes.NewReader(hashes), s.retrieveC)
// if err != nil {
// return nil, err
// }
// if !bytes.Equal(root, expRoot) {
// return nil, fmt.Errorf("HandoverProof mismatch")
// }
// s.currentRoot = root
// } else {
// expHashes := make([]byte, len(hashes))
// _, err := s.sessionReader.ReadAt(expHashes, int64(s.end*HashSize))
// if err != nil && err != io.EOF {
// return nil, err
// }
// if !bytes.Equal(expHashes, hashes) {
// return nil, errors.New("invalid proof")
// }
// }
// return nil, nil
// }
s.end += uint64(len(hashes)) / HashSize
takeover := &Takeover{
Stream: stream,
Start: s.start,
End: s.end,
Root: root,
}
// serialise and sign
return &TakeoverProof{
Takeover: takeover,
Sig: nil,
}, nil
}
func (s *SwarmSyncerClient) Close() {} func (s *SwarmSyncerClient) Close() {}
// base for parsing and formating sync bin key // base for parsing and formating sync bin key

View file

@ -69,7 +69,6 @@ func createMockStore(globalStore mock.GlobalStorer, id enode.ID, addr *network.B
func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool, po uint8) { func testSyncBetweenNodes(t *testing.T, nodes, conns, chunkCount int, skipCheck bool, po uint8) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
sim := simulation.New(map[string]simulation.ServiceFunc{ sim := simulation.New(map[string]simulation.ServiceFunc{
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) { "streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
var store storage.ChunkStore var store storage.ChunkStore

View file

@ -19,16 +19,27 @@
package stream package stream
import ( import (
"bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io"
"os"
"sync"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/p2p/simulations" "github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/swarm/log" "github.com/ethereum/go-ethereum/swarm/log"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/network/simulation" "github.com/ethereum/go-ethereum/swarm/network/simulation"
"github.com/ethereum/go-ethereum/swarm/state"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
) )
@ -68,12 +79,12 @@ func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc)
disconnections := sim.PeerEvents( disconnections := sim.PeerEvents(
context.Background(), context.Background(),
sim.NodeIDs(), sim.NodeIDs(),
simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeDrop), simulation.NewPeerEventsFilter().Drop(),
) )
go func() { go func() {
for d := range disconnections { for d := range disconnections {
log.Error("peer drop", "node", d.NodeID, "peer", d.Event.Peer) log.Error("peer drop", "node", d.NodeID, "peer", d.PeerID)
panic("unexpected disconnect") panic("unexpected disconnect")
cancelSimRun() cancelSimRun()
} }
@ -85,7 +96,6 @@ func watchSim(sim *simulation.Simulation) (context.Context, context.CancelFunc)
//This test requests bogus hashes into the network //This test requests bogus hashes into the network
func TestNonExistingHashesWithServer(t *testing.T) { func TestNonExistingHashesWithServer(t *testing.T) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
nodeCount, _, sim := setupSim(retrievalSimServiceMap) nodeCount, _, sim := setupSim(retrievalSimServiceMap)
defer sim.Close() defer sim.Close()
@ -103,7 +113,7 @@ func TestNonExistingHashesWithServer(t *testing.T) {
result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error { result := sim.Run(ctx, func(ctx context.Context, sim *simulation.Simulation) error {
//check on the node's FileStore (netstore) //check on the node's FileStore (netstore)
id := sim.RandomUpNode().ID id := sim.Net.GetRandomUpNode().ID()
item, ok := sim.NodeItem(id, bucketKeyFileStore) item, ok := sim.NodeItem(id, bucketKeyFileStore)
if !ok { if !ok {
t.Fatalf("No filestore") t.Fatalf("No filestore")
@ -144,8 +154,62 @@ func sendSimTerminatedEvent(sim *simulation.Simulation) {
//It also sends some custom events so that the frontend //It also sends some custom events so that the frontend
//can visualize messages like SendOfferedMsg, WantedHashesMsg, DeliveryMsg //can visualize messages like SendOfferedMsg, WantedHashesMsg, DeliveryMsg
func TestSnapshotSyncWithServer(t *testing.T) { func TestSnapshotSyncWithServer(t *testing.T) {
//t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
//define a wrapper object to be able to pass around data
wrapper := &netWrapper{}
nodeCount := *nodes
chunkCount := *chunks
if nodeCount == 0 || chunkCount == 0 {
nodeCount = 32
chunkCount = 1
}
log.Info(fmt.Sprintf("Running the simulation with %d nodes and %d chunks", nodeCount, chunkCount))
sim := simulation.New(map[string]simulation.ServiceFunc{
"streamer": func(ctx *adapters.ServiceContext, bucket *sync.Map) (s node.Service, cleanup func(), err error) {
n := ctx.Config.Node()
addr := network.NewAddr(n)
store, datadir, err := createTestLocalStorageForID(n.ID(), addr)
if err != nil {
return nil, nil, err
}
bucket.Store(bucketKeyStore, store)
localStore := store.(*storage.LocalStore)
netStore, err := storage.NewNetStore(localStore, nil)
if err != nil {
return nil, nil, err
}
kad := network.NewKademlia(addr.Over(), network.NewKadParams())
delivery := NewDelivery(kad, netStore)
netStore.NewNetFetcherFunc = network.NewFetcherFactory(dummyRequestFromPeers, true).New
r := NewRegistry(addr.ID(), delivery, netStore, state.NewInmemoryStore(), &RegistryOptions{
Retrieval: RetrievalDisabled,
Syncing: SyncingAutoSubscribe,
SyncUpdateDelay: 3 * time.Second,
}, nil)
tr := &testRegistry{
Registry: r,
w: wrapper,
}
bucket.Store(bucketKeyRegistry, tr)
cleanup = func() {
netStore.Close()
tr.Close()
os.RemoveAll(datadir)
}
return tr, cleanup, nil
},
}).WithServer(":8888") //start with the HTTP server
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
nodeCount, chunkCount, sim := setupSim(simServiceMap) nodeCount, chunkCount, sim := setupSim(simServiceMap)
defer sim.Close() defer sim.Close()
@ -153,12 +217,13 @@ func TestSnapshotSyncWithServer(t *testing.T) {
conf := &synctestConfig{} conf := &synctestConfig{}
//map of discover ID to indexes of chunks expected at that ID //map of discover ID to indexes of chunks expected at that ID
conf.idToChunksMap = make(map[discover.NodeID][]int) conf.idToChunksMap = make(map[enode.ID][]int)
//map of overlay address to discover ID //map of overlay address to discover ID
conf.addrToIDMap = make(map[string]discover.NodeID) conf.addrToIDMap = make(map[string]enode.ID)
//array where the generated chunk hashes will be stored //array where the generated chunk hashes will be stored
conf.hashes = make([]storage.Address, 0) conf.hashes = make([]storage.Address, 0)
//pass the network to the wrapper object
wrapper.setNetwork(sim.Net)
err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount)) err := sim.UploadSnapshot(fmt.Sprintf("testing/snapshot_%d.json", nodeCount))
if err != nil { if err != nil {
panic(err) panic(err)
@ -167,49 +232,6 @@ func TestSnapshotSyncWithServer(t *testing.T) {
ctx, cancelSimRun := watchSim(sim) ctx, cancelSimRun := watchSim(sim)
defer cancelSimRun() defer cancelSimRun()
//setup filters in the event feed
offeredHashesFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(1)
wantedFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(2)
deliveryFilter := simulation.NewPeerEventsFilter().Type(p2p.PeerEventTypeMsgRecv).Protocol("stream").MsgCode(6)
eventC := sim.PeerEvents(ctx, sim.UpNodeIDs(), offeredHashesFilter, wantedFilter, deliveryFilter)
quit := make(chan struct{})
go func() {
for e := range eventC {
select {
case <-quit:
fmt.Println("quitting event loop")
return
default:
}
if e.Error != nil {
t.Fatal(e.Error)
}
if *e.Event.MsgCode == uint64(1) {
evt := &simulations.Event{
Type: EventTypeChunkOffered,
Node: sim.Net.GetNode(e.NodeID),
Control: false,
}
sim.Net.Events().Send(evt)
} else if *e.Event.MsgCode == uint64(2) {
evt := &simulations.Event{
Type: EventTypeChunkWanted,
Node: sim.Net.GetNode(e.NodeID),
Control: false,
}
sim.Net.Events().Send(evt)
} else if *e.Event.MsgCode == uint64(6) {
evt := &simulations.Event{
Type: EventTypeChunkDelivered,
Node: sim.Net.GetNode(e.NodeID),
Control: false,
}
sim.Net.Events().Send(evt)
}
}
}()
//run the sim //run the sim
result := runSim(conf, ctx, sim, chunkCount) result := runSim(conf, ctx, sim, chunkCount)
@ -218,11 +240,150 @@ func TestSnapshotSyncWithServer(t *testing.T) {
Type: EventTypeSimTerminated, Type: EventTypeSimTerminated,
Control: false, Control: false,
} }
sim.Net.Events().Send(evt) go sim.Net.Events().Send(evt)
if result.Error != nil { if result.Error != nil {
panic(result.Error) panic(result.Error)
} }
close(quit)
log.Info("Simulation ended") log.Info("Simulation ended")
} }
//testRegistry embeds registry
//it allows to replace the protocol run function
type testRegistry struct {
*Registry
w *netWrapper
}
//Protocols replaces the protocol's run function
func (tr *testRegistry) Protocols() []p2p.Protocol {
regProto := tr.Registry.Protocols()
//set the `stream` protocol's run function with the testRegistry's one
regProto[0].Run = tr.runProto
return regProto
}
//runProto is the new overwritten protocol's run function for this test
func (tr *testRegistry) runProto(p *p2p.Peer, rw p2p.MsgReadWriter) error {
//create a custom rw message ReadWriter
testRw := &testMsgReadWriter{
MsgReadWriter: rw,
Peer: p,
w: tr.w,
Registry: tr.Registry,
}
//now run the actual upper layer `Registry`'s protocol function
return tr.runProtocol(p, testRw)
}
//testMsgReadWriter is a custom rw
//it will allow us to re-use the message twice
type testMsgReadWriter struct {
*Registry
p2p.MsgReadWriter
*p2p.Peer
w *netWrapper
}
//netWrapper wrapper object so we can pass data around
type netWrapper struct {
net *simulations.Network
}
//set the network to the wrapper for later use (used inside the custom rw)
func (w *netWrapper) setNetwork(n *simulations.Network) {
w.net = n
}
//get he network from the wrapper (used inside the custom rw)
func (w *netWrapper) getNetwork() *simulations.Network {
return w.net
}
// ReadMsg reads a message from the underlying MsgReadWriter and emits a
// "message received" event
//we do this because we are interested in the Payload of the message for custom use
//in this test, but messages can only be consumed once (stream io.Reader)
func (ev *testMsgReadWriter) ReadMsg() (p2p.Msg, error) {
//read the message from the underlying rw
msg, err := ev.MsgReadWriter.ReadMsg()
if err != nil {
return msg, err
}
//don't do anything with message codes we actually are not needing/reading
subCodes := []uint64{1, 2, 10}
found := false
for _, c := range subCodes {
if c == msg.Code {
found = true
}
}
//just return if not a msg code we are interested in
if !found {
return msg, nil
}
//we use a io.TeeReader so that we can read the message twice
//the Payload is a io.Reader, so if we read from it, the actual protocol handler
//cannot access it anymore.
//But we need that handler to be able to consume the message as normal,
//as if we would not do anything here with that message
var buf bytes.Buffer
tee := io.TeeReader(msg.Payload, &buf)
mcp := &p2p.Msg{
Code: msg.Code,
Size: msg.Size,
ReceivedAt: msg.ReceivedAt,
Payload: tee,
}
//assign the copy for later use
msg.Payload = &buf
//now let's look into the message
var wmsg protocols.WrappedMsg
err = mcp.Decode(&wmsg)
if err != nil {
log.Error(err.Error())
return msg, err
}
//create a new message from the code
val, ok := ev.Registry.GetSpec().NewMsg(mcp.Code)
if !ok {
return msg, errors.New(fmt.Sprintf("Invalid message code: %v", msg.Code))
}
//decode it
if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil {
return msg, errors.New(fmt.Sprintf("Decoding error <= %v: %v", msg, err))
}
//now for every message type we are interested in, create a custom event and send it
var evt *simulations.Event
switch val := val.(type) {
case *OfferedHashesMsg:
evt = &simulations.Event{
Type: EventTypeChunkOffered,
Node: ev.w.getNetwork().GetNode(ev.ID()),
Control: false,
Data: val.Hashes,
}
case *WantedHashesMsg:
evt = &simulations.Event{
Type: EventTypeChunkWanted,
Node: ev.w.getNetwork().GetNode(ev.ID()),
Control: false,
}
case *ChunkDeliveryMsgSyncing:
evt = &simulations.Event{
Type: EventTypeChunkDelivered,
Node: ev.w.getNetwork().GetNode(ev.ID()),
Control: false,
Data: val.Addr.String(),
}
}
if evt != nil {
//send custom event to feed; frontend will listen to it and display
ev.w.getNetwork().Events().Send(evt)
}
return msg, nil
}

View file

@ -260,7 +260,6 @@ type testSwarmNetworkOptions struct {
// - Checking if a file is retrievable from all nodes. // - Checking if a file is retrievable from all nodes.
func testSwarmNetwork(t *testing.T, o *testSwarmNetworkOptions, steps ...testSwarmNetworkStep) { func testSwarmNetwork(t *testing.T, o *testSwarmNetworkOptions, steps ...testSwarmNetworkStep) {
t.Skip("temporarily disabled as simulations.WaitTillHealthy cannot be trusted")
if o == nil { if o == nil {
o = new(testSwarmNetworkOptions) o = new(testSwarmNetworkOptions)
} }

View file

@ -41,10 +41,6 @@ func NewAddressFromBytes(b []byte) Address {
return Address(h) return Address(h)
} }
func (a Address) IsZero() bool {
return a.Bin() == zerosBin
}
func (a Address) String() string { func (a Address) String() string {
return fmt.Sprintf("%x", a[:]) return fmt.Sprintf("%x", a[:])
} }

View file

@ -477,7 +477,7 @@ func (t *Pot) each(f func(Val, int) bool) bool {
return f(t.pin, t.po) return f(t.pin, t.po)
} }
// EachFrom called with (f, start) is a synchronous iterator over the elements of a Pot // eachFrom called with (f, start) is a synchronous iterator over the elements of a Pot
// within the inclusive range starting from proximity order start // within the inclusive range starting from proximity order start
// the function argument is passed the value and the proximity order wrt the root pin // the function argument is passed the value and the proximity order wrt the root pin
// it does NOT include the pinned item of the root // it does NOT include the pinned item of the root
@ -485,10 +485,6 @@ func (t *Pot) each(f func(Val, int) bool) bool {
// proximity > pinnedness // proximity > pinnedness
// the iteration ends if the function return false or there are no more elements // the iteration ends if the function return false or there are no more elements
// end of a po range can be implemented since po is passed to the function // end of a po range can be implemented since po is passed to the function
func (t *Pot) EachFrom(f func(Val, int) bool, po int) bool {
return t.eachFrom(f, po)
}
func (t *Pot) eachFrom(f func(Val, int) bool, po int) bool { func (t *Pot) eachFrom(f func(Val, int) bool, po int) bool {
var next bool var next bool
_, lim := t.getPos(po) _, lim := t.getPos(po)

View file

@ -92,7 +92,7 @@ func (pssapi *API) Receive(ctx context.Context, topic Topic, raw bool, prox bool
} }
func (pssapi *API) GetAddress(topic Topic, asymmetric bool, key string) (PssAddress, error) { func (pssapi *API) GetAddress(topic Topic, asymmetric bool, key string) (PssAddress, error) {
var addr *PssAddress var addr PssAddress
if asymmetric { if asymmetric {
peer, ok := pssapi.Pss.pubKeyPool[key][topic] peer, ok := pssapi.Pss.pubKeyPool[key][topic]
if !ok { if !ok {
@ -107,7 +107,7 @@ func (pssapi *API) GetAddress(topic Topic, asymmetric bool, key string) (PssAddr
addr = peer.address addr = peer.address
} }
return *addr, nil return addr, nil
} }
// Retrieves the node's base address in hex form // Retrieves the node's base address in hex form
@ -128,7 +128,7 @@ func (pssapi *API) SetPeerPublicKey(pubkey hexutil.Bytes, topic Topic, addr PssA
if err != nil { if err != nil {
return fmt.Errorf("Cannot unmarshal pubkey: %x", pubkey) return fmt.Errorf("Cannot unmarshal pubkey: %x", pubkey)
} }
err = pssapi.Pss.SetPeerPublicKey(pk, topic, &addr) err = pssapi.Pss.SetPeerPublicKey(pk, topic, addr)
if err != nil { if err != nil {
return fmt.Errorf("Invalid key: %x", pk) return fmt.Errorf("Invalid key: %x", pk)
} }
@ -141,11 +141,11 @@ func (pssapi *API) GetSymmetricKey(symkeyid string) (hexutil.Bytes, error) {
} }
func (pssapi *API) GetSymmetricAddressHint(topic Topic, symkeyid string) (PssAddress, error) { func (pssapi *API) GetSymmetricAddressHint(topic Topic, symkeyid string) (PssAddress, error) {
return *pssapi.Pss.symKeyPool[symkeyid][topic].address, nil return pssapi.Pss.symKeyPool[symkeyid][topic].address, nil
} }
func (pssapi *API) GetAsymmetricAddressHint(topic Topic, pubkeyid string) (PssAddress, error) { func (pssapi *API) GetAsymmetricAddressHint(topic Topic, pubkeyid string) (PssAddress, error) {
return *pssapi.Pss.pubKeyPool[pubkeyid][topic].address, nil return pssapi.Pss.pubKeyPool[pubkeyid][topic].address, nil
} }
func (pssapi *API) StringToTopic(topicstring string) (Topic, error) { func (pssapi *API) StringToTopic(topicstring string) (Topic, error) {
@ -157,14 +157,23 @@ func (pssapi *API) StringToTopic(topicstring string) (Topic, error) {
} }
func (pssapi *API) SendAsym(pubkeyhex string, topic Topic, msg hexutil.Bytes) error { func (pssapi *API) SendAsym(pubkeyhex string, topic Topic, msg hexutil.Bytes) error {
if err := validateMsg(msg); err != nil {
return err
}
return pssapi.Pss.SendAsym(pubkeyhex, topic, msg[:]) return pssapi.Pss.SendAsym(pubkeyhex, topic, msg[:])
} }
func (pssapi *API) SendSym(symkeyhex string, topic Topic, msg hexutil.Bytes) error { func (pssapi *API) SendSym(symkeyhex string, topic Topic, msg hexutil.Bytes) error {
if err := validateMsg(msg); err != nil {
return err
}
return pssapi.Pss.SendSym(symkeyhex, topic, msg[:]) return pssapi.Pss.SendSym(symkeyhex, topic, msg[:])
} }
func (pssapi *API) SendRaw(addr hexutil.Bytes, topic Topic, msg hexutil.Bytes) error { func (pssapi *API) SendRaw(addr hexutil.Bytes, topic Topic, msg hexutil.Bytes) error {
if err := validateMsg(msg); err != nil {
return err
}
return pssapi.Pss.SendRaw(PssAddress(addr), topic, msg[:]) return pssapi.Pss.SendRaw(PssAddress(addr), topic, msg[:])
} }
@ -177,3 +186,10 @@ func (pssapi *API) GetPeerTopics(pubkeyhex string) ([]Topic, error) {
func (pssapi *API) GetPeerAddress(pubkeyhex string, topic Topic) (PssAddress, error) { func (pssapi *API) GetPeerAddress(pubkeyhex string, topic Topic) (PssAddress, error) {
return pssapi.Pss.getPeerAddress(pubkeyhex, topic) return pssapi.Pss.getPeerAddress(pubkeyhex, topic)
} }
func validateMsg(msg []byte) error {
if len(msg) == 0 {
return errors.New("invalid message length")
}
return nil
}

View file

@ -0,0 +1,356 @@
package pss
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/protocols"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/pot"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
)
type testCase struct {
name string
recipient []byte
peers []pot.Address
expected []int
exclusive bool
nFails int
success bool
errors string
}
var testCases []testCase
// the purpose of this test is to see that pss.forward() function correctly
// selects the peers for message forwarding, depending on the message address
// and kademlia constellation.
func TestForwardBasic(t *testing.T) {
baseAddrBytes := make([]byte, 32)
for i := 0; i < len(baseAddrBytes); i++ {
baseAddrBytes[i] = 0xFF
}
var c testCase
base := pot.NewAddressFromBytes(baseAddrBytes)
var peerAddresses []pot.Address
const depth = 10
for i := 0; i <= depth; i++ {
// add two peers for each proximity order
a := pot.RandomAddressAt(base, i)
peerAddresses = append(peerAddresses, a)
a = pot.RandomAddressAt(base, i)
peerAddresses = append(peerAddresses, a)
}
// skip one level, add one peer at one level deeper.
// as a result, we will have an edge case of three peers in nearest neighbours' bin.
peerAddresses = append(peerAddresses, pot.RandomAddressAt(base, depth+2))
kad := network.NewKademlia(base[:], network.NewKadParams())
ps := createPss(t, kad)
addPeers(kad, peerAddresses)
const firstNearest = depth * 2 // shallowest peer in the nearest neighbours' bin
nearestNeighbours := []int{firstNearest, firstNearest + 1, firstNearest + 2}
var all []int // indices of all the peers
for i := 0; i < len(peerAddresses); i++ {
all = append(all, i)
}
for i := 0; i < len(peerAddresses); i++ {
// send msg directly to the known peers (recipient address == peer address)
c = testCase{
name: fmt.Sprintf("Send direct to known, id: [%d]", i),
recipient: peerAddresses[i][:],
peers: peerAddresses,
expected: []int{i},
exclusive: false,
}
testCases = append(testCases, c)
}
for i := 0; i < firstNearest; i++ {
// send random messages with proximity orders, corresponding to PO of each bin,
// with one peer being closer to the recipient address
a := pot.RandomAddressAt(peerAddresses[i], 64)
c = testCase{
name: fmt.Sprintf("Send random to each PO, id: [%d]", i),
recipient: a[:],
peers: peerAddresses,
expected: []int{i},
exclusive: false,
}
testCases = append(testCases, c)
}
for i := 0; i < firstNearest; i++ {
// send random messages with proximity orders, corresponding to PO of each bin,
// with random proximity relative to the recipient address
po := i / 2
a := pot.RandomAddressAt(base, po)
c = testCase{
name: fmt.Sprintf("Send direct to known, id: [%d]", i),
recipient: a[:],
peers: peerAddresses,
expected: []int{po * 2, po*2 + 1},
exclusive: true,
}
testCases = append(testCases, c)
}
for i := firstNearest; i < len(peerAddresses); i++ {
// recipient address falls into the nearest neighbours' bin
a := pot.RandomAddressAt(base, i)
c = testCase{
name: fmt.Sprintf("recipient address falls into the nearest neighbours' bin, id: [%d]", i),
recipient: a[:],
peers: peerAddresses,
expected: nearestNeighbours,
exclusive: false,
}
testCases = append(testCases, c)
}
// send msg with proximity order much deeper than the deepest nearest neighbour
a2 := pot.RandomAddressAt(base, 77)
c = testCase{
name: "proximity order much deeper than the deepest nearest neighbour",
recipient: a2[:],
peers: peerAddresses,
expected: nearestNeighbours,
exclusive: false,
}
testCases = append(testCases, c)
// test with partial addresses
const part = 12
for i := 0; i < firstNearest; i++ {
// send messages with partial address falling into different proximity orders
po := i / 2
if i%8 != 0 {
c = testCase{
name: fmt.Sprintf("partial address falling into different proximity orders, id: [%d]", i),
recipient: peerAddresses[i][:i],
peers: peerAddresses,
expected: []int{po * 2, po*2 + 1},
exclusive: true,
}
testCases = append(testCases, c)
}
c = testCase{
name: fmt.Sprintf("extended partial address falling into different proximity orders, id: [%d]", i),
recipient: peerAddresses[i][:part],
peers: peerAddresses,
expected: []int{po * 2, po*2 + 1},
exclusive: true,
}
testCases = append(testCases, c)
}
for i := firstNearest; i < len(peerAddresses); i++ {
// partial address falls into the nearest neighbours' bin
c = testCase{
name: fmt.Sprintf("partial address falls into the nearest neighbours' bin, id: [%d]", i),
recipient: peerAddresses[i][:part],
peers: peerAddresses,
expected: nearestNeighbours,
exclusive: false,
}
testCases = append(testCases, c)
}
// partial address with proximity order deeper than any of the nearest neighbour
a3 := pot.RandomAddressAt(base, part)
c = testCase{
name: "partial address with proximity order deeper than any of the nearest neighbour",
recipient: a3[:part],
peers: peerAddresses,
expected: nearestNeighbours,
exclusive: false,
}
testCases = append(testCases, c)
// special cases where partial address matches a large group of peers
// zero bytes of address is given, msg should be delivered to all the peers
c = testCase{
name: "zero bytes of address is given",
recipient: []byte{},
peers: peerAddresses,
expected: all,
exclusive: false,
}
testCases = append(testCases, c)
// luminous radius of 8 bits, proximity order 8
indexAtPo8 := 16
c = testCase{
name: "luminous radius of 8 bits",
recipient: []byte{0xFF},
peers: peerAddresses,
expected: all[indexAtPo8:],
exclusive: false,
}
testCases = append(testCases, c)
// luminous radius of 256 bits, proximity order 8
a4 := pot.Address{}
a4[0] = 0xFF
c = testCase{
name: "luminous radius of 256 bits",
recipient: a4[:],
peers: peerAddresses,
expected: []int{indexAtPo8, indexAtPo8 + 1},
exclusive: true,
}
testCases = append(testCases, c)
// check correct behaviour in case send fails
for i := 2; i < firstNearest-3; i += 2 {
po := i / 2
// send random messages with proximity orders, corresponding to PO of each bin,
// with different numbers of failed attempts.
// msg should be received by only one of the deeper peers.
a := pot.RandomAddressAt(base, po)
c = testCase{
name: fmt.Sprintf("Send direct to known, id: [%d]", i),
recipient: a[:],
peers: peerAddresses,
expected: all[i+1:],
exclusive: true,
nFails: rand.Int()%3 + 2,
}
testCases = append(testCases, c)
}
for _, c := range testCases {
testForwardMsg(t, ps, &c)
}
}
// this function tests the forwarding of a single message. the recipient address is passed as param,
// along with addresses of all peers, and indices of those peers which are expected to receive the message.
func testForwardMsg(t *testing.T, ps *Pss, c *testCase) {
recipientAddr := c.recipient
peers := c.peers
expected := c.expected
exclusive := c.exclusive
nFails := c.nFails
tries := 0 // number of previous failed tries
resultMap := make(map[pot.Address]int)
defer func() { sendFunc = sendMsg }()
sendFunc = func(_ *Pss, sp *network.Peer, _ *PssMsg) bool {
if tries < nFails {
tries++
return false
}
a := pot.NewAddressFromBytes(sp.Address())
resultMap[a]++
return true
}
msg := newTestMsg(recipientAddr)
ps.forward(msg)
// check test results
var fail bool
precision := len(recipientAddr)
if precision > 4 {
precision = 4
}
s := fmt.Sprintf("test [%s]\nmsg address: %x..., radius: %d", c.name, recipientAddr[:precision], 8*len(recipientAddr))
// false negatives (expected message didn't reach peer)
if exclusive {
var cnt int
for _, i := range expected {
a := peers[i]
cnt += resultMap[a]
resultMap[a] = 0
}
if cnt != 1 {
s += fmt.Sprintf("\n%d messages received by %d peers with indices: [%v]", cnt, len(expected), expected)
fail = true
}
} else {
for _, i := range expected {
a := peers[i]
received := resultMap[a]
if received != 1 {
s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", i, a[:4], received)
fail = true
}
resultMap[a] = 0
}
}
// false positives (unexpected message reached peer)
for k, v := range resultMap {
if v != 0 {
// find the index of the false positive peer
var j int
for j = 0; j < len(peers); j++ {
if peers[j] == k {
break
}
}
s += fmt.Sprintf("\npeer number %d [%x...] received %d messages", j, k[:4], v)
fail = true
}
}
if fail {
t.Fatal(s)
}
}
func addPeers(kad *network.Kademlia, addresses []pot.Address) {
for _, a := range addresses {
p := newTestDiscoveryPeer(a, kad)
kad.On(p)
}
}
func createPss(t *testing.T, kad *network.Kademlia) *Pss {
privKey, err := crypto.GenerateKey()
pssp := NewPssParams().WithPrivateKey(privKey)
ps, err := NewPss(kad, pssp)
if err != nil {
t.Fatal(err.Error())
}
return ps
}
func newTestDiscoveryPeer(addr pot.Address, kad *network.Kademlia) *network.Peer {
rw := &p2p.MsgPipeRW{}
p := p2p.NewPeer(enode.ID{}, "test", []p2p.Cap{})
pp := protocols.NewPeer(p, rw, &protocols.Spec{})
bp := &network.BzzPeer{
Peer: pp,
BzzAddr: &network.BzzAddr{
OAddr: addr.Bytes(),
UAddr: []byte(fmt.Sprintf("%x", addr[:])),
},
}
return network.NewPeer(bp, kad)
}
func newTestMsg(addr []byte) *PssMsg {
msg := newPssMsg(&msgParams{})
msg.To = addr[:]
msg.Expire = uint32(time.Now().Add(time.Second * 60).Unix())
msg.Payload = &whisper.Envelope{
Topic: [4]byte{},
Data: []byte("i have nothing to hide"),
}
return msg
}

View file

@ -321,9 +321,7 @@ func (ctl *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMsg
for _, key := range keymsg.Keys { for _, key := range keymsg.Keys {
sendsymkey := make([]byte, len(key)) sendsymkey := make([]byte, len(key))
copy(sendsymkey, key) copy(sendsymkey, key)
var address PssAddress sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, PssAddress(keymsg.From), false, false)
copy(address[:], keymsg.From)
sendsymkeyid, err := ctl.pss.setSymmetricKey(sendsymkey, keymsg.Topic, &address, false, false)
if err != nil { if err != nil {
return err return err
} }
@ -356,7 +354,7 @@ func (ctl *HandshakeController) handleKeys(pubkeyid string, keymsg *handshakeMsg
func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) { func (ctl *HandshakeController) sendKey(pubkeyid string, topic *Topic, keycount uint8) ([]string, error) {
var requestcount uint8 var requestcount uint8
to := &PssAddress{} to := PssAddress{}
if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok { if _, ok := ctl.pss.pubKeyPool[pubkeyid]; !ok {
return []string{}, errors.New("Invalid public key") return []string{}, errors.New("Invalid public key")
} else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok { } else if psp, ok := ctl.pss.pubKeyPool[pubkeyid][*topic]; ok {
@ -564,5 +562,5 @@ func (api *HandshakeAPI) SendSym(symkeyid string, topic Topic, msg hexutil.Bytes
api.ctrl.symKeyIndex[symkeyid].count++ api.ctrl.symKeyIndex[symkeyid].count++
log.Trace("increment symkey send use", "symkeyid", symkeyid, "count", api.ctrl.symKeyIndex[symkeyid].count, "limit", api.ctrl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(api.ctrl.pss.PublicKey()))) log.Trace("increment symkey send use", "symkeyid", symkeyid, "count", api.ctrl.symKeyIndex[symkeyid].count, "limit", api.ctrl.symKeyIndex[symkeyid].limit, "receiver", common.ToHex(crypto.FromECDSAPub(api.ctrl.pss.PublicKey())))
} }
return return err
} }

View file

@ -30,6 +30,7 @@ import (
// asymmetrical key exchange between two directly connected peers // asymmetrical key exchange between two directly connected peers
// full address, partial address (8 bytes) and empty address // full address, partial address (8 bytes) and empty address
func TestHandshake(t *testing.T) { func TestHandshake(t *testing.T) {
t.Skip("handshakes are not adapted to current pss core code")
t.Run("32", testHandshake) t.Run("32", testHandshake)
t.Run("8", testHandshake) t.Run("8", testHandshake)
t.Run("0", testHandshake) t.Run("0", testHandshake)

View file

@ -138,7 +138,7 @@ func (c *Controller) Subscribe(name string, pubkey *ecdsa.PublicKey, address pss
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
msg := NewMsg(MsgCodeStart, name, c.pss.BaseAddr()) msg := NewMsg(MsgCodeStart, name, c.pss.BaseAddr())
c.pss.SetPeerPublicKey(pubkey, controlTopic, &address) c.pss.SetPeerPublicKey(pubkey, controlTopic, address)
pubkeyId := hexutil.Encode(crypto.FromECDSAPub(pubkey)) pubkeyId := hexutil.Encode(crypto.FromECDSAPub(pubkey))
smsg, err := rlp.EncodeToBytes(msg) smsg, err := rlp.EncodeToBytes(msg)
if err != nil { if err != nil {
@ -271,7 +271,7 @@ func (c *Controller) addToBin(ntfr *notifier, address []byte) (symKeyId string,
currentBin.count++ currentBin.count++
symKeyId = currentBin.symKeyId symKeyId = currentBin.symKeyId
} else { } else {
symKeyId, err = c.pss.GenerateSymmetricKey(ntfr.topic, &pssAddress, false) symKeyId, err = c.pss.GenerateSymmetricKey(ntfr.topic, pssAddress, false)
if err != nil { if err != nil {
return "", nil, err return "", nil, err
} }
@ -312,7 +312,7 @@ func (c *Controller) handleStartMsg(msg *Msg, keyid string) (err error) {
if err != nil { if err != nil {
return err return err
} }
err = c.pss.SetPeerPublicKey(pubkey, controlTopic, &pssAddress) err = c.pss.SetPeerPublicKey(pubkey, controlTopic, pssAddress)
if err != nil { if err != nil {
return err return err
} }
@ -335,7 +335,7 @@ func (c *Controller) handleNotifyWithKeyMsg(msg *Msg) error {
// \TODO keep track of and add actual address // \TODO keep track of and add actual address
updaterAddr := pss.PssAddress([]byte{}) updaterAddr := pss.PssAddress([]byte{})
c.pss.SetSymmetricKey(symkey, topic, &updaterAddr, true) c.pss.SetSymmetricKey(symkey, topic, updaterAddr, true)
c.pss.Register(&topic, pss.NewHandler(c.Handler)) c.pss.Register(&topic, pss.NewHandler(c.Handler))
return c.subscriptions[msg.namestring].handler(msg.namestring, msg.Payload[:len(msg.Payload)-symKeyLength]) return c.subscriptions[msg.namestring].handler(msg.namestring, msg.Payload[:len(msg.Payload)-symKeyLength])
} }

View file

@ -81,7 +81,7 @@ type senderPeer interface {
// member `protected` prevents garbage collection of the instance // member `protected` prevents garbage collection of the instance
type pssPeer struct { type pssPeer struct {
lastSeen time.Time lastSeen time.Time
address *PssAddress address PssAddress
protected bool protected bool
} }
@ -396,9 +396,11 @@ func (p *Pss) handlePssMsg(ctx context.Context, msg interface{}) error {
// raw is simplest handler contingency to check, so check that first // raw is simplest handler contingency to check, so check that first
var isRaw bool var isRaw bool
if pssmsg.isRaw() { if pssmsg.isRaw() {
if !p.topicHandlerCaps[psstopic].raw { if _, ok := p.topicHandlerCaps[psstopic]; ok {
log.Debug("No handler for raw message", "topic", psstopic) if !p.topicHandlerCaps[psstopic].raw {
return nil log.Debug("No handler for raw message", "topic", psstopic)
return nil
}
} }
isRaw = true isRaw = true
} }
@ -437,10 +439,10 @@ func (p *Pss) process(pssmsg *PssMsg, raw bool, prox bool) error {
var err error var err error
var recvmsg *whisper.ReceivedMessage var recvmsg *whisper.ReceivedMessage
var payload []byte var payload []byte
var from *PssAddress var from PssAddress
var asymmetric bool var asymmetric bool
var keyid string var keyid string
var keyFunc func(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, *PssAddress, error) var keyFunc func(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, PssAddress, error)
envelope := pssmsg.Payload envelope := pssmsg.Payload
psstopic := Topic(envelope.Topic) psstopic := Topic(envelope.Topic)
@ -473,7 +475,7 @@ func (p *Pss) process(pssmsg *PssMsg, raw bool, prox bool) error {
} }
func (p *Pss) executeHandlers(topic Topic, payload []byte, from *PssAddress, raw bool, prox bool, asymmetric bool, keyid string) { func (p *Pss) executeHandlers(topic Topic, payload []byte, from PssAddress, raw bool, prox bool, asymmetric bool, keyid string) {
handlers := p.getHandlers(topic) handlers := p.getHandlers(topic)
peer := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%x", from), []p2p.Cap{}) peer := p2p.NewPeer(enode.ID{}, fmt.Sprintf("%x", from), []p2p.Cap{})
for h := range handlers { for h := range handlers {
@ -511,7 +513,7 @@ func (p *Pss) isSelfPossibleRecipient(msg *PssMsg, prox bool) bool {
} }
depth := p.Kademlia.NeighbourhoodDepth() depth := p.Kademlia.NeighbourhoodDepth()
po, _ := p.Kademlia.Pof(p.Kademlia.BaseAddr(), msg.To, 0) po, _ := network.Pof(p.Kademlia.BaseAddr(), msg.To, 0)
log.Trace("selfpossible", "po", po, "depth", depth) log.Trace("selfpossible", "po", po, "depth", depth)
return depth <= po return depth <= po
@ -528,7 +530,10 @@ func (p *Pss) isSelfPossibleRecipient(msg *PssMsg, prox bool) bool {
// //
// The value in `address` will be used as a routing hint for the // The value in `address` will be used as a routing hint for the
// public key / topic association // public key / topic association
func (p *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address *PssAddress) error { func (p *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address PssAddress) error {
if err := validateAddress(address); err != nil {
return err
}
pubkeybytes := crypto.FromECDSAPub(pubkey) pubkeybytes := crypto.FromECDSAPub(pubkey)
if len(pubkeybytes) == 0 { if len(pubkeybytes) == 0 {
return fmt.Errorf("invalid public key: %v", pubkey) return fmt.Errorf("invalid public key: %v", pubkey)
@ -543,12 +548,12 @@ func (p *Pss) SetPeerPublicKey(pubkey *ecdsa.PublicKey, topic Topic, address *Ps
} }
p.pubKeyPool[pubkeyid][topic] = psp p.pubKeyPool[pubkeyid][topic] = psp
p.pubKeyPoolMu.Unlock() p.pubKeyPoolMu.Unlock()
log.Trace("added pubkey", "pubkeyid", pubkeyid, "topic", topic, "address", common.ToHex(*address)) log.Trace("added pubkey", "pubkeyid", pubkeyid, "topic", topic, "address", address)
return nil return nil
} }
// Automatically generate a new symkey for a topic and address hint // Automatically generate a new symkey for a topic and address hint
func (p *Pss) GenerateSymmetricKey(topic Topic, address *PssAddress, addToCache bool) (string, error) { func (p *Pss) GenerateSymmetricKey(topic Topic, address PssAddress, addToCache bool) (string, error) {
keyid, err := p.w.GenerateSymKey() keyid, err := p.w.GenerateSymKey()
if err != nil { if err != nil {
return "", err return "", err
@ -569,11 +574,14 @@ func (p *Pss) GenerateSymmetricKey(topic Topic, address *PssAddress, addToCache
// //
// Returns a string id that can be used to retrieve the key bytes // Returns a string id that can be used to retrieve the key bytes
// from the whisper backend (see pss.GetSymmetricKey()) // from the whisper backend (see pss.GetSymmetricKey())
func (p *Pss) SetSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool) (string, error) { func (p *Pss) SetSymmetricKey(key []byte, topic Topic, address PssAddress, addtocache bool) (string, error) {
if err := validateAddress(address); err != nil {
return "", err
}
return p.setSymmetricKey(key, topic, address, addtocache, true) return p.setSymmetricKey(key, topic, address, addtocache, true)
} }
func (p *Pss) setSymmetricKey(key []byte, topic Topic, address *PssAddress, addtocache bool, protected bool) (string, error) { func (p *Pss) setSymmetricKey(key []byte, topic Topic, address PssAddress, addtocache bool, protected bool) (string, error) {
keyid, err := p.w.AddSymKeyDirect(key) keyid, err := p.w.AddSymKeyDirect(key)
if err != nil { if err != nil {
return "", err return "", err
@ -585,7 +593,7 @@ func (p *Pss) setSymmetricKey(key []byte, topic Topic, address *PssAddress, addt
// adds a symmetric key to the pss key pool, and optionally adds the key // adds a symmetric key to the pss key pool, and optionally adds the key
// to the collection of keys used to attempt symmetric decryption of // to the collection of keys used to attempt symmetric decryption of
// incoming messages // incoming messages
func (p *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddress, addtocache bool, protected bool) { func (p *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address PssAddress, addtocache bool, protected bool) {
psp := &pssPeer{ psp := &pssPeer{
address: address, address: address,
protected: protected, protected: protected,
@ -601,7 +609,7 @@ func (p *Pss) addSymmetricKeyToPool(keyid string, topic Topic, address *PssAddre
p.symKeyDecryptCache[p.symKeyDecryptCacheCursor%cap(p.symKeyDecryptCache)] = &keyid p.symKeyDecryptCache[p.symKeyDecryptCacheCursor%cap(p.symKeyDecryptCache)] = &keyid
} }
key, _ := p.GetSymmetricKey(keyid) key, _ := p.GetSymmetricKey(keyid)
log.Trace("added symkey", "symkeyid", keyid, "symkey", common.ToHex(key), "topic", topic, "address", fmt.Sprintf("%p", address), "cache", addtocache) log.Trace("added symkey", "symkeyid", keyid, "symkey", common.ToHex(key), "topic", topic, "address", address, "cache", addtocache)
} }
// Returns a symmetric key byte seqyence stored in the whisper backend // Returns a symmetric key byte seqyence stored in the whisper backend
@ -622,7 +630,7 @@ func (p *Pss) GetPublickeyPeers(keyid string) (topic []Topic, address []PssAddre
defer p.pubKeyPoolMu.RUnlock() defer p.pubKeyPoolMu.RUnlock()
for t, peer := range p.pubKeyPool[keyid] { for t, peer := range p.pubKeyPool[keyid] {
topic = append(topic, t) topic = append(topic, t)
address = append(address, *peer.address) address = append(address, peer.address)
} }
return topic, address, nil return topic, address, nil
@ -633,7 +641,7 @@ func (p *Pss) getPeerAddress(keyid string, topic Topic) (PssAddress, error) {
defer p.pubKeyPoolMu.RUnlock() defer p.pubKeyPoolMu.RUnlock()
if peers, ok := p.pubKeyPool[keyid]; ok { if peers, ok := p.pubKeyPool[keyid]; ok {
if t, ok := peers[topic]; ok { if t, ok := peers[topic]; ok {
return *t.address, nil return t.address, nil
} }
} }
return nil, fmt.Errorf("peer with pubkey %s, topic %x not found", keyid, topic) return nil, fmt.Errorf("peer with pubkey %s, topic %x not found", keyid, topic)
@ -645,7 +653,7 @@ func (p *Pss) getPeerAddress(keyid string, topic Topic) (PssAddress, error) {
// encapsulating the decrypted message, and the whisper backend id // encapsulating the decrypted message, and the whisper backend id
// of the symmetric key used to decrypt the message. // of the symmetric key used to decrypt the message.
// It fails if decryption of the message fails or if the message is corrupted // It fails if decryption of the message fails or if the message is corrupted
func (p *Pss) processSym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, *PssAddress, error) { func (p *Pss) processSym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, PssAddress, error) {
metrics.GetOrRegisterCounter("pss.process.sym", nil).Inc(1) metrics.GetOrRegisterCounter("pss.process.sym", nil).Inc(1)
for i := p.symKeyDecryptCacheCursor; i > p.symKeyDecryptCacheCursor-cap(p.symKeyDecryptCache) && i > 0; i-- { for i := p.symKeyDecryptCacheCursor; i > p.symKeyDecryptCacheCursor-cap(p.symKeyDecryptCache) && i > 0; i-- {
@ -677,7 +685,7 @@ func (p *Pss) processSym(envelope *whisper.Envelope) (*whisper.ReceivedMessage,
// encapsulating the decrypted message, and the byte representation of // encapsulating the decrypted message, and the byte representation of
// the public key used to decrypt the message. // the public key used to decrypt the message.
// It fails if decryption of message fails, or if the message is corrupted // It fails if decryption of message fails, or if the message is corrupted
func (p *Pss) processAsym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, *PssAddress, error) { func (p *Pss) processAsym(envelope *whisper.Envelope) (*whisper.ReceivedMessage, string, PssAddress, error) {
metrics.GetOrRegisterCounter("pss.process.asym", nil).Inc(1) metrics.GetOrRegisterCounter("pss.process.asym", nil).Inc(1)
recvmsg, err := envelope.OpenAsymmetric(p.privateKey) recvmsg, err := envelope.OpenAsymmetric(p.privateKey)
@ -689,7 +697,7 @@ func (p *Pss) processAsym(envelope *whisper.Envelope) (*whisper.ReceivedMessage,
return nil, "", nil, fmt.Errorf("invalid message") return nil, "", nil, fmt.Errorf("invalid message")
} }
pubkeyid := common.ToHex(crypto.FromECDSAPub(recvmsg.Src)) pubkeyid := common.ToHex(crypto.FromECDSAPub(recvmsg.Src))
var from *PssAddress var from PssAddress
p.pubKeyPoolMu.Lock() p.pubKeyPoolMu.Lock()
if p.pubKeyPool[pubkeyid][Topic(envelope.Topic)] != nil { if p.pubKeyPool[pubkeyid][Topic(envelope.Topic)] != nil {
from = p.pubKeyPool[pubkeyid][Topic(envelope.Topic)].address from = p.pubKeyPool[pubkeyid][Topic(envelope.Topic)].address
@ -751,6 +759,9 @@ func (p *Pss) enqueue(msg *PssMsg) error {
// //
// Will fail if raw messages are disallowed // Will fail if raw messages are disallowed
func (p *Pss) SendRaw(address PssAddress, topic Topic, msg []byte) error { func (p *Pss) SendRaw(address PssAddress, topic Topic, msg []byte) error {
if err := validateAddress(address); err != nil {
return err
}
pssMsgParams := &msgParams{ pssMsgParams := &msgParams{
raw: true, raw: true,
} }
@ -770,8 +781,10 @@ func (p *Pss) SendRaw(address PssAddress, topic Topic, msg []byte) error {
// if we have a proxhandler on this topic // if we have a proxhandler on this topic
// also deliver message to ourselves // also deliver message to ourselves
if p.isSelfPossibleRecipient(pssMsg, true) && p.topicHandlerCaps[topic].prox { if _, ok := p.topicHandlerCaps[topic]; ok {
return p.process(pssMsg, true, true) if p.isSelfPossibleRecipient(pssMsg, true) && p.topicHandlerCaps[topic].prox {
return p.process(pssMsg, true, true)
}
} }
return nil return nil
} }
@ -789,11 +802,8 @@ func (p *Pss) SendSym(symkeyid string, topic Topic, msg []byte) error {
p.symKeyPoolMu.Unlock() p.symKeyPoolMu.Unlock()
if !ok { if !ok {
return fmt.Errorf("invalid topic '%s' for symkey '%s'", topic.String(), symkeyid) return fmt.Errorf("invalid topic '%s' for symkey '%s'", topic.String(), symkeyid)
} else if psp.address == nil {
return fmt.Errorf("no address hint for topic '%s' symkey '%s'", topic.String(), symkeyid)
} }
err = p.send(*psp.address, topic, msg, false, symkey) return p.send(psp.address, topic, msg, false, symkey)
return err
} }
// Send a message using asymmetric encryption // Send a message using asymmetric encryption
@ -808,13 +818,8 @@ func (p *Pss) SendAsym(pubkeyid string, topic Topic, msg []byte) error {
p.pubKeyPoolMu.Unlock() p.pubKeyPoolMu.Unlock()
if !ok { if !ok {
return fmt.Errorf("invalid topic '%s' for pubkey '%s'", topic.String(), pubkeyid) return fmt.Errorf("invalid topic '%s' for pubkey '%s'", topic.String(), pubkeyid)
} else if psp.address == nil {
return fmt.Errorf("no address hint for topic '%s' pubkey '%s'", topic.String(), pubkeyid)
} }
go func() { return p.send(psp.address, topic, msg, true, common.FromHex(pubkeyid))
p.send(*psp.address, topic, msg, true, common.FromHex(pubkeyid))
}()
return nil
} }
// Send is payload agnostic, and will accept any byte slice as payload // Send is payload agnostic, and will accept any byte slice as payload
@ -886,68 +891,97 @@ func (p *Pss) send(to []byte, topic Topic, msg []byte, asymmetric bool, key []by
return nil return nil
} }
// Forwards a pss message to the peer(s) closest to the to recipient address in the PssMsg struct // sendFunc is a helper function that tries to send a message and returns true on success.
// The recipient address can be of any length, and the byte slice will be matched to the MSB slice // It is set here for usage in production, and optionally overridden in tests.
// of the peer address of the equivalent length. var sendFunc func(p *Pss, sp *network.Peer, msg *PssMsg) bool = sendMsg
// tries to send a message, returns true if successful
func sendMsg(p *Pss, sp *network.Peer, msg *PssMsg) bool {
var isPssEnabled bool
info := sp.Info()
for _, capability := range info.Caps {
if capability == p.capstring {
isPssEnabled = true
break
}
}
if !isPssEnabled {
log.Error("peer doesn't have matching pss capabilities, skipping", "peer", info.Name, "caps", info.Caps)
return false
}
// get the protocol peer from the forwarding peer cache
p.fwdPoolMu.RLock()
pp := p.fwdPool[sp.Info().ID]
p.fwdPoolMu.RUnlock()
err := pp.Send(context.TODO(), msg)
if err != nil {
metrics.GetOrRegisterCounter("pss.pp.send.error", nil).Inc(1)
log.Error(err.Error())
}
return err == nil
}
// Forwards a pss message to the peer(s) based on recipient address according to the algorithm
// described below. The recipient address can be of any length, and the byte slice will be matched
// to the MSB slice of the peer address of the equivalent length.
//
// If the recipient address (or partial address) is within the neighbourhood depth of the forwarding
// node, then it will be forwarded to all the nearest neighbours of the forwarding node. In case of
// partial address, it should be forwarded to all the peers matching the partial address, if there
// are any; otherwise only to one peer, closest to the recipient address. In any case, if the message
// forwarding fails, the node should try to forward it to the next best peer, until the message is
// successfully forwarded to at least one peer.
func (p *Pss) forward(msg *PssMsg) error { func (p *Pss) forward(msg *PssMsg) error {
metrics.GetOrRegisterCounter("pss.forward", nil).Inc(1) metrics.GetOrRegisterCounter("pss.forward", nil).Inc(1)
sent := 0 // number of successful sends
to := make([]byte, addressLength) to := make([]byte, addressLength)
copy(to[:len(msg.To)], msg.To) copy(to[:len(msg.To)], msg.To)
neighbourhoodDepth := p.Kademlia.NeighbourhoodDepth()
// send with kademlia // luminosity is the opposite of darkness. the more bytes are removed from the address, the higher is darkness,
// find the closest peer to the recipient and attempt to send // but the luminosity is less. here luminosity equals the number of bits given in the destination address.
sent := 0 luminosityRadius := len(msg.To) * 8
p.Kademlia.EachConn(to, 256, func(sp *network.Peer, po int, isproxbin bool) bool {
info := sp.Info()
// check if the peer is running pss // proximity order function matching up to neighbourhoodDepth bits (po <= neighbourhoodDepth)
var ispss bool pof := pot.DefaultPof(neighbourhoodDepth)
for _, cap := range info.Caps {
if cap == p.capstring { // soft threshold for msg broadcast
ispss = true broadcastThreshold, _ := pof(to, p.BaseAddr(), 0)
break if broadcastThreshold > luminosityRadius {
broadcastThreshold = luminosityRadius
}
var onlySendOnce bool // indicates if the message should only be sent to one peer with closest address
// if measured from the recipient address as opposed to the base address (see Kademlia.EachConn
// call below), then peers that fall in the same proximity bin as recipient address will appear
// [at least] one bit closer, but only if these additional bits are given in the recipient address.
if broadcastThreshold < luminosityRadius && broadcastThreshold < neighbourhoodDepth {
broadcastThreshold++
onlySendOnce = true
}
p.Kademlia.EachConn(to, addressLength*8, func(sp *network.Peer, po int, _ bool) bool {
if po < broadcastThreshold && sent > 0 {
return false // stop iterating
}
if sendFunc(p, sp, msg) {
sent++
if onlySendOnce {
return false
}
if po == addressLength*8 {
// stop iterating if successfully sent to the exact recipient (perfect match of full address)
return false
} }
} }
if !ispss { return true
log.Trace("peer doesn't have matching pss capabilities, skipping", "peer", info.Name, "caps", info.Caps)
return true
}
// get the protocol peer from the forwarding peer cache
sendMsg := fmt.Sprintf("MSG TO %x FROM %x VIA %x", to, p.BaseAddr(), sp.Address())
p.fwdPoolMu.RLock()
pp := p.fwdPool[sp.Info().ID]
p.fwdPoolMu.RUnlock()
// attempt to send the message
err := pp.Send(context.TODO(), msg)
if err != nil {
metrics.GetOrRegisterCounter("pss.pp.send.error", nil).Inc(1)
log.Error(err.Error())
return true
}
sent++
log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg))
// continue forwarding if:
// - if the peer is end recipient but the full address has not been disclosed
// - if the peer address matches the partial address fully
// - if the peer is in proxbin
if len(msg.To) < addressLength && bytes.Equal(msg.To, sp.Address()[:len(msg.To)]) {
log.Trace(fmt.Sprintf("Pss keep forwarding: Partial address + full partial match"))
return true
} else if isproxbin {
log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ToHex(sp.Address())))
return true
}
// at this point we stop forwarding, and the state is as follows:
// - the peer is end recipient and we have full address
// - we are not in proxbin (directed routing)
// - partial addresses don't fully match
return false
}) })
// if we failed to send to anyone, re-insert message in the send-queue
if sent == 0 { if sent == 0 {
log.Debug("unable to forward to any peers") log.Debug("unable to forward to any peers")
if err := p.enqueue(msg); err != nil { if err := p.enqueue(msg); err != nil {
@ -1034,3 +1068,10 @@ func (p *Pss) digestBytes(msg []byte) pssDigest {
copy(digest[:], key[:digestLength]) copy(digest[:], key[:digestLength])
return digest return digest
} }
func validateAddress(addr PssAddress) error {
if len(addr) > addressLength {
return errors.New("address too long")
}
return nil
}

View file

@ -407,7 +407,7 @@ func TestProxShortCircuit(t *testing.T) {
// try the same prox message with sym and asym send // try the same prox message with sym and asym send
proxAddrPss := PssAddress(proxMessageAddress) proxAddrPss := PssAddress(proxMessageAddress)
symKeyId, err := ps.GenerateSymmetricKey(topic, &proxAddrPss, true) symKeyId, err := ps.GenerateSymmetricKey(topic, proxAddrPss, true)
go func() { go func() {
err := ps.SendSym(symKeyId, topic, []byte("baz")) err := ps.SendSym(symKeyId, topic, []byte("baz"))
if err != nil { if err != nil {
@ -424,7 +424,7 @@ func TestProxShortCircuit(t *testing.T) {
t.Fatal("sym timeout") t.Fatal("sym timeout")
} }
err = ps.SetPeerPublicKey(&privKey.PublicKey, topic, &proxAddrPss) err = ps.SetPeerPublicKey(&privKey.PublicKey, topic, proxAddrPss)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -786,14 +786,14 @@ func TestKeys(t *testing.T) {
copy(addr, network.RandomAddr().Over()) copy(addr, network.RandomAddr().Over())
outkey := network.RandomAddr().Over() outkey := network.RandomAddr().Over()
topicobj := BytesToTopic([]byte("foo:42")) topicobj := BytesToTopic([]byte("foo:42"))
ps.SetPeerPublicKey(&theirprivkey.PublicKey, topicobj, &addr) ps.SetPeerPublicKey(&theirprivkey.PublicKey, topicobj, addr)
outkeyid, err := ps.SetSymmetricKey(outkey, topicobj, &addr, false) outkeyid, err := ps.SetSymmetricKey(outkey, topicobj, addr, false)
if err != nil { if err != nil {
t.Fatalf("failed to set 'our' outgoing symmetric key") t.Fatalf("failed to set 'our' outgoing symmetric key")
} }
// make a symmetric key that we will send to peer for encrypting messages to us // make a symmetric key that we will send to peer for encrypting messages to us
inkeyid, err := ps.GenerateSymmetricKey(topicobj, &addr, true) inkeyid, err := ps.GenerateSymmetricKey(topicobj, addr, true)
if err != nil { if err != nil {
t.Fatalf("failed to set 'our' incoming symmetric key") t.Fatalf("failed to set 'our' incoming symmetric key")
} }
@ -816,8 +816,8 @@ func TestKeys(t *testing.T) {
// check that the key is stored in the peerpool // check that the key is stored in the peerpool
psp := ps.symKeyPool[inkeyid][topicobj] psp := ps.symKeyPool[inkeyid][topicobj]
if psp.address != &addr { if !bytes.Equal(psp.address, addr) {
t.Fatalf("inkey address does not match; %p != %p", psp.address, &addr) t.Fatalf("inkey address does not match; %p != %p", psp.address, addr)
} }
} }
@ -1008,6 +1008,34 @@ func TestRawAllow(t *testing.T) {
} }
} }
// BELOW HERE ARE TESTS USING THE SIMULATION FRAMEWORK
// tests that the API layer can handle edge case values
func TestApi(t *testing.T) {
clients, err := setupNetwork(2, true)
if err != nil {
t.Fatal(err)
}
topic := "0xdeadbeef"
err = clients[0].Call(nil, "pss_sendRaw", "0x", topic, "0x666f6f")
if err != nil {
t.Fatal(err)
}
err = clients[0].Call(nil, "pss_sendRaw", "0xabcdef", topic, "0x")
if err == nil {
t.Fatal("expected error on empty msg")
}
overflowAddr := [33]byte{}
err = clients[0].Call(nil, "pss_sendRaw", hexutil.Encode(overflowAddr[:]), topic, "0x666f6f")
if err == nil {
t.Fatal("expected error on send too big address")
}
}
// verifies that nodes can send and receive raw (verbatim) messages // verifies that nodes can send and receive raw (verbatim) messages
func TestSendRaw(t *testing.T) { func TestSendRaw(t *testing.T) {
t.Run("32", testSendRaw) t.Run("32", testSendRaw)
@ -1668,7 +1696,7 @@ func benchmarkSymKeySend(b *testing.B) {
topic := BytesToTopic([]byte("foo")) topic := BytesToTopic([]byte("foo"))
to := make(PssAddress, 32) to := make(PssAddress, 32)
copy(to[:], network.RandomAddr().Over()) copy(to[:], network.RandomAddr().Over())
symkeyid, err := ps.GenerateSymmetricKey(topic, &to, true) symkeyid, err := ps.GenerateSymmetricKey(topic, to, true)
if err != nil { if err != nil {
b.Fatalf("could not generate symkey: %v", err) b.Fatalf("could not generate symkey: %v", err)
} }
@ -1676,7 +1704,7 @@ func benchmarkSymKeySend(b *testing.B) {
if err != nil { if err != nil {
b.Fatalf("could not retrieve symkey: %v", err) b.Fatalf("could not retrieve symkey: %v", err)
} }
ps.SetSymmetricKey(symkey, topic, &to, false) ps.SetSymmetricKey(symkey, topic, to, false)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
@ -1712,7 +1740,7 @@ func benchmarkAsymKeySend(b *testing.B) {
topic := BytesToTopic([]byte("foo")) topic := BytesToTopic([]byte("foo"))
to := make(PssAddress, 32) to := make(PssAddress, 32)
copy(to[:], network.RandomAddr().Over()) copy(to[:], network.RandomAddr().Over())
ps.SetPeerPublicKey(&privkey.PublicKey, topic, &to) ps.SetPeerPublicKey(&privkey.PublicKey, topic, to)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
ps.SendAsym(common.ToHex(crypto.FromECDSAPub(&privkey.PublicKey)), topic, msg) ps.SendAsym(common.ToHex(crypto.FromECDSAPub(&privkey.PublicKey)), topic, msg)
@ -1761,7 +1789,7 @@ func benchmarkSymkeyBruteforceChangeaddr(b *testing.B) {
for i := 0; i < int(keycount); i++ { for i := 0; i < int(keycount); i++ {
to := make(PssAddress, 32) to := make(PssAddress, 32)
copy(to[:], network.RandomAddr().Over()) copy(to[:], network.RandomAddr().Over())
keyid, err = ps.GenerateSymmetricKey(topic, &to, true) keyid, err = ps.GenerateSymmetricKey(topic, to, true)
if err != nil { if err != nil {
b.Fatalf("cant generate symkey #%d: %v", i, err) b.Fatalf("cant generate symkey #%d: %v", i, err)
} }
@ -1843,7 +1871,7 @@ func benchmarkSymkeyBruteforceSameaddr(b *testing.B) {
topic := BytesToTopic([]byte("foo")) topic := BytesToTopic([]byte("foo"))
for i := 0; i < int(keycount); i++ { for i := 0; i < int(keycount); i++ {
copy(addr[i], network.RandomAddr().Over()) copy(addr[i], network.RandomAddr().Over())
keyid, err = ps.GenerateSymmetricKey(topic, &addr[i], true) keyid, err = ps.GenerateSymmetricKey(topic, addr[i], true)
if err != nil { if err != nil {
b.Fatalf("cant generate symkey #%d: %v", i, err) b.Fatalf("cant generate symkey #%d: %v", i, err)
} }
@ -2044,12 +2072,13 @@ func NewAPITest(ps *Pss) *APITest {
return &APITest{Pss: ps} return &APITest{Pss: ps}
} }
func (apitest *APITest) SetSymKeys(pubkeyid string, recvsymkey []byte, sendsymkey []byte, limit uint16, topic Topic, to PssAddress) ([2]string, error) { func (apitest *APITest) SetSymKeys(pubkeyid string, recvsymkey []byte, sendsymkey []byte, limit uint16, topic Topic, to hexutil.Bytes) ([2]string, error) {
recvsymkeyid, err := apitest.SetSymmetricKey(recvsymkey, topic, &to, true)
recvsymkeyid, err := apitest.SetSymmetricKey(recvsymkey, topic, PssAddress(to), true)
if err != nil { if err != nil {
return [2]string{}, err return [2]string{}, err
} }
sendsymkeyid, err := apitest.SetSymmetricKey(sendsymkey, topic, &to, false) sendsymkeyid, err := apitest.SetSymmetricKey(sendsymkey, topic, PssAddress(to), false)
if err != nil { if err != nil {
return [2]string{}, err return [2]string{}, err
} }

View file

@ -1,28 +0,0 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package swarm
type Voidstore struct {
}
func (self Voidstore) Load(string) ([]byte, error) {
return nil, nil
}
func (self Voidstore) Save(string, []byte) error {
return nil
}

View file

@ -28,9 +28,6 @@ import (
// ErrNotFound is returned when no results are returned from the database // ErrNotFound is returned when no results are returned from the database
var ErrNotFound = errors.New("ErrorNotFound") var ErrNotFound = errors.New("ErrorNotFound")
// ErrInvalidArgument is returned when the argument type does not match the expected type
var ErrInvalidArgument = errors.New("ErrorInvalidArgument")
// Store defines methods required to get, set, delete values for different keys // Store defines methods required to get, set, delete values for different keys
// and close the underlying resources. // and close the underlying resources.
type Store interface { type Store interface {

View file

@ -65,10 +65,6 @@ If all is well it is possible to implement this by simply composing readers so t
The hashing itself does use extra copies and allocation though, since it does need it. The hashing itself does use extra copies and allocation though, since it does need it.
*/ */
var (
errAppendOppNotSuported = errors.New("Append operation not supported")
)
type ChunkerParams struct { type ChunkerParams struct {
chunkSize int64 chunkSize int64
hashSize int64 hashSize int64
@ -99,7 +95,6 @@ type TreeChunker struct {
ctx context.Context ctx context.Context
branches int64 branches int64
hashFunc SwarmHasher
dataSize int64 dataSize int64
data io.Reader data io.Reader
// calculated // calculated
@ -365,10 +360,6 @@ func (tc *TreeChunker) runWorker(ctx context.Context) {
}() }()
} }
func (tc *TreeChunker) Append() (Address, func(), error) {
return nil, nil, errAppendOppNotSuported
}
// LazyChunkReader implements LazySectionReader // LazyChunkReader implements LazySectionReader
type LazyChunkReader struct { type LazyChunkReader struct {
ctx context.Context ctx context.Context
@ -411,7 +402,6 @@ func (r *LazyChunkReader) Size(ctx context.Context, quitC chan bool) (n int64, e
log.Debug("lazychunkreader.size", "addr", r.addr) log.Debug("lazychunkreader.size", "addr", r.addr)
if r.chunkData == nil { if r.chunkData == nil {
startTime := time.Now() startTime := time.Now()
chunkData, err := r.getter.Get(cctx, Reference(r.addr)) chunkData, err := r.getter.Get(cctx, Reference(r.addr))
if err != nil { if err != nil {
@ -420,13 +410,8 @@ func (r *LazyChunkReader) Size(ctx context.Context, quitC chan bool) (n int64, e
} }
metrics.GetOrRegisterResettingTimer("lcr.getter.get", nil).UpdateSince(startTime) metrics.GetOrRegisterResettingTimer("lcr.getter.get", nil).UpdateSince(startTime)
r.chunkData = chunkData r.chunkData = chunkData
s := r.chunkData.Size()
log.Debug("lazychunkreader.size", "key", r.addr, "size", s)
if s < 0 {
return 0, errors.New("corrupt size")
}
return int64(s), nil
} }
s := r.chunkData.Size() s := r.chunkData.Size()
log.Debug("lazychunkreader.size", "key", r.addr, "size", s) log.Debug("lazychunkreader.size", "key", r.addr, "size", s)

View file

@ -179,8 +179,9 @@ func testStoreCorrect(m ChunkStore, n int, chunksize int64, t *testing.T) {
return fmt.Errorf("key does not match retrieved chunk Address") return fmt.Errorf("key does not match retrieved chunk Address")
} }
hasher := MakeHashFunc(DefaultHash)() hasher := MakeHashFunc(DefaultHash)()
hasher.ResetWithLength(chunk.SpanBytes()) data := chunk.Data()
hasher.Write(chunk.Payload()) hasher.ResetWithLength(data[:8])
hasher.Write(data[8:])
exp := hasher.Sum(nil) exp := hasher.Sum(nil)
if !bytes.Equal(h, exp) { if !bytes.Equal(h, exp) {
return fmt.Errorf("key is not hash of chunk data") return fmt.Errorf("key is not hash of chunk data")

View file

@ -64,16 +64,6 @@ func (db *LDBDatabase) Delete(key []byte) error {
return db.db.Delete(key, nil) return db.db.Delete(key, nil)
} }
func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.Get([]byte("LTD"))
if len(data) == 0 {
data = []byte{0x0}
}
return data
}
func (db *LDBDatabase) NewIterator() iterator.Iterator { func (db *LDBDatabase) NewIterator() iterator.Iterator {
metrics.GetOrRegisterCounter("ldbdatabase.newiterator", nil).Inc(1) metrics.GetOrRegisterCounter("ldbdatabase.newiterator", nil).Inc(1)

View file

@ -23,23 +23,15 @@ import (
const ( const (
ErrInit = iota ErrInit = iota
ErrNotFound ErrNotFound
ErrIO
ErrUnauthorized ErrUnauthorized
ErrInvalidValue ErrInvalidValue
ErrDataOverflow ErrDataOverflow
ErrNothingToReturn ErrNothingToReturn
ErrCorruptData
ErrInvalidSignature ErrInvalidSignature
ErrNotSynced ErrNotSynced
ErrPeriodDepth
ErrCnt
) )
var ( var (
ErrChunkNotFound = errors.New("chunk not found") ErrChunkNotFound = errors.New("chunk not found")
ErrFetching = errors.New("chunk still fetching") ErrChunkInvalid = errors.New("invalid chunk")
ErrChunkInvalid = errors.New("invalid chunk")
ErrChunkForward = errors.New("cannot forward")
ErrChunkUnavailable = errors.New("chunk unavailable")
ErrChunkTimeout = errors.New("timeout")
) )

View file

@ -23,7 +23,6 @@ import (
"context" "context"
"fmt" "fmt"
"sync" "sync"
"time"
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
@ -32,12 +31,10 @@ import (
) )
type Handler struct { type Handler struct {
chunkStore *storage.NetStore chunkStore *storage.NetStore
HashSize int HashSize int
cache map[uint64]*cacheEntry cache map[uint64]*cacheEntry
cacheLock sync.RWMutex cacheLock sync.RWMutex
storeTimeout time.Duration
queryMaxPeriods uint32
} }
// HandlerParams pass parameters to the Handler constructor NewHandler // HandlerParams pass parameters to the Handler constructor NewHandler

View file

@ -40,7 +40,6 @@ var (
} }
cleanF func() cleanF func()
subtopicName = "føø.bar" subtopicName = "føø.bar"
hashfunc = storage.MakeHashFunc(storage.DefaultHash)
) )
func init() { func init() {

View file

@ -17,7 +17,6 @@
package feed package feed
import ( import (
"encoding/binary"
"encoding/json" "encoding/json"
"time" "time"
) )
@ -30,32 +29,11 @@ type Timestamp struct {
Time uint64 `json:"time"` // Unix epoch timestamp, in seconds Time uint64 `json:"time"` // Unix epoch timestamp, in seconds
} }
// 8 bytes uint64 Time
const timestampLength = 8
// timestampProvider interface describes a source of timestamp information // timestampProvider interface describes a source of timestamp information
type timestampProvider interface { type timestampProvider interface {
Now() Timestamp // returns the current timestamp information Now() Timestamp // returns the current timestamp information
} }
// binaryGet populates the timestamp structure from the given byte slice
func (t *Timestamp) binaryGet(data []byte) error {
if len(data) != timestampLength {
return NewError(ErrCorruptData, "timestamp data has the wrong size")
}
t.Time = binary.LittleEndian.Uint64(data[:8])
return nil
}
// binaryPut Serializes a Timestamp to a byte slice
func (t *Timestamp) binaryPut(data []byte) error {
if len(data) != timestampLength {
return NewError(ErrCorruptData, "timestamp data has the wrong size")
}
binary.LittleEndian.PutUint64(data, t.Time)
return nil
}
// UnmarshalJSON implements the json.Unmarshaller interface // UnmarshalJSON implements the json.Unmarshaller interface
func (t *Timestamp) UnmarshalJSON(data []byte) error { func (t *Timestamp) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Time) return json.Unmarshal(data, &t.Time)

View file

@ -248,10 +248,6 @@ func U64ToBytes(val uint64) []byte {
return data return data
} }
func (s *LDBStore) updateIndexAccess(index *dpaDBIndex) {
index.Access = s.accessCnt
}
func getIndexKey(hash Address) []byte { func getIndexKey(hash Address) []byte {
hashSize := len(hash) hashSize := len(hash)
key := make([]byte, hashSize+1) key := make([]byte, hashSize+1)
@ -777,18 +773,6 @@ func (s *LDBStore) BinIndex(po uint8) uint64 {
return s.bucketCnt[po] return s.bucketCnt[po]
} }
func (s *LDBStore) Size() uint64 {
s.lock.RLock()
defer s.lock.RUnlock()
return s.entryCnt
}
func (s *LDBStore) CurrentStorageIndex() uint64 {
s.lock.RLock()
defer s.lock.RUnlock()
return s.dataIdx
}
// Put adds a chunk to the database, adding indices and incrementing global counters. // Put adds a chunk to the database, adding indices and incrementing global counters.
// If it already exists, it merely increments the access count of the existing entry. // If it already exists, it merely increments the access count of the existing entry.
// Is thread safe // Is thread safe
@ -810,11 +794,11 @@ func (s *LDBStore) Put(ctx context.Context, chunk Chunk) error {
batch := s.batch batch := s.batch
log.Trace("ldbstore.put: s.db.Get", "key", chunk.Address(), "ikey", fmt.Sprintf("%x", ikey)) log.Trace("ldbstore.put: s.db.Get", "key", chunk.Address(), "ikey", fmt.Sprintf("%x", ikey))
idata, err := s.db.Get(ikey) _, err := s.db.Get(ikey)
if err != nil { if err != nil {
s.doPut(chunk, &index, po) s.doPut(chunk, &index, po)
} }
idata = encodeIndex(&index) idata := encodeIndex(&index)
s.batch.Put(ikey, idata) s.batch.Put(ikey, idata)
// add the access-chunkindex index for garbage collection // add the access-chunkindex index for garbage collection

View file

@ -79,14 +79,6 @@ func testPoFunc(k Address) (ret uint8) {
return uint8(Proximity(basekey, k[:])) return uint8(Proximity(basekey, k[:]))
} }
func (db *testDbStore) close() {
db.Close()
err := os.RemoveAll(db.dir)
if err != nil {
panic(err)
}
}
func testDbStoreRandom(n int, chunksize int64, mock bool, t *testing.T) { func testDbStoreRandom(n int, chunksize int64, mock bool, t *testing.T) {
db, cleanup, err := newTestDbStore(mock, true) db, cleanup, err := newTestDbStore(mock, true)
defer cleanup() defer cleanup()
@ -453,7 +445,7 @@ func TestLDBStoreAddRemove(t *testing.T) {
log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt) log.Info("ldbstore", "entrycnt", ldb.entryCnt, "accesscnt", ldb.accessCnt)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
ret, err := ldb.Get(nil, chunks[i].Address()) ret, err := ldb.Get(context.TODO(), chunks[i].Address())
if i%2 == 0 { if i%2 == 0 {
// expect even chunks to be missing // expect even chunks to be missing

View file

@ -57,7 +57,7 @@ func (m *MemStore) Get(_ context.Context, addr Address) (Chunk, error) {
if !ok { if !ok {
return nil, ErrChunkNotFound return nil, ErrChunkNotFound
} }
return c.(*chunk), nil return c.(Chunk), nil
} }
func (m *MemStore) Put(_ context.Context, c Chunk) error { func (m *MemStore) Put(_ context.Context, c Chunk) error {

View file

@ -103,13 +103,6 @@ type Exporter interface {
Export(w io.Writer) (n int, err error) Export(w io.Writer) (n int, err error)
} }
// ImportExporter is an interface for importing and exporting
// mock store data to and from a tar archive.
type ImportExporter interface {
Importer
Exporter
}
// ExportedChunk is the structure that is saved in tar archive for // ExportedChunk is the structure that is saved in tar archive for
// each chunk as JSON-encoded bytes. // each chunk as JSON-encoded bytes.
type ExportedChunk struct { type ExportedChunk struct {

View file

@ -71,11 +71,6 @@ const (
splitTimeout = time.Minute * 5 splitTimeout = time.Minute * 5
) )
const (
DataChunk = 0
TreeChunk = 1
)
type PyramidSplitterParams struct { type PyramidSplitterParams struct {
SplitterParams SplitterParams
getter Getter getter Getter

View file

@ -23,7 +23,6 @@ import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"hash"
"io" "io"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -35,50 +34,10 @@ import (
const MaxPO = 16 const MaxPO = 16
const AddressLength = 32 const AddressLength = 32
type Hasher func() hash.Hash
type SwarmHasher func() SwarmHash type SwarmHasher func() SwarmHash
// Peer is the recorded as Source on the chunk
// should probably not be here? but network should wrap chunk object
type Peer interface{}
type Address []byte type Address []byte
func (a Address) Size() uint {
return uint(len(a))
}
func (a Address) isEqual(y Address) bool {
return bytes.Equal(a, y)
}
func (a Address) bits(i, j uint) uint {
ii := i >> 3
jj := i & 7
if ii >= a.Size() {
return 0
}
if jj+j <= 8 {
return uint((a[ii] >> jj) & ((1 << j) - 1))
}
res := uint(a[ii] >> jj)
jj = 8 - jj
j -= jj
for j != 0 {
ii++
if j < 8 {
res += uint(a[ii]&((1<<j)-1)) << jj
return res
}
res += uint(a[ii]) << jj
jj += 8
j -= 8
}
return res
}
// Proximity(x, y) returns the proximity order of the MSB distance between x and y // Proximity(x, y) returns the proximity order of the MSB distance between x and y
// //
// The distance metric MSB(x, y) of two equal length byte sequences x an y is the // The distance metric MSB(x, y) of two equal length byte sequences x an y is the
@ -112,10 +71,6 @@ func Proximity(one, other []byte) (ret int) {
return MaxPO return MaxPO
} }
func IsZeroAddr(addr Address) bool {
return len(addr) == 0 || bytes.Equal(addr, ZeroAddr)
}
var ZeroAddr = Address(common.Hash{}.Bytes()) var ZeroAddr = Address(common.Hash{}.Bytes())
func MakeHashFunc(hash string) SwarmHasher { func MakeHashFunc(hash string) SwarmHasher {
@ -184,9 +139,6 @@ func (c AddressCollection) Swap(i, j int) {
// Chunk interface implemented by context.Contexts and data chunks // Chunk interface implemented by context.Contexts and data chunks
type Chunk interface { type Chunk interface {
Address() Address Address() Address
Payload() []byte
SpanBytes() []byte
Span() int64
Data() []byte Data() []byte
} }
@ -208,25 +160,10 @@ func (c *chunk) Address() Address {
return c.addr return c.addr
} }
func (c *chunk) SpanBytes() []byte {
return c.sdata[:8]
}
func (c *chunk) Span() int64 {
if c.span == -1 {
c.span = int64(binary.LittleEndian.Uint64(c.sdata[:8]))
}
return c.span
}
func (c *chunk) Data() []byte { func (c *chunk) Data() []byte {
return c.sdata return c.sdata
} }
func (c *chunk) Payload() []byte {
return c.sdata[8:]
}
// String() for pretty printing // String() for pretty printing
func (self *chunk) String() string { func (self *chunk) String() string {
return fmt.Sprintf("Address: %v TreeSize: %v Chunksize: %v", self.addr.Log(), self.span, len(self.sdata)) return fmt.Sprintf("Address: %v TreeSize: %v Chunksize: %v", self.addr.Log(), self.span, len(self.sdata))
@ -322,10 +259,6 @@ func (c ChunkData) Size() uint64 {
return binary.LittleEndian.Uint64(c[:8]) return binary.LittleEndian.Uint64(c[:8])
} }
func (c ChunkData) Data() []byte {
return c[8:]
}
type ChunkValidator interface { type ChunkValidator interface {
Validate(chunk Chunk) bool Validate(chunk Chunk) bool
} }

View file

@ -74,8 +74,6 @@ type Swarm struct {
bzz *network.Bzz // the logistic manager bzz *network.Bzz // the logistic manager
backend chequebook.Backend // simple blockchain Backend backend chequebook.Backend // simple blockchain Backend
privateKey *ecdsa.PrivateKey privateKey *ecdsa.PrivateKey
corsString string
swapEnabled bool
netStore *storage.NetStore netStore *storage.NetStore
sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit
ps *pss.Pss ps *pss.Pss
@ -86,18 +84,6 @@ type Swarm struct {
tracerClose io.Closer tracerClose io.Closer
} }
type SwarmAPI struct {
Api *api.API
Backend chequebook.Backend
}
func (self *Swarm) API() *SwarmAPI {
return &SwarmAPI{
Api: self.api,
Backend: self.backend,
}
}
// creates a new swarm service instance // creates a new swarm service instance
// implements node.Service // implements node.Service
// If mockStore is not nil, it will be used as the storage for chunk data. // If mockStore is not nil, it will be used as the storage for chunk data.
@ -479,14 +465,6 @@ func (self *Swarm) Protocols() (protos []p2p.Protocol) {
return return
} }
func (self *Swarm) RegisterPssProtocol(spec *protocols.Spec, targetprotocol *p2p.Protocol, options *pss.ProtocolParams) (*pss.Protocol, error) {
if !pss.IsActiveProtocol {
return nil, fmt.Errorf("Pss protocols not available (built with !nopssprotocol tag)")
}
topic := pss.ProtocolTopic(spec)
return pss.RegisterProtocol(self.ps, &topic, spec, targetprotocol, options)
}
// implements node.Service // implements node.Service
// APIs returns the RPC API descriptors the Swarm implementation offers // APIs returns the RPC API descriptors the Swarm implementation offers
func (self *Swarm) APIs() []rpc.API { func (self *Swarm) APIs() []rpc.API {
@ -518,6 +496,12 @@ func (self *Swarm) APIs() []rpc.API {
Service: self.sfs, Service: self.sfs,
Public: false, Public: false,
}, },
{
Namespace: "accounting",
Version: protocols.AccountingVersion,
Service: protocols.NewAccountingApi(self.accountingMetrics),
Public: false,
},
} }
apis = append(apis, self.bzz.APIs()...) apis = append(apis, self.bzz.APIs()...)
@ -529,10 +513,6 @@ func (self *Swarm) APIs() []rpc.API {
return apis return apis
} }
func (self *Swarm) Api() *api.API {
return self.api
}
// SetChequebook ensures that the local checquebook is set up on chain. // SetChequebook ensures that the local checquebook is set up on chain.
func (self *Swarm) SetChequebook(ctx context.Context) error { func (self *Swarm) SetChequebook(ctx context.Context) error {
err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path) err := self.config.Swap.SetChequebook(ctx, self.backend, self.config.Path)