mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
Merge branch 'master' into p2p-testutil-unused-t
This commit is contained in:
commit
97936bc30c
28 changed files with 1126 additions and 863 deletions
191
accounts/abi/reflect_test.go
Normal file
191
accounts/abi/reflect_test.go
Normal file
|
|
@ -0,0 +1,191 @@
|
||||||
|
// Copyright 2019 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 abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type reflectTest struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
struc interface{}
|
||||||
|
want map[string]string
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
var reflectTests = []reflectTest{
|
||||||
|
{
|
||||||
|
name: "OneToOneCorrespondance",
|
||||||
|
args: []string{"fieldA"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MissingFieldsInStruct",
|
||||||
|
args: []string{"fieldA", "fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MoreFieldsInStructThanArgs",
|
||||||
|
args: []string{"fieldA"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
FieldB int
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MissingFieldInArgs",
|
||||||
|
args: []string{"fieldA"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
FieldB int `abi:"fieldB"`
|
||||||
|
}{},
|
||||||
|
err: "struct: abi tag 'fieldB' defined but not found in abi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NoAbiDescriptor",
|
||||||
|
args: []string{"fieldA"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NoArgs",
|
||||||
|
args: []string{},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
}{},
|
||||||
|
err: "struct: abi tag 'fieldA' defined but not found in abi",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DifferentName",
|
||||||
|
args: []string{"fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldB"`
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldB": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DifferentName",
|
||||||
|
args: []string{"fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldB"`
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldB": "FieldA",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MultipleFields",
|
||||||
|
args: []string{"fieldA", "fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
FieldB int `abi:"fieldB"`
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
"fieldB": "FieldB",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "MultipleFieldsABIMissing",
|
||||||
|
args: []string{"fieldA", "fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldA"`
|
||||||
|
FieldB int
|
||||||
|
}{},
|
||||||
|
want: map[string]string{
|
||||||
|
"fieldA": "FieldA",
|
||||||
|
"fieldB": "FieldB",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "NameConflict",
|
||||||
|
args: []string{"fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldB"`
|
||||||
|
FieldB int
|
||||||
|
}{},
|
||||||
|
err: "abi: multiple variables maps to the same abi field 'fieldB'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Underscored",
|
||||||
|
args: []string{"_"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int
|
||||||
|
}{},
|
||||||
|
err: "abi: purely underscored output cannot unpack to struct",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DoubleMapping",
|
||||||
|
args: []string{"fieldB", "fieldC", "fieldA"},
|
||||||
|
struc: struct {
|
||||||
|
FieldA int `abi:"fieldC"`
|
||||||
|
FieldB int
|
||||||
|
}{},
|
||||||
|
err: "abi: multiple outputs mapping to the same struct field 'FieldA'",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AlreadyMapped",
|
||||||
|
args: []string{"fieldB", "fieldB"},
|
||||||
|
struc: struct {
|
||||||
|
FieldB int `abi:"fieldB"`
|
||||||
|
}{},
|
||||||
|
err: "struct: abi tag in 'FieldB' already mapped",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReflectNameToStruct(t *testing.T) {
|
||||||
|
for _, test := range reflectTests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
m, err := mapArgNamesToStructFields(test.args, reflect.ValueOf(test.struc))
|
||||||
|
if len(test.err) > 0 {
|
||||||
|
if err == nil || err.Error() != test.err {
|
||||||
|
t.Fatalf("Invalid error: expected %v, got %v", test.err, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
for fname := range test.want {
|
||||||
|
if m[fname] != test.want[fname] {
|
||||||
|
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -110,6 +110,11 @@ var (
|
||||||
Name: "nostack",
|
Name: "nostack",
|
||||||
Usage: "disable stack output",
|
Usage: "disable stack output",
|
||||||
}
|
}
|
||||||
|
EVMInterpreterFlag = cli.StringFlag{
|
||||||
|
Name: "vm.evm",
|
||||||
|
Usage: "External EVM configuration (default = built-in interpreter)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -133,6 +138,7 @@ func init() {
|
||||||
ReceiverFlag,
|
ReceiverFlag,
|
||||||
DisableMemoryFlag,
|
DisableMemoryFlag,
|
||||||
DisableStackFlag,
|
DisableStackFlag,
|
||||||
|
EVMInterpreterFlag,
|
||||||
}
|
}
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
compileCommand,
|
compileCommand,
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
EVMConfig: vm.Config{
|
EVMConfig: vm.Config{
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
|
||||||
|
EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ func init() {
|
||||||
// Initialize the CLI app and start Geth
|
// Initialize the CLI app and start Geth
|
||||||
app.Action = geth
|
app.Action = geth
|
||||||
app.HideVersion = true // we have a command to print the version
|
app.HideVersion = true // we have a command to print the version
|
||||||
app.Copyright = "Copyright 2013-2018 The go-ethereum Authors"
|
app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
// See chaincmd.go:
|
// See chaincmd.go:
|
||||||
initCommand,
|
initCommand,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
var AppHelpTemplate = `NAME:
|
var AppHelpTemplate = `NAME:
|
||||||
{{.App.Name}} - {{.App.Usage}}
|
{{.App.Name}} - {{.App.Usage}}
|
||||||
|
|
||||||
Copyright 2013-2018 The go-ethereum Authors
|
Copyright 2013-2019 The go-ethereum Authors
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
|
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,12 @@ func main() {
|
||||||
Usage: "feed update generate, upload and sync",
|
Usage: "feed update generate, upload and sync",
|
||||||
Action: cliFeedUploadAndSync,
|
Action: cliFeedUploadAndSync,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "upload_speed",
|
||||||
|
Aliases: []string{"u"},
|
||||||
|
Usage: "measure upload speed",
|
||||||
|
Action: cliUploadSpeed,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(cli.FlagsByName(app.Flags))
|
sort.Sort(cli.FlagsByName(app.Flags))
|
||||||
|
|
|
||||||
96
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
96
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||||
|
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var endpoint string
|
||||||
|
|
||||||
|
//just use the first endpoint
|
||||||
|
func generateEndpoint(scheme string, cluster string, app string, from int) {
|
||||||
|
if cluster == "prod" {
|
||||||
|
endpoint = fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, from)
|
||||||
|
} else {
|
||||||
|
endpoint = fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, from, cluster)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cliUploadSpeed(c *cli.Context) error {
|
||||||
|
log.PrintOrigins(true)
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||||
|
|
||||||
|
metrics.GetOrRegisterCounter("upload-speed", nil).Inc(1)
|
||||||
|
|
||||||
|
errc := make(chan error)
|
||||||
|
go func() {
|
||||||
|
errc <- uploadSpeed(c)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errc:
|
||||||
|
if err != nil {
|
||||||
|
metrics.GetOrRegisterCounter("upload-speed.fail", nil).Inc(1)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
case <-time.After(time.Duration(timeout) * time.Second):
|
||||||
|
metrics.GetOrRegisterCounter("upload-speed.timeout", nil).Inc(1)
|
||||||
|
return fmt.Errorf("timeout after %v sec", timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func uploadSpeed(c *cli.Context) error {
|
||||||
|
defer func(now time.Time) {
|
||||||
|
totalTime := time.Since(now)
|
||||||
|
|
||||||
|
log.Info("total time", "time", totalTime, "kb", filesize)
|
||||||
|
metrics.GetOrRegisterCounter("upload-speed.total-time", nil).Inc(int64(totalTime))
|
||||||
|
}(time.Now())
|
||||||
|
|
||||||
|
generateEndpoint(scheme, cluster, appName, from)
|
||||||
|
seed := int(time.Now().UnixNano() / 1e6)
|
||||||
|
log.Info("uploading to "+endpoint, "seed", seed)
|
||||||
|
|
||||||
|
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
hash, err := upload(&randomBytes, endpoint)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
metrics.GetOrRegisterCounter("upload-speed.upload-time", nil).Inc(int64(time.Since(t1)))
|
||||||
|
|
||||||
|
fhash, err := digest(bytes.NewReader(randomBytes))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
157
cmd/swarm/swarm-snapshot/create.go
Normal file
157
cmd/swarm/swarm-snapshot/create.go
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network"
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/network/simulation"
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// create is used as the entry function for "create" app command.
|
||||||
|
func create(ctx *cli.Context) error {
|
||||||
|
log.PrintOrigins(true)
|
||||||
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(ctx.Int("verbosity")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||||
|
|
||||||
|
if len(ctx.Args()) < 1 {
|
||||||
|
return errors.New("argument should be the filename to verify or write-to")
|
||||||
|
}
|
||||||
|
filename, err := touchPath(ctx.Args()[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return createSnapshot(filename, ctx.Int("nodes"), strings.Split(ctx.String("services"), ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSnapshot creates a new snapshot on filesystem with provided filename,
|
||||||
|
// number of nodes and service names.
|
||||||
|
func createSnapshot(filename string, nodes int, services []string) (err error) {
|
||||||
|
log.Debug("create snapshot", "filename", filename, "nodes", nodes, "services", services)
|
||||||
|
|
||||||
|
sim := simulation.New(map[string]simulation.ServiceFunc{
|
||||||
|
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
||||||
|
addr := network.NewAddr(ctx.Config.Node())
|
||||||
|
kad := network.NewKademlia(addr.Over(), network.NewKadParams())
|
||||||
|
hp := network.NewHiveParams()
|
||||||
|
hp.KeepAliveInterval = time.Duration(200) * time.Millisecond
|
||||||
|
hp.Discovery = true // discovery must be enabled when creating a snapshot
|
||||||
|
|
||||||
|
config := &network.BzzConfig{
|
||||||
|
OverlayAddr: addr.Over(),
|
||||||
|
UnderlayAddr: addr.Under(),
|
||||||
|
HiveParams: hp,
|
||||||
|
}
|
||||||
|
return network.NewBzz(config, kad, nil, nil, nil), nil, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
_, err = sim.AddNodes(nodes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("add nodes: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sim.Net.ConnectNodesRing(nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connect nodes: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||||
|
defer cancelSimRun()
|
||||||
|
if _, err := sim.WaitTillHealthy(ctx); err != nil {
|
||||||
|
return fmt.Errorf("wait for healthy kademlia: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var snap *simulations.Snapshot
|
||||||
|
if len(services) > 0 {
|
||||||
|
// If service names are provided, include them in the snapshot.
|
||||||
|
// But, check if "bzz" service is not among them to remove it
|
||||||
|
// form the snapshot as it exists on snapshot creation.
|
||||||
|
var removeServices []string
|
||||||
|
var wantBzz bool
|
||||||
|
for _, s := range services {
|
||||||
|
if s == "bzz" {
|
||||||
|
wantBzz = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !wantBzz {
|
||||||
|
removeServices = []string{"bzz"}
|
||||||
|
}
|
||||||
|
snap, err = sim.Net.SnapshotWithServices(services, removeServices)
|
||||||
|
} else {
|
||||||
|
snap, err = sim.Net.Snapshot()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create snapshot: %v", err)
|
||||||
|
}
|
||||||
|
jsonsnapshot, err := json.Marshal(snap)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("json encode snapshot: %v", err)
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(filename, jsonsnapshot, 0666)
|
||||||
|
}
|
||||||
|
|
||||||
|
// touchPath creates an empty file and all subdirectories
|
||||||
|
// that are missing.
|
||||||
|
func touchPath(filename string) (string, error) {
|
||||||
|
if path.IsAbs(filename) {
|
||||||
|
if _, err := os.Stat(filename); err == nil {
|
||||||
|
// path exists, overwrite
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d, f := path.Split(filename)
|
||||||
|
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = os.Stat(path.Join(dir, filename))
|
||||||
|
if err == nil {
|
||||||
|
// path exists, overwrite
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dirPath := path.Join(dir, d)
|
||||||
|
filePath := path.Join(dirPath, f)
|
||||||
|
if d != "" {
|
||||||
|
err = os.MkdirAll(dirPath, os.ModeDir)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePath, nil
|
||||||
|
}
|
||||||
138
cmd/swarm/swarm-snapshot/create_test.go
Normal file
138
cmd/swarm/swarm-snapshot/create_test.go
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/simulations"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestSnapshotCreate is a high level e2e test that tests for snapshot generation.
|
||||||
|
// It runs a few "create" commands with different flag values and loads generated
|
||||||
|
// snapshot files to validate their content.
|
||||||
|
func TestSnapshotCreate(t *testing.T) {
|
||||||
|
for _, v := range []struct {
|
||||||
|
name string
|
||||||
|
nodes int
|
||||||
|
services string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "more nodes",
|
||||||
|
nodes: defaultNodes + 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "services",
|
||||||
|
services: "stream,pss,zorglub",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "services with bzz",
|
||||||
|
services: "bzz,pss",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(v.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
file, err := ioutil.TempFile("", "swarm-snapshot")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
|
if err = file.Close(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"create"}
|
||||||
|
if v.nodes > 0 {
|
||||||
|
args = append(args, "--nodes", strconv.Itoa(v.nodes))
|
||||||
|
}
|
||||||
|
if v.services != "" {
|
||||||
|
args = append(args, "--services", v.services)
|
||||||
|
}
|
||||||
|
testCmd := runSnapshot(t, append(args, file.Name())...)
|
||||||
|
|
||||||
|
testCmd.ExpectExit()
|
||||||
|
if code := testCmd.ExitStatus(); code != 0 {
|
||||||
|
t.Fatalf("command exit code %v, expected 0", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := f.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("closing snapshot file", "err", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var snap simulations.Snapshot
|
||||||
|
err = json.Unmarshal(b, &snap)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wantNodes := v.nodes
|
||||||
|
if wantNodes == 0 {
|
||||||
|
wantNodes = defaultNodes
|
||||||
|
}
|
||||||
|
gotNodes := len(snap.Nodes)
|
||||||
|
if gotNodes != wantNodes {
|
||||||
|
t.Errorf("got %v nodes, want %v", gotNodes, wantNodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(snap.Conns) == 0 {
|
||||||
|
t.Error("no connections in a snapshot")
|
||||||
|
}
|
||||||
|
|
||||||
|
var wantServices []string
|
||||||
|
if v.services != "" {
|
||||||
|
wantServices = strings.Split(v.services, ",")
|
||||||
|
} else {
|
||||||
|
wantServices = []string{"bzz"}
|
||||||
|
}
|
||||||
|
// sort service names so they can be comparable
|
||||||
|
// as strings to every node sorted services
|
||||||
|
sort.Strings(wantServices)
|
||||||
|
|
||||||
|
for i, n := range snap.Nodes {
|
||||||
|
gotServices := n.Node.Config.Services
|
||||||
|
sort.Strings(gotServices)
|
||||||
|
if fmt.Sprint(gotServices) != fmt.Sprint(wantServices) {
|
||||||
|
t.Errorf("got services %v for node %v, want %v", gotServices, i, wantServices)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
82
cmd/swarm/swarm-snapshot/main.go
Normal file
82
cmd/swarm/swarm-snapshot/main.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||||
|
|
||||||
|
// default value for "create" command --nodes flag
|
||||||
|
const defaultNodes = 10
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := newApp().Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newApp construct a new instance of Swarm Snapshot Utility.
|
||||||
|
// Method Run is called on it in the main function and in tests.
|
||||||
|
func newApp() (app *cli.App) {
|
||||||
|
app = utils.NewApp(gitCommit, "Swarm Snapshot Utility")
|
||||||
|
|
||||||
|
app.Name = "swarm-snapshot"
|
||||||
|
app.Usage = ""
|
||||||
|
|
||||||
|
// app flags (for all commands)
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "verbosity",
|
||||||
|
Value: 1,
|
||||||
|
Usage: "verbosity level",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Commands = []cli.Command{
|
||||||
|
{
|
||||||
|
Name: "create",
|
||||||
|
Aliases: []string{"c"},
|
||||||
|
Usage: "create a swarm snapshot",
|
||||||
|
Action: create,
|
||||||
|
// Flags only for "create" command.
|
||||||
|
// Allow app flags to be specified after the
|
||||||
|
// command argument.
|
||||||
|
Flags: append(app.Flags,
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "nodes",
|
||||||
|
Value: defaultNodes,
|
||||||
|
Usage: "number of nodes",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "services",
|
||||||
|
Value: "bzz",
|
||||||
|
Usage: "comma separated list of services to boot the nodes with",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
49
cmd/swarm/swarm-snapshot/run_test.go
Normal file
49
cmd/swarm/swarm-snapshot/run_test.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum 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 General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/reexec"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/cmdtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
reexec.Register("swarm-snapshot", func() {
|
||||||
|
if err := newApp().Run(os.Args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSnapshot(t *testing.T, args ...string) *cmdtest.TestCmd {
|
||||||
|
tt := cmdtest.NewTestCmd(t, nil)
|
||||||
|
tt.Run("swarm-snapshot", args...)
|
||||||
|
return tt
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
if reexec.Init() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
@ -108,7 +108,6 @@ type BlockChain struct {
|
||||||
scope event.SubscriptionScope
|
scope event.SubscriptionScope
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
|
|
||||||
mu sync.RWMutex // global mutex for locking chain operations
|
|
||||||
chainmu sync.RWMutex // blockchain insertion lock
|
chainmu sync.RWMutex // blockchain insertion lock
|
||||||
procmu sync.RWMutex // block processor lock
|
procmu sync.RWMutex // block processor lock
|
||||||
|
|
||||||
|
|
@ -281,8 +280,8 @@ func (bc *BlockChain) loadLastState() error {
|
||||||
func (bc *BlockChain) SetHead(head uint64) error {
|
func (bc *BlockChain) SetHead(head uint64) error {
|
||||||
log.Warn("Rewinding blockchain", "target", head)
|
log.Warn("Rewinding blockchain", "target", head)
|
||||||
|
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
// Rewind the header chain, deleting all block bodies until then
|
// Rewind the header chain, deleting all block bodies until then
|
||||||
delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
|
delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
|
||||||
|
|
@ -340,9 +339,9 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// If all checks out, manually set the head block
|
// If all checks out, manually set the head block
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
bc.currentBlock.Store(block)
|
bc.currentBlock.Store(block)
|
||||||
bc.mu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
|
|
||||||
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -420,8 +419,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||||
if err := bc.SetHead(0); err != nil {
|
if err := bc.SetHead(0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
// Prepare the genesis block and reinitialise the chain
|
// Prepare the genesis block and reinitialise the chain
|
||||||
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||||
|
|
@ -468,8 +467,8 @@ func (bc *BlockChain) Export(w io.Writer) error {
|
||||||
|
|
||||||
// ExportN writes a subset of the active chain to the given writer.
|
// ExportN writes a subset of the active chain to the given writer.
|
||||||
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||||
bc.mu.RLock()
|
bc.chainmu.RLock()
|
||||||
defer bc.mu.RUnlock()
|
defer bc.chainmu.RUnlock()
|
||||||
|
|
||||||
if first > last {
|
if first > last {
|
||||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||||
|
|
@ -490,7 +489,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||||
reported = time.Now()
|
reported = time.Now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -646,6 +644,9 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
||||||
|
if receipts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
bc.receiptsCache.Add(hash, receipts)
|
bc.receiptsCache.Add(hash, receipts)
|
||||||
return receipts
|
return receipts
|
||||||
}
|
}
|
||||||
|
|
@ -756,8 +757,8 @@ const (
|
||||||
// Rollback is designed to remove a chain of links from the database that aren't
|
// Rollback is designed to remove a chain of links from the database that aren't
|
||||||
// certain enough to be valid.
|
// certain enough to be valid.
|
||||||
func (bc *BlockChain) Rollback(chain []common.Hash) {
|
func (bc *BlockChain) Rollback(chain []common.Hash) {
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
for i := len(chain) - 1; i >= 0; i-- {
|
for i := len(chain) - 1; i >= 0; i-- {
|
||||||
hash := chain[i]
|
hash := chain[i]
|
||||||
|
|
@ -881,7 +882,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the head fast sync block if better
|
// Update the head fast sync block if better
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
head := blockChain[len(blockChain)-1]
|
head := blockChain[len(blockChain)-1]
|
||||||
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
||||||
currentFastBlock := bc.CurrentFastBlock()
|
currentFastBlock := bc.CurrentFastBlock()
|
||||||
|
|
@ -890,7 +891,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
bc.currentFastBlock.Store(head)
|
bc.currentFastBlock.Store(head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bc.mu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
|
|
||||||
context := []interface{}{
|
context := []interface{}{
|
||||||
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
|
|
@ -924,6 +925,15 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e
|
||||||
|
|
||||||
// WriteBlockWithState writes the block and all associated state to the database.
|
// WriteBlockWithState writes the block and all associated state to the database.
|
||||||
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
||||||
|
bc.chainmu.Lock()
|
||||||
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
|
return bc.writeBlockWithState(block, receipts, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBlockWithState writes the block and all associated state to the database,
|
||||||
|
// but is expects the chain mutex to be held.
|
||||||
|
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
defer bc.wg.Done()
|
defer bc.wg.Done()
|
||||||
|
|
||||||
|
|
@ -933,9 +943,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
return NonStatTy, consensus.ErrUnknownAncestor
|
return NonStatTy, consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
// Make sure no inconsistent state is leaked during insertion
|
// Make sure no inconsistent state is leaked during insertion
|
||||||
bc.mu.Lock()
|
|
||||||
defer bc.mu.Unlock()
|
|
||||||
|
|
||||||
currentBlock := bc.CurrentBlock()
|
currentBlock := bc.CurrentBlock()
|
||||||
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
||||||
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
||||||
|
|
@ -1212,7 +1219,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
proctime := time.Since(start)
|
proctime := time.Since(start)
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
// Write the block to the chain and get the status.
|
||||||
status, err := bc.WriteBlockWithState(block, receipts, state)
|
status, err := bc.writeBlockWithState(block, receipts, state)
|
||||||
t3 := time.Now()
|
t3 := time.Now()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, events, coalescedLogs, err
|
return it.index, events, coalescedLogs, err
|
||||||
|
|
@ -1281,7 +1288,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||||
func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||||
var (
|
var (
|
||||||
externTd *big.Int
|
externTd *big.Int
|
||||||
current = bc.CurrentBlock().NumberU64()
|
current = bc.CurrentBlock()
|
||||||
)
|
)
|
||||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||||
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
// Since we don't import them here, we expect ErrUnknownAncestor for the remaining
|
||||||
|
|
@ -1290,7 +1297,7 @@ func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, [
|
||||||
block, err := it.current(), consensus.ErrPrunedAncestor
|
block, err := it.current(), consensus.ErrPrunedAncestor
|
||||||
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
|
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
|
||||||
// Check the canonical state root for that number
|
// Check the canonical state root for that number
|
||||||
if number := block.NumberU64(); current >= number {
|
if number := block.NumberU64(); current.NumberU64() >= number {
|
||||||
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() {
|
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() {
|
||||||
// This is most likely a shadow-state attack. When a fork is imported into the
|
// This is most likely a shadow-state attack. When a fork is imported into the
|
||||||
// database, and it eventually reaches a block height which is not pruned, we
|
// database, and it eventually reaches a block height which is not pruned, we
|
||||||
|
|
@ -1329,7 +1336,7 @@ func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, [
|
||||||
//
|
//
|
||||||
// If the externTd was larger than our local TD, we now need to reimport the previous
|
// If the externTd was larger than our local TD, we now need to reimport the previous
|
||||||
// blocks to regenerate the required state
|
// blocks to regenerate the required state
|
||||||
localTd := bc.GetTd(bc.CurrentBlock().Hash(), current)
|
localTd := bc.GetTd(current.Hash(), current.NumberU64())
|
||||||
if localTd.Cmp(externTd) > 0 {
|
if localTd.Cmp(externTd) > 0 {
|
||||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().NumberU64(), "sidetd", externTd, "localtd", localTd)
|
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().NumberU64(), "sidetd", externTd, "localtd", localTd)
|
||||||
return it.index, nil, nil, err
|
return it.index, nil, nil, err
|
||||||
|
|
@ -1597,36 +1604,12 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
|
||||||
defer bc.wg.Done()
|
defer bc.wg.Done()
|
||||||
|
|
||||||
whFunc := func(header *types.Header) error {
|
whFunc := func(header *types.Header) error {
|
||||||
bc.mu.Lock()
|
|
||||||
defer bc.mu.Unlock()
|
|
||||||
|
|
||||||
_, err := bc.hc.WriteHeader(header)
|
_, err := bc.hc.WriteHeader(header)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return bc.hc.InsertHeaderChain(chain, whFunc, start)
|
return bc.hc.InsertHeaderChain(chain, whFunc, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeHeader writes a header into the local chain, given that its parent is
|
|
||||||
// already known. If the total difficulty of the newly inserted header becomes
|
|
||||||
// greater than the current known TD, the canonical chain is re-routed.
|
|
||||||
//
|
|
||||||
// Note: This method is not concurrent-safe with inserting blocks simultaneously
|
|
||||||
// into the chain, as side effects caused by reorganisations cannot be emulated
|
|
||||||
// without the real blocks. Hence, writing headers directly should only be done
|
|
||||||
// in two scenarios: pure-header mode of operation (light clients), or properly
|
|
||||||
// separated header/block phases (non-archive clients).
|
|
||||||
func (bc *BlockChain) writeHeader(header *types.Header) error {
|
|
||||||
bc.wg.Add(1)
|
|
||||||
defer bc.wg.Done()
|
|
||||||
|
|
||||||
bc.mu.Lock()
|
|
||||||
defer bc.mu.Unlock()
|
|
||||||
|
|
||||||
_, err := bc.hc.WriteHeader(header)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||||
// header is retrieved from the HeaderChain's internal cache.
|
// header is retrieved from the HeaderChain's internal cache.
|
||||||
func (bc *BlockChain) CurrentHeader() *types.Header {
|
func (bc *BlockChain) CurrentHeader() *types.Header {
|
||||||
|
|
@ -1675,8 +1658,8 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com
|
||||||
//
|
//
|
||||||
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
||||||
func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.RLock()
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.RUnlock()
|
||||||
|
|
||||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,11 +162,11 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
blockchain.reportBlock(block, receipts, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
blockchain.mu.Lock()
|
blockchain.chainmu.Lock()
|
||||||
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
|
||||||
rawdb.WriteBlock(blockchain.db, block)
|
rawdb.WriteBlock(blockchain.db, block)
|
||||||
statedb.Commit(false)
|
statedb.Commit(false)
|
||||||
blockchain.mu.Unlock()
|
blockchain.chainmu.Unlock()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -180,10 +180,10 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||||
blockchain.mu.Lock()
|
blockchain.chainmu.Lock()
|
||||||
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
|
||||||
rawdb.WriteHeader(blockchain.db, header)
|
rawdb.WriteHeader(blockchain.db, header)
|
||||||
blockchain.mu.Unlock()
|
blockchain.chainmu.Unlock()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@ type LightChain struct {
|
||||||
scope event.SubscriptionScope
|
scope event.SubscriptionScope
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
|
|
||||||
mu sync.RWMutex
|
|
||||||
chainmu sync.RWMutex
|
chainmu sync.RWMutex
|
||||||
|
|
||||||
bodyCache *lru.Cache // Cache for the most recent block bodies
|
bodyCache *lru.Cache // Cache for the most recent block bodies
|
||||||
|
|
@ -165,8 +164,8 @@ func (self *LightChain) loadLastState() error {
|
||||||
// SetHead rewinds the local chain to a new head. Everything above the new
|
// SetHead rewinds the local chain to a new head. Everything above the new
|
||||||
// head will be deleted and the new one set.
|
// head will be deleted and the new one set.
|
||||||
func (bc *LightChain) SetHead(head uint64) {
|
func (bc *LightChain) SetHead(head uint64) {
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
bc.hc.SetHead(head, nil)
|
bc.hc.SetHead(head, nil)
|
||||||
bc.loadLastState()
|
bc.loadLastState()
|
||||||
|
|
@ -188,8 +187,8 @@ func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) {
|
||||||
// Dump the entire block chain and purge the caches
|
// Dump the entire block chain and purge the caches
|
||||||
bc.SetHead(0)
|
bc.SetHead(0)
|
||||||
|
|
||||||
bc.mu.Lock()
|
bc.chainmu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
// Prepare the genesis block and reinitialise the chain
|
// Prepare the genesis block and reinitialise the chain
|
||||||
rawdb.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
rawdb.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||||
|
|
@ -315,8 +314,8 @@ func (bc *LightChain) Stop() {
|
||||||
// Rollback is designed to remove a chain of links from the database that aren't
|
// Rollback is designed to remove a chain of links from the database that aren't
|
||||||
// certain enough to be valid.
|
// certain enough to be valid.
|
||||||
func (self *LightChain) Rollback(chain []common.Hash) {
|
func (self *LightChain) Rollback(chain []common.Hash) {
|
||||||
self.mu.Lock()
|
self.chainmu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.chainmu.Unlock()
|
||||||
|
|
||||||
for i := len(chain) - 1; i >= 0; i-- {
|
for i := len(chain) - 1; i >= 0; i-- {
|
||||||
hash := chain[i]
|
hash := chain[i]
|
||||||
|
|
@ -362,19 +361,13 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
|
|
||||||
// Make sure only one thread manipulates the chain at once
|
// Make sure only one thread manipulates the chain at once
|
||||||
self.chainmu.Lock()
|
self.chainmu.Lock()
|
||||||
defer func() {
|
defer self.chainmu.Unlock()
|
||||||
self.chainmu.Unlock()
|
|
||||||
time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation
|
|
||||||
}()
|
|
||||||
|
|
||||||
self.wg.Add(1)
|
self.wg.Add(1)
|
||||||
defer self.wg.Done()
|
defer self.wg.Done()
|
||||||
|
|
||||||
var events []interface{}
|
var events []interface{}
|
||||||
whFunc := func(header *types.Header) error {
|
whFunc := func(header *types.Header) error {
|
||||||
self.mu.Lock()
|
|
||||||
defer self.mu.Unlock()
|
|
||||||
|
|
||||||
status, err := self.hc.WriteHeader(header)
|
status, err := self.hc.WriteHeader(header)
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
|
|
@ -441,8 +434,8 @@ func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []c
|
||||||
//
|
//
|
||||||
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
||||||
func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.RLock()
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.RUnlock()
|
||||||
|
|
||||||
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
|
||||||
}
|
}
|
||||||
|
|
@ -483,8 +476,8 @@ func (self *LightChain) SyncCht(ctx context.Context) bool {
|
||||||
}
|
}
|
||||||
// Retrieve the latest useful header and update to it
|
// Retrieve the latest useful header and update to it
|
||||||
if header, err := GetHeaderByNumber(ctx, self.odr, latest); header != nil && err == nil {
|
if header, err := GetHeaderByNumber(ctx, self.odr, latest); header != nil && err == nil {
|
||||||
self.mu.Lock()
|
self.chainmu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.chainmu.Unlock()
|
||||||
|
|
||||||
// Ensure the chain didn't move past the latest block while retrieving it
|
// Ensure the chain didn't move past the latest block while retrieving it
|
||||||
if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
|
if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() {
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,10 @@ func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
|
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
|
||||||
lightchain.mu.Lock()
|
lightchain.chainmu.Lock()
|
||||||
rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash)))
|
rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash)))
|
||||||
rawdb.WriteHeader(lightchain.chainDb, header)
|
rawdb.WriteHeader(lightchain.chainDb, header)
|
||||||
lightchain.mu.Unlock()
|
lightchain.chainmu.Unlock()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ var (
|
||||||
EIP155Block: big.NewInt(2675000),
|
EIP155Block: big.NewInt(2675000),
|
||||||
EIP158Block: big.NewInt(2675000),
|
EIP158Block: big.NewInt(2675000),
|
||||||
ByzantiumBlock: big.NewInt(4370000),
|
ByzantiumBlock: big.NewInt(4370000),
|
||||||
ConstantinopleBlock: big.NewInt(7080000),
|
ConstantinopleBlock: nil,
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
VersionMinor = 9 // Minor version component of the current release
|
VersionMinor = 8 // Minor version component of the current release
|
||||||
VersionPatch = 0 // Patch version component of the current release
|
VersionPatch = 22 // Patch version component of the current release
|
||||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,20 +26,23 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var searchTimeout = 1 * time.Second
|
const (
|
||||||
|
defaultSearchTimeout = 1 * time.Second
|
||||||
|
// maximum number of forwarded requests (hops), to make sure requests are not
|
||||||
|
// forwarded forever in peer loops
|
||||||
|
maxHopCount uint8 = 20
|
||||||
|
)
|
||||||
|
|
||||||
// Time to consider peer to be skipped.
|
// Time to consider peer to be skipped.
|
||||||
// Also used in stream delivery.
|
// Also used in stream delivery.
|
||||||
var RequestTimeout = 10 * time.Second
|
var RequestTimeout = 10 * time.Second
|
||||||
|
|
||||||
var maxHopCount uint8 = 20 // maximum number of forwarded requests (hops), to make sure requests are not forwarded forever in peer loops
|
|
||||||
|
|
||||||
type RequestFunc func(context.Context, *Request) (*enode.ID, chan struct{}, error)
|
type RequestFunc func(context.Context, *Request) (*enode.ID, chan struct{}, error)
|
||||||
|
|
||||||
// Fetcher is created when a chunk is not found locally. It starts a request handler loop once and
|
// Fetcher is created when a chunk is not found locally. It starts a request handler loop once and
|
||||||
// keeps it alive until all active requests are completed. This can happen:
|
// keeps it alive until all active requests are completed. This can happen:
|
||||||
// 1. either because the chunk is delivered
|
// 1. either because the chunk is delivered
|
||||||
// 2. or becuse the requestor cancelled/timed out
|
// 2. or because the requester cancelled/timed out
|
||||||
// Fetcher self destroys itself after it is completed.
|
// Fetcher self destroys itself after it is completed.
|
||||||
// TODO: cancel all forward requests after termination
|
// TODO: cancel all forward requests after termination
|
||||||
type Fetcher struct {
|
type Fetcher struct {
|
||||||
|
|
@ -47,6 +50,7 @@ type Fetcher struct {
|
||||||
addr storage.Address // the address of the chunk to be fetched
|
addr storage.Address // the address of the chunk to be fetched
|
||||||
offerC chan *enode.ID // channel of sources (peer node id strings)
|
offerC chan *enode.ID // channel of sources (peer node id strings)
|
||||||
requestC chan uint8 // channel for incoming requests (with the hopCount value in it)
|
requestC chan uint8 // channel for incoming requests (with the hopCount value in it)
|
||||||
|
searchTimeout time.Duration
|
||||||
skipCheck bool
|
skipCheck bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +83,7 @@ func (r *Request) SkipPeer(nodeID string) bool {
|
||||||
}
|
}
|
||||||
t, ok := val.(time.Time)
|
t, ok := val.(time.Time)
|
||||||
if ok && time.Now().After(t.Add(RequestTimeout)) {
|
if ok && time.Now().After(t.Add(RequestTimeout)) {
|
||||||
// deadine expired
|
// deadline expired
|
||||||
r.peersToSkip.Delete(nodeID)
|
r.peersToSkip.Delete(nodeID)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -100,9 +104,10 @@ func NewFetcherFactory(request RequestFunc, skipCheck bool) *FetcherFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New contructs a new Fetcher, for the given chunk. All peers in peersToSkip are not requested to
|
// New constructs a new Fetcher, for the given chunk. All peers in peersToSkip
|
||||||
// deliver the given chunk. peersToSkip should always contain the peers which are actively requesting
|
// are not requested to deliver the given chunk. peersToSkip should always
|
||||||
// this chunk, to make sure we don't request back the chunks from them.
|
// contain the peers which are actively requesting this chunk, to make sure we
|
||||||
|
// don't request back the chunks from them.
|
||||||
// The created Fetcher is started and returned.
|
// The created Fetcher is started and returned.
|
||||||
func (f *FetcherFactory) New(ctx context.Context, source storage.Address, peersToSkip *sync.Map) storage.NetFetcher {
|
func (f *FetcherFactory) New(ctx context.Context, source storage.Address, peersToSkip *sync.Map) storage.NetFetcher {
|
||||||
fetcher := NewFetcher(source, f.request, f.skipCheck)
|
fetcher := NewFetcher(source, f.request, f.skipCheck)
|
||||||
|
|
@ -117,6 +122,7 @@ func NewFetcher(addr storage.Address, rf RequestFunc, skipCheck bool) *Fetcher {
|
||||||
protoRequestFunc: rf,
|
protoRequestFunc: rf,
|
||||||
offerC: make(chan *enode.ID),
|
offerC: make(chan *enode.ID),
|
||||||
requestC: make(chan uint8),
|
requestC: make(chan uint8),
|
||||||
|
searchTimeout: defaultSearchTimeout,
|
||||||
skipCheck: skipCheck,
|
skipCheck: skipCheck,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -176,7 +182,7 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
||||||
// loop that keeps the fetching process alive
|
// loop that keeps the fetching process alive
|
||||||
// after every request a timer is set. If this goes off we request again from another peer
|
// after every request a timer is set. If this goes off we request again from another peer
|
||||||
// note that the previous request is still alive and has the chance to deliver, so
|
// note that the previous request is still alive and has the chance to deliver, so
|
||||||
// rerequesting extends the search. ie.,
|
// requesting again extends the search. ie.,
|
||||||
// if a peer we requested from is gone we issue a new request, so the number of active
|
// if a peer we requested from is gone we issue a new request, so the number of active
|
||||||
// requests never decreases
|
// requests never decreases
|
||||||
for {
|
for {
|
||||||
|
|
@ -209,13 +215,13 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
||||||
// search timeout: too much time passed since the last request,
|
// search timeout: too much time passed since the last request,
|
||||||
// extend the search to a new peer if we can find one
|
// extend the search to a new peer if we can find one
|
||||||
case <-waitC:
|
case <-waitC:
|
||||||
log.Trace("search timed out: rerequesting", "request addr", f.addr)
|
log.Trace("search timed out: requesting", "request addr", f.addr)
|
||||||
doRequest = requested
|
doRequest = requested
|
||||||
|
|
||||||
// all Fetcher context closed, can quit
|
// all Fetcher context closed, can quit
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
log.Trace("terminate fetcher", "request addr", f.addr)
|
log.Trace("terminate fetcher", "request addr", f.addr)
|
||||||
// TODO: send cancelations to all peers left over in peers map (i.e., those we requested from)
|
// TODO: send cancellations to all peers left over in peers map (i.e., those we requested from)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,7 +237,7 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
||||||
// if wait channel is not set, set it to a timer
|
// if wait channel is not set, set it to a timer
|
||||||
if requested {
|
if requested {
|
||||||
if wait == nil {
|
if wait == nil {
|
||||||
wait = time.NewTimer(searchTimeout)
|
wait = time.NewTimer(f.searchTimeout)
|
||||||
defer wait.Stop()
|
defer wait.Stop()
|
||||||
waitC = wait.C
|
waitC = wait.C
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -242,8 +248,8 @@ func (f *Fetcher) run(ctx context.Context, peers *sync.Map) {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// reset the timer to go off after searchTimeout
|
// reset the timer to go off after defaultSearchTimeout
|
||||||
wait.Reset(searchTimeout)
|
wait.Reset(f.searchTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
doRequest = false
|
doRequest = false
|
||||||
|
|
|
||||||
|
|
@ -284,15 +284,11 @@ func TestFetcherRetryOnTimeout(t *testing.T) {
|
||||||
requester := newMockRequester()
|
requester := newMockRequester()
|
||||||
addr := make([]byte, 32)
|
addr := make([]byte, 32)
|
||||||
fetcher := NewFetcher(addr, requester.doRequest, true)
|
fetcher := NewFetcher(addr, requester.doRequest, true)
|
||||||
|
// set searchTimeOut to low value so the test is quicker
|
||||||
|
fetcher.searchTimeout = 250 * time.Millisecond
|
||||||
|
|
||||||
peersToSkip := &sync.Map{}
|
peersToSkip := &sync.Map{}
|
||||||
|
|
||||||
// set searchTimeOut to low value so the test is quicker
|
|
||||||
defer func(t time.Duration) {
|
|
||||||
searchTimeout = t
|
|
||||||
}(searchTimeout)
|
|
||||||
searchTimeout = 250 * time.Millisecond
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
@ -359,11 +355,9 @@ func TestFetcherRequestQuitRetriesRequest(t *testing.T) {
|
||||||
addr := make([]byte, 32)
|
addr := make([]byte, 32)
|
||||||
fetcher := NewFetcher(addr, requester.doRequest, true)
|
fetcher := NewFetcher(addr, requester.doRequest, true)
|
||||||
|
|
||||||
// make sure searchTimeout is long so it is sure the request is not retried because of timeout
|
// make sure the searchTimeout is long so it is sure the request is not
|
||||||
defer func(t time.Duration) {
|
// retried because of timeout
|
||||||
searchTimeout = t
|
fetcher.searchTimeout = 10 * time.Second
|
||||||
}(searchTimeout)
|
|
||||||
searchTimeout = 10 * time.Second
|
|
||||||
|
|
||||||
peersToSkip := &sync.Map{}
|
peersToSkip := &sync.Map{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,82 +168,115 @@ func (k *Kademlia) Register(peers ...*BzzAddr) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SuggestPeer returns a known peer for the lowest proximity bin for the
|
// SuggestPeer returns an unconnected peer address as a peer suggestion for connection
|
||||||
// lowest bincount below depth
|
func (k *Kademlia) SuggestPeer() (suggestedPeer *BzzAddr, saturationDepth int, changed bool) {
|
||||||
// naturally if there is an empty row it returns a peer for that
|
|
||||||
func (k *Kademlia) SuggestPeer() (a *BzzAddr, o int, want bool) {
|
|
||||||
k.lock.Lock()
|
k.lock.Lock()
|
||||||
defer k.lock.Unlock()
|
defer k.lock.Unlock()
|
||||||
minsize := k.MinBinSize
|
radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base)
|
||||||
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
// collect undersaturated bins in ascending order of number of connected peers
|
||||||
// if there is a callable neighbour within the current proxBin, connect
|
// and from shallow to deep (ascending order of PO)
|
||||||
// this makes sure nearest neighbour set is fully connected
|
// insert them in a map of bin arrays, keyed with the number of connected peers
|
||||||
var ppo int
|
saturation := make(map[int][]int)
|
||||||
k.addrs.EachNeighbour(k.base, Pof, func(val pot.Val, po int) bool {
|
var lastPO int // the last non-empty PO bin in the iteration
|
||||||
if po < depth {
|
saturationDepth = -1 // the deepest PO such that all shallower bins have >= k.MinBinSize peers
|
||||||
return false
|
var pastDepth bool // whether po of iteration >= depth
|
||||||
}
|
|
||||||
e := val.(*entry)
|
|
||||||
c := k.callable(e)
|
|
||||||
if c {
|
|
||||||
a = e.BzzAddr
|
|
||||||
}
|
|
||||||
ppo = po
|
|
||||||
return !c
|
|
||||||
})
|
|
||||||
if a != nil {
|
|
||||||
log.Trace(fmt.Sprintf("%08x candidate nearest neighbour found: %v (%v)", k.BaseAddr()[:4], a, ppo))
|
|
||||||
return a, 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
var bpo []int
|
|
||||||
prev := -1
|
|
||||||
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool {
|
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool {
|
||||||
prev++
|
// process skipped empty bins
|
||||||
for ; prev < po; prev++ {
|
for ; lastPO < po; lastPO++ {
|
||||||
bpo = append(bpo, prev)
|
// find the lowest unsaturated bin
|
||||||
minsize = 0
|
if saturationDepth == -1 {
|
||||||
|
saturationDepth = lastPO
|
||||||
}
|
}
|
||||||
if size < minsize {
|
// if there is an empty bin, depth is surely passed
|
||||||
bpo = append(bpo, po)
|
pastDepth = true
|
||||||
minsize = size
|
saturation[0] = append(saturation[0], lastPO)
|
||||||
}
|
}
|
||||||
return size > 0 && po < depth
|
lastPO = po + 1
|
||||||
|
// past radius, depth is surely passed
|
||||||
|
if po >= radius {
|
||||||
|
pastDepth = true
|
||||||
|
}
|
||||||
|
// beyond depth the bin is treated as unsaturated even if size >= k.MinBinSize
|
||||||
|
// in order to achieve full connectivity to all neighbours
|
||||||
|
if pastDepth && size >= k.MinBinSize {
|
||||||
|
size = k.MinBinSize - 1
|
||||||
|
}
|
||||||
|
// process non-empty unsaturated bins
|
||||||
|
if size < k.MinBinSize {
|
||||||
|
// find the lowest unsaturated bin
|
||||||
|
if saturationDepth == -1 {
|
||||||
|
saturationDepth = po
|
||||||
|
}
|
||||||
|
saturation[size] = append(saturation[size], po)
|
||||||
|
}
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
// all buckets are full, ie., minsize == k.MinBinSize
|
// to trigger peer requests for peers closer than closest connection, include
|
||||||
if len(bpo) == 0 {
|
// all bins from nearest connection upto nearest address as unsaturated
|
||||||
|
var nearestAddrAt int
|
||||||
|
k.addrs.EachNeighbour(k.base, Pof, func(_ pot.Val, po int) bool {
|
||||||
|
nearestAddrAt = po
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
// including bins as size 0 has the effect that requesting connection
|
||||||
|
// is prioritised over non-empty shallower bins
|
||||||
|
for ; lastPO <= nearestAddrAt; lastPO++ {
|
||||||
|
saturation[0] = append(saturation[0], lastPO)
|
||||||
|
}
|
||||||
|
// all PO bins are saturated, ie., minsize >= k.MinBinSize, no peer suggested
|
||||||
|
if len(saturation) == 0 {
|
||||||
return nil, 0, false
|
return nil, 0, false
|
||||||
}
|
}
|
||||||
// as long as we got candidate peers to connect to
|
// find the first callable peer in the address book
|
||||||
// dont ask for new peers (want = false)
|
// starting from the bins with smallest size proceeding from shallow to deep
|
||||||
// try to select a candidate peer
|
// for each bin (up until neighbourhood radius) we find callable candidate peers
|
||||||
// find the first callable peer
|
for size := 0; size < k.MinBinSize && suggestedPeer == nil; size++ {
|
||||||
nxt := bpo[0]
|
bins, ok := saturation[size]
|
||||||
k.addrs.EachBin(k.base, Pof, nxt, func(po, _ int, f func(func(pot.Val) bool) bool) bool {
|
if !ok {
|
||||||
// for each bin (up until depth) we find callable candidate peers
|
// no bin with this size
|
||||||
if po >= depth {
|
continue
|
||||||
|
}
|
||||||
|
cur := 0
|
||||||
|
curPO := bins[0]
|
||||||
|
k.addrs.EachBin(k.base, Pof, curPO, func(po, _ int, f func(func(pot.Val) bool) bool) bool {
|
||||||
|
curPO = bins[cur]
|
||||||
|
// find the next bin that has size size
|
||||||
|
if curPO == po {
|
||||||
|
cur++
|
||||||
|
} else {
|
||||||
|
// skip bins that have no addresses
|
||||||
|
for ; cur < len(bins) && curPO < po; cur++ {
|
||||||
|
curPO = bins[cur]
|
||||||
|
}
|
||||||
|
if po < curPO {
|
||||||
|
cur--
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// stop if there are no addresses
|
||||||
|
if curPO < po {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return f(func(val pot.Val) bool {
|
}
|
||||||
|
// curPO found
|
||||||
|
// find a callable peer out of the addresses in the unsaturated bin
|
||||||
|
// stop if found
|
||||||
|
f(func(val pot.Val) bool {
|
||||||
e := val.(*entry)
|
e := val.(*entry)
|
||||||
c := k.callable(e)
|
if k.callable(e) {
|
||||||
if c {
|
suggestedPeer = e.BzzAddr
|
||||||
a = e.BzzAddr
|
return false
|
||||||
}
|
}
|
||||||
return !c
|
return true
|
||||||
})
|
})
|
||||||
|
return cur < len(bins) && suggestedPeer == nil
|
||||||
})
|
})
|
||||||
// found a candidate
|
|
||||||
if a != nil {
|
|
||||||
return a, 0, false
|
|
||||||
}
|
}
|
||||||
// no candidate peer found, request for the short bin
|
|
||||||
var changed bool
|
if uint8(saturationDepth) < k.depth {
|
||||||
if uint8(nxt) < k.depth {
|
k.depth = uint8(saturationDepth)
|
||||||
k.depth = uint8(nxt)
|
return suggestedPeer, saturationDepth, true
|
||||||
changed = true
|
|
||||||
}
|
}
|
||||||
return a, nxt, changed
|
return suggestedPeer, 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// On inserts the peer as a kademlia peer into the live peers
|
// On inserts the peer as a kademlia peer into the live peers
|
||||||
|
|
@ -398,29 +431,25 @@ func (k *Kademlia) eachAddr(base []byte, o int, f func(*BzzAddr, int) bool) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NeighbourhoodDepth returns the depth for the pot, see depthForPot
|
||||||
func (k *Kademlia) NeighbourhoodDepth() (depth int) {
|
func (k *Kademlia) NeighbourhoodDepth() (depth int) {
|
||||||
k.lock.RLock()
|
k.lock.RLock()
|
||||||
defer k.lock.RUnlock()
|
defer k.lock.RUnlock()
|
||||||
return depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
return depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
||||||
}
|
}
|
||||||
|
|
||||||
// depthForPot returns the proximity order that defines the distance of
|
// neighbourhoodRadiusForPot returns the neighbourhood radius of the kademlia
|
||||||
// the nearest neighbour set with cardinality >= NeighbourhoodSize
|
// neighbourhood radius encloses the nearest neighbour set with size >= neighbourhoodSize
|
||||||
// if there is altogether less than NeighbourhoodSize peers it returns 0
|
// i.e., neighbourhood radius is the deepest PO such that all bins not shallower altogether
|
||||||
|
// contain at least neighbourhoodSize connected peers
|
||||||
|
// if there is altogether less than neighbourhoodSize peers connected, it returns 0
|
||||||
// caller must hold the lock
|
// caller must hold the lock
|
||||||
func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) {
|
func neighbourhoodRadiusForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) {
|
||||||
if p.Size() <= neighbourhoodSize {
|
if p.Size() <= neighbourhoodSize {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// total number of peers in iteration
|
// total number of peers in iteration
|
||||||
var size int
|
var size int
|
||||||
|
|
||||||
// determining the depth is a two-step process
|
|
||||||
// first we find the proximity bin of the shallowest of the NeighbourhoodSize peers
|
|
||||||
// the numeric value of depth cannot be higher than this
|
|
||||||
var maxDepth 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)
|
||||||
if i == 256 {
|
if i == 256 {
|
||||||
|
|
@ -431,13 +460,30 @@ func depthForPot(p *pot.Pot, neighbourhoodSize 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 == neighbourhoodSize {
|
if size == neighbourhoodSize {
|
||||||
maxDepth = i
|
depth = i
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
p.EachNeighbour(pivotAddr, Pof, f)
|
p.EachNeighbour(pivotAddr, Pof, f)
|
||||||
|
return depth
|
||||||
|
}
|
||||||
|
|
||||||
|
// depthForPot returns the depth for the pot
|
||||||
|
// depth is the radius of the minimal extension of nearest neighbourhood that
|
||||||
|
// includes all empty PO bins. I.e., depth is the deepest PO such that
|
||||||
|
// - it is not deeper than neighbourhood radius
|
||||||
|
// - all bins shallower than depth are not empty
|
||||||
|
// caller must hold the lock
|
||||||
|
func depthForPot(p *pot.Pot, neighbourhoodSize int, pivotAddr []byte) (depth int) {
|
||||||
|
if p.Size() <= neighbourhoodSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
// determining the depth is a two-step process
|
||||||
|
// first we find the proximity bin of the shallowest of the neighbourhoodSize peers
|
||||||
|
// the numeric value of depth cannot be higher than this
|
||||||
|
maxDepth := neighbourhoodRadiusForPot(p, neighbourhoodSize, pivotAddr)
|
||||||
|
|
||||||
// the second step is to test for empty bins in order from shallowest to deepest
|
// the second step is to test for empty bins in order from shallowest to deepest
|
||||||
// if an empty bin is found, this will be the actual depth
|
// if an empty bin is found, this will be the actual depth
|
||||||
|
|
@ -627,21 +673,20 @@ func NewPeerPotMap(neighbourhoodSize int, addrs [][]byte) map[string]*PeerPot {
|
||||||
return ppmap
|
return ppmap
|
||||||
}
|
}
|
||||||
|
|
||||||
// saturation iterates through all peers and
|
// saturation returns the smallest po value in which the node has less than MinBinSize peers
|
||||||
// returns the smallest po value in which the node has less than n peers
|
// if the iterator reaches neighbourhood radius, then the last bin + 1 is returned
|
||||||
// if the iterator reaches depth, then value for depth is returned
|
|
||||||
// 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 {
|
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) bool) bool) bool {
|
radius := neighbourhoodRadiusForPot(k.conns, k.NeighbourhoodSize, k.base)
|
||||||
|
k.conns.EachBin(k.base, Pof, 0, func(po, size int, f func(func(val pot.Val) bool) bool) bool {
|
||||||
prev++
|
prev++
|
||||||
|
if po >= radius {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return prev == po && size >= k.MinBinSize
|
return prev == po && size >= k.MinBinSize
|
||||||
})
|
})
|
||||||
// TODO evaluate whether this check cannot just as well be done within the eachbin
|
if prev < 0 {
|
||||||
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
return 0
|
||||||
if depth < prev {
|
|
||||||
return depth
|
|
||||||
}
|
}
|
||||||
return prev
|
return prev
|
||||||
}
|
}
|
||||||
|
|
@ -743,6 +788,9 @@ type Health struct {
|
||||||
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()
|
||||||
|
if len(pp.NNSet) < k.NeighbourhoodSize {
|
||||||
|
log.Warn("peerpot NNSet < NeighbourhoodSize")
|
||||||
|
}
|
||||||
gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
|
gotnn, countgotnn, culpritsgotnn := k.connectedNeighbours(pp.NNSet)
|
||||||
knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
|
knownn, countknownn, culpritsknownn := k.knowNeighbours(pp.NNSet)
|
||||||
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -43,39 +42,46 @@ func testKadPeerAddr(s string) *BzzAddr {
|
||||||
|
|
||||||
func newTestKademliaParams() *KadParams {
|
func newTestKademliaParams() *KadParams {
|
||||||
params := NewKadParams()
|
params := NewKadParams()
|
||||||
// TODO why is this 1?
|
params.MinBinSize = 2
|
||||||
params.MinBinSize = 1
|
|
||||||
params.NeighbourhoodSize = 2
|
params.NeighbourhoodSize = 2
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestKademlia(b string) *Kademlia {
|
type testKademlia struct {
|
||||||
|
*Kademlia
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestKademlia(t *testing.T, b string) *testKademlia {
|
||||||
base := pot.NewAddressFromString(b)
|
base := pot.NewAddressFromString(b)
|
||||||
return NewKademlia(base, newTestKademliaParams())
|
return &testKademlia{
|
||||||
|
Kademlia: NewKademlia(base, newTestKademliaParams()),
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestKadPeer(k *Kademlia, s string, lightNode bool) *Peer {
|
func (tk *testKademlia) newTestKadPeer(s string, lightNode bool) *Peer {
|
||||||
return NewPeer(&BzzPeer{BzzAddr: testKadPeerAddr(s), LightNode: lightNode}, k)
|
return NewPeer(&BzzPeer{BzzAddr: testKadPeerAddr(s), LightNode: lightNode}, tk.Kademlia)
|
||||||
}
|
}
|
||||||
|
|
||||||
func On(k *Kademlia, ons ...string) {
|
func (tk *testKademlia) On(ons ...string) {
|
||||||
for _, s := range ons {
|
for _, s := range ons {
|
||||||
k.On(newTestKadPeer(k, s, false))
|
tk.Kademlia.On(tk.newTestKadPeer(s, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Off(k *Kademlia, offs ...string) {
|
func (tk *testKademlia) Off(offs ...string) {
|
||||||
for _, s := range offs {
|
for _, s := range offs {
|
||||||
k.Off(newTestKadPeer(k, s, false))
|
tk.Kademlia.Off(tk.newTestKadPeer(s, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(k *Kademlia, regs ...string) {
|
func (tk *testKademlia) Register(regs ...string) {
|
||||||
var as []*BzzAddr
|
var as []*BzzAddr
|
||||||
for _, s := range regs {
|
for _, s := range regs {
|
||||||
as = append(as, testKadPeerAddr(s))
|
as = append(as, testKadPeerAddr(s))
|
||||||
}
|
}
|
||||||
err := k.Register(as...)
|
err := tk.Kademlia.Register(as...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -169,76 +175,71 @@ func TestHealthStrict(t *testing.T) {
|
||||||
// base address is all zeros
|
// base address is all zeros
|
||||||
// no peers
|
// no peers
|
||||||
// unhealthy (and lonely)
|
// unhealthy (and lonely)
|
||||||
k := newTestKademlia("11111111")
|
tk := newTestKademlia(t, "11111111")
|
||||||
assertHealth(t, k, false, false)
|
tk.checkHealth(false, false)
|
||||||
|
|
||||||
// know one peer but not connected
|
// know one peer but not connected
|
||||||
// unhealthy
|
// unhealthy
|
||||||
Register(k, "11100000")
|
tk.Register("11100000")
|
||||||
log.Trace(k.String())
|
tk.checkHealth(false, false)
|
||||||
assertHealth(t, k, false, false)
|
|
||||||
|
|
||||||
// know one peer and connected
|
// know one peer and connected
|
||||||
// healthy
|
// healthy
|
||||||
On(k, "11100000")
|
tk.On("11100000")
|
||||||
assertHealth(t, k, true, false)
|
tk.checkHealth(true, false)
|
||||||
|
|
||||||
// know two peers, only one connected
|
// know two peers, only one connected
|
||||||
// unhealthy
|
// unhealthy
|
||||||
Register(k, "11111100")
|
tk.Register("11111100")
|
||||||
log.Trace(k.String())
|
tk.checkHealth(false, false)
|
||||||
assertHealth(t, k, false, false)
|
|
||||||
|
|
||||||
// know two peers and connected to both
|
// know two peers and connected to both
|
||||||
// healthy
|
// healthy
|
||||||
On(k, "11111100")
|
tk.On("11111100")
|
||||||
assertHealth(t, k, true, false)
|
tk.checkHealth(true, false)
|
||||||
|
|
||||||
// know three peers, connected to the two deepest
|
// know three peers, connected to the two deepest
|
||||||
// healthy
|
// healthy
|
||||||
Register(k, "00000000")
|
tk.Register("00000000")
|
||||||
log.Trace(k.String())
|
tk.checkHealth(true, false)
|
||||||
assertHealth(t, k, true, false)
|
|
||||||
|
|
||||||
// know three peers, connected to all three
|
// know three peers, connected to all three
|
||||||
// healthy
|
// healthy
|
||||||
On(k, "00000000")
|
tk.On("00000000")
|
||||||
assertHealth(t, k, true, false)
|
tk.checkHealth(true, false)
|
||||||
|
|
||||||
// add fourth peer deeper than current depth
|
// add fourth peer deeper than current depth
|
||||||
// unhealthy
|
// unhealthy
|
||||||
Register(k, "11110000")
|
tk.Register("11110000")
|
||||||
log.Trace(k.String())
|
tk.checkHealth(false, false)
|
||||||
assertHealth(t, k, false, false)
|
|
||||||
|
|
||||||
// connected to three deepest peers
|
// connected to three deepest peers
|
||||||
// healthy
|
// healthy
|
||||||
On(k, "11110000")
|
tk.On("11110000")
|
||||||
assertHealth(t, k, true, false)
|
tk.checkHealth(true, false)
|
||||||
|
|
||||||
// add additional peer in same bin as deepest peer
|
// add additional peer in same bin as deepest peer
|
||||||
// unhealthy
|
// unhealthy
|
||||||
Register(k, "11111101")
|
tk.Register("11111101")
|
||||||
log.Trace(k.String())
|
tk.checkHealth(false, false)
|
||||||
assertHealth(t, k, false, false)
|
|
||||||
|
|
||||||
// four deepest of five peers connected
|
// four deepest of five peers connected
|
||||||
// healthy
|
// healthy
|
||||||
On(k, "11111101")
|
tk.On("11111101")
|
||||||
assertHealth(t, k, true, false)
|
tk.checkHealth(true, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertHealth(t *testing.T, k *Kademlia, expectHealthy bool, expectSaturation bool) {
|
func (tk *testKademlia) checkHealth(expectHealthy bool, expectSaturation bool) {
|
||||||
t.Helper()
|
tk.t.Helper()
|
||||||
kid := common.Bytes2Hex(k.BaseAddr())
|
kid := common.Bytes2Hex(tk.BaseAddr())
|
||||||
addrs := [][]byte{k.BaseAddr()}
|
addrs := [][]byte{tk.BaseAddr()}
|
||||||
k.EachAddr(nil, 255, func(addr *BzzAddr, po int) bool {
|
tk.EachAddr(nil, 255, func(addr *BzzAddr, po int) bool {
|
||||||
addrs = append(addrs, addr.Address())
|
addrs = append(addrs, addr.Address())
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
pp := NewPeerPotMap(k.NeighbourhoodSize, addrs)
|
pp := NewPeerPotMap(tk.NeighbourhoodSize, addrs)
|
||||||
healthParams := k.Healthy(pp[kid])
|
healthParams := tk.Healthy(pp[kid])
|
||||||
|
|
||||||
// definition of health, all conditions but be true:
|
// definition of health, all conditions but be true:
|
||||||
// - we at least know one peer
|
// - we at least know one peer
|
||||||
|
|
@ -246,23 +247,23 @@ func assertHealth(t *testing.T, k *Kademlia, expectHealthy bool, expectSaturatio
|
||||||
// - we are connected to all known neighbors
|
// - we are connected to all known neighbors
|
||||||
health := healthParams.KnowNN && healthParams.ConnectNN && healthParams.CountKnowNN > 0
|
health := healthParams.KnowNN && healthParams.ConnectNN && healthParams.CountKnowNN > 0
|
||||||
if expectHealthy != health {
|
if expectHealthy != health {
|
||||||
t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, k.String())
|
tk.t.Fatalf("expected kademlia health %v, is %v\n%v", expectHealthy, health, tk.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSuggestPeer(k *Kademlia, expAddr string, expPo int, expWant bool) error {
|
func (tk *testKademlia) checkSuggestPeer(expAddr string, expDepth int, expChanged bool) {
|
||||||
addr, o, want := k.SuggestPeer()
|
tk.t.Helper()
|
||||||
log.Trace("suggestpeer return", "a", addr, "o", o, "want", want)
|
addr, depth, changed := tk.SuggestPeer()
|
||||||
|
log.Trace("suggestPeer return", "addr", addr, "depth", depth, "changed", changed)
|
||||||
if binStr(addr) != expAddr {
|
if binStr(addr) != expAddr {
|
||||||
return fmt.Errorf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
|
tk.t.Fatalf("incorrect peer address suggested. expected %v, got %v", expAddr, binStr(addr))
|
||||||
}
|
}
|
||||||
if o != expPo {
|
if depth != expDepth {
|
||||||
return fmt.Errorf("incorrect prox order suggested. expected %v, got %v", expPo, o)
|
tk.t.Fatalf("incorrect saturation depth suggested. expected %v, got %v", expDepth, depth)
|
||||||
}
|
}
|
||||||
if want != expWant {
|
if changed != expChanged {
|
||||||
return fmt.Errorf("expected SuggestPeer to want peers: %v", expWant)
|
tk.t.Fatalf("expected depth change = %v, got %v", expChanged, changed)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func binStr(a *BzzAddr) string {
|
func binStr(a *BzzAddr) string {
|
||||||
|
|
@ -272,609 +273,184 @@ 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) {
|
|
||||||
// 2 row gap, unsaturated proxbin, no callables -> want PO 0
|
|
||||||
k := newTestKademlia("00000000")
|
|
||||||
On(k,
|
|
||||||
"10000000", "11000000",
|
|
||||||
"01000000",
|
|
||||||
|
|
||||||
"00010000", "00011000",
|
|
||||||
)
|
|
||||||
Off(k,
|
|
||||||
"01000000",
|
|
||||||
)
|
|
||||||
err := testSuggestPeer(k, "01000000", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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")
|
tk := newTestKademlia(t, "00000000")
|
||||||
|
tk.On("00100000")
|
||||||
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
|
|
||||||
testnum := 0
|
tk.On("00010000")
|
||||||
// test 0
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
// 2 row gap, unsaturated proxbin, no callables -> want PO 0
|
|
||||||
k := newTestKademlia("00000000")
|
|
||||||
On(k, "00100000")
|
|
||||||
err := testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 1
|
tk.On("10000000", "10000001")
|
||||||
// 2 row gap, saturated proxbin, no callables -> want PO 0
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
On(k, "00010000")
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 2
|
tk.On("01000000")
|
||||||
// 1 row gap (1 less), saturated proxbin, no callables -> want PO 1
|
tk.Off("10000001")
|
||||||
On(k, "10000000")
|
tk.checkSuggestPeer("10000001", 0, true)
|
||||||
err = testSuggestPeer(k, "<nil>", 1, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 3
|
tk.On("00100001")
|
||||||
// no gap (1 less), saturated proxbin, no callables -> do not want more
|
tk.Off("01000000")
|
||||||
On(k, "01000000", "00100001")
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 4
|
|
||||||
// oversaturated proxbin, > do not want more
|
|
||||||
On(k, "00100001")
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 5
|
|
||||||
// reintroduce gap, disconnected peer callable
|
|
||||||
Off(k, "01000000")
|
|
||||||
log.Trace(k.String())
|
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
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
|
||||||
log.Trace("foo")
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
log.Trace(k.String())
|
|
||||||
err = testSuggestPeer(k, "<nil>", 1, false)
|
|
||||||
if err != nil {
|
|
||||||
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")
|
tk.On("01000000")
|
||||||
Off(k, "01000000")
|
tk.Off("01000000")
|
||||||
log.Trace(k.String())
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 7
|
tk.On("01000000", "10000001")
|
||||||
// new closer peer appears, it is immediately wanted
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
On(k, "01000000")
|
|
||||||
Register(k, "00010001")
|
|
||||||
err = testSuggestPeer(k, "00010001", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 8
|
tk.Register("00010001")
|
||||||
// PO1 disconnects
|
tk.checkSuggestPeer("00010001", 0, false)
|
||||||
On(k, "00010001")
|
|
||||||
log.Info(k.String())
|
|
||||||
Off(k, "01000000")
|
|
||||||
log.Info(k.String())
|
|
||||||
// second time, gap filling
|
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 9
|
tk.On("00010001")
|
||||||
On(k, "01000000")
|
tk.Off("01000000")
|
||||||
log.Info(k.String())
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 10
|
tk.On("01000000")
|
||||||
k.MinBinSize = 2
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
log.Info(k.String())
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 11
|
tk.Register("01000001")
|
||||||
Register(k, "01000001")
|
tk.checkSuggestPeer("01000001", 0, false)
|
||||||
log.Info(k.String())
|
|
||||||
err = testSuggestPeer(k, "01000001", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 12
|
tk.On("01000001")
|
||||||
On(k, "10000001")
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
log.Trace(fmt.Sprintf("Kad:\n%v", k.String()))
|
|
||||||
err = testSuggestPeer(k, "<nil>", 1, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 13
|
tk.Register("10000010", "01000010", "00100010")
|
||||||
On(k, "01000001")
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 14
|
tk.Register("00010010")
|
||||||
k.MinBinSize = 3
|
tk.checkSuggestPeer("00010010", 0, false)
|
||||||
Register(k, "10000010")
|
|
||||||
err = testSuggestPeer(k, "10000010", 0, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 15
|
tk.Off("00100001")
|
||||||
On(k, "10000010")
|
tk.checkSuggestPeer("00100010", 2, true)
|
||||||
err = testSuggestPeer(k, "<nil>", 1, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 16
|
tk.Off("01000001")
|
||||||
On(k, "01000010")
|
tk.checkSuggestPeer("01000010", 1, true)
|
||||||
err = testSuggestPeer(k, "<nil>", 2, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 17
|
tk.checkSuggestPeer("01000001", 0, false)
|
||||||
On(k, "00100010")
|
tk.checkSuggestPeer("00100001", 0, false)
|
||||||
err = testSuggestPeer(k, "<nil>", 3, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
|
||||||
testnum++
|
|
||||||
|
|
||||||
// test 18
|
tk.On("01000001", "00100001")
|
||||||
On(k, "00010010")
|
tk.Register("10000100", "01000100", "00100100")
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.Register("00000100", "00000101", "00000110")
|
||||||
if err != nil {
|
tk.Register("00000010", "00000011", "00000001")
|
||||||
t.Fatalf("%d %v", testnum, err.Error())
|
|
||||||
}
|
tk.checkSuggestPeer("00000110", 0, false)
|
||||||
testnum++
|
tk.checkSuggestPeer("00000101", 0, false)
|
||||||
|
tk.checkSuggestPeer("00000100", 0, false)
|
||||||
|
tk.checkSuggestPeer("00000011", 0, false)
|
||||||
|
tk.checkSuggestPeer("00000010", 0, false)
|
||||||
|
tk.checkSuggestPeer("00000001", 0, false)
|
||||||
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// a node should stay in the address book if it's removed from the kademlia
|
// a node should stay in the address book if it's removed from the kademlia
|
||||||
func TestOffEffectingAddressBookNormalNode(t *testing.T) {
|
func TestOffEffectingAddressBookNormalNode(t *testing.T) {
|
||||||
k := newTestKademlia("00000000")
|
tk := newTestKademlia(t, "00000000")
|
||||||
// peer added to kademlia
|
// peer added to kademlia
|
||||||
k.On(newTestKadPeer(k, "01000000", false))
|
tk.On("01000000")
|
||||||
// peer should be in the address book
|
// peer should be in the address book
|
||||||
if k.addrs.Size() != 1 {
|
if tk.addrs.Size() != 1 {
|
||||||
t.Fatal("known peer addresses should contain 1 entry")
|
t.Fatal("known peer addresses should contain 1 entry")
|
||||||
}
|
}
|
||||||
// peer should be among live connections
|
// peer should be among live connections
|
||||||
if k.conns.Size() != 1 {
|
if tk.conns.Size() != 1 {
|
||||||
t.Fatal("live peers should contain 1 entry")
|
t.Fatal("live peers should contain 1 entry")
|
||||||
}
|
}
|
||||||
// remove peer from kademlia
|
// remove peer from kademlia
|
||||||
k.Off(newTestKadPeer(k, "01000000", false))
|
tk.Off("01000000")
|
||||||
// peer should be in the address book
|
// peer should be in the address book
|
||||||
if k.addrs.Size() != 1 {
|
if tk.addrs.Size() != 1 {
|
||||||
t.Fatal("known peer addresses should contain 1 entry")
|
t.Fatal("known peer addresses should contain 1 entry")
|
||||||
}
|
}
|
||||||
// peer should not be among live connections
|
// peer should not be among live connections
|
||||||
if k.conns.Size() != 0 {
|
if tk.conns.Size() != 0 {
|
||||||
t.Fatal("live peers should contain 0 entry")
|
t.Fatal("live peers should contain 0 entry")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a light node should not be in the address book
|
// a light node should not be in the address book
|
||||||
func TestOffEffectingAddressBookLightNode(t *testing.T) {
|
func TestOffEffectingAddressBookLightNode(t *testing.T) {
|
||||||
k := newTestKademlia("00000000")
|
tk := newTestKademlia(t, "00000000")
|
||||||
// light node peer added to kademlia
|
// light node peer added to kademlia
|
||||||
k.On(newTestKadPeer(k, "01000000", true))
|
tk.Kademlia.On(tk.newTestKadPeer("01000000", true))
|
||||||
// peer should not be in the address book
|
// peer should not be in the address book
|
||||||
if k.addrs.Size() != 0 {
|
if tk.addrs.Size() != 0 {
|
||||||
t.Fatal("known peer addresses should contain 0 entry")
|
t.Fatal("known peer addresses should contain 0 entry")
|
||||||
}
|
}
|
||||||
// peer should be among live connections
|
// peer should be among live connections
|
||||||
if k.conns.Size() != 1 {
|
if tk.conns.Size() != 1 {
|
||||||
t.Fatal("live peers should contain 1 entry")
|
t.Fatal("live peers should contain 1 entry")
|
||||||
}
|
}
|
||||||
// remove peer from kademlia
|
// remove peer from kademlia
|
||||||
k.Off(newTestKadPeer(k, "01000000", true))
|
tk.Kademlia.Off(tk.newTestKadPeer("01000000", true))
|
||||||
// peer should not be in the address book
|
// peer should not be in the address book
|
||||||
if k.addrs.Size() != 0 {
|
if tk.addrs.Size() != 0 {
|
||||||
t.Fatal("known peer addresses should contain 0 entry")
|
t.Fatal("known peer addresses should contain 0 entry")
|
||||||
}
|
}
|
||||||
// peer should not be among live connections
|
// peer should not be among live connections
|
||||||
if k.conns.Size() != 0 {
|
if tk.conns.Size() != 0 {
|
||||||
t.Fatal("live peers should contain 0 entry")
|
t.Fatal("live peers should contain 0 entry")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSuggestPeerRetries(t *testing.T) {
|
func TestSuggestPeerRetries(t *testing.T) {
|
||||||
k := newTestKademlia("00000000")
|
tk := newTestKademlia(t, "00000000")
|
||||||
k.RetryInterval = int64(300 * time.Millisecond) // cycle
|
tk.RetryInterval = int64(300 * time.Millisecond) // cycle
|
||||||
k.MaxRetries = 50
|
tk.MaxRetries = 50
|
||||||
k.RetryExponent = 2
|
tk.RetryExponent = 2
|
||||||
sleep := func(n int) {
|
sleep := func(n int) {
|
||||||
ts := k.RetryInterval
|
ts := tk.RetryInterval
|
||||||
for i := 1; i < n; i++ {
|
for i := 1; i < n; i++ {
|
||||||
ts *= int64(k.RetryExponent)
|
ts *= int64(tk.RetryExponent)
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(ts))
|
time.Sleep(time.Duration(ts))
|
||||||
}
|
}
|
||||||
|
|
||||||
Register(k, "01000000")
|
tk.Register("01000000")
|
||||||
On(k, "00000001", "00000010")
|
tk.On("00000001", "00000010")
|
||||||
err := testSuggestPeer(k, "01000000", 0, false)
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
err = testSuggestPeer(k, "01000000", 0, false)
|
tk.checkSuggestPeer("01000000", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
sleep(2)
|
sleep(2)
|
||||||
err = testSuggestPeer(k, "<nil>", 0, false)
|
tk.checkSuggestPeer("<nil>", 0, false)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKademliaHiveString(t *testing.T) {
|
func TestKademliaHiveString(t *testing.T) {
|
||||||
k := newTestKademlia("00000000")
|
tk := newTestKademlia(t, "00000000")
|
||||||
On(k, "01000000", "00100000")
|
tk.On("01000000", "00100000")
|
||||||
Register(k, "10000000", "10000001")
|
tk.Register("10000000", "10000001")
|
||||||
k.MaxProxDisplay = 8
|
tk.MaxProxDisplay = 8
|
||||||
h := k.String()
|
h := tk.String()
|
||||||
expH := "\n=========================================================================\nMon Feb 27 12:10:28 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 000000\npopulation: 2 (4), NeighbourhoodSize: 2, MinBinSize: 1, MaxBinSize: 4\n============ DEPTH: 0 ==========================================\n000 0 | 2 8100 (0) 8000 (0)\n001 1 4000 | 1 4000 (0)\n002 1 2000 | 1 2000 (0)\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n========================================================================="
|
expH := "\n=========================================================================\nMon Feb 27 12:10:28 UTC 2017 KΛÐΞMLIΛ hive: queen's address: 000000\npopulation: 2 (4), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4\n============ DEPTH: 0 ==========================================\n000 0 | 2 8100 (0) 8000 (0)\n001 1 4000 | 1 4000 (0)\n002 1 2000 | 1 2000 (0)\n003 0 | 0\n004 0 | 0\n005 0 | 0\n006 0 | 0\n007 0 | 0\n========================================================================="
|
||||||
if expH[104:] != h[104:] {
|
if expH[104:] != h[104:] {
|
||||||
t.Fatalf("incorrect hive output. expected %v, got %v", expH, h)
|
t.Fatalf("incorrect hive output. expected %v, got %v", expH, h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testKademliaCase constructs the kademlia and PeerPot map to validate
|
|
||||||
// the SuggestPeer and Healthy methods for provided hex-encoded addresses.
|
|
||||||
// Argument pivotAddr is the address of the kademlia.
|
|
||||||
func testKademliaCase(t *testing.T, pivotAddr string, addrs ...string) {
|
|
||||||
|
|
||||||
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())
|
|
||||||
|
|
||||||
// our pivot kademlia is the last one in the array
|
|
||||||
for _, a := range byteAddrs {
|
|
||||||
if bytes.Equal(a, addr) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
p := &BzzAddr{OAddr: a, UAddr: a}
|
|
||||||
if err := k.Register(p); err != nil {
|
|
||||||
t.Fatalf("a %x addr %x: %v", a, addr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ppmap := NewPeerPotMap(k.NeighbourhoodSize, byteAddrs)
|
|
||||||
|
|
||||||
pp := ppmap[pivotAddr]
|
|
||||||
|
|
||||||
for {
|
|
||||||
a, _, _ := k.SuggestPeer()
|
|
||||||
if a == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
k.On(NewPeer(&BzzPeer{BzzAddr: a}, k))
|
|
||||||
}
|
|
||||||
|
|
||||||
h := k.Healthy(pp)
|
|
||||||
if !(h.ConnectNN && h.KnowNN && h.CountKnowNN > 0) {
|
|
||||||
t.Fatalf("not healthy: %#v\n%v", h, k.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The regression test for the following invalid kademlia edge case.
|
|
||||||
|
|
||||||
Addresses used in this test are discovered as part of the simulation network
|
|
||||||
in higher level tests for streaming. They were generated randomly.
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Mon Apr 9 12:18:24 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 7efef1
|
|
||||||
population: 9 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
|
|
||||||
000 2 d7e5 ec56 | 18 ec56 (0) d7e5 (0) d9e0 (0) c735 (0)
|
|
||||||
001 2 18f1 3176 | 14 18f1 (0) 10bb (0) 10d1 (0) 0421 (0)
|
|
||||||
002 2 52aa 47cd | 11 52aa (0) 51d9 (0) 5161 (0) 5130 (0)
|
|
||||||
003 1 646e | 1 646e (0)
|
|
||||||
004 0 | 3 769c (0) 76d1 (0) 7656 (0)
|
|
||||||
============ DEPTH: 5 ==========================================
|
|
||||||
005 1 7a48 | 1 7a48 (0)
|
|
||||||
006 1 7cbd | 1 7cbd (0)
|
|
||||||
007 0 | 0
|
|
||||||
008 0 | 0
|
|
||||||
009 0 | 0
|
|
||||||
010 0 | 0
|
|
||||||
011 0 | 0
|
|
||||||
012 0 | 0
|
|
||||||
013 0 | 0
|
|
||||||
014 0 | 0
|
|
||||||
015 0 | 0
|
|
||||||
=========================================================================
|
|
||||||
*/
|
|
||||||
func TestKademliaCase1(t *testing.T) {
|
|
||||||
testKademliaCase(t,
|
|
||||||
"7efef1c41d77f843ad167be95f6660567eb8a4a59f39240000cce2e0d65baf8e",
|
|
||||||
"ec560e6a4806aa37f147ee83687f3cf044d9953e61eedb8c34b6d50d9e2c5623",
|
|
||||||
"646e9540c84f6a2f9cf6585d45a4c219573b4fd1b64a3c9a1386fc5cf98c0d4d",
|
|
||||||
"18f13c5fba653781019025ab10e8d2fdc916d6448729268afe9e928ffcdbb8e8",
|
|
||||||
"317617acf99b4ffddda8a736f8fc6c6ede0bf690bc23d834123823e6d03e2f69",
|
|
||||||
"d7e52d9647a5d1c27a68c3ee65d543be3947ae4b68537b236d71ef9cb15fb9ab",
|
|
||||||
"7a48f75f8ca60487ae42d6f92b785581b40b91f2da551ae73d5eae46640e02e8",
|
|
||||||
"7cbd42350bde8e18ae5b955b5450f8e2cef3419f92fbf5598160c60fd78619f0",
|
|
||||||
"52aa3ddec61f4d48dd505a2385403c634f6ad06ee1d99c5c90a5ba6006f9af9c",
|
|
||||||
"47cdb6fa93eeb8bc91a417ff4e3b14a9c2ea85137462e2f575fae97f0c4be60d",
|
|
||||||
"5161943eb42e2a03e715fe8afa1009ff5200060c870ead6ab103f63f26cb107f",
|
|
||||||
"a38eaa1255f76bf883ca0830c86e8c4bb7eed259a8348aae9b03f21f90105bee",
|
|
||||||
"b2522bdf1ab26f324e75424fdf6e493b47e8a27687fe76347607b344fc010075",
|
|
||||||
"5bd7213964efb2580b91d02ac31ef126838abeba342f5dbdbe8d4d03562671a2",
|
|
||||||
"0b531adb82744768b694d7f94f73d4f0c9de591266108daeb8c74066bfc9c9ca",
|
|
||||||
"28501f59f70e888d399570145ed884353e017443c675aa12731ada7c87ea14f7",
|
|
||||||
"4a45f1fc63e1a9cb9dfa44c98da2f3d20c2923e5d75ff60b2db9d1bdb0c54d51",
|
|
||||||
"b193431ee35cd32de95805e7c1c749450c47486595aae7195ea6b6019a64fd61",
|
|
||||||
"baebf36a1e35a7ed834e1c72faf44ba16c159fa47d3289ceb3ca35fefa8739b5",
|
|
||||||
"a3659bd32e05fa36c8d20dbaaed8362bf1a8a7bd116aed62d8a43a2efbdf513f",
|
|
||||||
"10d1b50881a4770ebebdd0a75589dabb931e6716747b0f65fd6b080b88c4fdb6",
|
|
||||||
"3c76b8ca5c7ce6a03320646826213f59229626bf5b9d25da0c3ec0662dcb8ff3",
|
|
||||||
"4d72a04ddeb851a68cd197ef9a92a3e2ff01fbbff638e64929dd1a9c2e150112",
|
|
||||||
"c7353d320987956075b5bc1668571c7a36c800d5598fdc4832ec6569561e15d1",
|
|
||||||
"d9e0c7c90878c20ab7639d5954756f54775404b3483407fe1b483635182734f6",
|
|
||||||
"8fca67216b7939c0824fb06c5279901a94da41da9482b000f56df9906736ee75",
|
|
||||||
"460719d7f7aa7d7438f0eaf30333484fa3bd0f233632c10ba89e6e46dd3604be",
|
|
||||||
"0421d92c8a1c79ed5d01305a3d25aaf22a8f5f9e3d4bc80da47ee16ce20465fe",
|
|
||||||
"3441d9d9c0f05820a1bb6459fc7d8ef266a1bd929e7db939a10f544efe8261ea",
|
|
||||||
"ab198a66c293586746758468c610e5d3914d4ce629147eff6dd55a31f863ff8f",
|
|
||||||
"3a1c8c16b0763f3d2c35269f454ff779d1255e954d2deaf6c040fb3f0bcdc945",
|
|
||||||
"5561c0ea3b203e173b11e6aa9d0e621a4e10b1d8b178b8fe375220806557b823",
|
|
||||||
"7656caccdc79cd8d7ce66d415cc96a718e8271c62fb35746bfc2b49faf3eebf3",
|
|
||||||
"5130594fd54c1652cf2debde2c4204573ed76555d1e26757fe345b409af1544a",
|
|
||||||
"76d1e83c71ca246d042e37ff1db181f2776265fbcfdc890ce230bfa617c9c2f0",
|
|
||||||
"89580231962624c53968c1b0095b4a2732b2a2640a19fdd7d21fd064fcc0a5ef",
|
|
||||||
"3d10d001fff44680c7417dd66ecf2e984f0baa20a9bbcea348583ba5ff210c4f",
|
|
||||||
"43754e323f0f3a1155b1852bd6edd55da86b8c4cfe3df8b33733fca50fc202b8",
|
|
||||||
"a9e7b1bb763ae6452ddcacd174993f82977d81a85206bb2ae3c842e2d8e19b4c",
|
|
||||||
"10bb07da7bc7c7757f74149eff167d528a94a253cdc694a863f4d50054c00b6d",
|
|
||||||
"28f0bc1b44658548d6e05dd16d4c2fe77f1da5d48b6774bc4263b045725d0c19",
|
|
||||||
"835fbbf1d16ba7347b6e2fc552d6e982148d29c624ea20383850df3c810fa8fc",
|
|
||||||
"8e236c56a77d7f46e41e80f7092b1a68cd8e92f6156365f41813ad1ca2c6b6f3",
|
|
||||||
"51d9c857e9238c49186e37b4eccf17a82de3d5739f026f6043798ab531456e73",
|
|
||||||
"bbddf7db6a682225301f36a9fd5b0d0121d2951753e1681295f3465352ad511f",
|
|
||||||
"2690a910c33ee37b91eb6c4e0731d1d345e2dc3b46d308503a6e85bbc242c69e",
|
|
||||||
"769ce86aa90b518b7ed382f9fdacfbed93574e18dc98fe6c342e4f9f409c2d5a",
|
|
||||||
"ba3bebec689ce51d3e12776c45f80d25164fdfb694a8122d908081aaa2e7122c",
|
|
||||||
"3a51f4146ea90a815d0d283d1ceb20b928d8b4d45875e892696986a3c0d8fb9b",
|
|
||||||
"81968a2d8fb39114342ee1da85254ec51e0608d7f0f6997c2a8354c260a71009",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The regression test for the following invalid kademlia edge case.
|
|
||||||
|
|
||||||
Addresses used in this test are discovered as part of the simulation network
|
|
||||||
in higher level tests for streaming. They were generated randomly.
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Mon Apr 9 18:43:48 UTC 2018 KΛÐΞMLIΛ hive: queen's address: bc7f3b
|
|
||||||
population: 9 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
|
|
||||||
000 2 0f49 67ff | 28 0f49 (0) 0211 (0) 07b2 (0) 0703 (0)
|
|
||||||
001 2 e84b f3a4 | 13 f3a4 (0) e84b (0) e58b (0) e60b (0)
|
|
||||||
002 1 8dba | 1 8dba (0)
|
|
||||||
003 2 a008 ad72 | 2 ad72 (0) a008 (0)
|
|
||||||
004 0 | 3 b61f (0) b27f (0) b027 (0)
|
|
||||||
============ DEPTH: 5 ==========================================
|
|
||||||
005 1 ba19 | 1 ba19 (0)
|
|
||||||
006 0 | 0
|
|
||||||
007 1 bdd6 | 1 bdd6 (0)
|
|
||||||
008 0 | 0
|
|
||||||
009 0 | 0
|
|
||||||
010 0 | 0
|
|
||||||
011 0 | 0
|
|
||||||
012 0 | 0
|
|
||||||
013 0 | 0
|
|
||||||
014 0 | 0
|
|
||||||
015 0 | 0
|
|
||||||
=========================================================================
|
|
||||||
*/
|
|
||||||
func TestKademliaCase2(t *testing.T) {
|
|
||||||
testKademliaCase(t,
|
|
||||||
"bc7f3b6a4a7e3c91b100ca6680b6c06ff407972b88956324ca853295893e0237", "67ffb61d3aa27449d277016188f35f19e2321fbda5008c68cf6303faa080534f", "600cd54c842eadac1729c04abfc369bc244572ca76117105b9dd910283b82730", "d955a05409650de151218557425105a8aa2867bb6a0e0462fa1cf90abcf87ad6", "7a6b726de45abdf7bb3e5fd9fb0dc8932270ca4dedef92238c80c05bcdb570e3", "263e99424ebfdb652adb4e3dcd27d59e11bb7ae1c057b3ef6f390d0228006254", "ba195d1a53aafde68e661c64d39db8c2a73505bf336125c15c3560de3b48b7ed", "3458c762169937115f67cabc35a6c384ed70293a8aec37b077a6c1b8e02d510e", "4ef4dc2e28ac6efdba57e134ac24dd4e0be68b9d54f7006515eb9509105f700c", "2a8782b79b0c24b9714dfd2c8ff1932bebc08aa6520b4eaeaa59ff781238890c", "625d02e960506f4524e9cdeac85b33faf3ea437fceadbd478b62b78720cf24fc", "e051a36a8c8637f520ba259c9ed3fadaf740dadc6a04c3f0e21778ebd4cd6ac4", "e34bc014fa2504f707bb3d904872b56c2fa250bee3cb19a147a0418541f1bd90", "28036dc79add95799916893890add5d8972f3b95325a509d6ded3d448f4dc652", "1b013c407794fa2e4c955d8f51cbc6bd78588a174b6548246b291281304b5409", "34f71b68698e1534095ff23ee9c35bf64c7f12b8463e7c6f6b19c25cf03928b4", "c712c6e9bbb7076832972a95890e340b94ed735935c3c0bb788e61f011b59479", "a008d5becdcda4b9dbfdaafc3cec586cf61dcf2d4b713b6168fff02e3b9f0b08", "29de15555cdbebaab214009e416ee92f947dcec5dab9894129f50f1b17138f34", "5df9449f700bd4b5a23688b68b293f2e92fa6ca524c93bc6bb9936efba9d9ada", "3ab0168a5f87fedc6a39b53c628256ac87a98670d8691bbdaaecec22418d13a2", "1ee299b2d2a74a568494130e6869e66d57982d345c482a0e0eeb285ac219ae3b", "e0e0e3b860cea9b7a74cf1b0675cc632dc64e80a02f20bbc5e96e2e8bb670606", "dc1ba6f169b0fcdcca021dcebaf39fe5d4875e7e69b854fad65687c1d7719ec0", "d321f73e42fcfb1d3a303eddf018ca5dffdcfd5567cd5ec1212f045f6a07e47d", "070320c3da7b542e5ca8aaf6a0a53d2bb5113ed264ab1db2dceee17c729edcb1", "17d314d65fdd136b50d182d2c8f5edf16e7838c2be8cf2c00abe4b406dbcd1d8", "e60b99e0a06f7d2d99d84085f67cdf8cc22a9ae22c339365d80f90289834a2b4", "02115771e18932e1f67a45f11f5bf743c5dae97fbc477d34d35c996012420eac", "3102a40eb2e5060353dd19bf61eeec8782dd1bebfcb57f4c796912252b591827", "8dbaf231062f2dc7ddaba5f9c7761b0c21292be51bf8c2ef503f31d4a2f63f79", "b02787b713c83a9f9183216310f04251994e04c2763a9024731562e8978e7cc4", "b27fe6cd33989e10909ce794c4b0b88feae286b614a59d49a3444c1a7b51ea82", "07b2d2c94fdc6fd148fe23be2ed9eff54f5e12548f29ed8416e6860fc894466f", "e58bf9f451ef62ac44ff0a9bb0610ec0fd14d423235954f0d3695e83017cbfc4", "bdd600b91bb79d1ee0053b854de308cfaa7e2abce575ea6815a0a7b3449609c2", "0f49c93c1edc7999920b21977cedd51a763940dac32e319feb9c1df2da0f3071", "7cbf0297cd41acf655cd6f960d7aaf61479edb4189d5c001cbc730861f0deb41", "79265193778d87ad626a5f59397bc075872d7302a12634ce2451a767d0a82da2", "2fe7d705f7c370b9243dbaafe007d555ff58d218822fca49d347b12a0282457c", "e84bc0c83d05e55a0080eed41dda5a795da4b9313a4da697142e69a65834cbb3", "cc4d278bd9aa0e9fb3cd8d2e0d68fb791aab5de4b120b845c409effbed47a180", "1a2317a8646cd4b6d3c4aa4cc25f676533abb689cf180787db216880a1239ad8", "cbafd6568cf8e99076208e6b6843f5808a7087897c67aad0c54694669398f889", "7b7c8357255fc37b4dae0e1af61589035fd39ff627e0938c6b3da8b4e4ec5d23", "2b8d782c1f5bac46c922cf439f6aa79f91e9ba5ffc0020d58455188a2075b334", "b61f45af2306705740742e76197a119235584ced01ef3f7cf3d4370f6c557cd1", "2775612e7cdae2780bf494c370bdcbe69c55e4a1363b1dc79ea0135e61221cce", "f3a49bb22f40885e961299abfa697a7df690a79f067bf3a4847a3ad48d826c9f", "ad724ac218dc133c0aadf4618eae21fdd0c2f3787af279846b49e2b4f97ff167",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The regression test for the following invalid kademlia edge case.
|
|
||||||
|
|
||||||
Addresses used in this test are discovered as part of the simulation network
|
|
||||||
in higher level tests for streaming. They were generated randomly.
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Mon Apr 9 19:04:35 UTC 2018 KΛÐΞMLIΛ hive: queen's address: b4822e
|
|
||||||
population: 8 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
|
|
||||||
000 2 786c 774b | 29 774b (0) 786c (0) 7a79 (0) 7d2f (0)
|
|
||||||
001 2 d9de cf19 | 10 cf19 (0) d9de (0) d2ff (0) d2a2 (0)
|
|
||||||
002 2 8ca1 8d74 | 5 8d74 (0) 8ca1 (0) 9793 (0) 9f51 (0)
|
|
||||||
003 0 | 0
|
|
||||||
004 0 | 3 bfac (0) bcbb (0) bde9 (0)
|
|
||||||
005 0 | 0
|
|
||||||
============ DEPTH: 6 ==========================================
|
|
||||||
006 1 b660 | 1 b660 (0)
|
|
||||||
007 0 | 0
|
|
||||||
008 1 b450 | 1 b450 (0)
|
|
||||||
009 0 | 0
|
|
||||||
010 0 | 0
|
|
||||||
011 0 | 0
|
|
||||||
012 0 | 0
|
|
||||||
013 0 | 0
|
|
||||||
014 0 | 0
|
|
||||||
015 0 | 0
|
|
||||||
=========================================================================
|
|
||||||
*/
|
|
||||||
func TestKademliaCase3(t *testing.T) {
|
|
||||||
testKademliaCase(t,
|
|
||||||
"b4822e874a01b94ac3a35c821e6db131e785c2fcbb3556e84b36102caf09b091", "2ecf54ea38d58f9cfc3862e54e5854a7c506fbc640e0b38e46d7d45a19794999", "442374092be50fc7392e8dd3f6fab3158ff7f14f26ff98060aed9b2eecf0b97d", "b450a4a67fcfa3b976cf023d8f1f15052b727f712198ce901630efe2f95db191", "9a7291638eb1c989a6dd6661a42c735b23ac6605b5d3e428aa5ffe650e892c85", "67f62eeab9804cfcac02b25ebeab9113d1b9d03dd5200b1c5a324cc0163e722f", "2e4a0e4b53bca4a9d7e2734150e9f579f29a255ade18a268461b20d026c9ee90", "30dd79c5fcdaa1b106f6960c45c9fde7c046aa3d931088d98c52ab759d0b2ac4", "97936fb5a581e59753c54fa5feec493714f2218245f61f97a62eafd4699433e4", "3a2899b6e129e3e193f6e2aefb82589c948c246d2ec1d4272af32ef3b2660f44", "f0e2a8aa88e67269e9952431ef12e5b29b7f41a1871fbfc38567fad95655d607", "7fa12b3f3c5f8383bfc644b958f72a486969733fa097d8952b3eb4f7b4f73192", "360c167aad5fc992656d6010ec45fdce5bcd492ad9608bc515e2be70d4e430c1", "fe21bc969b3d8e5a64a6484a829c1e04208f26f3cd4de6afcbc172a5bd17f1f1", "b660a1f40141d7ccd282fe5bd9838744119bd1cb3780498b5173578cc5ad308f", "44dcb3370e76680e2fba8cd986ad45ff0b77ca45680ee8d950e47922c4af6226", "8ca126923d17fccb689647307b89f38aa14e2a7b9ebcf3c1e31ccf3d2291a3bc", "f0ae19ae9ce6329327cbf42baf090e084c196b0877d8c7b69997e0123be23ef8", "d2a2a217385158e3e1e348883a14bc423e57daa12077e8c49797d16121ea0810", "f5467ccd85bb4ebe768527db520a210459969a5f1fae6e07b43f519799f0b224", "68be5fd9f9d142a5099e3609011fe3bab7bb992c595999e31e0b3d1668dfb3cf", "4d49a8a476e4934afc6b5c36db9bece3ed1804f20b952da5a21b2b0de766aa73", "ea7155745ef3fb2d099513887a2ba279333ced65c65facbd890ce58bd3fce772", "cf19f51f4e848053d289ac95a9138cdd23fc3077ae913cd58cda8cc7a521b2e1", "590b1cd41c7e6144e76b5cd515a3a4d0a4317624620a3f1685f43ae68bdcd890", "d2ffe0626b5f94a7e00fa0b506e7455a3d9399c15800db108d5e715ef5f6e346", "69630878c50a91f6c2edd23a706bfa0b50bd5661672a37d67bab38e6bca3b698", "445e9067079899bb5faafaca915ae6c0f6b1b730a5a628835dd827636f7feb1e", "6461c77491f1c4825958949f23c153e6e1759a5be53abbcee17c9da3867f3141", "23a235f4083771ccc207771daceda700b525a59ab586788d4f6892e69e34a6e2", "bde99f79ef41a81607ddcf92b9f95dcbc6c3537e91e8bf740e193dc73b19485e", "177957c0e5f0fbd12b88022a91768095d193830986caec8d888097d3ff4310b8", "bcbbdbaa4cdf8352422072f332e05111b732354a35c4d7c617ce1fc3b8b42a5a", "774b6717fdfb0d1629fb9d4c04a9ca40079ae2955d7f82e897477055ed017abb", "16443bf625be6d39ecaa6f114e5d2c1d47a64bfd3c13808d94b55b6b6acef2ee", "8d7495d9008066505ed00ce8198af82bfa5a6b4c08768b4c9fb3aa4eb0b0cca2", "15800849a53349508cb382959527f6c3cf1a46158ff1e6e2316b7dea7967e35f", "7a792f0f4a2b731781d1b244b2a57947f1a2e32900a1c0793449f9f7ae18a7b7", "5e517c2832c9deaa7df77c7bad4d20fd6eda2b7815e155e68bc48238fac1416f", "9f51a14f0019c72bd1d472706d8c80a18c1873c6a0663e754b60eae8094483d7", "7d2fabb565122521d22ba99fed9e5be6a458fbc93156d54db27d97a00b8c3a97", "786c9e412a7db4ec278891fa534caa9a1d1a028c631c6f3aeb9c4d96ad895c36", "3bd6341d40641c2632a5a0cd7a63553a04e251efd7195897a1d27e02a7a8bfde", "31efd1f5fb57b8cff0318d77a1a9e8d67e1d1c8d18ce90f99c3a240dff48cdc8", "d9de3e1156ce1380150948acbcfecd99c96e7f4b0bc97745f4681593d017f74f", "427a2201e09f9583cd990c03b81b58148c297d474a3b50f498d83b1c7a9414cd", "bfaca11596d3dec406a9fcf5d97536516dfe7f0e3b12078428a7e1700e25218a", "351c4770a097248a650008152d0cab5825d048bef770da7f3364f59d1e721bc0", "ee00f205d1486b2be7381d962bd2867263758e880529e4e2bfedfa613bbc0e71", "6aa3b6418d89e3348e4859c823ef4d6d7cd46aa7f7e77aba586c4214d760d8f8",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The regression test for the following invalid kademlia edge case.
|
|
||||||
|
|
||||||
Addresses used in this test are discovered as part of the simulation network
|
|
||||||
in higher level tests for streaming. They were generated randomly.
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Mon Apr 9 19:16:25 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 9a90fe
|
|
||||||
population: 8 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
|
|
||||||
000 2 72ef 4e6c | 24 0b1e (0) 0d66 (0) 17f5 (0) 17e8 (0)
|
|
||||||
001 2 fc2b fa47 | 13 fa47 (0) fc2b (0) fffd (0) ecef (0)
|
|
||||||
002 2 b847 afa8 | 6 afa8 (0) ad77 (0) bb7c (0) b847 (0)
|
|
||||||
003 0 | 0
|
|
||||||
004 0 | 4 91fc (0) 957d (0) 9482 (0) 949a (0)
|
|
||||||
============ DEPTH: 5 ==========================================
|
|
||||||
005 1 9ccf | 1 9ccf (0)
|
|
||||||
006 0 | 0
|
|
||||||
007 1 9bb2 | 1 9bb2 (0)
|
|
||||||
008 0 | 0
|
|
||||||
009 0 | 0
|
|
||||||
010 0 | 0
|
|
||||||
011 0 | 0
|
|
||||||
012 0 | 0
|
|
||||||
013 0 | 0
|
|
||||||
014 0 | 0
|
|
||||||
015 0 | 0
|
|
||||||
=========================================================================
|
|
||||||
*/
|
|
||||||
func TestKademliaCase4(t *testing.T) {
|
|
||||||
testKademliaCase(t,
|
|
||||||
"9a90fe3506277244549064b8c3276abb06284a199d9063a97331947f2b7da7f4",
|
|
||||||
"c19359eddef24b7be1a833b4475f212cd944263627a53f9ef4837d106c247730", "fc2b6fef99ef947f7e57c3df376891769e2a2fd83d2b8e634e0fc1e91eaa080c", "ecefc0e1a8ea7bb4b48c469e077401fce175dd75294255b96c4e54f6a2950a55", "bb7ce598efc056bba343cc2614aa3f67a575557561290b44c73a63f8f433f9f7", "55fbee6ca52dfd7f0be0db969ee8e524b654ab4f0cce7c05d83887d7d2a15460", "afa852b6b319998c6a283cc0c82d2f5b8e9410075d7700f3012761f1cfbd0f76", "36c370cfb63f2087971ba6e58d7585b04e16b8f0da335efb91554c2dd8fe191c", "6be41e029985edebc901fb77fc4fb65516b6d85086e2a98bfa3159c99391e585", "dd3cfc72ea553e7d2b28f0037a65646b30955b929d29ba4c40f4a2a811248e77", "da3a8f18e09c7b0ca235c4e33e1441a5188f1df023138bf207753ee63e768f7d", "de9e3ab4dc572d54a2d4b878329fd832bb51a149f4ce167316eeb177b61e7e01", "4e6c1ecde6ed917706257fe020a1d02d2e9d87fca4c85f0f7b132491008c5032", "72ef04b77a070e13463b3529dd312bcacfb7a12d20dc597f5ec3de0501e9b834", "3fef57186675d524ab8bb1f54ba8cb68610babca1247c0c46dbb60aed003c69d", "1d8e6b71f7a052865d6558d4ba44ad5fab7b908cc1badf5766822e1c20d0d823", "6be2f2b4ffa173014d4ec7df157d289744a2bda54bb876b264ccfa898a0da315", "b0ba3fff8643f9985c744327b0c4c869763509fd5da2de9a80a4a0a082021255", "9ccf40b9406ba2e6567101fb9b4e5334a9ec74263eff47267da266ba45e6c158", "d7347f02c180a448e60f73931845062ce00048750b584790278e9c93ef31ad81", "b68c6359a22b3bee6fecb8804311cfd816648ea31d530c9fb48e477e029d707a", "0d668a18ad7c2820214df6df95a6c855ce19fb1cb765f8ca620e45db76686d37", "3fbd2663bff65533246f1fabb9f38086854c6218aeb3dc9ac6ac73d4f0988f91", "949aa5719ca846052bfaa1b38c97b6eca3df3e24c0e0630042c6bccafbb4cdb5", "77b8a2b917bef5d54f3792183b014cca7798f713ff14fe0b2ac79b4c9f6f996d", "17e853cbd8dc00cba3cd9ffeb36f26a9f41a0eb92f80b62c2cda16771c935388", "5f682ed7a8cf2f98387c3def7c97f9f05ae39e39d393eeca3cf621268d6347f8", "ad77487eaf11fd8084ba4517a51766eb0e5b77dd3492dfa79aa3a2802fb29d20", "d247cfcacf9a8200ebaddf639f8c926ab0a001abe682f40df3785e80ed124e91", "195589442e11907eede1ee6524157f1125f68399f3170c835ff81c603b069f6c", "5b5ca0a67f3c54e7d3a6a862ef56168ec9ed1f4945e6c24de6d336b2be2e6f8c", "56430e4caa253015f1f998dce4a48a88af1953f68e94eca14f53074ae9c3e467", "0b1eed6a5bf612d1d8e08f5c546f3d12e838568fd3aa43ed4c537f10c65545d6", "7058db19a56dfff01988ac4a62e1310597f9c8d7ebde6890dadabf047d722d39", "b847380d6888ff7cd11402d086b19eccc40950b52c9d67e73cb4f8462f5df078", "df6c048419a2290ab546d527e9eeba349e7f7e1759bafe4adac507ce60ef9670", "91fc5b4b24fc3fbfea7f9a3d0f0437cb5733c0c2345d8bdffd7048d6e3b8a37b", "957d8ea51b37523952b6f5ae95462fcd4aed1483ef32cc80b69580aaeee03606", "efa82e4e91ad9ab781977400e9ac0bb9de7389aaedebdae979b73d1d3b8d72b0", "7400c9f3f3fc0cc6fe8cc37ab24b9771f44e9f78be913f73cd35fc4be030d6bd", "9bb28f4122d61f7bb56fe27ef706159fb802fef0f5de9dfa32c9c5b3183235f1", "40a8de6e98953498b806614532ea4abf8b99ad7f9719fb68203a6eae2efa5b2a", "412de0b218b8f7dcacc9205cd16ffb4eca5b838f46a2f4f9f534026061a47308", "17f56ecad51075080680ad9faa0fd8946b824d3296ddb20be07f9809fe8d1c5a", "fffd4e7ae885a41948a342b6647955a7ec8a8039039f510cff467ef597675457", "35e78e11b5ac46a29dd04ab0043136c3291f4ca56cb949ace33111ed56395463", "94824fc80230af82077c83bfc01dc9675b1f9d3d538b1e5f41c21ac753598691", "fa470ae314ca3fce493f21b423eef2a49522e09126f6f2326fa3c9cac0b344f7", "7078860b5b621b21ac7b95f9fc4739c8235ce5066a8b9bd7d938146a34fa88ec", "eea53560f0428bfd2eca4f86a5ce9dec5ff1309129a975d73465c1c9e9da71d1",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The regression test for the following invalid kademlia edge case.
|
|
||||||
|
|
||||||
Addresses used in this test are discovered as part of the simulation network
|
|
||||||
in higher level tests for streaming. They were generated randomly.
|
|
||||||
|
|
||||||
=========================================================================
|
|
||||||
Mon Apr 9 19:25:18 UTC 2018 KΛÐΞMLIΛ hive: queen's address: 5dd5c7
|
|
||||||
population: 13 (49), NeighbourhoodSize: 2, MinBinSize: 2, MaxBinSize: 4
|
|
||||||
000 2 e528 fad0 | 22 fad0 (0) e528 (0) e3bb (0) ed13 (0)
|
|
||||||
001 3 3f30 18e0 1dd3 | 7 3f30 (0) 23db (0) 10b6 (0) 18e0 (0)
|
|
||||||
002 4 7c54 7804 61e4 60f9 | 10 61e4 (0) 60f9 (0) 636c (0) 7186 (0)
|
|
||||||
003 2 40ae 4bae | 5 4bae (0) 4d5c (0) 403a (0) 40ae (0)
|
|
||||||
004 0 | 0
|
|
||||||
005 0 | 3 5808 (0) 5a0e (0) 5bdb (0)
|
|
||||||
============ DEPTH: 6 ==========================================
|
|
||||||
006 2 5f14 5f61 | 2 5f14 (0) 5f61 (0)
|
|
||||||
007 0 | 0
|
|
||||||
008 0 | 0
|
|
||||||
009 0 | 0
|
|
||||||
010 0 | 0
|
|
||||||
011 0 | 0
|
|
||||||
012 0 | 0
|
|
||||||
013 0 | 0
|
|
||||||
014 0 | 0
|
|
||||||
015 0 | 0
|
|
||||||
=========================================================================
|
|
||||||
*/
|
|
||||||
func TestKademliaCase5(t *testing.T) {
|
|
||||||
testKademliaCase(t,
|
|
||||||
"5dd5c77dd9006a800478fcebb02d48d4036389e7d3c8f6a83b97dbad13f4c0a9",
|
|
||||||
"78fafa0809929a1279ece089a51d12457c2d8416dff859aeb2ccc24bb50df5ec", "1dd39b1257e745f147cbbc3cadd609ccd6207c41056dbc4254bba5d2527d3ee5", "5f61dd66d4d94aec8fcc3ce0e7885c7edf30c43143fa730e2841c5d28e3cd081", "8aa8b0472cb351d967e575ad05c4b9f393e76c4b01ef4b3a54aac5283b78abc9", "4502f385152a915b438a6726ce3ea9342e7a6db91a23c2f6bee83a885ed7eb82", "718677a504249db47525e959ef1784bed167e1c46f1e0275b9c7b588e28a3758", "7c54c6ed1f8376323896ed3a4e048866410de189e9599dd89bf312ca4adb96b5", "18e03bd3378126c09e799a497150da5c24c895aedc84b6f0dbae41fc4bac081a", "23db76ac9e6e58d9f5395ca78252513a7b4118b4155f8462d3d5eec62486cadc", "40ae0e8f065e96c7adb7fa39505136401f01780481e678d718b7f6dbb2c906ec", "c1539998b8bae19d339d6bbb691f4e9daeb0e86847545229e80fe0dffe716e92", "ed139d73a2699e205574c08722ca9f030ad2d866c662f1112a276b91421c3cb9", "5bdb19584b7a36d09ca689422ef7e6bb681b8f2558a6b2177a8f7c812f631022", "636c9de7fe234ffc15d67a504c69702c719f626c17461d3f2918e924cd9d69e2", "de4455413ff9335c440d52458c6544191bd58a16d85f700c1de53b62773064ea", "de1963310849527acabc7885b6e345a56406a8f23e35e436b6d9725e69a79a83", "a80a50a467f561210a114cba6c7fb1489ed43a14d61a9edd70e2eb15c31f074d", "7804f12b8d8e6e4b375b242058242068a3809385e05df0e64973cde805cf729c", "60f9aa320c02c6f2e6370aa740cf7cea38083fa95fca8c99552cda52935c1520", "d8da963602390f6c002c00ce62a84b514edfce9ebde035b277a957264bb54d21", "8463d93256e026fe436abad44697152b9a56ac8e06a0583d318e9571b83d073c", "9a3f78fcefb9a05e40a23de55f6153d7a8b9d973ede43a380bf46bb3b3847de1", "e3bb576f4b3760b9ca6bff59326f4ebfc4a669d263fb7d67ab9797adea54ed13", "4d5cdbd6dcca5bdf819a0fe8d175dc55cc96f088d37462acd5ea14bc6296bdbe", "5a0ed28de7b5258c727cb85447071c74c00a5fbba9e6bc0393bc51944d04ab2a", "61e4ddb479c283c638f4edec24353b6cc7a3a13b930824aad016b0996ca93c47", "7e3610868acf714836cafaaa7b8c009a9ac6e3a6d443e5586cf661530a204ee2", "d74b244d4345d2c86e30a097105e4fb133d53c578320285132a952cdaa64416e", "cfeed57d0f935bfab89e3f630a7c97e0b1605f0724d85a008bbfb92cb47863a8", "580837af95055670e20d494978f60c7f1458dc4b9e389fc7aa4982b2aca3bce3", "df55c0c49e6c8a83d82dfa1c307d3bf6a20e18721c80d8ec4f1f68dc0a137ced", "5f149c51ce581ba32a285439a806c063ced01ccd4211cd024e6a615b8f216f95", "1eb76b00aeb127b10dd1b7cd4c3edeb4d812b5a658f0feb13e85c4d2b7c6fe06", "7a56ba7c3fb7cbfb5561a46a75d95d7722096b45771ec16e6fa7bbfab0b35dfe", "4bae85ad88c28470f0015246d530adc0cd1778bdd5145c3c6b538ee50c4e04bd", "afd1892e2a7145c99ec0ebe9ded0d3fec21089b277a68d47f45961ec5e39e7e0", "953138885d7b36b0ef79e46030f8e61fd7037fbe5ce9e0a94d728e8c8d7eab86", "de761613ef305e4f628cb6bf97d7b7dc69a9d513dc233630792de97bcda777a6", "3f3087280063d09504c084bbf7fdf984347a72b50d097fd5b086ffabb5b3fb4c", "7d18a94bb1ebfdef4d3e454d2db8cb772f30ca57920dd1e402184a9e598581a0", "a7d6fbdc9126d9f10d10617f49fb9f5474ffe1b229f76b7dd27cebba30eccb5d", "fad0246303618353d1387ec10c09ee991eb6180697ed3470ed9a6b377695203d", "1cf66e09ea51ee5c23df26615a9e7420be2ac8063f28f60a3bc86020e94fe6f3", "8269cdaa153da7c358b0b940791af74d7c651cd4d3f5ed13acfe6d0f2c539e7f", "90d52eaaa60e74bf1c79106113f2599471a902d7b1c39ac1f55b20604f453c09", "9788fd0c09190a3f3d0541f68073a2f44c2fcc45bb97558a7c319f36c25a75b3", "10b68fc44157ecfdae238ee6c1ce0333f906ad04d1a4cb1505c8e35c3c87fbb0", "e5284117fdf3757920475c786e0004cb00ba0932163659a89b36651a01e57394", "403ad51d911e113dcd5f9ff58c94f6d278886a2a4da64c3ceca2083282c92de3",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestDiscoveryPeer(addr pot.Address, kad *Kademlia) *Peer {
|
func newTestDiscoveryPeer(addr pot.Address, kad *Kademlia) *Peer {
|
||||||
rw := &p2p.MsgPipeRW{}
|
rw := &p2p.MsgPipeRW{}
|
||||||
p := p2p.NewPeer(enode.ID{}, "foo", []p2p.Cap{})
|
p := p2p.NewPeer(enode.ID{}, "foo", []p2p.Cap{})
|
||||||
|
|
|
||||||
|
|
@ -76,13 +76,12 @@ func TestNetworkID(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error setting up network: %v", err)
|
t.Fatalf("Error setting up network: %v", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
//shutdown the snapshot network
|
|
||||||
log.Trace("Shutting down network")
|
|
||||||
net.Shutdown()
|
|
||||||
}()
|
|
||||||
//let's sleep to ensure all nodes are connected
|
//let's sleep to ensure all nodes are connected
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
// shutdown the the network to avoid race conditions
|
||||||
|
// on accessing kademlias global map while network nodes
|
||||||
|
// are accepting messages
|
||||||
|
net.Shutdown()
|
||||||
//for each group sharing the same network ID...
|
//for each group sharing the same network ID...
|
||||||
for _, netIDGroup := range nodeMap {
|
for _, netIDGroup := range nodeMap {
|
||||||
log.Trace("netIDGroup size", "size", len(netIDGroup))
|
log.Trace("netIDGroup size", "size", len(netIDGroup))
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ func (b *Bzz) APIs() []rpc.API {
|
||||||
func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error {
|
func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error {
|
||||||
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
return func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
// wait for the bzz protocol to perform the handshake
|
// wait for the bzz protocol to perform the handshake
|
||||||
handshake, _ := b.GetHandshake(p.ID())
|
handshake, _ := b.GetOrCreateHandshake(p.ID())
|
||||||
defer b.removeHandshake(p.ID())
|
defer b.removeHandshake(p.ID())
|
||||||
select {
|
select {
|
||||||
case <-handshake.done:
|
case <-handshake.done:
|
||||||
|
|
@ -213,7 +213,7 @@ func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error
|
||||||
// runBzz is the p2p protocol run function for the bzz base protocol
|
// runBzz is the p2p protocol run function for the bzz base protocol
|
||||||
// that negotiates the bzz handshake
|
// that negotiates the bzz handshake
|
||||||
func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
handshake, _ := b.GetHandshake(p.ID())
|
handshake, _ := b.GetOrCreateHandshake(p.ID())
|
||||||
if !<-handshake.init {
|
if !<-handshake.init {
|
||||||
return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], p.ID().Bytes()[:4])
|
return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], p.ID().Bytes()[:4])
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +303,7 @@ func (b *Bzz) removeHandshake(peerID enode.ID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHandshake returns the bzz handhake that the remote peer with peerID sent
|
// GetHandshake returns the bzz handhake that the remote peer with peerID sent
|
||||||
func (b *Bzz) GetHandshake(peerID enode.ID) (*HandshakeMsg, bool) {
|
func (b *Bzz) GetOrCreateHandshake(peerID enode.ID) (*HandshakeMsg, bool) {
|
||||||
b.mtx.Lock()
|
b.mtx.Lock()
|
||||||
defer b.mtx.Unlock()
|
defer b.mtx.Unlock()
|
||||||
handshake, found := b.handshakes[peerID]
|
handshake, found := b.handshakes[peerID]
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -225,6 +226,7 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
randomAddr := RandomAddr()
|
randomAddr := RandomAddr()
|
||||||
pt := newBzzHandshakeTester(1, randomAddr, false)
|
pt := newBzzHandshakeTester(1, randomAddr, false)
|
||||||
|
|
||||||
node := pt.Nodes[0]
|
node := pt.Nodes[0]
|
||||||
addr := NewAddr(node)
|
addr := NewAddr(node)
|
||||||
|
|
||||||
|
|
@ -237,9 +239,15 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
|
||||||
|
case <-pt.bzz.handshakes[node.ID()].done:
|
||||||
if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
|
if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
|
||||||
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
|
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
|
||||||
}
|
}
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatal("test timeout")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,12 @@ package discovery
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -86,12 +82,10 @@ func getDbStore(nodeID string) (*state.DBStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nodeCount = flag.Int("nodes", 10, "number of nodes to create (default 10)")
|
nodeCount = flag.Int("nodes", 32, "number of nodes to create (default 32)")
|
||||||
initCount = flag.Int("conns", 1, "number of originally connected peers (default 1)")
|
initCount = flag.Int("conns", 1, "number of originally connected peers (default 1)")
|
||||||
snapshotFile = flag.String("snapshot", "", "path to create snapshot file in")
|
|
||||||
loglevel = flag.Int("loglevel", 3, "verbosity of logs")
|
loglevel = flag.Int("loglevel", 3, "verbosity of logs")
|
||||||
rawlog = flag.Bool("rawlog", false, "remove terminal formatting from logs")
|
rawlog = flag.Bool("rawlog", false, "remove terminal formatting from logs")
|
||||||
serviceOverride = flag.String("services", "", "remove or add services to the node snapshot; prefix with \"+\" to add, \"-\" to remove; example: +pss,-discovery")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -157,7 +151,6 @@ 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 {
|
||||||
|
|
@ -185,7 +178,6 @@ 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
|
||||||
|
|
||||||
|
|
@ -247,25 +239,14 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
||||||
action := func(ctx context.Context) error {
|
action := func(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
for i := range ids {
|
for i := range ids {
|
||||||
// collect the overlay addresses, to
|
// collect the overlay addresses, to
|
||||||
addrs = append(addrs, ids[i].Bytes())
|
addrs = append(addrs, ids[i].Bytes())
|
||||||
for j := 0; j < conns; j++ {
|
|
||||||
var k int
|
|
||||||
if j == 0 {
|
|
||||||
k = (i + 1) % len(ids)
|
|
||||||
} else {
|
|
||||||
k = rand.Intn(len(ids))
|
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
err := net.ConnectNodesChain(nil)
|
||||||
go func(i, k int) {
|
if err != nil {
|
||||||
defer wg.Done()
|
return nil, err
|
||||||
net.Connect(ids[i], ids[k])
|
|
||||||
}(i, k)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
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(network.NewKadParams().NeighbourhoodSize, addrs)
|
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
|
||||||
|
|
@ -286,10 +267,10 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
||||||
}
|
}
|
||||||
|
|
||||||
healthy := &network.Health{}
|
healthy := &network.Health{}
|
||||||
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
|
if err := client.Call(&healthy, "hive_healthy", ppmap[common.Bytes2Hex(id.Bytes())]); 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: connected nearest neighbours: %v, know nearest neighbours: %v,\n\n%v", id, healthy.ConnectNN, healthy.KnowNN, healthy.Hive))
|
log.Debug(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.ConnectNN, nil
|
return healthy.KnowNN && healthy.ConnectNN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,40 +290,6 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if *snapshotFile != "" {
|
|
||||||
var err error
|
|
||||||
var snap *simulations.Snapshot
|
|
||||||
if len(*serviceOverride) > 0 {
|
|
||||||
var addServices []string
|
|
||||||
var removeServices []string
|
|
||||||
for _, osvc := range strings.Split(*serviceOverride, ",") {
|
|
||||||
if strings.Index(osvc, "+") == 0 {
|
|
||||||
addServices = append(addServices, osvc[1:])
|
|
||||||
} else if strings.Index(osvc, "-") == 0 {
|
|
||||||
removeServices = append(removeServices, osvc[1:])
|
|
||||||
} else {
|
|
||||||
panic("stick to the rules, you know what they are")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
snap, err = net.SnapshotWithServices(addServices, removeServices)
|
|
||||||
} else {
|
|
||||||
snap, err = net.Snapshot()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.New("no shapshot dude")
|
|
||||||
}
|
|
||||||
jsonsnapshot, err := json.Marshal(snap)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("corrupt json snapshot: %v", err)
|
|
||||||
}
|
|
||||||
log.Info("writing snapshot", "file", *snapshotFile)
|
|
||||||
err = ioutil.WriteFile(*snapshotFile, jsonsnapshot, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,7 +352,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
||||||
healthy := &network.Health{}
|
healthy := &network.Health{}
|
||||||
addr := id.String()
|
addr := id.String()
|
||||||
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
|
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
|
||||||
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
|
if err := client.Call(&healthy, "hive_healthy", ppmap[common.Bytes2Hex(id.Bytes())]); err != nil {
|
||||||
return fmt.Errorf("error getting node health: %s", err)
|
return fmt.Errorf("error getting node health: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,23 +404,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
//connects in a chain
|
net.ConnectNodesChain(nil)
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
//connects in a ring
|
|
||||||
for i := range ids {
|
|
||||||
for j := 1; j <= conns; j++ {
|
|
||||||
k := (i + j) % len(ids)
|
|
||||||
if k == i {
|
|
||||||
k = (k + 1) % len(ids)
|
|
||||||
}
|
|
||||||
wg.Add(1)
|
|
||||||
go func(i, k int) {
|
|
||||||
defer wg.Done()
|
|
||||||
net.Connect(ids[i], ids[k])
|
|
||||||
}(i, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
||||||
check := func(ctx context.Context, id enode.ID) (bool, error) {
|
check := func(ctx context.Context, id enode.ID) (bool, error) {
|
||||||
|
|
@ -494,7 +425,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
|
||||||
healthy := &network.Health{}
|
healthy := &network.Health{}
|
||||||
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
|
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
|
||||||
|
|
||||||
if err := client.Call(&healthy, "hive_healthy", ppmap); err != nil {
|
if err := client.Call(&healthy, "hive_healthy", ppmap[common.Bytes2Hex(id.Bytes())]); 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", id, healthy.ConnectNN, healthy.KnowNN))
|
log.Info(fmt.Sprintf("node %4s healthy: got nearest neighbours: %v, know nearest neighbours: %v", id, healthy.ConnectNN, healthy.KnowNN))
|
||||||
|
|
|
||||||
|
|
@ -666,17 +666,16 @@ func peerStreamIntervalsKey(p *Peer, s Stream) string {
|
||||||
return p.ID().String() + s.String()
|
return p.ID().String() + s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c client) AddInterval(start, end uint64) (err error) {
|
func (c *client) AddInterval(start, end uint64) (err error) {
|
||||||
i := &intervals.Intervals{}
|
i := &intervals.Intervals{}
|
||||||
err = c.intervalsStore.Get(c.intervalsKey, i)
|
if err = c.intervalsStore.Get(c.intervalsKey, i); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i.Add(start, end)
|
i.Add(start, end)
|
||||||
return c.intervalsStore.Put(c.intervalsKey, i)
|
return c.intervalsStore.Put(c.intervalsKey, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c client) NextInterval() (start, end uint64, err error) {
|
func (c *client) NextInterval() (start, end uint64, err error) {
|
||||||
i := &intervals.Intervals{}
|
i := &intervals.Intervals{}
|
||||||
err = c.intervalsStore.Get(c.intervalsKey, i)
|
err = c.intervalsStore.Get(c.intervalsKey, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -733,11 +732,7 @@ func (c *client) batchDone(p *Peer, req *OfferedHashesMsg, hashes []byte) error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// TODO: make a test case for testing if the interval is added when the batch is done
|
return c.AddInterval(req.From, req.To)
|
||||||
if err := c.AddInterval(req.From, req.To); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) close() {
|
func (c *client) close() {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ type mockNetFetcher struct {
|
||||||
quit <-chan struct{}
|
quit <-chan struct{}
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
hopCounts []uint8
|
hopCounts []uint8
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetFetcher) Offer(ctx context.Context, source *enode.ID) {
|
func (m *mockNetFetcher) Offer(ctx context.Context, source *enode.ID) {
|
||||||
|
|
@ -51,6 +52,9 @@ func (m *mockNetFetcher) Offer(ctx context.Context, source *enode.ID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetFetcher) Request(ctx context.Context, hopCount uint8) {
|
func (m *mockNetFetcher) Request(ctx context.Context, hopCount uint8) {
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
m.requestCalled = true
|
m.requestCalled = true
|
||||||
var peers []Address
|
var peers []Address
|
||||||
m.peers.Range(func(key interface{}, _ interface{}) bool {
|
m.peers.Range(func(key interface{}, _ interface{}) bool {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 0 // Major version component of the current release
|
VersionMajor = 0 // Major version component of the current release
|
||||||
VersionMinor = 3 // Minor version component of the current release
|
VersionMinor = 3 // Minor version component of the current release
|
||||||
VersionPatch = 9 // Patch version component of the current release
|
VersionPatch = 10 // Patch version component of the current release
|
||||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue