mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
9dacf4b9e7
72 changed files with 2305 additions and 1385 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
if index < len(results) {
|
||||||
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
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
|
||||||
|
|
|
||||||
|
|
@ -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++
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
|
||||||
94
p2p/protocols/accounting_api.go
Normal file
94
p2p/protocols/accounting_api.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
return net.connect(ids[l-1], ids[0])
|
||||||
return s.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)
|
||||||
|
}
|
||||||
190
p2p/simulations/connect_test.go
Normal file
190
p2p/simulations/connect_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -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 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 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
134
p2p/simulations/test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,6 @@ func TestParseURI(t *testing.T) {
|
||||||
expectImmutable bool
|
expectImmutable bool
|
||||||
expectList bool
|
expectList bool
|
||||||
expectHash bool
|
expectHash bool
|
||||||
expectDeprecatedRaw bool
|
|
||||||
expectDeprecatedImmutable bool
|
|
||||||
expectValidKey bool
|
expectValidKey bool
|
||||||
expectAddr storage.Address
|
expectAddr storage.Address
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -89,7 +89,6 @@ type Kademlia struct {
|
||||||
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)
|
||||||
|
|
||||||
// cover edge case where more than one farthest nn
|
// the second step is to test for empty bins in order from shallowest to deepest
|
||||||
// AND we only have nn-peers
|
// if an empty bin is found, this will be the actual depth
|
||||||
if lastPo == depth {
|
// we stop iterating if we hit the maxDepth determined in the first step
|
||||||
depth = 0
|
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
|
||||||
|
})
|
||||||
|
|
||||||
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
|
||||||
|
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
|
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, ", ")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
midSameAddress := pot.RandomAddressAt(baseAddress, 4)
|
|
||||||
midSamePeer := newTestDiscoveryPeer(midSameAddress, kad)
|
|
||||||
kad.Off(closerPeer)
|
|
||||||
kad.On(midPeer)
|
|
||||||
kad.On(midSamePeer)
|
|
||||||
depth = kad.NeighbourhoodDepth()
|
|
||||||
if depth != 2 {
|
|
||||||
t.Fatalf("expected depth 2, was %d", depth)
|
|
||||||
}
|
}
|
||||||
|
testNum++
|
||||||
|
|
||||||
kad.Off(fartherPeer)
|
kad.Off(sevenPeers[1])
|
||||||
log.Trace(kad.string())
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
depth = kad.NeighbourhoodDepth()
|
depth = kad.NeighbourhoodDepth()
|
||||||
if depth != 0 {
|
if depth != 6 {
|
||||||
t.Fatalf("expected depth 0, was %d", depth)
|
t.Fatalf("%d expected depth 6, was %d", testNum, depth)
|
||||||
|
}
|
||||||
|
testNum++
|
||||||
|
|
||||||
|
kad.Off(peers[4])
|
||||||
|
depth = kad.NeighbourhoodDepth()
|
||||||
|
if depth != 4 {
|
||||||
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,17 +127,7 @@ func (s *SwarmSyncerServer) SetNextBatch(from, to uint64) ([]byte, uint64, uint6
|
||||||
|
|
||||||
// SwarmSyncerClient
|
// SwarmSyncerClient
|
||||||
type SwarmSyncerClient struct {
|
type SwarmSyncerClient struct {
|
||||||
sessionAt uint64
|
|
||||||
nextC chan struct{}
|
|
||||||
sessionRoot storage.Address
|
|
||||||
sessionReader storage.LazySectionReader
|
|
||||||
retrieveC chan *storage.Chunk
|
|
||||||
storeC chan *storage.Chunk
|
|
||||||
store storage.SyncChunkStore
|
store storage.SyncChunkStore
|
||||||
// chunker storage.Chunker
|
|
||||||
currentRoot storage.Address
|
|
||||||
requestFunc func(chunk *storage.Chunk)
|
|
||||||
end, start uint64
|
|
||||||
peer *Peer
|
peer *Peer
|
||||||
stream Stream
|
stream Stream
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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[:])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
356
swarm/pss/forwarding_test.go
Normal file
356
swarm/pss/forwarding_test.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
175
swarm/pss/pss.go
175
swarm/pss/pss.go
|
|
@ -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,10 +396,12 @@ 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 _, ok := p.topicHandlerCaps[psstopic]; ok {
|
||||||
if !p.topicHandlerCaps[psstopic].raw {
|
if !p.topicHandlerCaps[psstopic].raw {
|
||||||
log.Debug("No handler for raw message", "topic", psstopic)
|
log.Debug("No handler for raw message", "topic", psstopic)
|
||||||
return nil
|
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,9 +781,11 @@ 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 _, ok := p.topicHandlerCaps[topic]; ok {
|
||||||
if p.isSelfPossibleRecipient(pssMsg, true) && p.topicHandlerCaps[topic].prox {
|
if p.isSelfPossibleRecipient(pssMsg, true) && p.topicHandlerCaps[topic].prox {
|
||||||
return p.process(pssMsg, true, true)
|
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
|
||||||
func (p *Pss) forward(msg *PssMsg) error {
|
|
||||||
metrics.GetOrRegisterCounter("pss.forward", nil).Inc(1)
|
|
||||||
|
|
||||||
to := make([]byte, addressLength)
|
// tries to send a message, returns true if successful
|
||||||
copy(to[:len(msg.To)], msg.To)
|
func sendMsg(p *Pss, sp *network.Peer, msg *PssMsg) bool {
|
||||||
|
var isPssEnabled bool
|
||||||
// send with kademlia
|
|
||||||
// find the closest peer to the recipient and attempt to send
|
|
||||||
sent := 0
|
|
||||||
p.Kademlia.EachConn(to, 256, func(sp *network.Peer, po int, isproxbin bool) bool {
|
|
||||||
info := sp.Info()
|
info := sp.Info()
|
||||||
|
for _, capability := range info.Caps {
|
||||||
// check if the peer is running pss
|
if capability == p.capstring {
|
||||||
var ispss bool
|
isPssEnabled = true
|
||||||
for _, cap := range info.Caps {
|
|
||||||
if cap == p.capstring {
|
|
||||||
ispss = true
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ispss {
|
if !isPssEnabled {
|
||||||
log.Trace("peer doesn't have matching pss capabilities, skipping", "peer", info.Name, "caps", info.Caps)
|
log.Error("peer doesn't have matching pss capabilities, skipping", "peer", info.Name, "caps", info.Caps)
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the protocol peer from the forwarding peer cache
|
// 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()
|
p.fwdPoolMu.RLock()
|
||||||
pp := p.fwdPool[sp.Info().ID]
|
pp := p.fwdPool[sp.Info().ID]
|
||||||
p.fwdPoolMu.RUnlock()
|
p.fwdPoolMu.RUnlock()
|
||||||
|
|
||||||
// attempt to send the message
|
|
||||||
err := pp.Send(context.TODO(), msg)
|
err := pp.Send(context.TODO(), msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
metrics.GetOrRegisterCounter("pss.pp.send.error", nil).Inc(1)
|
metrics.GetOrRegisterCounter("pss.pp.send.error", nil).Inc(1)
|
||||||
log.Error(err.Error())
|
log.Error(err.Error())
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
sent++
|
|
||||||
log.Trace(fmt.Sprintf("%v: successfully forwarded", sendMsg))
|
|
||||||
|
|
||||||
// continue forwarding if:
|
return err == nil
|
||||||
// - 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
|
// Forwards a pss message to the peer(s) based on recipient address according to the algorithm
|
||||||
if len(msg.To) < addressLength && bytes.Equal(msg.To, sp.Address()[:len(msg.To)]) {
|
// described below. The recipient address can be of any length, and the byte slice will be matched
|
||||||
log.Trace(fmt.Sprintf("Pss keep forwarding: Partial address + full partial match"))
|
// to the MSB slice of the peer address of the equivalent length.
|
||||||
return true
|
//
|
||||||
} else if isproxbin {
|
// If the recipient address (or partial address) is within the neighbourhood depth of the forwarding
|
||||||
log.Trace(fmt.Sprintf("%x is in proxbin, keep forwarding", common.ToHex(sp.Address())))
|
// node, then it will be forwarded to all the nearest neighbours of the forwarding node. In case of
|
||||||
return true
|
// 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 {
|
||||||
|
metrics.GetOrRegisterCounter("pss.forward", nil).Inc(1)
|
||||||
|
sent := 0 // number of successful sends
|
||||||
|
to := make([]byte, addressLength)
|
||||||
|
copy(to[:len(msg.To)], msg.To)
|
||||||
|
neighbourhoodDepth := p.Kademlia.NeighbourhoodDepth()
|
||||||
|
|
||||||
|
// luminosity is the opposite of darkness. the more bytes are removed from the address, the higher is darkness,
|
||||||
|
// but the luminosity is less. here luminosity equals the number of bits given in the destination address.
|
||||||
|
luminosityRadius := len(msg.To) * 8
|
||||||
|
|
||||||
|
// proximity order function matching up to neighbourhoodDepth bits (po <= neighbourhoodDepth)
|
||||||
|
pof := pot.DefaultPof(neighbourhoodDepth)
|
||||||
|
|
||||||
|
// soft threshold for msg broadcast
|
||||||
|
broadcastThreshold, _ := pof(to, p.BaseAddr(), 0)
|
||||||
|
if broadcastThreshold > luminosityRadius {
|
||||||
|
broadcastThreshold = luminosityRadius
|
||||||
}
|
}
|
||||||
// at this point we stop forwarding, and the state is as follows:
|
|
||||||
// - the peer is end recipient and we have full address
|
var onlySendOnce bool // indicates if the message should only be sent to one peer with closest address
|
||||||
// - we are not in proxbin (directed routing)
|
|
||||||
// - partial addresses don't fully match
|
// 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
|
return false
|
||||||
|
}
|
||||||
|
if po == addressLength*8 {
|
||||||
|
// stop iterating if successfully sent to the exact recipient (perfect match of full address)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
@ -36,8 +35,6 @@ type Handler struct {
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ var (
|
||||||
}
|
}
|
||||||
cleanF func()
|
cleanF func()
|
||||||
subtopicName = "føø.bar"
|
subtopicName = "føø.bar"
|
||||||
hashfunc = storage.MakeHashFunc(storage.DefaultHash)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue