mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 02:42:27 +00:00
commit
6fe1915e0d
144 changed files with 9608 additions and 1475 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
|
@ -10,4 +10,7 @@ les/ @zsfelfoldi
|
||||||
light/ @zsfelfoldi
|
light/ @zsfelfoldi
|
||||||
mobile/ @karalabe
|
mobile/ @karalabe
|
||||||
p2p/ @fjl @zsfelfoldi
|
p2p/ @fjl @zsfelfoldi
|
||||||
|
p2p/simulations @zelig @nonsense @janos @justelad
|
||||||
|
p2p/protocols @zelig @nonsense @janos @justelad
|
||||||
|
p2p/testing @zelig @nonsense @janos @justelad
|
||||||
whisper/ @gballet @gluk256
|
whisper/ @gballet @gluk256
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ matrix:
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
before_install:
|
before_install:
|
||||||
- curl https://storage.googleapis.com/golang/go1.11.4.linux-amd64.tar.gz | tar -xz
|
- curl https://storage.googleapis.com/golang/go1.11.5.linux-amd64.tar.gz | tar -xz
|
||||||
- export PATH=`pwd`/go/bin:$PATH
|
- export PATH=`pwd`/go/bin:$PATH
|
||||||
- export GOROOT=`pwd`/go
|
- export GOROOT=`pwd`/go
|
||||||
- export GOPATH=$HOME/go
|
- export GOPATH=$HOME/go
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
||||||
func set(dst, src reflect.Value) error {
|
func set(dst, src reflect.Value) error {
|
||||||
dstType, srcType := dst.Type(), src.Type()
|
dstType, srcType := dst.Type(), src.Type()
|
||||||
switch {
|
switch {
|
||||||
case dstType.Kind() == reflect.Interface:
|
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
|
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
|
|
|
||||||
|
|
@ -512,6 +512,11 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
Int *big.Int
|
Int *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newInterfaceSlice := func(len int) interface{} {
|
||||||
|
slice := make([]interface{}, len)
|
||||||
|
return &slice
|
||||||
|
}
|
||||||
|
|
||||||
abi, data, expected := methodMultiReturn(require.New(t))
|
abi, data, expected := methodMultiReturn(require.New(t))
|
||||||
bigint := new(big.Int)
|
bigint := new(big.Int)
|
||||||
var testCases = []struct {
|
var testCases = []struct {
|
||||||
|
|
@ -539,6 +544,16 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
&[2]interface{}{&expected.Int, &expected.String},
|
&[2]interface{}{&expected.Int, &expected.String},
|
||||||
"",
|
"",
|
||||||
"Can unpack into an array",
|
"Can unpack into an array",
|
||||||
|
}, {
|
||||||
|
&[2]interface{}{},
|
||||||
|
&[2]interface{}{expected.Int, expected.String},
|
||||||
|
"",
|
||||||
|
"Can unpack into interface array",
|
||||||
|
}, {
|
||||||
|
newInterfaceSlice(2),
|
||||||
|
&[]interface{}{expected.Int, expected.String},
|
||||||
|
"",
|
||||||
|
"Can unpack into interface slice",
|
||||||
}, {
|
}, {
|
||||||
&[]interface{}{new(int), new(int)},
|
&[]interface{}{new(int), new(int)},
|
||||||
&[]interface{}{&expected.Int, &expected.String},
|
&[]interface{}{&expected.Int, &expected.String},
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/usbwallet/internal/trezor"
|
"github.com/ethereum/go-ethereum/accounts/usbwallet/trezor"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -41,6 +41,9 @@ import (
|
||||||
// encoded passphrase.
|
// encoded passphrase.
|
||||||
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
|
||||||
|
|
||||||
|
// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
|
||||||
|
var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
|
||||||
|
|
||||||
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
|
||||||
// if the device replies with a mismatching header. This usually means the device
|
// if the device replies with a mismatching header. This usually means the device
|
||||||
// is in browser mode.
|
// is in browser mode.
|
||||||
|
|
@ -48,12 +51,13 @@ var errTrezorReplyInvalidHeader = errors.New("trezor: invalid reply header")
|
||||||
|
|
||||||
// trezorDriver implements the communication with a Trezor hardware wallet.
|
// trezorDriver implements the communication with a Trezor hardware wallet.
|
||||||
type trezorDriver struct {
|
type trezorDriver struct {
|
||||||
device io.ReadWriter // USB device connection to communicate through
|
device io.ReadWriter // USB device connection to communicate through
|
||||||
version [3]uint32 // Current version of the Trezor firmware
|
version [3]uint32 // Current version of the Trezor firmware
|
||||||
label string // Current textual label of the Trezor device
|
label string // Current textual label of the Trezor device
|
||||||
pinwait bool // Flags whether the device is waiting for PIN entry
|
pinwait bool // Flags whether the device is waiting for PIN entry
|
||||||
failure error // Any failure that would make the device unusable
|
passphrasewait bool // Flags whether the device is waiting for passphrase entry
|
||||||
log log.Logger // Contextual logger to tag the trezor with its id
|
failure error // Any failure that would make the device unusable
|
||||||
|
log log.Logger // Contextual logger to tag the trezor with its id
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
|
// newTrezorDriver creates a new instance of a Trezor USB protocol driver.
|
||||||
|
|
@ -79,7 +83,7 @@ func (w *trezorDriver) Status() (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open implements usbwallet.driver, attempting to initialize the connection to
|
// Open implements usbwallet.driver, attempting to initialize the connection to
|
||||||
// the Trezor hardware wallet. Initializing the Trezor is a two phase operation:
|
// the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
|
||||||
// * The first phase is to initialize the connection and read the wallet's
|
// * The first phase is to initialize the connection and read the wallet's
|
||||||
// features. This phase is invoked is the provided passphrase is empty. The
|
// features. This phase is invoked is the provided passphrase is empty. The
|
||||||
// device will display the pinpad as a result and will return an appropriate
|
// device will display the pinpad as a result and will return an appropriate
|
||||||
|
|
@ -87,11 +91,13 @@ func (w *trezorDriver) Status() (string, error) {
|
||||||
// * The second phase is to unlock access to the Trezor, which is done by the
|
// * The second phase is to unlock access to the Trezor, which is done by the
|
||||||
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
// user actually providing a passphrase mapping a keyboard keypad to the pin
|
||||||
// number of the user (shuffled according to the pinpad displayed).
|
// number of the user (shuffled according to the pinpad displayed).
|
||||||
|
// * If needed the device will ask for passphrase which will require calling
|
||||||
|
// open again with the actual passphrase (3rd phase)
|
||||||
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
w.device, w.failure = device, nil
|
w.device, w.failure = device, nil
|
||||||
|
|
||||||
// If phase 1 is requested, init the connection and wait for user callback
|
// If phase 1 is requested, init the connection and wait for user callback
|
||||||
if passphrase == "" {
|
if passphrase == "" && !w.passphrasewait {
|
||||||
// If we're already waiting for a PIN entry, insta-return
|
// If we're already waiting for a PIN entry, insta-return
|
||||||
if w.pinwait {
|
if w.pinwait {
|
||||||
return ErrTrezorPINNeeded
|
return ErrTrezorPINNeeded
|
||||||
|
|
@ -104,26 +110,46 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
||||||
w.label = features.GetLabel()
|
w.label = features.GetLabel()
|
||||||
|
|
||||||
// Do a manual ping, forcing the device to ask for its PIN
|
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
||||||
askPin := true
|
askPin := true
|
||||||
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin}, new(trezor.PinMatrixRequest), new(trezor.Success))
|
askPassphrase := true
|
||||||
|
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Only return the PIN request if the device wasn't unlocked until now
|
// Only return the PIN request if the device wasn't unlocked until now
|
||||||
if res == 1 {
|
switch res {
|
||||||
return nil // Device responded with trezor.Success
|
case 0:
|
||||||
|
w.pinwait = true
|
||||||
|
return ErrTrezorPINNeeded
|
||||||
|
case 1:
|
||||||
|
w.pinwait = false
|
||||||
|
w.passphrasewait = true
|
||||||
|
return ErrTrezorPassphraseNeeded
|
||||||
|
case 2:
|
||||||
|
return nil // responded with trezor.Success
|
||||||
}
|
}
|
||||||
w.pinwait = true
|
|
||||||
return ErrTrezorPINNeeded
|
|
||||||
}
|
}
|
||||||
// Phase 2 requested with actual PIN entry
|
// Phase 2 requested with actual PIN entry
|
||||||
w.pinwait = false
|
if w.pinwait {
|
||||||
|
w.pinwait = false
|
||||||
if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil {
|
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
||||||
w.failure = err
|
if err != nil {
|
||||||
return err
|
w.failure = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res == 1 {
|
||||||
|
w.passphrasewait = true
|
||||||
|
return ErrTrezorPassphraseNeeded
|
||||||
|
}
|
||||||
|
} else if w.passphrasewait {
|
||||||
|
w.passphrasewait = false
|
||||||
|
if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
|
||||||
|
w.failure = err
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ environment:
|
||||||
install:
|
install:
|
||||||
- git submodule update --init
|
- git submodule update --init
|
||||||
- rmdir C:\go /s /q
|
- rmdir C:\go /s /q
|
||||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.4.windows-%GETH_ARCH%.zip
|
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.5.windows-%GETH_ARCH%.zip
|
||||||
- 7z x go1.11.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
- 7z x go1.11.5.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||||
- go version
|
- go version
|
||||||
- gcc --version
|
- gcc --version
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -513,7 +513,7 @@ func doDebianSource(cmdline []string) {
|
||||||
for _, distro := range debDistros {
|
for _, distro := range debDistros {
|
||||||
meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
|
meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
|
||||||
pkgdir := stageDebianSource(*workdir, meta)
|
pkgdir := stageDebianSource(*workdir, meta)
|
||||||
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc")
|
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d")
|
||||||
debuild.Dir = pkgdir
|
debuild.Dir = pkgdir
|
||||||
build.MustRun(debuild)
|
build.MustRun(debuild)
|
||||||
|
|
||||||
|
|
@ -523,7 +523,7 @@ func doDebianSource(cmdline []string) {
|
||||||
build.MustRunCommand("debsign", changes)
|
build.MustRunCommand("debsign", changes)
|
||||||
}
|
}
|
||||||
if *upload != "" {
|
if *upload != "" {
|
||||||
build.MustRunCommand("dput", *upload, changes)
|
build.MustRunCommand("dput", "--passive", "--no-upload-log", *upload, changes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,13 @@ func main() {
|
||||||
if !realaddr.IP.IsLoopback() {
|
if !realaddr.IP.IsLoopback() {
|
||||||
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||||
}
|
}
|
||||||
// TODO: react to external IP changes over time.
|
|
||||||
if ext, err := natm.ExternalIP(); err == nil {
|
if ext, err := natm.ExternalIP(); err == nil {
|
||||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printNotice(&nodeKey.PublicKey, *realaddr)
|
||||||
|
|
||||||
if *runv5 {
|
if *runv5 {
|
||||||
if _, err := discv5.ListenUDP(nodeKey, conn, "", restrictList); err != nil {
|
if _, err := discv5.ListenUDP(nodeKey, conn, "", restrictList); err != nil {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
|
|
@ -136,3 +137,13 @@ func main() {
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
|
||||||
|
if addr.IP.IsUnspecified() {
|
||||||
|
addr.IP = net.IP{127, 0, 0, 1}
|
||||||
|
}
|
||||||
|
n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port)
|
||||||
|
fmt.Println(n.String())
|
||||||
|
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
|
||||||
|
fmt.Println("We recommend using a regular node as bootstrap node for production deployments.")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/dashboard"
|
"github.com/ethereum/go-ethereum/dashboard"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/graphql"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
|
|
@ -124,6 +125,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply flags.
|
// Apply flags.
|
||||||
|
utils.SetULC(ctx, &cfg.Eth)
|
||||||
utils.SetNodeConfig(ctx, &cfg.Node)
|
utils.SetNodeConfig(ctx, &cfg.Node)
|
||||||
stack, err := node.New(&cfg.Node)
|
stack, err := node.New(&cfg.Node)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -176,6 +178,13 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
utils.RegisterShhService(stack, &cfg.Shh)
|
utils.RegisterShhService(stack, &cfg.Shh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure GraphQL if required
|
||||||
|
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
|
||||||
|
if err := graphql.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts); err != nil {
|
||||||
|
utils.Fatalf("Failed to register the Ethereum service: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the Ethereum Stats daemon if requested.
|
// Add the Ethereum Stats daemon if requested.
|
||||||
if cfg.Ethstats.URL != "" {
|
if cfg.Ethstats.URL != "" {
|
||||||
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -82,6 +82,10 @@ var (
|
||||||
utils.TxPoolAccountQueueFlag,
|
utils.TxPoolAccountQueueFlag,
|
||||||
utils.TxPoolGlobalQueueFlag,
|
utils.TxPoolGlobalQueueFlag,
|
||||||
utils.TxPoolLifetimeFlag,
|
utils.TxPoolLifetimeFlag,
|
||||||
|
utils.ULCModeConfigFlag,
|
||||||
|
utils.OnlyAnnounceModeFlag,
|
||||||
|
utils.ULCTrustedNodesFlag,
|
||||||
|
utils.ULCMinTrustedFractionFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
utils.LightServFlag,
|
utils.LightServFlag,
|
||||||
|
|
@ -141,6 +145,11 @@ var (
|
||||||
utils.RPCEnabledFlag,
|
utils.RPCEnabledFlag,
|
||||||
utils.RPCListenAddrFlag,
|
utils.RPCListenAddrFlag,
|
||||||
utils.RPCPortFlag,
|
utils.RPCPortFlag,
|
||||||
|
utils.GraphQLEnabledFlag,
|
||||||
|
utils.GraphQLListenAddrFlag,
|
||||||
|
utils.GraphQLPortFlag,
|
||||||
|
utils.GraphQLCORSDomainFlag,
|
||||||
|
utils.GraphQLVirtualHostsFlag,
|
||||||
utils.RPCApiFlag,
|
utils.RPCApiFlag,
|
||||||
utils.WSEnabledFlag,
|
utils.WSEnabledFlag,
|
||||||
utils.WSListenAddrFlag,
|
utils.WSListenAddrFlag,
|
||||||
|
|
@ -164,7 +173,7 @@ var (
|
||||||
utils.MetricsInfluxDBDatabaseFlag,
|
utils.MetricsInfluxDBDatabaseFlag,
|
||||||
utils.MetricsInfluxDBUsernameFlag,
|
utils.MetricsInfluxDBUsernameFlag,
|
||||||
utils.MetricsInfluxDBPasswordFlag,
|
utils.MetricsInfluxDBPasswordFlag,
|
||||||
utils.MetricsInfluxDBHostTagFlag,
|
utils.MetricsInfluxDBTagsFlag,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AppHelpTemplate is the test template for the default, global app help topic.
|
// AppHelpTemplate is the test template for the default, global app help topic.
|
||||||
|
|
@ -229,7 +229,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.MetricsInfluxDBDatabaseFlag,
|
utils.MetricsInfluxDBDatabaseFlag,
|
||||||
utils.MetricsInfluxDBUsernameFlag,
|
utils.MetricsInfluxDBUsernameFlag,
|
||||||
utils.MetricsInfluxDBPasswordFlag,
|
utils.MetricsInfluxDBPasswordFlag,
|
||||||
utils.MetricsInfluxDBHostTagFlag,
|
utils.MetricsInfluxDBTagsFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -223,28 +223,29 @@ type parityChainSpec struct {
|
||||||
} `json:"engine"`
|
} `json:"engine"`
|
||||||
|
|
||||||
Params struct {
|
Params struct {
|
||||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||||
ChainID hexutil.Uint64 `json:"chainID"`
|
ChainID hexutil.Uint64 `json:"chainID"`
|
||||||
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
||||||
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
||||||
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
||||||
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
||||||
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
||||||
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
||||||
EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
|
EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
|
||||||
EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
|
EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
|
||||||
EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
|
EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
|
||||||
EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
|
EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
|
||||||
EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
|
EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
|
||||||
EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
|
EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
|
||||||
EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
|
EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
|
||||||
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
|
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
|
||||||
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
|
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
|
||||||
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
|
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
|
||||||
|
EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"`
|
||||||
} `json:"params"`
|
} `json:"params"`
|
||||||
|
|
||||||
Genesis struct {
|
Genesis struct {
|
||||||
|
|
@ -347,6 +348,11 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
||||||
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||||
spec.setConstantinople(num)
|
spec.setConstantinople(num)
|
||||||
}
|
}
|
||||||
|
// ConstantinopleFix (remove eip-1283)
|
||||||
|
if num := genesis.Config.PetersburgBlock; num != nil {
|
||||||
|
spec.setConstantinopleFix(num)
|
||||||
|
}
|
||||||
|
|
||||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||||
|
|
@ -441,6 +447,10 @@ func (spec *parityChainSpec) setConstantinople(num *big.Int) {
|
||||||
spec.Params.EIP1283Transition = n
|
spec.Params.EIP1283Transition = n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
|
||||||
|
spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
||||||
// Python Ethereum implementation.
|
// Python Ethereum implementation.
|
||||||
type pyEthereumGenesisSpec struct {
|
type pyEthereumGenesisSpec struct {
|
||||||
|
|
|
||||||
|
|
@ -608,30 +608,31 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
|
||||||
bootPython[i] = "'" + boot + "'"
|
bootPython[i] = "'" + boot + "'"
|
||||||
}
|
}
|
||||||
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
|
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
|
||||||
"Network": network,
|
"Network": network,
|
||||||
"NetworkID": conf.Genesis.Config.ChainID,
|
"NetworkID": conf.Genesis.Config.ChainID,
|
||||||
"NetworkTitle": strings.Title(network),
|
"NetworkTitle": strings.Title(network),
|
||||||
"EthstatsPage": config.ethstats,
|
"EthstatsPage": config.ethstats,
|
||||||
"ExplorerPage": config.explorer,
|
"ExplorerPage": config.explorer,
|
||||||
"WalletPage": config.wallet,
|
"WalletPage": config.wallet,
|
||||||
"FaucetPage": config.faucet,
|
"FaucetPage": config.faucet,
|
||||||
"GethGenesis": network + ".json",
|
"GethGenesis": network + ".json",
|
||||||
"Bootnodes": conf.bootnodes,
|
"Bootnodes": conf.bootnodes,
|
||||||
"BootnodesFlat": strings.Join(conf.bootnodes, ","),
|
"BootnodesFlat": strings.Join(conf.bootnodes, ","),
|
||||||
"Ethstats": statsLogin,
|
"Ethstats": statsLogin,
|
||||||
"Ethash": conf.Genesis.Config.Ethash != nil,
|
"Ethash": conf.Genesis.Config.Ethash != nil,
|
||||||
"CppGenesis": network + "-cpp.json",
|
"CppGenesis": network + "-cpp.json",
|
||||||
"CppBootnodes": strings.Join(bootCpp, " "),
|
"CppBootnodes": strings.Join(bootCpp, " "),
|
||||||
"HarmonyGenesis": network + "-harmony.json",
|
"HarmonyGenesis": network + "-harmony.json",
|
||||||
"HarmonyBootnodes": strings.Join(bootHarmony, " "),
|
"HarmonyBootnodes": strings.Join(bootHarmony, " "),
|
||||||
"ParityGenesis": network + "-parity.json",
|
"ParityGenesis": network + "-parity.json",
|
||||||
"PythonGenesis": network + "-python.json",
|
"PythonGenesis": network + "-python.json",
|
||||||
"PythonBootnodes": strings.Join(bootPython, ","),
|
"PythonBootnodes": strings.Join(bootPython, ","),
|
||||||
"Homestead": conf.Genesis.Config.HomesteadBlock,
|
"Homestead": conf.Genesis.Config.HomesteadBlock,
|
||||||
"Tangerine": conf.Genesis.Config.EIP150Block,
|
"Tangerine": conf.Genesis.Config.EIP150Block,
|
||||||
"Spurious": conf.Genesis.Config.EIP155Block,
|
"Spurious": conf.Genesis.Config.EIP155Block,
|
||||||
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
|
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
|
||||||
"Constantinople": conf.Genesis.Config.ConstantinopleBlock,
|
"Constantinople": conf.Genesis.Config.ConstantinopleBlock,
|
||||||
|
"ConstantinopleFix": conf.Genesis.Config.PetersburgBlock,
|
||||||
})
|
})
|
||||||
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
|
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,10 @@ func (w *wizard) manageGenesis() {
|
||||||
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
|
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
|
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("Which block should Constantinople-Fix (remove EIP-1283) come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
|
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
|
|
||||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||||
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,61 +17,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
var SwarmBootnodes = []string{
|
var SwarmBootnodes = []string{
|
||||||
// Foundation Swarm Gateway Cluster
|
// EF Swarm Bootnode - AWS - eu-central-1
|
||||||
"enode://e5c6f9215c919a5450a7b8c14c22535607b69f2c8e1e7f6f430cb25d7a2c27cd1df4c4f18ad7c1d7e5162e271ffcd3f20b1a1467fb6e790e7d727f3b2193de97@52.232.7.187:30399",
|
"enode://4c113504601930bf2000c29bcd98d1716b6167749f58bad703bae338332fe93cc9d9204f08afb44100dc7bea479205f5d162df579f9a8f76f8b402d339709023@3.122.203.99:30301",
|
||||||
"enode://9b2fe07e69ccc7db5fef15793dab7d7d2e697ed92132d6e9548218e68a34613a8671ad03a6658d862b468ed693cae8a0f8f8d37274e4a657ffb59ca84676e45b@52.232.7.187:30400",
|
// EF Swarm Bootnode - AWS - us-west-2
|
||||||
"enode://76c1059162c93ef9df0f01097c824d17c492634df211ef4c806935b349082233b63b90c23970254b3b7138d630400f7cf9b71e80355a446a8b733296cb04169a@52.232.7.187:30401",
|
"enode://89f2ede3371bff1ad9f2088f2012984e280287a4e2b68007c2a6ad994909c51886b4a8e9e2ecc97f9910aca538398e0a5804b0ee80a187fde1ba4f32626322ba@52.35.212.179:30301",
|
||||||
"enode://ce46bbe2a8263145d65252d52da06e000ad350ed09c876a71ea9544efa42f63c1e1b6cc56307373aaad8f9dd069c90d0ed2dd1530106200e16f4ca681dd8ae2d@52.232.7.187:30402",
|
|
||||||
"enode://f431e0d6008a6c35c6e670373d828390c8323e53da8158e7bfc43cf07e632cc9e472188be8df01decadea2d4a068f1428caba769b632554a8fb0607bc296988f@52.232.7.187:30403",
|
|
||||||
"enode://174720abfff83d7392f121108ae50ea54e04889afe020df883655c0f6cb95414db945a0228d8982fe000d86fc9f4b7669161adc89cd7cd56f78f01489ab2b99b@52.232.7.187:30404",
|
|
||||||
"enode://2ae89be4be61a689b6f9ecee4360a59e185e010ab750f14b63b4ae43d4180e872e18e3437d4386ce44875dc7cc6eb761acba06412fe3178f3dac1dab3b65703e@52.232.7.187:30405",
|
|
||||||
"enode://24abebe1c0e6d75d6052ce3219a87be8573fd6397b4cb51f0773b83abba9b3d872bfb273cdc07389715b87adfac02f5235f5241442c5089802cbd8d42e310fce@52.232.7.187:30406",
|
|
||||||
"enode://d08dfa46bfbbdbcaafbb6e34abee4786610f6c91e0b76d7881f0334ac10dda41d8c1f2b6eedffb4493293c335c0ad46776443b2208d1fbbb9e1a90b25ee4eef2@52.232.7.187:30407",
|
|
||||||
"enode://8d95eb0f837d27581a43668ed3b8783d69dc4e84aa3edd7a0897e026155c8f59c8702fdc0375ee7bac15757c9c78e1315d9b73e4ce59c936db52ea4ae2f501c7@52.232.7.187:30408",
|
|
||||||
"enode://a5967cc804aebd422baaaba9f06f27c9e695ccab335b61088130f8cbe64e3cdf78793868c7051dfc06eecfe844fad54bc7f6dfaed9db3c7ecef279cb829c25fb@52.232.7.187:30409",
|
|
||||||
"enode://5f00134d81a8f2ebcc46f8766f627f492893eda48138f811b7de2168308171968f01710bca6da05764e74f14bae41652f554e6321f1aed85fa3461e89d075dbf@52.232.7.187:30410",
|
|
||||||
"enode://b2142b79b01a5aa66a5e23cc35e78219a8e97bc2412a6698cee24ae02e87078b725d71730711bd62e25ff1aa8658c6633778af8ac14c63814a337c3dd0ebda9f@52.232.7.187:30411",
|
|
||||||
"enode://1ffa7651094867d6486ce3ef46d27a052c2cb968b618346c6df7040322c7efc3337547ba85d4cbba32e8b31c42c867202554735c06d4c664b9afada2ed0c4b3c@52.232.7.187:30412",
|
|
||||||
"enode://129e0c3d5f5df12273754f6f703d2424409fa4baa599e0b758c55600169313887855e75b082028d2302ec034b303898cd697cc7ae8256ba924ce927510da2c8d@52.232.7.187:30413",
|
|
||||||
"enode://419e2dc0d2f5b022cf16b0e28842658284909fa027a0fbbb5e2b755e7f846ea02a8f0b66a7534981edf6a7bcf8a14855344c6668e2cd4476ccd35a11537c9144@52.232.7.187:30414",
|
|
||||||
"enode://23d55ad900583231b91f2f62e3f72eb498b342afd58b682be3af052eed62b5651094471065981de33d8786f075f05e3cca499503b0ac8ae84b2a06e99f5b0723@52.232.7.187:30415",
|
|
||||||
"enode://bc56e4158c00e9f616d7ea533def20a89bef959df4e62a768ff238ff4e1e9223f57ecff969941c20921bad98749baae311c0fbebce53bf7bbb9d3dc903640990@52.232.7.187:30416",
|
|
||||||
"enode://433ce15199c409875e7e72fffd69fdafe746f17b20f0d5555281722a65fde6c80328fab600d37d8624509adc072c445ce0dad4a1c01cff6acf3132c11d429d4d@52.232.7.187:30417",
|
|
||||||
"enode://632ee95b8f0eac51ef89ceb29313fef3a60050181d66a6b125583b1a225a7694b252edc016efb58aa3b251da756cb73280842a022c658ed405223b2f58626343@52.232.7.187:30418",
|
|
||||||
"enode://4a0f9bcff7a4b9ee453fb298d0fb222592efe121512e30cd72fef631beb8c6a15153a1456eb073ee18551c0e003c569651a101892dc4124e90b933733a498bb5@52.232.7.187:30419",
|
|
||||||
"enode://f0d80fbc72d16df30e19aac3051eb56a7aff0c8367686702e01ea132d8b0b3ee00cadd6a859d2cca98ec68d3d574f8a8a87dba2347ec1e2818dc84bc3fa34fae@52.232.7.187:30420",
|
|
||||||
"enode://a199146906e4f9f2b94b195a8308d9a59a3564b92efaab898a4243fe4c2ad918b7a8e4853d9d901d94fad878270a2669d644591299c3d43de1b298c00b92b4a7@52.232.7.187:30421",
|
|
||||||
"enode://052036ea8736b37adbfb684d90ce43e11b3591b51f31489d7c726b03618dea4f73b1e659deb928e6bf40564edcdcf08351643f42db3d4ca1c2b5db95dad59e94@52.232.7.187:30422",
|
|
||||||
"enode://460e2b8c6da8f12fac96c836e7d108f4b7ec55a1c64631bb8992339e117e1c28328fee83af863196e20af1487a655d13e5ceba90e980e92502d5bac5834c1f71@52.232.7.187:30423",
|
|
||||||
"enode://6d2cdd13741b2e72e9031e1b93c6d9a4e68de2844aa4e939f6a8a8498a7c1d7e2ee4c64217e92a6df08c9a32c6764d173552810ef1bd2ecb356532d389dd2136@52.232.7.187:30424",
|
|
||||||
"enode://62105fc25ce2cd5b299647f47eaa9211502dc76f0e9f461df915782df7242ac3223e3db04356ae6ed2977ccac20f0b16864406e9ca514a40a004cb6a5d0402aa@52.232.7.187:30425",
|
|
||||||
"enode://e0e388fc520fd493c33f0ce16685e6f98fb6aec28f2edc14ee6b179594ee519a896425b0025bb6f0e182dd3e468443f19c70885fbc66560d000093a668a86aa8@52.232.7.187:30426",
|
|
||||||
"enode://63f3353a72521ea10022127a4fe6b4acbef197c3fe668fd9f4805542d8a6fcf79f6335fbab62d180a35e19b739483e740858b113fdd7c13a26ad7b4e318a5aef@52.232.7.187:30427",
|
|
||||||
"enode://33a42b927085678d4aefd4e70b861cfca6ef5f6c143696c4f755973fd29e64c9e658cad57a66a687a7a156da1e3688b1fbdd17bececff2ee009fff038fa5666b@52.232.7.187:30428",
|
|
||||||
"enode://259ab5ab5c1daee3eab7e3819ab3177b82d25c29e6c2444fdd3f956e356afae79a72840ccf2d0665fe82c81ebc3b3734da1178ac9fd5d62c67e674b69f86b6be@52.232.7.187:30429",
|
|
||||||
"enode://558bccad7445ce3fd8db116ed6ab4aed1324fdbdac2348417340c1764dc46d46bffe0728e5b7d5c36f12e794c289f18f57f08f085d2c65c9910a5c7a65b6a66a@52.232.7.187:30430",
|
|
||||||
"enode://abe60937a0657ffded718e3f84a32987286983be257bdd6004775c4b525747c2b598f4fac49c8de324de5ce75b22673fa541a7ce2d555fb7f8ca325744ae3577@52.232.7.187:30431",
|
|
||||||
"enode://bce6f0aaa5b230742680084df71d4f026b3eff7f564265599216a1b06b765303fdc9325de30ffd5dfdaf302ce4b14322891d2faea50ce2ca298d7409f5858339@52.232.7.187:30432",
|
|
||||||
"enode://21b957c4e03277d42be6660730ec1b93f540764f26c6abdb54d006611139c7081248486206dfbf64fcaffd62589e9c6b8ea77a5297e4b21a605f1bcf49483ed0@52.232.7.187:30433",
|
|
||||||
"enode://ff104e30e64f24c3d7328acee8b13354e5551bc8d60bb25ecbd9632d955c7e34bb2d969482d173355baad91c8282f8b592624eb3929151090da3b4448d4d58fb@52.232.7.187:30434",
|
|
||||||
"enode://c76e2b5f81a521bceaec1518926a21380a345df9cf463461562c6845795512497fb67679e155fc96a74350f8b78de8f4c135dd52b106dbbb9795452021d09ea5@52.232.7.187:30435",
|
|
||||||
"enode://3288fd860105164f3e9b69934c4eb18f7146cfab31b5a671f994e21a36e9287766e5f9f075aefbc404538c77f7c2eb2a4495020a7633a1c3970d94e9fa770aeb@52.232.7.187:30436",
|
|
||||||
"enode://6cea859c7396d46b20cfcaa80f9a11cd112f8684f2f782f7b4c0e1e0af9212113429522075101923b9b957603e6c32095a6a07b5e5e35183c521952ee108dfaf@52.232.7.187:30437",
|
|
||||||
"enode://f628ec56e4ca8317cc24cc4ac9b27b95edcce7b96e1c7f3b53e30de4a8580fe44f2f0694a513bdb0a431acaf2824074d6ace4690247bbc34c14f426af8c056ea@52.232.7.187:30438",
|
|
||||||
"enode://055ec8b26fc105c4f97970a1cce9773a5e34c03f511b839db742198a1c571e292c54aa799e9afb991cc8a560529b8cdf3e0c344bc6c282aff2f68eec59361ddf@52.232.7.187:30439",
|
|
||||||
"enode://48cb0d430c328974226aa33a931d8446cd5a8d40f3ead8f4ce7ad60faa1278192eb6d58bed91258d63e81f255fc107eec2425ce2ae8b22350dd556076e160610@52.232.7.187:30440",
|
|
||||||
"enode://3fadb7af7f770d5ffc6b073b8d42834bebb18ce1fe8a4fe270d2b799e7051327093960dc61d9a18870db288f7746a0e6ea2a013cd6ab0e5f97ca08199473aace@52.232.7.187:30441",
|
|
||||||
"enode://a5d7168024c9992769cf380ffa559a64b4f39a29d468f579559863814eb0ae0ed689ac0871a3a2b4c78b03297485ec322d578281131ef5d5c09a4beb6200a97a@52.232.7.187:30442",
|
|
||||||
"enode://9c57744c5b2c2d71abcbe80512652f9234d4ab041b768a2a886ab390fe6f184860f40e113290698652d7e20a8ac74d27ac8671db23eb475b6c5e6253e4693bf8@52.232.7.187:30443",
|
|
||||||
"enode://daca9ff0c3176045a0e0ed228dee00ec86bc0939b135dc6b1caa23745d20fd0332e1ee74ad04020e89df56c7146d831a91b89d15ca3df05ba7618769fefab376@52.232.7.187:30444",
|
|
||||||
"enode://a3f6af59428cb4b9acb198db15ef5554fa43c2b0c18e468a269722d64a27218963a2975eaf82750b6262e42192b5e3669ea51337b4cda62b33987981bc5e0c1a@52.232.7.187:30445",
|
|
||||||
"enode://fe571422fa4651c3354c85dac61911a6a6520dd3c0332967a49d4133ca30e16a8a4946fa73ca2cb5de77917ea701a905e1c3015b2f4defcd53132b61cc84127a@52.232.7.187:30446",
|
|
||||||
|
|
||||||
// Mainframe
|
|
||||||
"enode://ee9a5a571ea6c8a59f9a8bb2c569c865e922b41c91d09b942e8c1d4dd2e1725bd2c26149da14de1f6321a2c6fdf1e07c503c3e093fb61696daebf74d6acd916b@54.186.219.160:30399",
|
|
||||||
"enode://a03f0562ecb8a992ad5242345535e73483cdc18ab934d36bf24b567d43447c2cea68f89f1d51d504dd13acc30f24ebce5a150bea2ccb1b722122ce4271dc199d@52.67.248.147:30399",
|
|
||||||
"enode://e2cbf9eafd85903d3b1c56743035284320695e0072bc8d7396e0542aa5e1c321b236f67eab66b79c2f15d4447fa4bbe74dd67d0467da23e7eb829f60ec8a812b@13.58.169.1:30399",
|
|
||||||
"enode://8b8c6bda6047f1cad9fab2db4d3d02b7aa26279902c32879f7bcd4a7d189fee77fdc36ee151ce6b84279b4792e72578fd529d2274d014132465758fbfee51cee@13.209.13.15:30399",
|
|
||||||
"enode://63f6a8818927e429585287cf2ca0cb9b11fa990b7b9b331c2962cdc6f21807a2473b26e8256225c26caff70d7218e59586d704d49061452c6852e382c885d03c@35.154.106.174:30399",
|
|
||||||
"enode://ed4bd3b794ed73f18e6dcc70c6624dfec63b5654f6ab54e8f40b16eff8afbd342d4230e099ddea40e84423f81b2d2ea79799dc345257b1fec6f6c422c9d008f7@52.213.20.99:30399",
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ const (
|
||||||
SWARM_ENV_STORE_PATH = "SWARM_STORE_PATH"
|
SWARM_ENV_STORE_PATH = "SWARM_STORE_PATH"
|
||||||
SWARM_ENV_STORE_CAPACITY = "SWARM_STORE_CAPACITY"
|
SWARM_ENV_STORE_CAPACITY = "SWARM_STORE_CAPACITY"
|
||||||
SWARM_ENV_STORE_CACHE_CAPACITY = "SWARM_STORE_CACHE_CAPACITY"
|
SWARM_ENV_STORE_CACHE_CAPACITY = "SWARM_STORE_CACHE_CAPACITY"
|
||||||
|
SWARM_ENV_BOOTNODE_MODE = "SWARM_BOOTNODE_MODE"
|
||||||
SWARM_ACCESS_PASSWORD = "SWARM_ACCESS_PASSWORD"
|
SWARM_ACCESS_PASSWORD = "SWARM_ACCESS_PASSWORD"
|
||||||
SWARM_AUTO_DEFAULTPATH = "SWARM_AUTO_DEFAULTPATH"
|
SWARM_AUTO_DEFAULTPATH = "SWARM_AUTO_DEFAULTPATH"
|
||||||
GETH_ENV_DATADIR = "GETH_DATADIR"
|
GETH_ENV_DATADIR = "GETH_DATADIR"
|
||||||
|
|
@ -164,10 +165,9 @@ func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config
|
||||||
return config, err
|
return config, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//override the current config with whatever is provided through the command line
|
// cmdLineOverride overrides the current config with whatever is provided through the command line
|
||||||
//most values are not allowed a zero value (empty string), if not otherwise noted
|
// most values are not allowed a zero value (empty string), if not otherwise noted
|
||||||
func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
|
func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
|
||||||
|
|
||||||
if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
|
if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
|
||||||
currentConfig.BzzAccount = keyid
|
currentConfig.BzzAccount = keyid
|
||||||
}
|
}
|
||||||
|
|
@ -258,14 +258,17 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
|
||||||
currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity
|
currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(SwarmBootnodeModeFlag.Name) {
|
||||||
|
currentConfig.BootnodeMode = ctx.GlobalBool(SwarmBootnodeModeFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
return currentConfig
|
return currentConfig
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//override the current config with whatver is provided in environment variables
|
// envVarsOverride overrides the current config with whatver is provided in environment variables
|
||||||
//most values are not allowed a zero value (empty string), if not otherwise noted
|
// most values are not allowed a zero value (empty string), if not otherwise noted
|
||||||
func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
|
func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
|
||||||
|
|
||||||
if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
|
if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
|
||||||
currentConfig.BzzAccount = keyid
|
currentConfig.BzzAccount = keyid
|
||||||
}
|
}
|
||||||
|
|
@ -364,6 +367,14 @@ func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
|
||||||
currentConfig.Cors = cors
|
currentConfig.Cors = cors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bm := os.Getenv(SWARM_ENV_BOOTNODE_MODE); bm != "" {
|
||||||
|
bootnodeMode, err := strconv.ParseBool(bm)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("invalid environment variable %s: %v", SWARM_ENV_BOOTNODE_MODE, err)
|
||||||
|
}
|
||||||
|
currentConfig.BootnodeMode = bootnodeMode
|
||||||
|
}
|
||||||
|
|
||||||
return currentConfig
|
return currentConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,10 @@ var (
|
||||||
Name: "compressed",
|
Name: "compressed",
|
||||||
Usage: "Prints encryption keys in compressed form",
|
Usage: "Prints encryption keys in compressed form",
|
||||||
}
|
}
|
||||||
|
SwarmBootnodeModeFlag = cli.BoolFlag{
|
||||||
|
Name: "bootnode-mode",
|
||||||
|
Usage: "Run Swarm in Bootnode mode",
|
||||||
|
}
|
||||||
SwarmFeedNameFlag = cli.StringFlag{
|
SwarmFeedNameFlag = cli.StringFlag{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Usage: "User-defined name for the new feed, limited to 32 characters. If combined with topic, it will refer to a subtopic with this name",
|
Usage: "User-defined name for the new feed, limited to 32 characters. If combined with topic, it will refer to a subtopic with this name",
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,10 @@ OPTIONS:
|
||||||
{{end}}{{end}}
|
{{end}}{{end}}
|
||||||
`
|
`
|
||||||
|
|
||||||
var (
|
// Git SHA1 commit hash of the release (set via linker flags)
|
||||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
// this variable will be assigned if corresponding parameter is passed with install, but not with test
|
||||||
)
|
// e.g.: go install -ldflags "-X main.gitCommit=ed1312d01b19e04ef578946226e5d8069d5dfd5a" ./cmd/swarm
|
||||||
|
var gitCommit string
|
||||||
|
|
||||||
//declare a few constant error messages, useful for later error check comparisons in test
|
//declare a few constant error messages, useful for later error check comparisons in test
|
||||||
var (
|
var (
|
||||||
|
|
@ -89,6 +90,7 @@ var defaultNodeConfig = node.DefaultConfig
|
||||||
|
|
||||||
// This init function sets defaults so cmd/swarm can run alongside geth.
|
// This init function sets defaults so cmd/swarm can run alongside geth.
|
||||||
func init() {
|
func init() {
|
||||||
|
sv.GitCommit = gitCommit
|
||||||
defaultNodeConfig.Name = clientIdentifier
|
defaultNodeConfig.Name = clientIdentifier
|
||||||
defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
|
defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
|
||||||
defaultNodeConfig.P2P.ListenAddr = ":30399"
|
defaultNodeConfig.P2P.ListenAddr = ":30399"
|
||||||
|
|
@ -154,7 +156,6 @@ func init() {
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.KeyStoreDirFlag,
|
utils.KeyStoreDirFlag,
|
||||||
utils.ListenPortFlag,
|
utils.ListenPortFlag,
|
||||||
utils.NoDiscoverFlag,
|
|
||||||
utils.DiscoveryV5Flag,
|
utils.DiscoveryV5Flag,
|
||||||
utils.NetrestrictFlag,
|
utils.NetrestrictFlag,
|
||||||
utils.NodeKeyFileFlag,
|
utils.NodeKeyFileFlag,
|
||||||
|
|
@ -187,6 +188,8 @@ func init() {
|
||||||
SwarmUploadDefaultPath,
|
SwarmUploadDefaultPath,
|
||||||
SwarmUpFromStdinFlag,
|
SwarmUpFromStdinFlag,
|
||||||
SwarmUploadMimeType,
|
SwarmUploadMimeType,
|
||||||
|
// bootnode mode
|
||||||
|
SwarmBootnodeModeFlag,
|
||||||
// storage flags
|
// storage flags
|
||||||
SwarmStorePath,
|
SwarmStorePath,
|
||||||
SwarmStoreCapacity,
|
SwarmStoreCapacity,
|
||||||
|
|
@ -227,12 +230,17 @@ func main() {
|
||||||
|
|
||||||
func keys(ctx *cli.Context) error {
|
func keys(ctx *cli.Context) error {
|
||||||
privateKey := getPrivKey(ctx)
|
privateKey := getPrivKey(ctx)
|
||||||
pub := hex.EncodeToString(crypto.FromECDSAPub(&privateKey.PublicKey))
|
pubkey := crypto.FromECDSAPub(&privateKey.PublicKey)
|
||||||
|
pubkeyhex := hex.EncodeToString(pubkey)
|
||||||
pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
|
pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
|
||||||
|
bzzkey := crypto.Keccak256Hash(pubkey).Hex()
|
||||||
|
|
||||||
if !ctx.Bool(SwarmCompressedFlag.Name) {
|
if !ctx.Bool(SwarmCompressedFlag.Name) {
|
||||||
fmt.Println(fmt.Sprintf("publicKey=%s", pub))
|
fmt.Println(fmt.Sprintf("bzzkey=%s", bzzkey[2:]))
|
||||||
|
fmt.Println(fmt.Sprintf("publicKey=%s", pubkeyhex))
|
||||||
}
|
}
|
||||||
fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
|
fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,6 +280,10 @@ func bzzd(ctx *cli.Context) error {
|
||||||
setSwarmBootstrapNodes(ctx, &cfg)
|
setSwarmBootstrapNodes(ctx, &cfg)
|
||||||
//setup the ethereum node
|
//setup the ethereum node
|
||||||
utils.SetNodeConfig(ctx, &cfg)
|
utils.SetNodeConfig(ctx, &cfg)
|
||||||
|
|
||||||
|
//always disable discovery from p2p package - swarm discovery is done with the `hive` protocol
|
||||||
|
cfg.P2P.NoDiscovery = true
|
||||||
|
|
||||||
stack, err := node.New(&cfg)
|
stack, err := node.New(&cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("can't create node: %v", err)
|
utils.Fatalf("can't create node: %v", err)
|
||||||
|
|
@ -294,6 +306,15 @@ func bzzd(ctx *cli.Context) error {
|
||||||
stack.Stop()
|
stack.Stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5
|
||||||
|
go func() {
|
||||||
|
s := stack.Server()
|
||||||
|
|
||||||
|
for _, n := range cfg.P2P.BootstrapNodes {
|
||||||
|
s.AddPeer(n)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
stack.Wait()
|
stack.Wait()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,6 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
||||||
node.Cmd = runSwarm(t,
|
node.Cmd = runSwarm(t,
|
||||||
"--port", p2pPort,
|
"--port", p2pPort,
|
||||||
"--nat", "extip:127.0.0.1",
|
"--nat", "extip:127.0.0.1",
|
||||||
"--nodiscover",
|
|
||||||
"--datadir", dir,
|
"--datadir", dir,
|
||||||
"--ipcpath", conf.IPCPath,
|
"--ipcpath", conf.IPCPath,
|
||||||
"--ens-api", "",
|
"--ens-api", "",
|
||||||
|
|
@ -330,7 +329,6 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
||||||
node.Cmd = runSwarm(t,
|
node.Cmd = runSwarm(t,
|
||||||
"--port", p2pPort,
|
"--port", p2pPort,
|
||||||
"--nat", "extip:127.0.0.1",
|
"--nat", "extip:127.0.0.1",
|
||||||
"--nodiscover",
|
|
||||||
"--datadir", dir,
|
"--datadir", dir,
|
||||||
"--ipcpath", conf.IPCPath,
|
"--ipcpath", conf.IPCPath,
|
||||||
"--ens-api", "",
|
"--ens-api", "",
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ func main() {
|
||||||
swarmmetrics.MetricsInfluxDBDatabaseFlag,
|
swarmmetrics.MetricsInfluxDBDatabaseFlag,
|
||||||
swarmmetrics.MetricsInfluxDBUsernameFlag,
|
swarmmetrics.MetricsInfluxDBUsernameFlag,
|
||||||
swarmmetrics.MetricsInfluxDBPasswordFlag,
|
swarmmetrics.MetricsInfluxDBPasswordFlag,
|
||||||
swarmmetrics.MetricsInfluxDBHostTagFlag,
|
swarmmetrics.MetricsInfluxDBTagsFlag,
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
app.Flags = append(app.Flags, tracing.Flags...)
|
app.Flags = append(app.Flags, tracing.Flags...)
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -177,13 +183,14 @@ func emitMetrics(ctx *cli.Context) error {
|
||||||
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
|
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
|
||||||
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
|
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
|
||||||
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
|
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
|
||||||
hosttag = ctx.GlobalString(swarmmetrics.MetricsInfluxDBHostTagFlag.Name)
|
tags = ctx.GlobalString(swarmmetrics.MetricsInfluxDBTagsFlag.Name)
|
||||||
)
|
)
|
||||||
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", map[string]string{
|
|
||||||
"host": hosttag,
|
tagsMap := utils.SplitTagsFlag(tags)
|
||||||
"version": gitCommit,
|
tagsMap["version"] = gitCommit
|
||||||
"filesize": fmt.Sprintf("%v", filesize),
|
tagsMap["filesize"] = fmt.Sprintf("%v", filesize)
|
||||||
})
|
|
||||||
|
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", tagsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ func generateEndpoints(scheme string, cluster string, app string, from int, to i
|
||||||
for port := from; port < to; port++ {
|
for port := from; port < to; port++ {
|
||||||
endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
|
endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
|
||||||
}
|
}
|
||||||
|
} else if cluster == "private-internal" {
|
||||||
|
for port := from; port < to; port++ {
|
||||||
|
endpoints = append(endpoints, fmt.Sprintf("%s://swarm-private-internal-%v:8500", scheme, port))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for port := from; port < to; port++ {
|
for port := from; port < to; port++ {
|
||||||
endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
|
endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
|
||||||
|
|
@ -86,9 +90,8 @@ func cliUploadAndSync(c *cli.Context) error {
|
||||||
func uploadAndSync(c *cli.Context) error {
|
func uploadAndSync(c *cli.Context) error {
|
||||||
defer func(now time.Time) {
|
defer func(now time.Time) {
|
||||||
totalTime := time.Since(now)
|
totalTime := time.Since(now)
|
||||||
|
|
||||||
log.Info("total time", "time", totalTime, "kb", filesize)
|
log.Info("total time", "time", totalTime, "kb", filesize)
|
||||||
metrics.GetOrRegisterCounter("upload-and-sync.total-time", nil).Inc(int64(totalTime))
|
metrics.GetOrRegisterResettingTimer("upload-and-sync.total-time", nil).Update(totalTime)
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
generateEndpoints(scheme, cluster, appName, from, to)
|
generateEndpoints(scheme, cluster, appName, from, to)
|
||||||
|
|
@ -103,7 +106,7 @@ func uploadAndSync(c *cli.Context) error {
|
||||||
log.Error(err.Error())
|
log.Error(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
metrics.GetOrRegisterCounter("upload-and-sync.upload-time", nil).Inc(int64(time.Since(t1)))
|
metrics.GetOrRegisterResettingTimer("upload-and-sync.upload-time", nil).UpdateSince(t1)
|
||||||
|
|
||||||
fhash, err := digest(bytes.NewReader(randomBytes))
|
fhash, err := digest(bytes.NewReader(randomBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -125,30 +128,28 @@ func uploadAndSync(c *cli.Context) error {
|
||||||
for {
|
for {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := fetch(hash, endpoint, fhash, ruid)
|
err := fetch(hash, endpoint, fhash, ruid)
|
||||||
fetchTime := time.Since(start)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.GetOrRegisterMeter("upload-and-sync.single.fetch-time", nil).Mark(int64(fetchTime))
|
metrics.GetOrRegisterResettingTimer("upload-and-sync.single.fetch-time", nil).UpdateSince(start)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}(endpoints[randIndex], ruid)
|
}(endpoints[randIndex], ruid)
|
||||||
} else {
|
} else {
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints[1:] {
|
||||||
ruid := uuid.New()[:8]
|
ruid := uuid.New()[:8]
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(endpoint string, ruid string) {
|
go func(endpoint string, ruid string) {
|
||||||
for {
|
for {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
err := fetch(hash, endpoint, fhash, ruid)
|
err := fetch(hash, endpoint, fhash, ruid)
|
||||||
fetchTime := time.Since(start)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
metrics.GetOrRegisterMeter("upload-and-sync.each.fetch-time", nil).Mark(int64(fetchTime))
|
metrics.GetOrRegisterResettingTimer("upload-and-sync.each.fetch-time", nil).UpdateSince(start)
|
||||||
wg.Done()
|
wg.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -166,8 +167,6 @@ func fetch(hash string, endpoint string, original []byte, ruid string) error {
|
||||||
ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
|
ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
|
||||||
defer sp.Finish()
|
defer sp.Finish()
|
||||||
|
|
||||||
log.Trace("sleeping", "ruid", ruid)
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
|
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
|
||||||
|
|
||||||
var tn time.Time
|
var tn time.Time
|
||||||
|
|
|
||||||
98
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
98
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
// 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 if cluster == "private-internal" {
|
||||||
|
endpoint = fmt.Sprintf("%s://swarm-private-internal-%v:8500", 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
|
||||||
|
}
|
||||||
143
cmd/swarm/swarm-snapshot/create_test.go
Normal file
143
cmd/swarm/swarm-snapshot/create_test.go
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
// 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"
|
||||||
|
"runtime"
|
||||||
|
"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) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -57,7 +58,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
"gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -161,6 +162,23 @@ var (
|
||||||
Usage: "Document Root for HTTPClient file scheme",
|
Usage: "Document Root for HTTPClient file scheme",
|
||||||
Value: DirectoryString{homeDir()},
|
Value: DirectoryString{homeDir()},
|
||||||
}
|
}
|
||||||
|
ULCModeConfigFlag = cli.StringFlag{
|
||||||
|
Name: "ulc.config",
|
||||||
|
Usage: "Config file to use for ultra light client mode",
|
||||||
|
}
|
||||||
|
OnlyAnnounceModeFlag = cli.BoolFlag{
|
||||||
|
Name: "ulc.onlyannounce",
|
||||||
|
Usage: "ULC server sends announcements only",
|
||||||
|
}
|
||||||
|
ULCMinTrustedFractionFlag = cli.IntFlag{
|
||||||
|
Name: "ulc.fraction",
|
||||||
|
Usage: "Minimum % of trusted ULC servers required to announce a new head",
|
||||||
|
}
|
||||||
|
ULCTrustedNodesFlag = cli.StringFlag{
|
||||||
|
Name: "ulc.trusted",
|
||||||
|
Usage: "List of trusted ULC servers",
|
||||||
|
}
|
||||||
|
|
||||||
defaultSyncMode = eth.DefaultConfig.SyncMode
|
defaultSyncMode = eth.DefaultConfig.SyncMode
|
||||||
SyncModeFlag = TextMarshalerFlag{
|
SyncModeFlag = TextMarshalerFlag{
|
||||||
Name: "syncmode",
|
Name: "syncmode",
|
||||||
|
|
@ -435,6 +453,30 @@ var (
|
||||||
Usage: "HTTP-RPC server listening port",
|
Usage: "HTTP-RPC server listening port",
|
||||||
Value: node.DefaultHTTPPort,
|
Value: node.DefaultHTTPPort,
|
||||||
}
|
}
|
||||||
|
GraphQLEnabledFlag = cli.BoolFlag{
|
||||||
|
Name: "graphql",
|
||||||
|
Usage: "Enable the GraphQL server",
|
||||||
|
}
|
||||||
|
GraphQLListenAddrFlag = cli.StringFlag{
|
||||||
|
Name: "graphql.addr",
|
||||||
|
Usage: "GraphQL server listening interface",
|
||||||
|
Value: node.DefaultGraphQLHost,
|
||||||
|
}
|
||||||
|
GraphQLPortFlag = cli.IntFlag{
|
||||||
|
Name: "graphql.port",
|
||||||
|
Usage: "GraphQL server listening port",
|
||||||
|
Value: node.DefaultGraphQLPort,
|
||||||
|
}
|
||||||
|
GraphQLCORSDomainFlag = cli.StringFlag{
|
||||||
|
Name: "graphql.rpccorsdomain",
|
||||||
|
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||||
|
Value: "",
|
||||||
|
}
|
||||||
|
GraphQLVirtualHostsFlag = cli.StringFlag{
|
||||||
|
Name: "graphql.rpcvhosts",
|
||||||
|
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
||||||
|
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
|
||||||
|
}
|
||||||
RPCCORSDomainFlag = cli.StringFlag{
|
RPCCORSDomainFlag = cli.StringFlag{
|
||||||
Name: "rpccorsdomain",
|
Name: "rpccorsdomain",
|
||||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||||
|
|
@ -614,14 +656,14 @@ var (
|
||||||
Usage: "Password to authorize access to the database",
|
Usage: "Password to authorize access to the database",
|
||||||
Value: "test",
|
Value: "test",
|
||||||
}
|
}
|
||||||
// The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
// Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
||||||
// It is used so that we can group all nodes and average a measurement across all of them, but also so
|
// For example `host` tag could be used so that we can group all nodes and average a measurement
|
||||||
// that we can select a specific node and inspect its measurements.
|
// across all of them, but also so that we can select a specific node and inspect its measurements.
|
||||||
// https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
|
// https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
|
||||||
MetricsInfluxDBHostTagFlag = cli.StringFlag{
|
MetricsInfluxDBTagsFlag = cli.StringFlag{
|
||||||
Name: "metrics.influxdb.host.tag",
|
Name: "metrics.influxdb.tags",
|
||||||
Usage: "InfluxDB `host` tag attached to all measurements",
|
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
||||||
Value: "localhost",
|
Value: "host=localhost",
|
||||||
}
|
}
|
||||||
|
|
||||||
EWASMInterpreterFlag = cli.StringFlag{
|
EWASMInterpreterFlag = cli.StringFlag{
|
||||||
|
|
@ -707,11 +749,13 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
|
|
||||||
cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
|
cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := enode.ParseV4(url)
|
if url != "" {
|
||||||
if err != nil {
|
node, err := enode.ParseV4(url)
|
||||||
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
if err != nil {
|
||||||
|
log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
|
}
|
||||||
|
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -796,6 +840,24 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setGraphQL creates the GraphQL listener interface string from the set
|
||||||
|
// command line flags, returning empty if the GraphQL endpoint is disabled.
|
||||||
|
func setGraphQL(ctx *cli.Context, cfg *node.Config) {
|
||||||
|
if ctx.GlobalBool(GraphQLEnabledFlag.Name) && cfg.GraphQLHost == "" {
|
||||||
|
cfg.GraphQLHost = "127.0.0.1"
|
||||||
|
if ctx.GlobalIsSet(GraphQLListenAddrFlag.Name) {
|
||||||
|
cfg.GraphQLHost = ctx.GlobalString(GraphQLListenAddrFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cfg.GraphQLPort = ctx.GlobalInt(GraphQLPortFlag.Name)
|
||||||
|
if ctx.GlobalIsSet(GraphQLCORSDomainFlag.Name) {
|
||||||
|
cfg.GraphQLCors = splitAndTrim(ctx.GlobalString(GraphQLCORSDomainFlag.Name))
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(GraphQLVirtualHostsFlag.Name) {
|
||||||
|
cfg.GraphQLVirtualHosts = splitAndTrim(ctx.GlobalString(GraphQLVirtualHostsFlag.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setWS creates the WebSocket RPC listener interface string from the set
|
// setWS creates the WebSocket RPC listener interface string from the set
|
||||||
// command line flags, returning empty if the HTTP endpoint is disabled.
|
// command line flags, returning empty if the HTTP endpoint is disabled.
|
||||||
func setWS(ctx *cli.Context, cfg *node.Config) {
|
func setWS(ctx *cli.Context, cfg *node.Config) {
|
||||||
|
|
@ -829,6 +891,40 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetULC setup ULC config from file if given.
|
||||||
|
func SetULC(ctx *cli.Context, cfg *eth.Config) {
|
||||||
|
// ULC config isn't loaded from global config and ULC config and ULC trusted nodes are not defined.
|
||||||
|
if cfg.ULC == nil && !(ctx.GlobalIsSet(ULCModeConfigFlag.Name) || ctx.GlobalIsSet(ULCTrustedNodesFlag.Name)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg.ULC = ð.ULCConfig{}
|
||||||
|
|
||||||
|
path := ctx.GlobalString(ULCModeConfigFlag.Name)
|
||||||
|
if path != "" {
|
||||||
|
cfgData, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to unmarshal ULC configuration: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(cfgData, &cfg.ULC)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to unmarshal ULC configuration: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if trustedNodes := ctx.GlobalString(ULCTrustedNodesFlag.Name); trustedNodes != "" {
|
||||||
|
cfg.ULC.TrustedServers = strings.Split(trustedNodes, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
if trustedFraction := ctx.GlobalInt(ULCMinTrustedFractionFlag.Name); trustedFraction > 0 {
|
||||||
|
cfg.ULC.MinTrustedFraction = trustedFraction
|
||||||
|
}
|
||||||
|
if cfg.ULC.MinTrustedFraction <= 0 && cfg.ULC.MinTrustedFraction > 100 {
|
||||||
|
log.Error("MinTrustedFraction is invalid", "MinTrustedFraction", cfg.ULC.MinTrustedFraction, "Changed to default", eth.DefaultULCMinTrustedFraction)
|
||||||
|
cfg.ULC.MinTrustedFraction = eth.DefaultULCMinTrustedFraction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for Geth and returns half of the allowance to assign to the database.
|
// for Geth and returns half of the allowance to assign to the database.
|
||||||
func makeDatabaseHandles() int {
|
func makeDatabaseHandles() int {
|
||||||
|
|
@ -978,6 +1074,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||||
SetP2PConfig(ctx, &cfg.P2P)
|
SetP2PConfig(ctx, &cfg.P2P)
|
||||||
setIPC(ctx, cfg)
|
setIPC(ctx, cfg)
|
||||||
setHTTP(ctx, cfg)
|
setHTTP(ctx, cfg)
|
||||||
|
setGraphQL(ctx, cfg)
|
||||||
setWS(ctx, cfg)
|
setWS(ctx, cfg)
|
||||||
setNodeUserIdent(ctx, cfg)
|
setNodeUserIdent(ctx, cfg)
|
||||||
|
|
||||||
|
|
@ -1179,6 +1276,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
if ctx.GlobalIsSet(LightPeersFlag.Name) {
|
if ctx.GlobalIsSet(LightPeersFlag.Name) {
|
||||||
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
|
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet(OnlyAnnounceModeFlag.Name) {
|
||||||
|
cfg.OnlyAnnounce = ctx.GlobalBool(OnlyAnnounceModeFlag.Name)
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
|
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
@ -1360,18 +1460,35 @@ func SetupMetrics(ctx *cli.Context) {
|
||||||
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
||||||
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
||||||
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
||||||
hosttag = ctx.GlobalString(MetricsInfluxDBHostTagFlag.Name)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if enableExport {
|
if enableExport {
|
||||||
|
tagsMap := SplitTagsFlag(ctx.GlobalString(MetricsInfluxDBTagsFlag.Name))
|
||||||
|
|
||||||
log.Info("Enabling metrics export to InfluxDB")
|
log.Info("Enabling metrics export to InfluxDB")
|
||||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", map[string]string{
|
|
||||||
"host": hosttag,
|
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SplitTagsFlag(tagsFlag string) map[string]string {
|
||||||
|
tags := strings.Split(tagsFlag, ",")
|
||||||
|
tagsMap := map[string]string{}
|
||||||
|
|
||||||
|
for _, t := range tags {
|
||||||
|
if t != "" {
|
||||||
|
kv := strings.Split(t, "=")
|
||||||
|
|
||||||
|
if len(kv) == 2 {
|
||||||
|
tagsMap[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tagsMap
|
||||||
|
}
|
||||||
|
|
||||||
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
||||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
64
cmd/utils/flags_test.go
Normal file
64
cmd/utils/flags_test.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright 2019 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 utils contains internal helper functions for go-ethereum commands.
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_SplitTagsFlag(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args string
|
||||||
|
want map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"2 tags case",
|
||||||
|
"host=localhost,bzzkey=123",
|
||||||
|
map[string]string{
|
||||||
|
"host": "localhost",
|
||||||
|
"bzzkey": "123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1 tag case",
|
||||||
|
"host=localhost123",
|
||||||
|
map[string]string{
|
||||||
|
"host": "localhost123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"empty case",
|
||||||
|
"",
|
||||||
|
map[string]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"garbage",
|
||||||
|
"smth=smthelse=123",
|
||||||
|
map[string]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := SplitTagsFlag(tt.args); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("splitTagsFlag() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -72,6 +72,25 @@ func (b Bytes) String() string {
|
||||||
return Encode(b)
|
return Encode(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementsGraphQLType returns true if Bytes implements the specified GraphQL type.
|
||||||
|
func (b Bytes) ImplementsGraphQLType(name string) bool { return name == "Bytes" }
|
||||||
|
|
||||||
|
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||||
|
func (b *Bytes) UnmarshalGraphQL(input interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch input := input.(type) {
|
||||||
|
case string:
|
||||||
|
data, err := Decode(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*b = data
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unexpected type for Bytes: %v", input)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
|
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
|
||||||
// determines the required input length. This function is commonly used to implement the
|
// determines the required input length. This function is commonly used to implement the
|
||||||
// UnmarshalJSON method for fixed-size types.
|
// UnmarshalJSON method for fixed-size types.
|
||||||
|
|
@ -187,6 +206,25 @@ func (b *Big) String() string {
|
||||||
return EncodeBig(b.ToInt())
|
return EncodeBig(b.ToInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementsGraphQLType returns true if Big implements the provided GraphQL type.
|
||||||
|
func (b Big) ImplementsGraphQLType(name string) bool { return name == "BigInt" }
|
||||||
|
|
||||||
|
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||||
|
func (b *Big) UnmarshalGraphQL(input interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch input := input.(type) {
|
||||||
|
case string:
|
||||||
|
return b.UnmarshalText([]byte(input))
|
||||||
|
case int32:
|
||||||
|
var num big.Int
|
||||||
|
num.SetInt64(int64(input))
|
||||||
|
*b = Big(num)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unexpected type for BigInt: %v", input)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
// The zero value marshals as "0x0".
|
// The zero value marshals as "0x0".
|
||||||
type Uint64 uint64
|
type Uint64 uint64
|
||||||
|
|
@ -234,6 +272,23 @@ func (b Uint64) String() string {
|
||||||
return EncodeUint64(uint64(b))
|
return EncodeUint64(uint64(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementsGraphQLType returns true if Uint64 implements the provided GraphQL type.
|
||||||
|
func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" }
|
||||||
|
|
||||||
|
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||||
|
func (b *Uint64) UnmarshalGraphQL(input interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch input := input.(type) {
|
||||||
|
case string:
|
||||||
|
return b.UnmarshalText([]byte(input))
|
||||||
|
case int32:
|
||||||
|
*b = Uint64(input)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unexpected type for Long: %v", input)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
// The zero value marshals as "0x0".
|
// The zero value marshals as "0x0".
|
||||||
type Uint uint
|
type Uint uint
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,21 @@ func (h Hash) Value() (driver.Value, error) {
|
||||||
return h[:], nil
|
return h[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
|
||||||
|
func (_ Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
|
||||||
|
|
||||||
|
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||||
|
func (h *Hash) UnmarshalGraphQL(input interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch input := input.(type) {
|
||||||
|
case string:
|
||||||
|
*h = HexToHash(input)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unexpected type for Bytes32: %v", input)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
||||||
type UnprefixedHash Hash
|
type UnprefixedHash Hash
|
||||||
|
|
||||||
|
|
@ -268,6 +283,21 @@ func (a Address) Value() (driver.Value, error) {
|
||||||
return a[:], nil
|
return a[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
|
||||||
|
func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" }
|
||||||
|
|
||||||
|
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
|
||||||
|
func (a *Address) UnmarshalGraphQL(input interface{}) error {
|
||||||
|
var err error
|
||||||
|
switch input := input.(type) {
|
||||||
|
case string:
|
||||||
|
*a = HexToAddress(input)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unexpected type for Address: %v", input)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// UnprefixedAddress allows marshaling an Address without 0x prefix.
|
// UnprefixedAddress allows marshaling an Address without 0x prefix.
|
||||||
type UnprefixedAddress Address
|
type UnprefixedAddress Address
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,37 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
// Wallet open failed, report error unless it's a PIN entry
|
// Wallet open failed, report error unless it's a PIN entry
|
||||||
if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) {
|
if strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) {
|
||||||
|
val, err = b.readPinAndReopenWallet(call)
|
||||||
|
if err == nil {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the user needs to input a passphrase
|
||||||
|
if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPassphraseNeeded.Error()) {
|
||||||
throwJSException(err.Error())
|
throwJSException(err.Error())
|
||||||
}
|
}
|
||||||
|
val, err = b.readPassphraseAndReopenWallet(call)
|
||||||
|
if err != nil {
|
||||||
|
throwJSException(err.Error())
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) readPassphraseAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
|
||||||
|
var passwd otto.Value
|
||||||
|
wallet := call.Argument(0)
|
||||||
|
if input, err := b.prompter.PromptPassword("Please enter your passphrase: "); err != nil {
|
||||||
|
throwJSException(err.Error())
|
||||||
|
} else {
|
||||||
|
passwd, _ = otto.ToValue(input)
|
||||||
|
}
|
||||||
|
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
|
||||||
|
var passwd otto.Value
|
||||||
|
wallet := call.Argument(0)
|
||||||
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data
|
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data
|
||||||
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
|
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
|
||||||
fmt.Fprintf(b.printer, "7 | 8 | 9\n")
|
fmt.Fprintf(b.printer, "7 | 8 | 9\n")
|
||||||
|
|
@ -121,10 +149,7 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
|
||||||
} else {
|
} else {
|
||||||
passwd, _ = otto.ToValue(input)
|
passwd, _ = otto.ToValue(input)
|
||||||
}
|
}
|
||||||
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
|
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
|
||||||
throwJSException(err.Error())
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
|
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constant
|
||||||
newcfg := genesis.configOrDefault(stored)
|
newcfg := genesis.configOrDefault(stored)
|
||||||
if constantinopleOverride != nil {
|
if constantinopleOverride != nil {
|
||||||
newcfg.ConstantinopleBlock = constantinopleOverride
|
newcfg.ConstantinopleBlock = constantinopleOverride
|
||||||
|
newcfg.PetersburgBlock = constantinopleOverride
|
||||||
}
|
}
|
||||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||||
if storedcfg == nil {
|
if storedcfg == nil {
|
||||||
|
|
|
||||||
|
|
@ -219,14 +219,18 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
|
|
||||||
// Generate the list of seal verification requests, and start the parallel verifier
|
// Generate the list of seal verification requests, and start the parallel verifier
|
||||||
seals := make([]bool, len(chain))
|
seals := make([]bool, len(chain))
|
||||||
for i := 0; i < len(seals)/checkFreq; i++ {
|
if checkFreq != 0 {
|
||||||
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
// In case of checkFreq == 0 all seals are left false.
|
||||||
if index >= len(seals) {
|
for i := 0; i < len(seals)/checkFreq; i++ {
|
||||||
index = len(seals) - 1
|
index := i*checkFreq + hc.rand.Intn(checkFreq)
|
||||||
|
if index >= len(seals) {
|
||||||
|
index = len(seals) - 1
|
||||||
|
}
|
||||||
|
seals[index] = true
|
||||||
}
|
}
|
||||||
seals[index] = true
|
// Last should always be verified to avoid junk.
|
||||||
|
seals[len(seals)-1] = true
|
||||||
}
|
}
|
||||||
seals[len(seals)-1] = true // Last should always be verified to avoid junk
|
|
||||||
|
|
||||||
abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
||||||
defer close(abort)
|
defer close(abort)
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,10 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -102,10 +102,12 @@ func (h *Header) Hash() common.Hash {
|
||||||
return rlpHash(h)
|
return rlpHash(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())
|
||||||
|
|
||||||
// Size returns the approximate memory used by all internal contents. It is used
|
// Size returns the approximate memory used by all internal contents. It is used
|
||||||
// to approximate and limit the memory consumption of various caches.
|
// to approximate and limit the memory consumption of various caches.
|
||||||
func (h *Header) Size() common.StorageSize {
|
func (h *Header) Size() common.StorageSize {
|
||||||
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
|
return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// memoryGasCosts calculates the quadratic gas for memory expansion. It does so
|
// memoryGasCost calculates the quadratic gas for memory expansion. It does so
|
||||||
// only for the memory region that is expanded, not the total memory.
|
// only for the memory region that is expanded, not the total memory.
|
||||||
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
|
||||||
|
|
||||||
|
|
@ -121,7 +121,9 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
|
||||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||||
)
|
)
|
||||||
// The legacy gas metering only takes into consideration the current state
|
// The legacy gas metering only takes into consideration the current state
|
||||||
if !evm.chainRules.IsConstantinople {
|
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
||||||
|
// OR Constantinople is not active
|
||||||
|
if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople {
|
||||||
// This checks for 3 scenario's and calculates gas accordingly:
|
// This checks for 3 scenario's and calculates gas accordingly:
|
||||||
//
|
//
|
||||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||||
|
|
@ -380,7 +382,7 @@ func gasExp(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem
|
||||||
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
|
gas = expByteLen * gt.ExpByte // no overflow check required. Max is 256 * ExpByte gas
|
||||||
overflow bool
|
overflow bool
|
||||||
)
|
)
|
||||||
if gas, overflow = math.SafeAdd(gas, GasSlowStep); overflow {
|
if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
|
||||||
return 0, errGasUintOverflow
|
return 0, errGasUintOverflow
|
||||||
}
|
}
|
||||||
return gas, nil
|
return gas, nil
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,11 @@ type JSONLogger struct {
|
||||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||||
// into the provided stream.
|
// into the provided stream.
|
||||||
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
||||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
l := &JSONLogger{json.NewEncoder(writer), cfg}
|
||||||
|
if l.cfg == nil {
|
||||||
|
l.cfg = &LogConfig{}
|
||||||
|
}
|
||||||
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
||||||
|
|
||||||
vmConf vm.Config
|
vmConf vm.Config
|
||||||
dump *os.File
|
dump *os.File
|
||||||
|
writer *bufio.Writer
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
// If the transaction needs tracing, swap out the configs
|
// If the transaction needs tracing, swap out the configs
|
||||||
|
|
@ -590,16 +591,19 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
||||||
dumps = append(dumps, dump.Name())
|
dumps = append(dumps, dump.Name())
|
||||||
|
|
||||||
// Swap out the noop logger to the standard tracer
|
// Swap out the noop logger to the standard tracer
|
||||||
|
writer = bufio.NewWriter(dump)
|
||||||
vmConf = vm.Config{
|
vmConf = vm.Config{
|
||||||
Debug: true,
|
Debug: true,
|
||||||
Tracer: vm.NewJSONLogger(&logConfig, bufio.NewWriter(dump)),
|
Tracer: vm.NewJSONLogger(&logConfig, writer),
|
||||||
EnablePreimageRecording: true,
|
EnablePreimageRecording: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
vmenv := vm.NewEVM(vmctx, statedb, api.config, vmConf)
|
vmenv := vm.NewEVM(vmctx, statedb, api.config, vmConf)
|
||||||
_, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
_, _, _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
|
||||||
|
if writer != nil {
|
||||||
|
writer.Flush()
|
||||||
|
}
|
||||||
if dump != nil {
|
if dump != nil {
|
||||||
dump.Close()
|
dump.Close()
|
||||||
log.Info("Wrote standard trace", "file", dump.Name())
|
log.Info("Wrote standard trace", "file", dump.Name())
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,12 @@ type Config struct {
|
||||||
Whitelist map[uint64]common.Hash `toml:"-"`
|
Whitelist map[uint64]common.Hash `toml:"-"`
|
||||||
|
|
||||||
// Light client options
|
// Light client options
|
||||||
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
||||||
LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
|
LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
|
||||||
|
OnlyAnnounce bool // Maximum number of LES client peers
|
||||||
|
|
||||||
|
// Ultra Light client options
|
||||||
|
ULC *ULCConfig `toml:",omitempty"`
|
||||||
|
|
||||||
// Database options
|
// Database options
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,6 @@ type Fetcher struct {
|
||||||
notify chan *announce
|
notify chan *announce
|
||||||
inject chan *inject
|
inject chan *inject
|
||||||
|
|
||||||
blockFilter chan chan []*types.Block
|
|
||||||
headerFilter chan chan *headerFilterTask
|
headerFilter chan chan *headerFilterTask
|
||||||
bodyFilter chan chan *bodyFilterTask
|
bodyFilter chan chan *bodyFilterTask
|
||||||
|
|
||||||
|
|
@ -150,7 +149,6 @@ func New(getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBloc
|
||||||
return &Fetcher{
|
return &Fetcher{
|
||||||
notify: make(chan *announce),
|
notify: make(chan *announce),
|
||||||
inject: make(chan *inject),
|
inject: make(chan *inject),
|
||||||
blockFilter: make(chan chan []*types.Block),
|
|
||||||
headerFilter: make(chan chan *headerFilterTask),
|
headerFilter: make(chan chan *headerFilterTask),
|
||||||
bodyFilter: make(chan chan *bodyFilterTask),
|
bodyFilter: make(chan chan *bodyFilterTask),
|
||||||
done: make(chan common.Hash),
|
done: make(chan common.Hash),
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ func verifyImportEvent(t *testing.T, imported chan *types.Block, arrive bool) {
|
||||||
select {
|
select {
|
||||||
case <-imported:
|
case <-imported:
|
||||||
t.Fatalf("import invoked")
|
t.Fatalf("import invoked")
|
||||||
case <-time.After(10 * time.Millisecond):
|
case <-time.After(20 * time.Millisecond):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
NetworkId uint64
|
NetworkId uint64
|
||||||
SyncMode downloader.SyncMode
|
SyncMode downloader.SyncMode
|
||||||
NoPruning bool
|
NoPruning bool
|
||||||
LightServ int `toml:",omitempty"`
|
LightServ int `toml:",omitempty"`
|
||||||
LightPeers int `toml:",omitempty"`
|
LightPeers int `toml:",omitempty"`
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
OnlyAnnounce bool
|
||||||
DatabaseHandles int `toml:"-"`
|
ULC *ULCConfig `toml:",omitempty"`
|
||||||
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
|
DatabaseHandles int `toml:"-"`
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
TrieCleanCache int
|
TrieCleanCache int
|
||||||
TrieDirtyCache int
|
TrieDirtyCache int
|
||||||
|
|
@ -54,6 +56,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.NoPruning = c.NoPruning
|
enc.NoPruning = c.NoPruning
|
||||||
enc.LightServ = c.LightServ
|
enc.LightServ = c.LightServ
|
||||||
enc.LightPeers = c.LightPeers
|
enc.LightPeers = c.LightPeers
|
||||||
|
enc.OnlyAnnounce = c.OnlyAnnounce
|
||||||
|
enc.ULC = c.ULC
|
||||||
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
||||||
enc.DatabaseHandles = c.DatabaseHandles
|
enc.DatabaseHandles = c.DatabaseHandles
|
||||||
enc.DatabaseCache = c.DatabaseCache
|
enc.DatabaseCache = c.DatabaseCache
|
||||||
|
|
@ -71,6 +75,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
enc.Ethash = c.Ethash
|
enc.Ethash = c.Ethash
|
||||||
enc.TxPool = c.TxPool
|
enc.TxPool = c.TxPool
|
||||||
enc.GPO = c.GPO
|
enc.GPO = c.GPO
|
||||||
|
|
||||||
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
||||||
enc.DocRoot = c.DocRoot
|
enc.DocRoot = c.DocRoot
|
||||||
enc.EWASMInterpreter = c.EWASMInterpreter
|
enc.EWASMInterpreter = c.EWASMInterpreter
|
||||||
|
|
@ -85,10 +90,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
NetworkId *uint64
|
NetworkId *uint64
|
||||||
SyncMode *downloader.SyncMode
|
SyncMode *downloader.SyncMode
|
||||||
NoPruning *bool
|
NoPruning *bool
|
||||||
LightServ *int `toml:",omitempty"`
|
LightServ *int `toml:",omitempty"`
|
||||||
LightPeers *int `toml:",omitempty"`
|
LightPeers *int `toml:",omitempty"`
|
||||||
SkipBcVersionCheck *bool `toml:"-"`
|
OnlyAnnounce *bool
|
||||||
DatabaseHandles *int `toml:"-"`
|
ULC *ULCConfig `toml:",omitempty"`
|
||||||
|
SkipBcVersionCheck *bool `toml:"-"`
|
||||||
|
DatabaseHandles *int `toml:"-"`
|
||||||
DatabaseCache *int
|
DatabaseCache *int
|
||||||
TrieCleanCache *int
|
TrieCleanCache *int
|
||||||
TrieDirtyCache *int
|
TrieDirtyCache *int
|
||||||
|
|
@ -131,6 +138,12 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
if dec.LightPeers != nil {
|
if dec.LightPeers != nil {
|
||||||
c.LightPeers = *dec.LightPeers
|
c.LightPeers = *dec.LightPeers
|
||||||
}
|
}
|
||||||
|
if dec.OnlyAnnounce != nil {
|
||||||
|
c.OnlyAnnounce = *dec.OnlyAnnounce
|
||||||
|
}
|
||||||
|
if dec.ULC != nil {
|
||||||
|
c.ULC = dec.ULC
|
||||||
|
}
|
||||||
if dec.SkipBcVersionCheck != nil {
|
if dec.SkipBcVersionCheck != nil {
|
||||||
c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
|
c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
eth/ulc_config.go
Normal file
9
eth/ulc_config.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package eth
|
||||||
|
|
||||||
|
const DefaultULCMinTrustedFraction = 75
|
||||||
|
|
||||||
|
// ULCConfig is a Ultra Light client options.
|
||||||
|
type ULCConfig struct {
|
||||||
|
TrustedServers []string `toml:",omitempty"` // A list of trusted servers
|
||||||
|
MinTrustedFraction int `toml:",omitempty"` // Minimum percentage of connected trusted servers to validate trusted (1-100)
|
||||||
|
}
|
||||||
1104
graphql/grahpql.go
Normal file
1104
graphql/grahpql.go
Normal file
File diff suppressed because it is too large
Load diff
95
graphql/graphiql.go
Normal file
95
graphql/graphiql.go
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
// The MIT License (MIT)
|
||||||
|
//
|
||||||
|
// Copyright (c) 2016 Muhammed Thanish
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
|
||||||
|
package graphql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GraphiQL is an in-browser IDE for exploring GraphiQL APIs.
|
||||||
|
// This handler returns GraphiQL when requested.
|
||||||
|
//
|
||||||
|
// For more information, see https://github.com/graphql/graphiql.
|
||||||
|
type GraphiQL struct{}
|
||||||
|
|
||||||
|
func respond(w http.ResponseWriter, body []byte, code int) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
|
w.WriteHeader(code)
|
||||||
|
_, _ = w.Write(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func errorJSON(msg string) []byte {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
fmt.Fprintf(&buf, `{"error": "%s"}`, msg)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h GraphiQL) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != "GET" {
|
||||||
|
respond(w, errorJSON("only GET requests are supported"), http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(graphiql)
|
||||||
|
}
|
||||||
|
|
||||||
|
var graphiql = []byte(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.css"/>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.3/fetch.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/graphiql/0.11.11/graphiql.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body style="width: 100%; height: 100%; margin: 0; overflow: hidden;">
|
||||||
|
<div id="graphiql" style="height: 100vh;">Loading...</div>
|
||||||
|
<script>
|
||||||
|
function fetchGQL(params) {
|
||||||
|
return fetch("/graphql", {
|
||||||
|
method: "post",
|
||||||
|
body: JSON.stringify(params),
|
||||||
|
credentials: "include",
|
||||||
|
}).then(function (resp) {
|
||||||
|
return resp.text();
|
||||||
|
}).then(function (body) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(body);
|
||||||
|
} catch (error) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
React.createElement(GraphiQL, {fetcher: fetchGQL}),
|
||||||
|
document.getElementById("graphiql")
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
29
graphql/graphql_test.go
Normal file
29
graphql/graphql_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package graphql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuildSchema(t *testing.T) {
|
||||||
|
// Make sure the schema can be parsed and matched up to the object model.
|
||||||
|
_, err := NewHandler(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Could not construct GraphQL handler: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
305
graphql/schema.go
Normal file
305
graphql/schema.go
Normal file
|
|
@ -0,0 +1,305 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package graphql
|
||||||
|
|
||||||
|
const schema string = `
|
||||||
|
# Bytes32 is a 32 byte binary string, represented as 0x-prefixed hexadecimal.
|
||||||
|
scalar Bytes32
|
||||||
|
# Address is a 20 byte Ethereum address, represented as 0x-prefixed hexadecimal.
|
||||||
|
scalar Address
|
||||||
|
# Bytes is an arbitrary length binary string, represented as 0x-prefixed hexadecimal.
|
||||||
|
scalar Bytes
|
||||||
|
# BigInt is a large integer. Input is accepted as either a JSON number or as a string.
|
||||||
|
# Strings may be either decimal or 0x-prefixed hexadecimal. Output values are all
|
||||||
|
# 0x-prefixed hexadecimal.
|
||||||
|
scalar BigInt
|
||||||
|
# Long is a 64 bit unsigned integer.
|
||||||
|
scalar Long
|
||||||
|
|
||||||
|
schema {
|
||||||
|
query: Query
|
||||||
|
mutation: Mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
# Account is an Ethereum account at a particular block.
|
||||||
|
type Account {
|
||||||
|
# Address is the address owning the account.
|
||||||
|
address: Address!
|
||||||
|
# Balance is the balance of the account, in wei.
|
||||||
|
balance: BigInt!
|
||||||
|
# TransactionCount is the number of transactions sent from this account,
|
||||||
|
# or in the case of a contract, the number of contracts created. Otherwise
|
||||||
|
# known as the nonce.
|
||||||
|
transactionCount: Long!
|
||||||
|
# Code contains the smart contract code for this account, if the account
|
||||||
|
# is a (non-self-destructed) contract.
|
||||||
|
code: Bytes!
|
||||||
|
# Storage provides access to the storage of a contract account, indexed
|
||||||
|
# by its 32 byte slot identifier.
|
||||||
|
storage(slot: Bytes32!): Bytes32!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Log is an Ethereum event log.
|
||||||
|
type Log {
|
||||||
|
# Index is the index of this log in the block.
|
||||||
|
index: Int!
|
||||||
|
# Account is the account which generated this log - this will always
|
||||||
|
# be a contract account.
|
||||||
|
account(block: Long): Account!
|
||||||
|
# Topics is a list of 0-4 indexed topics for the log.
|
||||||
|
topics: [Bytes32!]!
|
||||||
|
# Data is unindexed data for this log.
|
||||||
|
data: Bytes!
|
||||||
|
# Transaction is the transaction that generated this log entry.
|
||||||
|
transaction: Transaction!
|
||||||
|
}
|
||||||
|
|
||||||
|
# Transaction is an Ethereum transaction.
|
||||||
|
type Transaction {
|
||||||
|
# Hash is the hash of this transaction.
|
||||||
|
hash: Bytes32!
|
||||||
|
# Nonce is the nonce of the account this transaction was generated with.
|
||||||
|
nonce: Long!
|
||||||
|
# Index is the index of this transaction in the parent block. This will
|
||||||
|
# be null if the transaction has not yet beenn mined.
|
||||||
|
index: Int
|
||||||
|
# From is the account that sent this transaction - this will always be
|
||||||
|
# an externally owned account.
|
||||||
|
from(block: Long): Account!
|
||||||
|
# To is the account the transaction was sent to. This is null for
|
||||||
|
# contract-creating transactions.
|
||||||
|
to(block: Long): Account
|
||||||
|
# Value is the value, in wei, sent along with this transaction.
|
||||||
|
value: BigInt!
|
||||||
|
# GasPrice is the price offered to miners for gas, in wei per unit.
|
||||||
|
gasPrice: BigInt!
|
||||||
|
# Gas is the maximum amount of gas this transaction can consume.
|
||||||
|
gas: Long!
|
||||||
|
# InputData is the data supplied to the target of the transaction.
|
||||||
|
inputData: Bytes!
|
||||||
|
# Block is the block this transaction was mined in. This will be null if
|
||||||
|
# the transaction has not yet been mined.
|
||||||
|
block: Block
|
||||||
|
|
||||||
|
# Status is the return status of the transaction. This will be 1 if the
|
||||||
|
# transaction succeeded, or 0 if it failed (due to a revert, or due to
|
||||||
|
# running out of gas). If the transaction has not yet been mined, this
|
||||||
|
# field will be null.
|
||||||
|
status: Long
|
||||||
|
# GasUsed is the amount of gas that was used processing this transaction.
|
||||||
|
# If the transaction has not yet been mined, this field will be null.
|
||||||
|
gasUsed: Long
|
||||||
|
# CumulativeGasUsed is the total gas used in the block up to and including
|
||||||
|
# this transaction. If the transaction has not yet been mined, this field
|
||||||
|
# will be null.
|
||||||
|
cumulativeGasUsed: Long
|
||||||
|
# CreatedContract is the account that was created by a contract creation
|
||||||
|
# transaction. If the transaction was not a contract creation transaction,
|
||||||
|
# or it has not yet been mined, this field will be null.
|
||||||
|
createdContract(block: Long): Account
|
||||||
|
# Logs is a list of log entries emitted by this transaction. If the
|
||||||
|
# transaction has not yet been mined, this field will be null.
|
||||||
|
logs: [Log!]
|
||||||
|
}
|
||||||
|
|
||||||
|
# BlockFilterCriteria encapsulates log filter criteria for a filter applied
|
||||||
|
# to a single block.
|
||||||
|
input BlockFilterCriteria {
|
||||||
|
# Addresses is list of addresses that are of interest. If this list is
|
||||||
|
# empty, results will not be filtered by address.
|
||||||
|
addresses: [Address!]
|
||||||
|
# Topics list restricts matches to particular event topics. Each event has a list
|
||||||
|
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||||
|
# topic. Non-empty elements represent an alternative that matches any of the
|
||||||
|
# contained topics.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# - [] or nil matches any topic list
|
||||||
|
# - [[A]] matches topic A in first position
|
||||||
|
# - [[], [B]] matches any topic in first position, B in second position
|
||||||
|
# - [[A], [B]] matches topic A in first position, B in second position
|
||||||
|
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||||
|
topics: [[Bytes32!]!]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Block is an Ethereum block.
|
||||||
|
type Block {
|
||||||
|
# Number is the number of this block, starting at 0 for the genesis block.
|
||||||
|
number: Long!
|
||||||
|
# Hash is the block hash of this block.
|
||||||
|
hash: Bytes32!
|
||||||
|
# Parent is the parent block of this block.
|
||||||
|
parent: Block
|
||||||
|
# Nonce is the block nonce, an 8 byte sequence determined by the miner.
|
||||||
|
nonce: Bytes!
|
||||||
|
# TransactionsRoot is the keccak256 hash of the root of the trie of transactions in this block.
|
||||||
|
transactionsRoot: Bytes32!
|
||||||
|
# TransactionCount is the number of transactions in this block. if
|
||||||
|
# transactions are not available for this block, this field will be null.
|
||||||
|
transactionCount: Int
|
||||||
|
# StateRoot is the keccak256 hash of the state trie after this block was processed.
|
||||||
|
stateRoot: Bytes32!
|
||||||
|
# ReceiptsRoot is the keccak256 hash of the trie of transaction receipts in this block.
|
||||||
|
receiptsRoot: Bytes32!
|
||||||
|
# Miner is the account that mined this block.
|
||||||
|
miner(block: Long): Account!
|
||||||
|
# ExtraData is an arbitrary data field supplied by the miner.
|
||||||
|
extraData: Bytes!
|
||||||
|
# GasLimit is the maximum amount of gas that was available to transactions in this block.
|
||||||
|
gasLimit: Long!
|
||||||
|
# GasUsed is the amount of gas that was used executing transactions in this block.
|
||||||
|
gasUsed: Long!
|
||||||
|
# Timestamp is the unix timestamp at which this block was mined.
|
||||||
|
timestamp: BigInt!
|
||||||
|
# LogsBloom is a bloom filter that can be used to check if a block may
|
||||||
|
# contain log entries matching a filter.
|
||||||
|
logsBloom: Bytes!
|
||||||
|
# MixHash is the hash that was used as an input to the PoW process.
|
||||||
|
mixHash: Bytes32!
|
||||||
|
# Difficulty is a measure of the difficulty of mining this block.
|
||||||
|
difficulty: BigInt!
|
||||||
|
# TotalDifficulty is the sum of all difficulty values up to and including
|
||||||
|
# this block.
|
||||||
|
totalDifficulty: BigInt!
|
||||||
|
# OmmerCount is the number of ommers (AKA uncles) associated with this
|
||||||
|
# block. If ommers are unavailable, this field will be null.
|
||||||
|
ommerCount: Int
|
||||||
|
# Ommers is a list of ommer (AKA uncle) blocks associated with this block.
|
||||||
|
# If ommers are unavailable, this field will be null. Depending on your
|
||||||
|
# node, the transactions, transactionAt, transactionCount, ommers,
|
||||||
|
# ommerCount and ommerAt fields may not be available on any ommer blocks.
|
||||||
|
ommers: [Block]
|
||||||
|
# OmmerAt returns the ommer (AKA uncle) at the specified index. If ommers
|
||||||
|
# are unavailable, or the index is out of bounds, this field will be null.
|
||||||
|
ommerAt(index: Int!): Block
|
||||||
|
# OmmerHash is the keccak256 hash of all the ommers (AKA uncles)
|
||||||
|
# associated with this block.
|
||||||
|
ommerHash: Bytes32!
|
||||||
|
# Transactions is a list of transactions associated with this block. If
|
||||||
|
# transactions are unavailable for this block, this field will be null.
|
||||||
|
transactions: [Transaction!]
|
||||||
|
# TransactionAt returns the transaction at the specified index. If
|
||||||
|
# transactions are unavailable for this block, or if the index is out of
|
||||||
|
# bounds, this field will be null.
|
||||||
|
transactionAt(index: Int!): Transaction
|
||||||
|
# Logs returns a filtered set of logs from this block.
|
||||||
|
logs(filter: BlockFilterCriteria!): [Log!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
# CallData represents the data associated with a local contract call.
|
||||||
|
# All fields are optional.
|
||||||
|
input CallData {
|
||||||
|
# From is the address making the call.
|
||||||
|
from: Address
|
||||||
|
# To is the address the call is sent to.
|
||||||
|
to: Address
|
||||||
|
# Gas is the amount of gas sent with the call.
|
||||||
|
gas: Long
|
||||||
|
# GasPrice is the price, in wei, offered for each unit of gas.
|
||||||
|
gasPrice: BigInt
|
||||||
|
# Value is the value, in wei, sent along with the call.
|
||||||
|
value: BigInt
|
||||||
|
# Data is the data sent to the callee.
|
||||||
|
data: Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
# CallResult is the result of a local call operationn.
|
||||||
|
type CallResult {
|
||||||
|
# Data is the return data of the called contract.
|
||||||
|
data: Bytes!
|
||||||
|
# GasUsed is the amount of gas used by the call, after any refunds.
|
||||||
|
gasUsed: Long!
|
||||||
|
# Status is the result of the call - 1 for success or 0 for failure.
|
||||||
|
status: Long!
|
||||||
|
}
|
||||||
|
|
||||||
|
# FilterCriteria encapsulates log filter criteria for searching log entries.
|
||||||
|
input FilterCriteria {
|
||||||
|
# FromBlock is the block at which to start searching, inclusive. Defaults
|
||||||
|
# to the latest block if not supplied.
|
||||||
|
fromBlock: Long
|
||||||
|
# ToBlock is the block at which to stop searching, inclusive. Defaults
|
||||||
|
# to the latest block if not supplied.
|
||||||
|
toBlock: Long
|
||||||
|
# Addresses is a list of addresses that are of interest. If this list is
|
||||||
|
# empty, results will not be filtered by address.
|
||||||
|
addresses: [Address!]
|
||||||
|
# Topics list restricts matches to particular event topics. Each event has a list
|
||||||
|
# of topics. Topics matches a prefix of that list. An empty element array matches any
|
||||||
|
# topic. Non-empty elements represent an alternative that matches any of the
|
||||||
|
# contained topics.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# - [] or nil matches any topic list
|
||||||
|
# - [[A]] matches topic A in first position
|
||||||
|
# - [[], [B]] matches any topic in first position, B in second position
|
||||||
|
# - [[A], [B]] matches topic A in first position, B in second position
|
||||||
|
# - [[A, B]], [C, D]] matches topic (A OR B) in first position, (C OR D) in second position
|
||||||
|
topics: [[Bytes32!]!]
|
||||||
|
}
|
||||||
|
|
||||||
|
# SyncState contains the current synchronisation state of the client.
|
||||||
|
type SyncState{
|
||||||
|
# StartingBlock is the block number at which synchronisation started.
|
||||||
|
startingBlock: Long!
|
||||||
|
# CurrentBlock is the point at which synchronisation has presently reached.
|
||||||
|
currentBlock: Long!
|
||||||
|
# HighestBlock is the latest known block number.
|
||||||
|
highestBlock: Long!
|
||||||
|
# PulledStates is the number of state entries fetched so far, or null
|
||||||
|
# if this is not known or not relevant.
|
||||||
|
pulledStates: Long
|
||||||
|
# KnownStates is the number of states the node knows of so far, or null
|
||||||
|
# if this is not known or not relevant.
|
||||||
|
knownStates: Long
|
||||||
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
# Account fetches an Ethereum account at the specified block number.
|
||||||
|
# If blockNumber is not provided, it defaults to the most recent block.
|
||||||
|
account(address: Address!, blockNumber: Long): Account!
|
||||||
|
# Block fetches an Ethereum block by number or by hash. If neither is
|
||||||
|
# supplied, the most recent known block is returned.
|
||||||
|
block(number: Long, hash: Bytes32): Block
|
||||||
|
# Blocks returns all the blocks between two numbers, inclusive. If
|
||||||
|
# to is not supplied, it defaults to the most recent known block.
|
||||||
|
blocks(from: Long!, to: Long): [Block!]!
|
||||||
|
# Transaction returns a transaction specified by its hash.
|
||||||
|
transaction(hash: Bytes32!): Transaction
|
||||||
|
# Call executes a local call operation. If blockNumber is not specified,
|
||||||
|
# it defaults to the most recent known block.
|
||||||
|
call(data: CallData!, blockNumber: Long): CallResult
|
||||||
|
# EstimateGas estimates the amount of gas that will be required for
|
||||||
|
# successful execution of a transaction. If blockNumber is not specified,
|
||||||
|
# it defaults ot the most recent known block.
|
||||||
|
estimateGas(data: CallData!, blockNumber: Long): Long!
|
||||||
|
# Logs returns log entries matching the provided filter.
|
||||||
|
logs(filter: FilterCriteria!): [Log!]!
|
||||||
|
# GasPrice returns the node's estimate of a gas price sufficient to
|
||||||
|
# ensure a transaction is mined in a timely fashion.
|
||||||
|
gasPrice: BigInt!
|
||||||
|
# ProtocolVersion returns the current wire protocol version number.
|
||||||
|
protocolVersion: Int!
|
||||||
|
# Syncing returns information on the current synchronisation state.
|
||||||
|
syncing: SyncState
|
||||||
|
}
|
||||||
|
|
||||||
|
type Mutation {
|
||||||
|
# SendRawTransaction sends an RLP-encoded transaction to the network.
|
||||||
|
sendRawTransaction(data: Bytes!): Bytes32!
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
@ -144,9 +144,9 @@ type FilterQuery struct {
|
||||||
// Examples:
|
// Examples:
|
||||||
// {} or nil matches any topic list
|
// {} or nil matches any topic list
|
||||||
// {{A}} matches topic A in first position
|
// {{A}} matches topic A in first position
|
||||||
// {{}, {B}} matches any topic in first position, B in second position
|
// {{}, {B}} matches any topic in first position AND B in second position
|
||||||
// {{A}, {B}} matches topic A in first position, B in second position
|
// {{A}, {B}} matches topic A in first position AND B in second position
|
||||||
// {{A, B}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
// {{A, B}, {C, D}} matches topic (A OR B) in first position AND (C OR D) in second position
|
||||||
Topics [][]common.Hash
|
Topics [][]common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
||||||
log.Warn("Failed transaction send attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
|
log.Warn("Failed transaction send attempt", "from", args.From, "to", args.To, "value", args.Value.ToInt(), "err", err)
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
return submitTransaction(ctx, s.b, signed)
|
return SubmitTransaction(ctx, s.b, signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTransaction will create a transaction from the given arguments and
|
// SignTransaction will create a transaction from the given arguments and
|
||||||
|
|
@ -675,41 +675,54 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
|
||||||
|
|
||||||
// CallArgs represents the arguments for a call.
|
// CallArgs represents the arguments for a call.
|
||||||
type CallArgs struct {
|
type CallArgs struct {
|
||||||
From common.Address `json:"from"`
|
From *common.Address `json:"from"`
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
Gas hexutil.Uint64 `json:"gas"`
|
Gas *hexutil.Uint64 `json:"gas"`
|
||||||
GasPrice hexutil.Big `json:"gasPrice"`
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
||||||
Value hexutil.Big `json:"value"`
|
Value *hexutil.Big `json:"value"`
|
||||||
Data hexutil.Bytes `json:"data"`
|
Data *hexutil.Bytes `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, timeout time.Duration) ([]byte, uint64, bool, error) {
|
func DoCall(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
|
||||||
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
|
||||||
|
|
||||||
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
|
state, header, err := b.StateAndHeaderByNumber(ctx, blockNr)
|
||||||
if state == nil || err != nil {
|
if state == nil || err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
// Set sender address or use a default if none specified
|
// Set sender address or use a default if none specified
|
||||||
addr := args.From
|
var addr common.Address
|
||||||
if addr == (common.Address{}) {
|
if args.From == nil {
|
||||||
if wallets := s.b.AccountManager().Wallets(); len(wallets) > 0 {
|
if wallets := b.AccountManager().Wallets(); len(wallets) > 0 {
|
||||||
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
if accounts := wallets[0].Accounts(); len(accounts) > 0 {
|
||||||
addr = accounts[0].Address
|
addr = accounts[0].Address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
addr = *args.From
|
||||||
}
|
}
|
||||||
// Set default gas & gas price if none were set
|
// Set default gas & gas price if none were set
|
||||||
gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
|
gas := uint64(math.MaxUint64 / 2)
|
||||||
if gas == 0 {
|
if args.Gas != nil {
|
||||||
gas = math.MaxUint64 / 2
|
gas = uint64(*args.Gas)
|
||||||
}
|
}
|
||||||
if gasPrice.Sign() == 0 {
|
gasPrice := new(big.Int).SetUint64(defaultGasPrice)
|
||||||
gasPrice = new(big.Int).SetUint64(defaultGasPrice)
|
if args.GasPrice != nil {
|
||||||
|
gasPrice = args.GasPrice.ToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
value := new(big.Int)
|
||||||
|
if args.Value != nil {
|
||||||
|
value = args.Value.ToInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
if args.Data != nil {
|
||||||
|
data = []byte(*args.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new call message
|
// Create new call message
|
||||||
msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
|
msg := types.NewMessage(addr, args.To, 0, value, gas, gasPrice, data, false)
|
||||||
|
|
||||||
// Setup context so it may be cancelled the call has completed
|
// Setup context so it may be cancelled the call has completed
|
||||||
// or, in case of unmetered gas, setup a context with a timeout.
|
// or, in case of unmetered gas, setup a context with a timeout.
|
||||||
|
|
@ -724,7 +737,7 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
// Get a new instance of the EVM.
|
// Get a new instance of the EVM.
|
||||||
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header)
|
evm, vmError, err := b.GetEVM(ctx, msg, state, header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, false, err
|
return nil, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -748,24 +761,22 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
|
||||||
// Call executes the given transaction on the state for the given block number.
|
// Call executes the given transaction on the state for the given block number.
|
||||||
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
// It doesn't make and changes in the state/blockchain and is useful to execute and retrieve values.
|
||||||
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
|
||||||
result, _, _, err := s.doCall(ctx, args, blockNr, 5*time.Second)
|
result, _, _, err := DoCall(ctx, s.b, args, blockNr, vm.Config{}, 5*time.Second)
|
||||||
return (hexutil.Bytes)(result), err
|
return (hexutil.Bytes)(result), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Uint64, error) {
|
||||||
// given transaction against the current pending block.
|
|
||||||
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
|
||||||
// Binary search the gas requirement, as it may be higher than the amount used
|
// Binary search the gas requirement, as it may be higher than the amount used
|
||||||
var (
|
var (
|
||||||
lo uint64 = params.TxGas - 1
|
lo uint64 = params.TxGas - 1
|
||||||
hi uint64
|
hi uint64
|
||||||
cap uint64
|
cap uint64
|
||||||
)
|
)
|
||||||
if uint64(args.Gas) >= params.TxGas {
|
if args.Gas != nil && uint64(*args.Gas) >= params.TxGas {
|
||||||
hi = uint64(args.Gas)
|
hi = uint64(*args.Gas)
|
||||||
} else {
|
} else {
|
||||||
// Retrieve the current pending block to act as the gas ceiling
|
// Retrieve the block to act as the gas ceiling
|
||||||
block, err := s.b.BlockByNumber(ctx, rpc.PendingBlockNumber)
|
block, err := b.BlockByNumber(ctx, blockNr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -775,9 +786,9 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
executable := func(gas uint64) bool {
|
executable := func(gas uint64) bool {
|
||||||
args.Gas = hexutil.Uint64(gas)
|
args.Gas = (*hexutil.Uint64)(&gas)
|
||||||
|
|
||||||
_, _, failed, err := s.doCall(ctx, args, rpc.PendingBlockNumber, 0)
|
_, _, failed, err := DoCall(ctx, b, args, rpc.PendingBlockNumber, vm.Config{}, 0)
|
||||||
if err != nil || failed {
|
if err != nil || failed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -801,6 +812,12 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
|
||||||
return hexutil.Uint64(hi), nil
|
return hexutil.Uint64(hi), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EstimateGas returns an estimate of the amount of gas needed to execute the
|
||||||
|
// given transaction against the current pending block.
|
||||||
|
func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (hexutil.Uint64, error) {
|
||||||
|
return DoEstimateGas(ctx, s.b, args, rpc.PendingBlockNumber)
|
||||||
|
}
|
||||||
|
|
||||||
// ExecutionResult groups all structured logs emitted by the EVM
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
|
|
@ -825,7 +842,7 @@ type StructLogRes struct {
|
||||||
Storage *map[string]string `json:"storage,omitempty"`
|
Storage *map[string]string `json:"storage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatLogs formats EVM returned structured logs for json output
|
// FormatLogs formats EVM returned structured logs for json output
|
||||||
func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
func FormatLogs(logs []vm.StructLog) []StructLogRes {
|
||||||
formatted := make([]StructLogRes, len(logs))
|
formatted := make([]StructLogRes, len(logs))
|
||||||
for index, trace := range logs {
|
for index, trace := range logs {
|
||||||
|
|
@ -1256,8 +1273,8 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
|
||||||
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
return types.NewTransaction(uint64(*args.Nonce), *args.To, (*big.Int)(args.Value), uint64(*args.Gas), (*big.Int)(args.GasPrice), input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// submitTransaction is a helper function that submits tx to txPool and logs a message.
|
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
|
||||||
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
|
func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
|
||||||
if err := b.SendTx(ctx, tx); err != nil {
|
if err := b.SendTx(ctx, tx); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -1309,7 +1326,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
return submitTransaction(ctx, s.b, signed)
|
return SubmitTransaction(ctx, s.b, signed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRawTransaction will add the signed transaction to the transaction pool.
|
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||||
|
|
@ -1319,7 +1336,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
|
||||||
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
return submitTransaction(ctx, s.b, tx)
|
return SubmitTransaction(ctx, s.b, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign calculates an ECDSA signature for:
|
// Sign calculates an ECDSA signature for:
|
||||||
|
|
|
||||||
|
|
@ -109,8 +109,12 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||||
bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
|
bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var trustedNodes []string
|
||||||
|
if leth.config.ULC != nil {
|
||||||
|
trustedNodes = leth.config.ULC.TrustedServers
|
||||||
|
}
|
||||||
leth.relay = NewLesTxRelay(peers, leth.reqDist)
|
leth.relay = NewLesTxRelay(peers, leth.reqDist)
|
||||||
leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg)
|
leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg, trustedNodes)
|
||||||
leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool)
|
leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool)
|
||||||
|
|
||||||
leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever)
|
leth.odr = NewLesOdr(chainDb, light.DefaultClientIndexerConfig, leth.retriever)
|
||||||
|
|
@ -136,10 +140,33 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
|
leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay)
|
||||||
if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, light.DefaultClientIndexerConfig, true, config.NetworkId, leth.eventMux, leth.engine, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.relay, leth.serverPool, quitSync, &leth.wg); err != nil {
|
|
||||||
|
if leth.protocolManager, err = NewProtocolManager(
|
||||||
|
leth.chainConfig,
|
||||||
|
light.DefaultClientIndexerConfig,
|
||||||
|
true,
|
||||||
|
config.NetworkId,
|
||||||
|
leth.eventMux,
|
||||||
|
leth.engine,
|
||||||
|
leth.peers,
|
||||||
|
leth.blockchain,
|
||||||
|
nil,
|
||||||
|
chainDb,
|
||||||
|
leth.odr,
|
||||||
|
leth.relay,
|
||||||
|
leth.serverPool,
|
||||||
|
quitSync,
|
||||||
|
&leth.wg,
|
||||||
|
config.ULC); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if leth.protocolManager.isULCEnabled() {
|
||||||
|
log.Warn("Ultra light client is enabled", "trustedNodes", len(leth.protocolManager.ulc.trustedKeys), "minTrustedFraction", leth.protocolManager.ulc.minTrustedFraction)
|
||||||
|
leth.blockchain.DisableCheckFreq()
|
||||||
|
}
|
||||||
leth.ApiBackend = &LesApiBackend{leth, nil}
|
leth.ApiBackend = &LesApiBackend{leth, nil}
|
||||||
|
|
||||||
gpoParams := config.GPO
|
gpoParams := config.GPO
|
||||||
if gpoParams.Default == nil {
|
if gpoParams.Default == nil {
|
||||||
gpoParams.Default = config.MinerGasPrice
|
gpoParams.Default = config.MinerGasPrice
|
||||||
|
|
|
||||||
306
les/fetcher.go
306
les/fetcher.go
|
|
@ -43,7 +43,7 @@ const (
|
||||||
type lightFetcher struct {
|
type lightFetcher struct {
|
||||||
pm *ProtocolManager
|
pm *ProtocolManager
|
||||||
odr *LesOdr
|
odr *LesOdr
|
||||||
chain *light.LightChain
|
chain lightChain
|
||||||
|
|
||||||
lock sync.Mutex // lock protects access to the fetcher's internal state variables except sent requests
|
lock sync.Mutex // lock protects access to the fetcher's internal state variables except sent requests
|
||||||
maxConfirmedTd *big.Int
|
maxConfirmedTd *big.Int
|
||||||
|
|
@ -52,11 +52,19 @@ type lightFetcher struct {
|
||||||
syncing bool
|
syncing bool
|
||||||
syncDone chan *peer
|
syncDone chan *peer
|
||||||
|
|
||||||
reqMu sync.RWMutex // reqMu protects access to sent header fetch requests
|
reqMu sync.RWMutex // reqMu protects access to sent header fetch requests
|
||||||
requested map[uint64]fetchRequest
|
requested map[uint64]fetchRequest
|
||||||
deliverChn chan fetchResponse
|
deliverChn chan fetchResponse
|
||||||
timeoutChn chan uint64
|
timeoutChn chan uint64
|
||||||
requestChn chan bool // true if initiated from outside
|
requestChn chan bool // true if initiated from outside
|
||||||
|
lastTrustedHeader *types.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
// lightChain extends the BlockChain interface by locking.
|
||||||
|
type lightChain interface {
|
||||||
|
BlockChain
|
||||||
|
LockChain()
|
||||||
|
UnlockChain()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetcherPeerInfo holds fetcher-specific information about each active peer
|
// fetcherPeerInfo holds fetcher-specific information about each active peer
|
||||||
|
|
@ -145,6 +153,7 @@ func (f *lightFetcher) syncLoop() {
|
||||||
reqID uint64
|
reqID uint64
|
||||||
syncing bool
|
syncing bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if !f.syncing && !(newAnnounce && s) {
|
if !f.syncing && !(newAnnounce && s) {
|
||||||
rq, reqID, syncing = f.nextRequest()
|
rq, reqID, syncing = f.nextRequest()
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +236,6 @@ func (f *lightFetcher) registerPeer(p *peer) {
|
||||||
|
|
||||||
f.lock.Lock()
|
f.lock.Lock()
|
||||||
defer f.lock.Unlock()
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
f.peers[p] = &fetcherPeerInfo{nodeByHash: make(map[common.Hash]*fetcherTreeNode)}
|
f.peers[p] = &fetcherPeerInfo{nodeByHash: make(map[common.Hash]*fetcherTreeNode)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,8 +288,10 @@ func (f *lightFetcher) announce(p *peer, head *announceData) {
|
||||||
fp.nodeCnt = 0
|
fp.nodeCnt = 0
|
||||||
fp.nodeByHash = make(map[common.Hash]*fetcherTreeNode)
|
fp.nodeByHash = make(map[common.Hash]*fetcherTreeNode)
|
||||||
}
|
}
|
||||||
|
// check if the node count is too high to add new nodes, discard oldest ones if necessary
|
||||||
if n != nil {
|
if n != nil {
|
||||||
// check if the node count is too high to add new nodes, discard oldest ones if necessary
|
// n is now the reorg common ancestor, add a new branch of nodes
|
||||||
|
// check if the node count is too high to add new nodes
|
||||||
locked := false
|
locked := false
|
||||||
for uint64(fp.nodeCnt)+head.Number-n.number > maxNodeCount && fp.root != nil {
|
for uint64(fp.nodeCnt)+head.Number-n.number > maxNodeCount && fp.root != nil {
|
||||||
if !locked {
|
if !locked {
|
||||||
|
|
@ -325,6 +335,7 @@ func (f *lightFetcher) announce(p *peer, head *announceData) {
|
||||||
fp.nodeByHash[n.hash] = n
|
fp.nodeByHash[n.hash] = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n == nil {
|
if n == nil {
|
||||||
// could not find reorg common ancestor or had to delete entire tree, a new root and a resync is needed
|
// could not find reorg common ancestor or had to delete entire tree, a new root and a resync is needed
|
||||||
if fp.root != nil {
|
if fp.root != nil {
|
||||||
|
|
@ -411,25 +422,13 @@ func (f *lightFetcher) requestedID(reqID uint64) bool {
|
||||||
// to be downloaded starting from the head backwards is also returned
|
// to be downloaded starting from the head backwards is also returned
|
||||||
func (f *lightFetcher) nextRequest() (*distReq, uint64, bool) {
|
func (f *lightFetcher) nextRequest() (*distReq, uint64, bool) {
|
||||||
var (
|
var (
|
||||||
bestHash common.Hash
|
bestHash common.Hash
|
||||||
bestAmount uint64
|
bestAmount uint64
|
||||||
|
bestTd *big.Int
|
||||||
|
bestSyncing bool
|
||||||
)
|
)
|
||||||
bestTd := f.maxConfirmedTd
|
bestHash, bestAmount, bestTd, bestSyncing = f.findBestRequest()
|
||||||
bestSyncing := false
|
|
||||||
|
|
||||||
for p, fp := range f.peers {
|
|
||||||
for hash, n := range fp.nodeByHash {
|
|
||||||
if !f.checkKnownNode(p, n) && !n.requested && (bestTd == nil || n.td.Cmp(bestTd) >= 0) {
|
|
||||||
amount := f.requestAmount(p, n)
|
|
||||||
if bestTd == nil || n.td.Cmp(bestTd) > 0 || amount < bestAmount {
|
|
||||||
bestHash = hash
|
|
||||||
bestAmount = amount
|
|
||||||
bestTd = n.td
|
|
||||||
bestSyncing = fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bestTd == f.maxConfirmedTd {
|
if bestTd == f.maxConfirmedTd {
|
||||||
return nil, 0, false
|
return nil, 0, false
|
||||||
}
|
}
|
||||||
|
|
@ -437,74 +436,142 @@ func (f *lightFetcher) nextRequest() (*distReq, uint64, bool) {
|
||||||
var rq *distReq
|
var rq *distReq
|
||||||
reqID := genReqID()
|
reqID := genReqID()
|
||||||
if bestSyncing {
|
if bestSyncing {
|
||||||
rq = &distReq{
|
rq = f.newFetcherDistReqForSync(bestHash)
|
||||||
getCost: func(dp distPeer) uint64 {
|
|
||||||
return 0
|
|
||||||
},
|
|
||||||
canSend: func(dp distPeer) bool {
|
|
||||||
p := dp.(*peer)
|
|
||||||
f.lock.Lock()
|
|
||||||
defer f.lock.Unlock()
|
|
||||||
|
|
||||||
fp := f.peers[p]
|
|
||||||
return fp != nil && fp.nodeByHash[bestHash] != nil
|
|
||||||
},
|
|
||||||
request: func(dp distPeer) func() {
|
|
||||||
go func() {
|
|
||||||
p := dp.(*peer)
|
|
||||||
p.Log().Debug("Synchronisation started")
|
|
||||||
f.pm.synchronise(p)
|
|
||||||
f.syncDone <- p
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rq = &distReq{
|
rq = f.newFetcherDistReq(bestHash, reqID, bestAmount)
|
||||||
getCost: func(dp distPeer) uint64 {
|
|
||||||
p := dp.(*peer)
|
|
||||||
return p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))
|
|
||||||
},
|
|
||||||
canSend: func(dp distPeer) bool {
|
|
||||||
p := dp.(*peer)
|
|
||||||
f.lock.Lock()
|
|
||||||
defer f.lock.Unlock()
|
|
||||||
|
|
||||||
fp := f.peers[p]
|
|
||||||
if fp == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
n := fp.nodeByHash[bestHash]
|
|
||||||
return n != nil && !n.requested
|
|
||||||
},
|
|
||||||
request: func(dp distPeer) func() {
|
|
||||||
p := dp.(*peer)
|
|
||||||
f.lock.Lock()
|
|
||||||
fp := f.peers[p]
|
|
||||||
if fp != nil {
|
|
||||||
n := fp.nodeByHash[bestHash]
|
|
||||||
if n != nil {
|
|
||||||
n.requested = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.lock.Unlock()
|
|
||||||
|
|
||||||
cost := p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))
|
|
||||||
p.fcServer.QueueRequest(reqID, cost)
|
|
||||||
f.reqMu.Lock()
|
|
||||||
f.requested[reqID] = fetchRequest{hash: bestHash, amount: bestAmount, peer: p, sent: mclock.Now()}
|
|
||||||
f.reqMu.Unlock()
|
|
||||||
go func() {
|
|
||||||
time.Sleep(hardRequestTimeout)
|
|
||||||
f.timeoutChn <- reqID
|
|
||||||
}()
|
|
||||||
return func() { p.RequestHeadersByHash(reqID, cost, bestHash, int(bestAmount), 0, true) }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return rq, reqID, bestSyncing
|
return rq, reqID, bestSyncing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findBestRequest finds the best head to request that has been announced by but not yet requested from a known peer.
|
||||||
|
// It also returns the announced Td (which should be verified after fetching the head),
|
||||||
|
// the necessary amount to request and whether a downloader sync is necessary instead of a normal header request.
|
||||||
|
func (f *lightFetcher) findBestRequest() (bestHash common.Hash, bestAmount uint64, bestTd *big.Int, bestSyncing bool) {
|
||||||
|
bestTd = f.maxConfirmedTd
|
||||||
|
bestSyncing = false
|
||||||
|
|
||||||
|
for p, fp := range f.peers {
|
||||||
|
for hash, n := range fp.nodeByHash {
|
||||||
|
if f.checkKnownNode(p, n) || n.requested {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//if ulc mode is disabled, isTrustedHash returns true
|
||||||
|
amount := f.requestAmount(p, n)
|
||||||
|
if (bestTd == nil || n.td.Cmp(bestTd) > 0 || amount < bestAmount) && (f.isTrustedHash(hash) || f.maxConfirmedTd.Int64() == 0) {
|
||||||
|
bestHash = hash
|
||||||
|
bestTd = n.td
|
||||||
|
bestAmount = amount
|
||||||
|
bestSyncing = fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTrustedHash checks if the block can be trusted by the minimum trusted fraction.
|
||||||
|
func (f *lightFetcher) isTrustedHash(hash common.Hash) bool {
|
||||||
|
if !f.pm.isULCEnabled() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var numAgreed int
|
||||||
|
for p, fp := range f.peers {
|
||||||
|
if !p.isTrusted {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := fp.nodeByHash[hash]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
numAgreed++
|
||||||
|
}
|
||||||
|
|
||||||
|
return 100*numAgreed/len(f.pm.ulc.trustedKeys) >= f.pm.ulc.minTrustedFraction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *lightFetcher) newFetcherDistReqForSync(bestHash common.Hash) *distReq {
|
||||||
|
return &distReq{
|
||||||
|
getCost: func(dp distPeer) uint64 {
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
canSend: func(dp distPeer) bool {
|
||||||
|
p := dp.(*peer)
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if p.isOnlyAnnounce {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fp := f.peers[p]
|
||||||
|
return fp != nil && fp.nodeByHash[bestHash] != nil
|
||||||
|
},
|
||||||
|
request: func(dp distPeer) func() {
|
||||||
|
if f.pm.isULCEnabled() {
|
||||||
|
//keep last trusted header before sync
|
||||||
|
f.setLastTrustedHeader(f.chain.CurrentHeader())
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
p := dp.(*peer)
|
||||||
|
p.Log().Debug("Synchronisation started")
|
||||||
|
f.pm.synchronise(p)
|
||||||
|
f.syncDone <- p
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFetcherDistReq creates a new request for the distributor.
|
||||||
|
func (f *lightFetcher) newFetcherDistReq(bestHash common.Hash, reqID uint64, bestAmount uint64) *distReq {
|
||||||
|
return &distReq{
|
||||||
|
getCost: func(dp distPeer) uint64 {
|
||||||
|
p := dp.(*peer)
|
||||||
|
return p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))
|
||||||
|
},
|
||||||
|
canSend: func(dp distPeer) bool {
|
||||||
|
p := dp.(*peer)
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
|
if p.isOnlyAnnounce {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fp := f.peers[p]
|
||||||
|
if fp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
n := fp.nodeByHash[bestHash]
|
||||||
|
return n != nil && !n.requested
|
||||||
|
},
|
||||||
|
request: func(dp distPeer) func() {
|
||||||
|
p := dp.(*peer)
|
||||||
|
f.lock.Lock()
|
||||||
|
fp := f.peers[p]
|
||||||
|
if fp != nil {
|
||||||
|
n := fp.nodeByHash[bestHash]
|
||||||
|
if n != nil {
|
||||||
|
n.requested = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.lock.Unlock()
|
||||||
|
|
||||||
|
cost := p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount))
|
||||||
|
p.fcServer.QueueRequest(reqID, cost)
|
||||||
|
f.reqMu.Lock()
|
||||||
|
f.requested[reqID] = fetchRequest{hash: bestHash, amount: bestAmount, peer: p, sent: mclock.Now()}
|
||||||
|
f.reqMu.Unlock()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(hardRequestTimeout)
|
||||||
|
f.timeoutChn <- reqID
|
||||||
|
}()
|
||||||
|
return func() { p.RequestHeadersByHash(reqID, cost, bestHash, int(bestAmount), 0, true) }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// deliverHeaders delivers header download request responses for processing
|
// deliverHeaders delivers header download request responses for processing
|
||||||
func (f *lightFetcher) deliverHeaders(peer *peer, reqID uint64, headers []*types.Header) {
|
func (f *lightFetcher) deliverHeaders(peer *peer, reqID uint64, headers []*types.Header) {
|
||||||
f.deliverChn <- fetchResponse{reqID: reqID, headers: headers, peer: peer}
|
f.deliverChn <- fetchResponse{reqID: reqID, headers: headers, peer: peer}
|
||||||
|
|
@ -520,6 +587,7 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo
|
||||||
for i, header := range resp.headers {
|
for i, header := range resp.headers {
|
||||||
headers[int(req.amount)-1-i] = header
|
headers[int(req.amount)-1-i] = header
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil {
|
if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil {
|
||||||
if err == consensus.ErrFutureBlock {
|
if err == consensus.ErrFutureBlock {
|
||||||
return true
|
return true
|
||||||
|
|
@ -544,6 +612,7 @@ func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) boo
|
||||||
// downloaded and validated batch or headers
|
// downloaded and validated batch or headers
|
||||||
func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) {
|
func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) {
|
||||||
var maxTd *big.Int
|
var maxTd *big.Int
|
||||||
|
|
||||||
for p, fp := range f.peers {
|
for p, fp := range f.peers {
|
||||||
if !f.checkAnnouncedHeaders(fp, headers, tds) {
|
if !f.checkAnnouncedHeaders(fp, headers, tds) {
|
||||||
p.Log().Debug("Inconsistent announcement")
|
p.Log().Debug("Inconsistent announcement")
|
||||||
|
|
@ -553,6 +622,7 @@ func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) {
|
||||||
maxTd = fp.confirmedTd
|
maxTd = fp.confirmedTd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if maxTd != nil {
|
if maxTd != nil {
|
||||||
f.updateMaxConfirmedTd(maxTd)
|
f.updateMaxConfirmedTd(maxTd)
|
||||||
}
|
}
|
||||||
|
|
@ -640,22 +710,72 @@ func (f *lightFetcher) checkSyncedHeaders(p *peer) {
|
||||||
p.Log().Debug("Unknown peer to check sync headers")
|
p.Log().Debug("Unknown peer to check sync headers")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n := fp.lastAnnounced
|
n := fp.lastAnnounced
|
||||||
var td *big.Int
|
var td *big.Int
|
||||||
|
|
||||||
|
var h *types.Header
|
||||||
|
if f.pm.isULCEnabled() {
|
||||||
|
var unapprovedHashes []common.Hash
|
||||||
|
// Overwrite last announced for ULC mode
|
||||||
|
h, unapprovedHashes = f.lastTrustedTreeNode(p)
|
||||||
|
//rollback untrusted blocks
|
||||||
|
f.chain.Rollback(unapprovedHashes)
|
||||||
|
//overwrite to last trusted
|
||||||
|
n = fp.nodeByHash[h.Hash()]
|
||||||
|
}
|
||||||
|
|
||||||
|
//find last valid block
|
||||||
for n != nil {
|
for n != nil {
|
||||||
if td = f.chain.GetTd(n.hash, n.number); td != nil {
|
if td = f.chain.GetTd(n.hash, n.number); td != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
n = n.parent
|
n = n.parent
|
||||||
}
|
}
|
||||||
// now n is the latest downloaded header after syncing
|
|
||||||
|
// Now n is the latest downloaded/approved header after syncing
|
||||||
if n == nil {
|
if n == nil {
|
||||||
p.Log().Debug("Synchronisation failed")
|
p.Log().Debug("Synchronisation failed")
|
||||||
go f.pm.removePeer(p.id)
|
go f.pm.removePeer(p.id)
|
||||||
} else {
|
return
|
||||||
header := f.chain.GetHeader(n.hash, n.number)
|
|
||||||
f.newHeaders([]*types.Header{header}, []*big.Int{td})
|
|
||||||
}
|
}
|
||||||
|
header := f.chain.GetHeader(n.hash, n.number)
|
||||||
|
f.newHeaders([]*types.Header{header}, []*big.Int{td})
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastTrustedTreeNode return last approved treeNode and a list of unapproved hashes
|
||||||
|
func (f *lightFetcher) lastTrustedTreeNode(p *peer) (*types.Header, []common.Hash) {
|
||||||
|
unapprovedHashes := make([]common.Hash, 0)
|
||||||
|
current := f.chain.CurrentHeader()
|
||||||
|
|
||||||
|
if f.lastTrustedHeader == nil {
|
||||||
|
return current, unapprovedHashes
|
||||||
|
}
|
||||||
|
|
||||||
|
canonical := f.chain.CurrentHeader()
|
||||||
|
if canonical.Number.Uint64() > f.lastTrustedHeader.Number.Uint64() {
|
||||||
|
canonical = f.chain.GetHeaderByNumber(f.lastTrustedHeader.Number.Uint64())
|
||||||
|
}
|
||||||
|
commonAncestor := rawdb.FindCommonAncestor(f.pm.chainDb, canonical, f.lastTrustedHeader)
|
||||||
|
if commonAncestor == nil {
|
||||||
|
log.Error("Common ancestor of last trusted header and canonical header is nil", "canonical hash", canonical.Hash(), "trusted hash", f.lastTrustedHeader.Hash())
|
||||||
|
return current, unapprovedHashes
|
||||||
|
}
|
||||||
|
|
||||||
|
for current.Hash() == commonAncestor.Hash() {
|
||||||
|
if f.isTrustedHash(current.Hash()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
unapprovedHashes = append(unapprovedHashes, current.Hash())
|
||||||
|
current = f.chain.GetHeader(current.ParentHash, current.Number.Uint64()-1)
|
||||||
|
}
|
||||||
|
return current, unapprovedHashes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *lightFetcher) setLastTrustedHeader(h *types.Header) {
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
f.lastTrustedHeader = h
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkKnownNode checks if a block tree node is known (downloaded and validated)
|
// checkKnownNode checks if a block tree node is known (downloaded and validated)
|
||||||
|
|
@ -747,6 +867,7 @@ func (f *lightFetcher) updateMaxConfirmedTd(td *big.Int) {
|
||||||
if f.lastUpdateStats != nil {
|
if f.lastUpdateStats != nil {
|
||||||
f.lastUpdateStats.next = newEntry
|
f.lastUpdateStats.next = newEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
f.lastUpdateStats = newEntry
|
f.lastUpdateStats = newEntry
|
||||||
for p := range f.peers {
|
for p := range f.peers {
|
||||||
f.checkUpdateStats(p, newEntry)
|
f.checkUpdateStats(p, newEntry)
|
||||||
|
|
@ -769,6 +890,7 @@ func (f *lightFetcher) checkUpdateStats(p *peer, newEntry *updateStatsEntry) {
|
||||||
p.Log().Debug("Unknown peer to check update stats")
|
p.Log().Debug("Unknown peer to check update stats")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if newEntry != nil && fp.firstUpdateStats == nil {
|
if newEntry != nil && fp.firstUpdateStats == nil {
|
||||||
fp.firstUpdateStats = newEntry
|
fp.firstUpdateStats = newEntry
|
||||||
}
|
}
|
||||||
|
|
|
||||||
155
les/fetcher_test.go
Normal file
155
les/fetcher_test.go
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFetcherULCPeerSelector(t *testing.T) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
id1 enode.ID = newNodeID(t).ID()
|
||||||
|
id2 enode.ID = newNodeID(t).ID()
|
||||||
|
id3 enode.ID = newNodeID(t).ID()
|
||||||
|
id4 enode.ID = newNodeID(t).ID()
|
||||||
|
)
|
||||||
|
|
||||||
|
ftn1 := &fetcherTreeNode{
|
||||||
|
hash: common.HexToHash("1"),
|
||||||
|
td: big.NewInt(1),
|
||||||
|
}
|
||||||
|
ftn2 := &fetcherTreeNode{
|
||||||
|
hash: common.HexToHash("2"),
|
||||||
|
td: big.NewInt(2),
|
||||||
|
parent: ftn1,
|
||||||
|
}
|
||||||
|
ftn3 := &fetcherTreeNode{
|
||||||
|
hash: common.HexToHash("3"),
|
||||||
|
td: big.NewInt(3),
|
||||||
|
parent: ftn2,
|
||||||
|
}
|
||||||
|
lf := lightFetcher{
|
||||||
|
pm: &ProtocolManager{
|
||||||
|
ulc: &ulc{
|
||||||
|
trustedKeys: map[string]struct{}{
|
||||||
|
id1.String(): {},
|
||||||
|
id2.String(): {},
|
||||||
|
id3.String(): {},
|
||||||
|
id4.String(): {},
|
||||||
|
},
|
||||||
|
minTrustedFraction: 70,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
maxConfirmedTd: ftn1.td,
|
||||||
|
|
||||||
|
peers: map[*peer]*fetcherPeerInfo{
|
||||||
|
{
|
||||||
|
id: "peer1",
|
||||||
|
Peer: p2p.NewPeer(id1, "peer1", []p2p.Cap{}),
|
||||||
|
isTrusted: true,
|
||||||
|
}: {
|
||||||
|
nodeByHash: map[common.Hash]*fetcherTreeNode{
|
||||||
|
ftn1.hash: ftn1,
|
||||||
|
ftn2.hash: ftn2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Peer: p2p.NewPeer(id2, "peer2", []p2p.Cap{}),
|
||||||
|
id: "peer2",
|
||||||
|
isTrusted: true,
|
||||||
|
}: {
|
||||||
|
nodeByHash: map[common.Hash]*fetcherTreeNode{
|
||||||
|
ftn1.hash: ftn1,
|
||||||
|
ftn2.hash: ftn2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "peer3",
|
||||||
|
Peer: p2p.NewPeer(id3, "peer3", []p2p.Cap{}),
|
||||||
|
isTrusted: true,
|
||||||
|
}: {
|
||||||
|
nodeByHash: map[common.Hash]*fetcherTreeNode{
|
||||||
|
ftn1.hash: ftn1,
|
||||||
|
ftn2.hash: ftn2,
|
||||||
|
ftn3.hash: ftn3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "peer4",
|
||||||
|
Peer: p2p.NewPeer(id4, "peer4", []p2p.Cap{}),
|
||||||
|
isTrusted: true,
|
||||||
|
}: {
|
||||||
|
nodeByHash: map[common.Hash]*fetcherTreeNode{
|
||||||
|
ftn1.hash: ftn1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
chain: &lightChainStub{
|
||||||
|
tds: map[common.Hash]*big.Int{},
|
||||||
|
headers: map[common.Hash]*types.Header{
|
||||||
|
ftn1.hash: {},
|
||||||
|
ftn2.hash: {},
|
||||||
|
ftn3.hash: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bestHash, bestAmount, bestTD, sync := lf.findBestRequest()
|
||||||
|
|
||||||
|
if bestTD == nil {
|
||||||
|
t.Fatal("Empty result")
|
||||||
|
}
|
||||||
|
|
||||||
|
if bestTD.Cmp(ftn2.td) != 0 {
|
||||||
|
t.Fatal("bad td", bestTD)
|
||||||
|
}
|
||||||
|
if bestHash != ftn2.hash {
|
||||||
|
t.Fatal("bad hash", bestTD)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = bestAmount, sync
|
||||||
|
}
|
||||||
|
|
||||||
|
type lightChainStub struct {
|
||||||
|
BlockChain
|
||||||
|
tds map[common.Hash]*big.Int
|
||||||
|
headers map[common.Hash]*types.Header
|
||||||
|
insertHeaderChainAssertFunc func(chain []*types.Header, checkFreq int) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lightChainStub) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
|
if h, ok := l.headers[hash]; ok {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lightChainStub) LockChain() {}
|
||||||
|
func (l *lightChainStub) UnlockChain() {}
|
||||||
|
|
||||||
|
func (l *lightChainStub) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
if td, ok := l.tds[hash]; ok {
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lightChainStub) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
||||||
|
return l.insertHeaderChainAssertFunc(chain, checkFreq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNodeID(t *testing.T) *enode.Node {
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("generate key err:", err)
|
||||||
|
}
|
||||||
|
return enode.NewV4(&key.PublicKey, net.IP{}, 35000, 35000)
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -119,12 +120,29 @@ type ProtocolManager struct {
|
||||||
|
|
||||||
// wait group is used for graceful shutdowns during downloading
|
// wait group is used for graceful shutdowns during downloading
|
||||||
// and processing
|
// and processing
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
|
ulc *ulc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||||
// with the ethereum network.
|
// with the ethereum network.
|
||||||
func NewProtocolManager(chainConfig *params.ChainConfig, indexerConfig *light.IndexerConfig, lightSync bool, networkId uint64, mux *event.TypeMux, engine consensus.Engine, peers *peerSet, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay, serverPool *serverPool, quitSync chan struct{}, wg *sync.WaitGroup) (*ProtocolManager, error) {
|
func NewProtocolManager(
|
||||||
|
chainConfig *params.ChainConfig,
|
||||||
|
indexerConfig *light.IndexerConfig,
|
||||||
|
lightSync bool,
|
||||||
|
networkId uint64,
|
||||||
|
mux *event.TypeMux,
|
||||||
|
engine consensus.Engine,
|
||||||
|
peers *peerSet,
|
||||||
|
blockchain BlockChain,
|
||||||
|
txpool txPool,
|
||||||
|
chainDb ethdb.Database,
|
||||||
|
odr *LesOdr,
|
||||||
|
txrelay *LesTxRelay,
|
||||||
|
serverPool *serverPool,
|
||||||
|
quitSync chan struct{},
|
||||||
|
wg *sync.WaitGroup,
|
||||||
|
ulcConfig *eth.ULCConfig) (*ProtocolManager, error) {
|
||||||
// Create the protocol manager with the base fields
|
// Create the protocol manager with the base fields
|
||||||
manager := &ProtocolManager{
|
manager := &ProtocolManager{
|
||||||
lightSync: lightSync,
|
lightSync: lightSync,
|
||||||
|
|
@ -149,6 +167,10 @@ func NewProtocolManager(chainConfig *params.ChainConfig, indexerConfig *light.In
|
||||||
manager.reqDist = odr.retriever.dist
|
manager.reqDist = odr.retriever.dist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ulcConfig != nil {
|
||||||
|
manager.ulc = newULC(ulcConfig)
|
||||||
|
}
|
||||||
|
|
||||||
removePeer := manager.removePeer
|
removePeer := manager.removePeer
|
||||||
if disableClientRemovePeer {
|
if disableClientRemovePeer {
|
||||||
removePeer = func(id string) {}
|
removePeer = func(id string) {}
|
||||||
|
|
@ -234,7 +256,11 @@ func (pm *ProtocolManager) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWrit
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||||
return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
|
var isTrusted bool
|
||||||
|
if pm.isULCEnabled() {
|
||||||
|
isTrusted = pm.ulc.isTrusted(p.ID())
|
||||||
|
}
|
||||||
|
return newPeer(pv, nv, isTrusted, p, newMeteredMsgWriter(rw))
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle is the callback invoked to manage the life cycle of a les peer. When
|
// handle is the callback invoked to manage the life cycle of a les peer. When
|
||||||
|
|
@ -276,6 +302,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
||||||
if rw, ok := p.rw.(*meteredMsgReadWriter); ok {
|
if rw, ok := p.rw.(*meteredMsgReadWriter); ok {
|
||||||
rw.Init(p.version)
|
rw.Init(p.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the peer locally
|
// Register the peer locally
|
||||||
if err := pm.peers.Register(p); err != nil {
|
if err := pm.peers.Register(p); err != nil {
|
||||||
p.Log().Error("Light Ethereum peer registration failed", "err", err)
|
p.Log().Error("Light Ethereum peer registration failed", "err", err)
|
||||||
|
|
@ -287,6 +314,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
||||||
}
|
}
|
||||||
pm.removePeer(p.id)
|
pm.removePeer(p.id)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
|
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
|
||||||
if pm.lightSync {
|
if pm.lightSync {
|
||||||
p.lock.Lock()
|
p.lock.Lock()
|
||||||
|
|
@ -371,16 +399,15 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
// Block header query, collect the requested headers and reply
|
// Block header query, collect the requested headers and reply
|
||||||
case AnnounceMsg:
|
case AnnounceMsg:
|
||||||
p.Log().Trace("Received announce message")
|
p.Log().Trace("Received announce message")
|
||||||
if p.requestAnnounceType == announceTypeNone {
|
if p.announceType == announceTypeNone {
|
||||||
return errResp(ErrUnexpectedResponse, "")
|
return errResp(ErrUnexpectedResponse, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
var req announceData
|
var req announceData
|
||||||
if err := msg.Decode(&req); err != nil {
|
if err := msg.Decode(&req); err != nil {
|
||||||
return errResp(ErrDecode, "%v: %v", msg, err)
|
return errResp(ErrDecode, "%v: %v", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.requestAnnounceType == announceTypeSigned {
|
if p.announceType == announceTypeSigned {
|
||||||
if err := req.checkSignature(p.ID()); err != nil {
|
if err := req.checkSignature(p.ID()); err != nil {
|
||||||
p.Log().Trace("Invalid announcement signature", "err", err)
|
p.Log().Trace("Invalid announcement signature", "err", err)
|
||||||
return err
|
return err
|
||||||
|
|
@ -1175,6 +1202,14 @@ func (pm *ProtocolManager) txStatus(hashes []common.Hash) []txStatus {
|
||||||
return stats
|
return stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isULCEnabled returns true if we can use ULC
|
||||||
|
func (pm *ProtocolManager) isULCEnabled() bool {
|
||||||
|
if pm.ulc == nil || len(pm.ulc.trustedKeys) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// downloaderPeerNotify implements peerSetNotify
|
// downloaderPeerNotify implements peerSetNotify
|
||||||
type downloaderPeerNotify ProtocolManager
|
type downloaderPeerNotify ProtocolManager
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -494,7 +494,7 @@ func TestGetBloombitsProofs(t *testing.T) {
|
||||||
|
|
||||||
func TestTransactionStatusLes2(t *testing.T) {
|
func TestTransactionStatusLes2(t *testing.T) {
|
||||||
db := ethdb.NewMemDatabase()
|
db := ethdb.NewMemDatabase()
|
||||||
pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db)
|
pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db, nil)
|
||||||
chain := pm.blockchain.(*core.BlockChain)
|
chain := pm.blockchain.(*core.BlockChain)
|
||||||
config := core.DefaultTxPoolConfig
|
config := core.DefaultTxPoolConfig
|
||||||
config.Journal = ""
|
config.Journal = ""
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func testRCL() RequestCostList {
|
||||||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, potential notification
|
// with the given number of blocks already known, potential notification
|
||||||
// channels for different events and relative chain indexers array.
|
// channels for different events and relative chain indexers array.
|
||||||
func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database) (*ProtocolManager, error) {
|
func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) (*ProtocolManager, error) {
|
||||||
var (
|
var (
|
||||||
evmux = new(event.TypeMux)
|
evmux = new(event.TypeMux)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
|
|
@ -176,7 +176,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
||||||
if lightSync {
|
if lightSync {
|
||||||
indexConfig = light.TestClientIndexerConfig
|
indexConfig = light.TestClientIndexerConfig
|
||||||
}
|
}
|
||||||
pm, err := NewProtocolManager(gspec.Config, indexConfig, lightSync, NetworkId, evmux, engine, peers, chain, nil, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup))
|
pm, err := NewProtocolManager(gspec.Config, indexConfig, lightSync, NetworkId, evmux, engine, peers, chain, nil, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup), ulcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -200,8 +200,8 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
||||||
// with the given number of blocks already known, potential notification
|
// with the given number of blocks already known, potential notification
|
||||||
// channels for different events and relative chain indexers array. In case of an error, the constructor force-
|
// channels for different events and relative chain indexers array. In case of an error, the constructor force-
|
||||||
// fails the test.
|
// fails the test.
|
||||||
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database) *ProtocolManager {
|
func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), odr *LesOdr, peers *peerSet, db ethdb.Database, ulcConfig *eth.ULCConfig) *ProtocolManager {
|
||||||
pm, err := newTestProtocolManager(lightSync, blocks, generator, odr, peers, db)
|
pm, err := newTestProtocolManager(lightSync, blocks, generator, odr, peers, db, ulcConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +343,7 @@ func newServerEnv(t *testing.T, blocks int, protocol int, waitIndexers func(*cor
|
||||||
db := ethdb.NewMemDatabase()
|
db := ethdb.NewMemDatabase()
|
||||||
cIndexer, bIndexer, btIndexer := testIndexers(db, nil, light.TestServerIndexerConfig)
|
cIndexer, bIndexer, btIndexer := testIndexers(db, nil, light.TestServerIndexerConfig)
|
||||||
|
|
||||||
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, nil, db)
|
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, nil, db, nil)
|
||||||
peer, _ := newTestPeer(t, "peer", protocol, pm, true)
|
peer, _ := newTestPeer(t, "peer", protocol, pm, true)
|
||||||
|
|
||||||
cIndexer.Start(pm.blockchain.(*core.BlockChain))
|
cIndexer.Start(pm.blockchain.(*core.BlockChain))
|
||||||
|
|
@ -383,8 +383,8 @@ func newClientServerEnv(t *testing.T, blocks int, protocol int, waitIndexers fun
|
||||||
lcIndexer, lbIndexer, lbtIndexer := testIndexers(ldb, odr, light.TestClientIndexerConfig)
|
lcIndexer, lbIndexer, lbtIndexer := testIndexers(ldb, odr, light.TestClientIndexerConfig)
|
||||||
odr.SetIndexers(lcIndexer, lbtIndexer, lbIndexer)
|
odr.SetIndexers(lcIndexer, lbtIndexer, lbIndexer)
|
||||||
|
|
||||||
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, peers, db)
|
pm := newTestProtocolManagerMust(t, false, blocks, testChainGen, nil, peers, db, nil)
|
||||||
lpm := newTestProtocolManagerMust(t, true, 0, nil, odr, lPeers, ldb)
|
lpm := newTestProtocolManagerMust(t, true, 0, nil, odr, lPeers, ldb, nil)
|
||||||
|
|
||||||
startIndexers := func(clientMode bool, pm *ProtocolManager) {
|
startIndexers := func(clientMode bool, pm *ProtocolManager) {
|
||||||
if clientMode {
|
if clientMode {
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,10 @@ func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err erro
|
||||||
},
|
},
|
||||||
canSend: func(dp distPeer) bool {
|
canSend: func(dp distPeer) bool {
|
||||||
p := dp.(*peer)
|
p := dp.(*peer)
|
||||||
return lreq.CanSend(p)
|
if !p.isOnlyAnnounce {
|
||||||
|
return lreq.CanSend(p)
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
request: func(dp distPeer) func() {
|
request: func(dp distPeer) func() {
|
||||||
p := dp.(*peer)
|
p := dp.(*peer)
|
||||||
|
|
|
||||||
46
les/peer.go
46
les/peer.go
|
|
@ -56,7 +56,7 @@ type peer struct {
|
||||||
version int // Protocol version negotiated
|
version int // Protocol version negotiated
|
||||||
network uint64 // Network ID being on
|
network uint64 // Network ID being on
|
||||||
|
|
||||||
announceType, requestAnnounceType uint64
|
announceType uint64
|
||||||
|
|
||||||
id string
|
id string
|
||||||
|
|
||||||
|
|
@ -74,9 +74,12 @@ type peer struct {
|
||||||
fcServer *flowcontrol.ServerNode // nil if the peer is client only
|
fcServer *flowcontrol.ServerNode // nil if the peer is client only
|
||||||
fcServerParams *flowcontrol.ServerParams
|
fcServerParams *flowcontrol.ServerParams
|
||||||
fcCosts requestCostTable
|
fcCosts requestCostTable
|
||||||
|
|
||||||
|
isTrusted bool
|
||||||
|
isOnlyAnnounce bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
func newPeer(version int, network uint64, isTrusted bool, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||||
id := p.ID()
|
id := p.ID()
|
||||||
|
|
||||||
return &peer{
|
return &peer{
|
||||||
|
|
@ -86,6 +89,7 @@ func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *pe
|
||||||
network: network,
|
network: network,
|
||||||
id: fmt.Sprintf("%x", id[:8]),
|
id: fmt.Sprintf("%x", id[:8]),
|
||||||
announceChn: make(chan announceData, 20),
|
announceChn: make(chan announceData, 20),
|
||||||
|
isTrusted: isTrusted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,23 +405,32 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||||
send = send.add("headNum", headNum)
|
send = send.add("headNum", headNum)
|
||||||
send = send.add("genesisHash", genesis)
|
send = send.add("genesisHash", genesis)
|
||||||
if server != nil {
|
if server != nil {
|
||||||
send = send.add("serveHeaders", nil)
|
if !server.onlyAnnounce {
|
||||||
send = send.add("serveChainSince", uint64(0))
|
//only announce server. It sends only announse requests
|
||||||
send = send.add("serveStateSince", uint64(0))
|
send = send.add("serveHeaders", nil)
|
||||||
send = send.add("txRelay", nil)
|
send = send.add("serveChainSince", uint64(0))
|
||||||
|
send = send.add("serveStateSince", uint64(0))
|
||||||
|
send = send.add("txRelay", nil)
|
||||||
|
}
|
||||||
send = send.add("flowControl/BL", server.defParams.BufLimit)
|
send = send.add("flowControl/BL", server.defParams.BufLimit)
|
||||||
send = send.add("flowControl/MRR", server.defParams.MinRecharge)
|
send = send.add("flowControl/MRR", server.defParams.MinRecharge)
|
||||||
list := server.fcCostStats.getCurrentList()
|
list := server.fcCostStats.getCurrentList()
|
||||||
send = send.add("flowControl/MRC", list)
|
send = send.add("flowControl/MRC", list)
|
||||||
p.fcCosts = list.decode()
|
p.fcCosts = list.decode()
|
||||||
} else {
|
} else {
|
||||||
p.requestAnnounceType = announceTypeSimple // set to default until "very light" client mode is implemented
|
//on client node
|
||||||
send = send.add("announceType", p.requestAnnounceType)
|
p.announceType = announceTypeSimple
|
||||||
|
if p.isTrusted {
|
||||||
|
p.announceType = announceTypeSigned
|
||||||
|
}
|
||||||
|
send = send.add("announceType", p.announceType)
|
||||||
}
|
}
|
||||||
|
|
||||||
recvList, err := p.sendReceiveHandshake(send)
|
recvList, err := p.sendReceiveHandshake(send)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recv := recvList.decode()
|
recv := recvList.decode()
|
||||||
|
|
||||||
var rGenesis, rHash common.Hash
|
var rGenesis, rHash common.Hash
|
||||||
|
|
@ -452,25 +465,33 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||||
if int(rVersion) != p.version {
|
if int(rVersion) != p.version {
|
||||||
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version)
|
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
if server != nil {
|
if server != nil {
|
||||||
// until we have a proper peer connectivity API, allow LES connection to other servers
|
// until we have a proper peer connectivity API, allow LES connection to other servers
|
||||||
/*if recv.get("serveStateSince", nil) == nil {
|
/*if recv.get("serveStateSince", nil) == nil {
|
||||||
return errResp(ErrUselessPeer, "wanted client, got server")
|
return errResp(ErrUselessPeer, "wanted client, got server")
|
||||||
}*/
|
}*/
|
||||||
if recv.get("announceType", &p.announceType) != nil {
|
if recv.get("announceType", &p.announceType) != nil {
|
||||||
|
//set default announceType on server side
|
||||||
p.announceType = announceTypeSimple
|
p.announceType = announceTypeSimple
|
||||||
}
|
}
|
||||||
p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams)
|
p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams)
|
||||||
} else {
|
} else {
|
||||||
|
//mark OnlyAnnounce server if "serveHeaders", "serveChainSince", "serveStateSince" or "txRelay" fields don't exist
|
||||||
if recv.get("serveChainSince", nil) != nil {
|
if recv.get("serveChainSince", nil) != nil {
|
||||||
return errResp(ErrUselessPeer, "peer cannot serve chain")
|
p.isOnlyAnnounce = true
|
||||||
}
|
}
|
||||||
if recv.get("serveStateSince", nil) != nil {
|
if recv.get("serveStateSince", nil) != nil {
|
||||||
return errResp(ErrUselessPeer, "peer cannot serve state")
|
p.isOnlyAnnounce = true
|
||||||
}
|
}
|
||||||
if recv.get("txRelay", nil) != nil {
|
if recv.get("txRelay", nil) != nil {
|
||||||
return errResp(ErrUselessPeer, "peer cannot relay transactions")
|
p.isOnlyAnnounce = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.isOnlyAnnounce && !p.isTrusted {
|
||||||
|
return errResp(ErrUselessPeer, "peer cannot serve requests")
|
||||||
|
}
|
||||||
|
|
||||||
params := &flowcontrol.ServerParams{}
|
params := &flowcontrol.ServerParams{}
|
||||||
if err := recv.get("flowControl/BL", ¶ms.BufLimit); err != nil {
|
if err := recv.get("flowControl/BL", ¶ms.BufLimit); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -486,7 +507,6 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis
|
||||||
p.fcServer = flowcontrol.NewServerNode(params)
|
p.fcServer = flowcontrol.NewServerNode(params)
|
||||||
p.fcCosts = MRC.decode()
|
p.fcCosts = MRC.decode()
|
||||||
}
|
}
|
||||||
|
|
||||||
p.headInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum}
|
p.headInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -576,8 +596,10 @@ func (ps *peerSet) Unregister(id string) error {
|
||||||
for _, n := range peers {
|
for _, n := range peers {
|
||||||
n.unregisterPeer(p)
|
n.unregisterPeer(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.sendQueue.quit()
|
p.sendQueue.quit()
|
||||||
p.Peer.Disconnect(p2p.DiscUselessPeer)
|
p.Peer.Disconnect(p2p.DiscUselessPeer)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
297
les/peer_test.go
Normal file
297
les/peer_test.go
Normal file
|
|
@ -0,0 +1,297 @@
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/les/flowcontrol"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
test_networkid = 10
|
||||||
|
protocol_version = 2123
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hash = common.HexToHash("some string")
|
||||||
|
genesis = common.HexToHash("genesis hash")
|
||||||
|
headNum = uint64(1234)
|
||||||
|
td = big.NewInt(123)
|
||||||
|
)
|
||||||
|
|
||||||
|
//ulc connects to trusted peer and send announceType=announceTypeSigned
|
||||||
|
func TestPeerHandshakeSetAnnounceTypeToAnnounceTypeSignedForTrustedPeer(t *testing.T) {
|
||||||
|
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
|
||||||
|
//peer to connect(on ulc side)
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
isTrusted: true,
|
||||||
|
rw: &rwStub{
|
||||||
|
WriteHook: func(recvList keyValueList) {
|
||||||
|
//checking that ulc sends to peer allowedRequests=onlyAnnounceRequests and announceType = announceTypeSigned
|
||||||
|
recv := recvList.decode()
|
||||||
|
var reqType uint64
|
||||||
|
|
||||||
|
err := recv.get("announceType", &reqType)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqType != announceTypeSigned {
|
||||||
|
t.Fatal("Expected announceTypeSigned")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("serveHeaders", nil)
|
||||||
|
l = l.add("serveChainSince", uint64(0))
|
||||||
|
l = l.add("serveStateSince", uint64(0))
|
||||||
|
l = l.add("txRelay", nil)
|
||||||
|
l = l.add("flowControl/BL", uint64(0))
|
||||||
|
l = l.add("flowControl/MRR", uint64(0))
|
||||||
|
l = l.add("flowControl/MRC", RequestCostList{})
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Handshake error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.announceType != announceTypeSigned {
|
||||||
|
t.Fatal("Incorrect announceType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerHandshakeAnnounceTypeSignedForTrustedPeersPeerNotInTrusted(t *testing.T) {
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
rw: &rwStub{
|
||||||
|
WriteHook: func(recvList keyValueList) {
|
||||||
|
//checking that ulc sends to peer allowedRequests=noRequests and announceType != announceTypeSigned
|
||||||
|
recv := recvList.decode()
|
||||||
|
var reqType uint64
|
||||||
|
|
||||||
|
err := recv.get("announceType", &reqType)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqType == announceTypeSigned {
|
||||||
|
t.Fatal("Expected not announceTypeSigned")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("serveHeaders", nil)
|
||||||
|
l = l.add("serveChainSince", uint64(0))
|
||||||
|
l = l.add("serveStateSince", uint64(0))
|
||||||
|
l = l.add("txRelay", nil)
|
||||||
|
l = l.add("flowControl/BL", uint64(0))
|
||||||
|
l = l.add("flowControl/MRR", uint64(0))
|
||||||
|
l = l.add("flowControl/MRC", RequestCostList{})
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if p.announceType == announceTypeSigned {
|
||||||
|
t.Fatal("Incorrect announceType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerHandshakeDefaultAllRequests(t *testing.T) {
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
|
||||||
|
s := generateLesServer()
|
||||||
|
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
rw: &rwStub{
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("announceType", uint64(announceTypeSigned))
|
||||||
|
l = l.add("allowedRequests", uint64(0))
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.isOnlyAnnounce {
|
||||||
|
t.Fatal("Incorrect announceType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerHandshakeServerSendOnlyAnnounceRequestsHeaders(t *testing.T) {
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
|
||||||
|
s := generateLesServer()
|
||||||
|
s.onlyAnnounce = true
|
||||||
|
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
rw: &rwStub{
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("announceType", uint64(announceTypeSigned))
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
WriteHook: func(l keyValueList) {
|
||||||
|
for _, v := range l {
|
||||||
|
if v.Key == "serveHeaders" ||
|
||||||
|
v.Key == "serveChainSince" ||
|
||||||
|
v.Key == "serveStateSince" ||
|
||||||
|
v.Key == "txRelay" {
|
||||||
|
t.Fatalf("%v exists", v.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestPeerHandshakeClientReceiveOnlyAnnounceRequestsHeaders(t *testing.T) {
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
rw: &rwStub{
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("flowControl/BL", uint64(0))
|
||||||
|
l = l.add("flowControl/MRR", uint64(0))
|
||||||
|
l = l.add("flowControl/MRC", RequestCostList{})
|
||||||
|
|
||||||
|
l = l.add("announceType", uint64(announceTypeSigned))
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
isTrusted: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.isOnlyAnnounce {
|
||||||
|
t.Fatal("isOnlyAnnounce must be true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerHandshakeClientReturnErrorOnUselessPeer(t *testing.T) {
|
||||||
|
var id enode.ID = newNodeID(t).ID()
|
||||||
|
|
||||||
|
p := peer{
|
||||||
|
Peer: p2p.NewPeer(id, "test peer", []p2p.Cap{}),
|
||||||
|
version: protocol_version,
|
||||||
|
rw: &rwStub{
|
||||||
|
ReadHook: func(l keyValueList) keyValueList {
|
||||||
|
l = l.add("flowControl/BL", uint64(0))
|
||||||
|
l = l.add("flowControl/MRR", uint64(0))
|
||||||
|
l = l.add("flowControl/MRC", RequestCostList{})
|
||||||
|
|
||||||
|
l = l.add("announceType", uint64(announceTypeSigned))
|
||||||
|
|
||||||
|
return l
|
||||||
|
},
|
||||||
|
},
|
||||||
|
network: test_networkid,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Handshake(td, hash, headNum, genesis, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateLesServer() *LesServer {
|
||||||
|
s := &LesServer{
|
||||||
|
defParams: &flowcontrol.ServerParams{
|
||||||
|
BufLimit: uint64(300000000),
|
||||||
|
MinRecharge: uint64(50000),
|
||||||
|
},
|
||||||
|
fcManager: flowcontrol.NewClientManager(1, 2, 3),
|
||||||
|
fcCostStats: &requestCostStats{
|
||||||
|
stats: make(map[uint64]*linReg, len(reqList)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, code := range reqList {
|
||||||
|
s.fcCostStats.stats[code] = &linReg{cnt: 100}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type rwStub struct {
|
||||||
|
ReadHook func(l keyValueList) keyValueList
|
||||||
|
WriteHook func(l keyValueList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rwStub) ReadMsg() (p2p.Msg, error) {
|
||||||
|
payload := keyValueList{}
|
||||||
|
payload = payload.add("protocolVersion", uint64(protocol_version))
|
||||||
|
payload = payload.add("networkId", uint64(test_networkid))
|
||||||
|
payload = payload.add("headTd", td)
|
||||||
|
payload = payload.add("headHash", hash)
|
||||||
|
payload = payload.add("headNum", headNum)
|
||||||
|
payload = payload.add("genesisHash", genesis)
|
||||||
|
|
||||||
|
if s.ReadHook != nil {
|
||||||
|
payload = s.ReadHook(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
size, p, err := rlp.EncodeToReader(payload)
|
||||||
|
if err != nil {
|
||||||
|
return p2p.Msg{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p2p.Msg{
|
||||||
|
Size: uint32(size),
|
||||||
|
Payload: p,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rwStub) WriteMsg(m p2p.Msg) error {
|
||||||
|
recvList := keyValueList{}
|
||||||
|
if err := m.Decode(&recvList); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.WriteHook != nil {
|
||||||
|
s.WriteHook(recvList)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -41,17 +41,34 @@ import (
|
||||||
type LesServer struct {
|
type LesServer struct {
|
||||||
lesCommons
|
lesCommons
|
||||||
|
|
||||||
fcManager *flowcontrol.ClientManager // nil if our node is client only
|
fcManager *flowcontrol.ClientManager // nil if our node is client only
|
||||||
fcCostStats *requestCostStats
|
fcCostStats *requestCostStats
|
||||||
defParams *flowcontrol.ServerParams
|
defParams *flowcontrol.ServerParams
|
||||||
lesTopics []discv5.Topic
|
lesTopics []discv5.Topic
|
||||||
privateKey *ecdsa.PrivateKey
|
privateKey *ecdsa.PrivateKey
|
||||||
quitSync chan struct{}
|
quitSync chan struct{}
|
||||||
|
onlyAnnounce bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
||||||
quitSync := make(chan struct{})
|
quitSync := make(chan struct{})
|
||||||
pm, err := NewProtocolManager(eth.BlockChain().Config(), light.DefaultServerIndexerConfig, false, config.NetworkId, eth.EventMux(), eth.Engine(), newPeerSet(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil, nil, quitSync, new(sync.WaitGroup))
|
pm, err := NewProtocolManager(
|
||||||
|
eth.BlockChain().Config(),
|
||||||
|
light.DefaultServerIndexerConfig,
|
||||||
|
false,
|
||||||
|
config.NetworkId,
|
||||||
|
eth.EventMux(),
|
||||||
|
eth.Engine(),
|
||||||
|
newPeerSet(),
|
||||||
|
eth.BlockChain(),
|
||||||
|
eth.TxPool(),
|
||||||
|
eth.ChainDb(),
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
quitSync,
|
||||||
|
new(sync.WaitGroup),
|
||||||
|
config.ULC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -70,8 +87,9 @@ func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
|
||||||
bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
|
bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), nil, params.BloomBitsBlocks, params.BloomTrieFrequency),
|
||||||
protocolManager: pm,
|
protocolManager: pm,
|
||||||
},
|
},
|
||||||
quitSync: quitSync,
|
quitSync: quitSync,
|
||||||
lesTopics: lesTopics,
|
lesTopics: lesTopics,
|
||||||
|
onlyAnnounce: config.OnlyAnnounce,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := log.New()
|
logger := log.New()
|
||||||
|
|
@ -289,10 +307,8 @@ func (s *requestCostStats) getCurrentList() RequestCostList {
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
|
|
||||||
list := make(RequestCostList, len(reqList))
|
list := make(RequestCostList, len(reqList))
|
||||||
//fmt.Println("RequestCostList")
|
|
||||||
for idx, code := range reqList {
|
for idx, code := range reqList {
|
||||||
b, m := s.stats[code].calc()
|
b, m := s.stats[code].calc()
|
||||||
//fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000)
|
|
||||||
if m < 0 {
|
if m < 0 {
|
||||||
b += m
|
b += m
|
||||||
m = 0
|
m = 0
|
||||||
|
|
|
||||||
|
|
@ -126,22 +126,22 @@ type serverPool struct {
|
||||||
discNodes chan *enode.Node
|
discNodes chan *enode.Node
|
||||||
discLookups chan bool
|
discLookups chan bool
|
||||||
|
|
||||||
|
trustedNodes map[enode.ID]*enode.Node
|
||||||
entries map[enode.ID]*poolEntry
|
entries map[enode.ID]*poolEntry
|
||||||
timeout, enableRetry chan *poolEntry
|
timeout, enableRetry chan *poolEntry
|
||||||
adjustStats chan poolStatAdjust
|
adjustStats chan poolStatAdjust
|
||||||
|
|
||||||
connCh chan *connReq
|
|
||||||
disconnCh chan *disconnReq
|
|
||||||
registerCh chan *registerReq
|
|
||||||
|
|
||||||
knownQueue, newQueue poolEntryQueue
|
knownQueue, newQueue poolEntryQueue
|
||||||
knownSelect, newSelect *weightedRandomSelect
|
knownSelect, newSelect *weightedRandomSelect
|
||||||
knownSelected, newSelected int
|
knownSelected, newSelected int
|
||||||
fastDiscover bool
|
fastDiscover bool
|
||||||
|
connCh chan *connReq
|
||||||
|
disconnCh chan *disconnReq
|
||||||
|
registerCh chan *registerReq
|
||||||
}
|
}
|
||||||
|
|
||||||
// newServerPool creates a new serverPool instance
|
// newServerPool creates a new serverPool instance
|
||||||
func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *serverPool {
|
func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup, trustedNodes []string) *serverPool {
|
||||||
pool := &serverPool{
|
pool := &serverPool{
|
||||||
db: db,
|
db: db,
|
||||||
quit: quit,
|
quit: quit,
|
||||||
|
|
@ -156,7 +156,9 @@ func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *s
|
||||||
knownSelect: newWeightedRandomSelect(),
|
knownSelect: newWeightedRandomSelect(),
|
||||||
newSelect: newWeightedRandomSelect(),
|
newSelect: newWeightedRandomSelect(),
|
||||||
fastDiscover: true,
|
fastDiscover: true,
|
||||||
|
trustedNodes: parseTrustedNodes(trustedNodes),
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry)
|
pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry)
|
||||||
pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry)
|
pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry)
|
||||||
return pool
|
return pool
|
||||||
|
|
@ -168,6 +170,7 @@ func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) {
|
||||||
pool.dbKey = append([]byte("serverPool/"), []byte(topic)...)
|
pool.dbKey = append([]byte("serverPool/"), []byte(topic)...)
|
||||||
pool.wg.Add(1)
|
pool.wg.Add(1)
|
||||||
pool.loadNodes()
|
pool.loadNodes()
|
||||||
|
pool.connectToTrustedNodes()
|
||||||
|
|
||||||
if pool.server.DiscV5 != nil {
|
if pool.server.DiscV5 != nil {
|
||||||
pool.discSetPeriod = make(chan time.Duration, 1)
|
pool.discSetPeriod = make(chan time.Duration, 1)
|
||||||
|
|
@ -337,8 +340,10 @@ func (pool *serverPool) eventLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
case node := <-pool.discNodes:
|
case node := <-pool.discNodes:
|
||||||
entry := pool.findOrNewNode(node)
|
if pool.trustedNodes[node.ID()] == nil {
|
||||||
pool.updateCheckDial(entry)
|
entry := pool.findOrNewNode(node)
|
||||||
|
pool.updateCheckDial(entry)
|
||||||
|
}
|
||||||
|
|
||||||
case conv := <-pool.discLookups:
|
case conv := <-pool.discLookups:
|
||||||
if conv {
|
if conv {
|
||||||
|
|
@ -355,29 +360,34 @@ func (pool *serverPool) eventLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
case req := <-pool.connCh:
|
case req := <-pool.connCh:
|
||||||
// Handle peer connection requests.
|
if pool.trustedNodes[req.p.ID()] != nil {
|
||||||
entry := pool.entries[req.p.ID()]
|
// ignore trusted nodes
|
||||||
if entry == nil {
|
|
||||||
entry = pool.findOrNewNode(req.node)
|
|
||||||
}
|
|
||||||
if entry.state == psConnected || entry.state == psRegistered {
|
|
||||||
req.result <- nil
|
req.result <- nil
|
||||||
continue
|
} else {
|
||||||
|
// Handle peer connection requests.
|
||||||
|
entry := pool.entries[req.p.ID()]
|
||||||
|
if entry == nil {
|
||||||
|
entry = pool.findOrNewNode(req.node)
|
||||||
|
}
|
||||||
|
if entry.state == psConnected || entry.state == psRegistered {
|
||||||
|
req.result <- nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pool.connWg.Add(1)
|
||||||
|
entry.peer = req.p
|
||||||
|
entry.state = psConnected
|
||||||
|
addr := &poolEntryAddress{
|
||||||
|
ip: req.node.IP(),
|
||||||
|
port: uint16(req.node.TCP()),
|
||||||
|
lastSeen: mclock.Now(),
|
||||||
|
}
|
||||||
|
entry.lastConnected = addr
|
||||||
|
entry.addr = make(map[string]*poolEntryAddress)
|
||||||
|
entry.addr[addr.strKey()] = addr
|
||||||
|
entry.addrSelect = *newWeightedRandomSelect()
|
||||||
|
entry.addrSelect.update(addr)
|
||||||
|
req.result <- entry
|
||||||
}
|
}
|
||||||
pool.connWg.Add(1)
|
|
||||||
entry.peer = req.p
|
|
||||||
entry.state = psConnected
|
|
||||||
addr := &poolEntryAddress{
|
|
||||||
ip: req.node.IP(),
|
|
||||||
port: uint16(req.node.TCP()),
|
|
||||||
lastSeen: mclock.Now(),
|
|
||||||
}
|
|
||||||
entry.lastConnected = addr
|
|
||||||
entry.addr = make(map[string]*poolEntryAddress)
|
|
||||||
entry.addr[addr.strKey()] = addr
|
|
||||||
entry.addrSelect = *newWeightedRandomSelect()
|
|
||||||
entry.addrSelect.update(addr)
|
|
||||||
req.result <- entry
|
|
||||||
|
|
||||||
case req := <-pool.registerCh:
|
case req := <-pool.registerCh:
|
||||||
// Handle peer registration requests.
|
// Handle peer registration requests.
|
||||||
|
|
@ -470,11 +480,42 @@ func (pool *serverPool) loadNodes() {
|
||||||
"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
|
"response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight),
|
||||||
"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
|
"timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight))
|
||||||
pool.entries[e.node.ID()] = e
|
pool.entries[e.node.ID()] = e
|
||||||
pool.knownQueue.setLatest(e)
|
if pool.trustedNodes[e.node.ID()] == nil {
|
||||||
pool.knownSelect.update((*knownEntry)(e))
|
pool.knownQueue.setLatest(e)
|
||||||
|
pool.knownSelect.update((*knownEntry)(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connectToTrustedNodes adds trusted server nodes as static trusted peers.
|
||||||
|
//
|
||||||
|
// Note: trusted nodes are not handled by the server pool logic, they are not
|
||||||
|
// added to either the known or new selection pools. They are connected/reconnected
|
||||||
|
// by p2p.Server whenever possible.
|
||||||
|
func (pool *serverPool) connectToTrustedNodes() {
|
||||||
|
//connect to trusted nodes
|
||||||
|
for _, node := range pool.trustedNodes {
|
||||||
|
pool.server.AddTrustedPeer(node)
|
||||||
|
pool.server.AddPeer(node)
|
||||||
|
log.Debug("Added trusted node", "id", node.ID().String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseTrustedNodes returns valid and parsed enodes
|
||||||
|
func parseTrustedNodes(trustedNodes []string) map[enode.ID]*enode.Node {
|
||||||
|
nodes := make(map[enode.ID]*enode.Node)
|
||||||
|
|
||||||
|
for _, node := range trustedNodes {
|
||||||
|
node, err := enode.ParseV4(node)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trusted node URL invalid", "enode", node, "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nodes[node.ID()] = node
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
// saveNodes saves known nodes and their statistics into the database. Nodes are
|
// saveNodes saves known nodes and their statistics into the database. Nodes are
|
||||||
// ordered from least to most recently connected.
|
// ordered from least to most recently connected.
|
||||||
func (pool *serverPool) saveNodes() {
|
func (pool *serverPool) saveNodes() {
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func (self *LesTxRelay) send(txs types.Transactions, count int) {
|
||||||
return peer.GetRequestCost(SendTxMsg, len(ll))
|
return peer.GetRequestCost(SendTxMsg, len(ll))
|
||||||
},
|
},
|
||||||
canSend: func(dp distPeer) bool {
|
canSend: func(dp distPeer) bool {
|
||||||
return dp.(*peer) == pp
|
return !dp.(*peer).isOnlyAnnounce && dp.(*peer) == pp
|
||||||
},
|
},
|
||||||
request: func(dp distPeer) func() {
|
request: func(dp distPeer) func() {
|
||||||
peer := dp.(*peer)
|
peer := dp.(*peer)
|
||||||
|
|
|
||||||
39
les/ulc.go
Normal file
39
les/ulc.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ulc struct {
|
||||||
|
trustedKeys map[string]struct{}
|
||||||
|
minTrustedFraction int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newULC(ulcConfig *eth.ULCConfig) *ulc {
|
||||||
|
if ulcConfig == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]struct{}, len(ulcConfig.TrustedServers))
|
||||||
|
for _, id := range ulcConfig.TrustedServers {
|
||||||
|
node, err := enode.ParseV4(id)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("node:", id, " err:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[node.ID().String()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ulc{m, ulcConfig.MinTrustedFraction}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ulc) isTrusted(p enode.ID) bool {
|
||||||
|
if u.trustedKeys == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := u.trustedKeys[p.String()]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
239
les/ulc_test.go
Normal file
239
les/ulc_test.go
Normal file
|
|
@ -0,0 +1,239 @@
|
||||||
|
package les
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/light"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestULCSyncWithOnePeer(t *testing.T) {
|
||||||
|
f := newFullPeerPair(t, 1, 4, testChainGen)
|
||||||
|
ulcConfig := ð.ULCConfig{
|
||||||
|
MinTrustedFraction: 100,
|
||||||
|
TrustedServers: []string{f.Node.String()},
|
||||||
|
}
|
||||||
|
|
||||||
|
l := newLightPeer(t, ulcConfig)
|
||||||
|
|
||||||
|
if reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) {
|
||||||
|
t.Fatal("blocks are equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := connectPeers(f, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PM.fetcher.lock.Lock()
|
||||||
|
l.PM.fetcher.nextRequest()
|
||||||
|
l.PM.fetcher.lock.Unlock()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) {
|
||||||
|
t.Fatal("sync doesn't work")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestULCReceiveAnnounce(t *testing.T) {
|
||||||
|
f := newFullPeerPair(t, 1, 4, testChainGen)
|
||||||
|
ulcConfig := ð.ULCConfig{
|
||||||
|
MinTrustedFraction: 100,
|
||||||
|
TrustedServers: []string{f.Node.String()},
|
||||||
|
}
|
||||||
|
|
||||||
|
l := newLightPeer(t, ulcConfig)
|
||||||
|
fPeer, lPeer, err := connectPeers(f, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PM.synchronise(fPeer)
|
||||||
|
|
||||||
|
//check that the sync is finished correctly
|
||||||
|
if !reflect.DeepEqual(f.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) {
|
||||||
|
t.Fatal("sync doesn't work")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PM.peers.lock.Lock()
|
||||||
|
if len(l.PM.peers.peers) == 0 {
|
||||||
|
t.Fatal("peer list should not be empty")
|
||||||
|
}
|
||||||
|
l.PM.peers.lock.Unlock()
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
//send a signed announce message(payload doesn't matter)
|
||||||
|
td := f.PM.blockchain.GetTd(l.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Number.Uint64())
|
||||||
|
announce := announceData{
|
||||||
|
Number: l.PM.blockchain.CurrentHeader().Number.Uint64() + 1,
|
||||||
|
Td: td.Add(td, big.NewInt(1)),
|
||||||
|
}
|
||||||
|
announce.sign(f.Key)
|
||||||
|
lPeer.SendAnnounce(announce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestULCShouldNotSyncWithTwoPeersOneHaveEmptyChain(t *testing.T) {
|
||||||
|
f1 := newFullPeerPair(t, 1, 4, testChainGen)
|
||||||
|
f2 := newFullPeerPair(t, 2, 0, nil)
|
||||||
|
ulcConf := &ulc{minTrustedFraction: 100, trustedKeys: make(map[string]struct{})}
|
||||||
|
ulcConf.trustedKeys[f1.Node.ID().String()] = struct{}{}
|
||||||
|
ulcConf.trustedKeys[f2.Node.ID().String()] = struct{}{}
|
||||||
|
ulcConfig := ð.ULCConfig{
|
||||||
|
MinTrustedFraction: 100,
|
||||||
|
TrustedServers: []string{f1.Node.String(), f2.Node.String()},
|
||||||
|
}
|
||||||
|
l := newLightPeer(t, ulcConfig)
|
||||||
|
l.PM.ulc.minTrustedFraction = 100
|
||||||
|
|
||||||
|
_, _, err := connectPeers(f1, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, _, err = connectPeers(f2, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PM.fetcher.lock.Lock()
|
||||||
|
l.PM.fetcher.nextRequest()
|
||||||
|
l.PM.fetcher.lock.Unlock()
|
||||||
|
|
||||||
|
if reflect.DeepEqual(f2.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) {
|
||||||
|
t.Fatal("Incorrect hash: second peer has empty chain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestULCShouldNotSyncWithThreePeersOneHaveEmptyChain(t *testing.T) {
|
||||||
|
f1 := newFullPeerPair(t, 1, 3, testChainGen)
|
||||||
|
f2 := newFullPeerPair(t, 2, 4, testChainGen)
|
||||||
|
f3 := newFullPeerPair(t, 3, 0, nil)
|
||||||
|
|
||||||
|
ulcConfig := ð.ULCConfig{
|
||||||
|
MinTrustedFraction: 60,
|
||||||
|
TrustedServers: []string{f1.Node.String(), f2.Node.String(), f3.Node.String()},
|
||||||
|
}
|
||||||
|
|
||||||
|
l := newLightPeer(t, ulcConfig)
|
||||||
|
_, _, err := connectPeers(f1, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = connectPeers(f2, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = connectPeers(f3, l, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.PM.fetcher.lock.Lock()
|
||||||
|
l.PM.fetcher.nextRequest()
|
||||||
|
l.PM.fetcher.lock.Unlock()
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(f1.PM.blockchain.CurrentHeader().Hash(), l.PM.blockchain.CurrentHeader().Hash()) {
|
||||||
|
t.Fatal("Incorrect hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pairPeer struct {
|
||||||
|
Name string
|
||||||
|
Node *enode.Node
|
||||||
|
PM *ProtocolManager
|
||||||
|
Key *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func connectPeers(full, light pairPeer, version int) (*peer, *peer, error) {
|
||||||
|
// Create a message pipe to communicate through
|
||||||
|
app, net := p2p.MsgPipe()
|
||||||
|
|
||||||
|
peerLight := full.PM.newPeer(version, NetworkId, p2p.NewPeer(light.Node.ID(), light.Name, nil), net)
|
||||||
|
peerFull := light.PM.newPeer(version, NetworkId, p2p.NewPeer(full.Node.ID(), full.Name, nil), app)
|
||||||
|
|
||||||
|
// Start the peerLight on a new thread
|
||||||
|
errc1 := make(chan error, 1)
|
||||||
|
errc2 := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case light.PM.newPeerCh <- peerFull:
|
||||||
|
errc1 <- light.PM.handle(peerFull)
|
||||||
|
case <-light.PM.quitSync:
|
||||||
|
errc1 <- p2p.DiscQuitting
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case full.PM.newPeerCh <- peerLight:
|
||||||
|
errc2 <- full.PM.handle(peerLight)
|
||||||
|
case <-full.PM.quitSync:
|
||||||
|
errc2 <- p2p.DiscQuitting
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Millisecond * 100):
|
||||||
|
case err := <-errc1:
|
||||||
|
return nil, nil, fmt.Errorf("peerLight handshake error: %v", err)
|
||||||
|
case err := <-errc2:
|
||||||
|
return nil, nil, fmt.Errorf("peerFull handshake error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return peerFull, peerLight, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newFullPeerPair creates node with full sync mode
|
||||||
|
func newFullPeerPair(t *testing.T, index int, numberOfblocks int, chainGen func(int, *core.BlockGen)) pairPeer {
|
||||||
|
db := ethdb.NewMemDatabase()
|
||||||
|
|
||||||
|
pmFull := newTestProtocolManagerMust(t, false, numberOfblocks, chainGen, nil, nil, db, nil)
|
||||||
|
|
||||||
|
peerPairFull := pairPeer{
|
||||||
|
Name: "full node",
|
||||||
|
PM: pmFull,
|
||||||
|
}
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("generate key err:", err)
|
||||||
|
}
|
||||||
|
peerPairFull.Key = key
|
||||||
|
peerPairFull.Node = enode.NewV4(&key.PublicKey, net.ParseIP("127.0.0.1"), 35000, 35000)
|
||||||
|
return peerPairFull
|
||||||
|
}
|
||||||
|
|
||||||
|
// newLightPeer creates node with light sync mode
|
||||||
|
func newLightPeer(t *testing.T, ulcConfig *eth.ULCConfig) pairPeer {
|
||||||
|
peers := newPeerSet()
|
||||||
|
dist := newRequestDistributor(peers, make(chan struct{}))
|
||||||
|
rm := newRetrieveManager(peers, dist, nil)
|
||||||
|
ldb := ethdb.NewMemDatabase()
|
||||||
|
|
||||||
|
odr := NewLesOdr(ldb, light.DefaultClientIndexerConfig, rm)
|
||||||
|
|
||||||
|
pmLight := newTestProtocolManagerMust(t, true, 0, nil, odr, peers, ldb, ulcConfig)
|
||||||
|
peerPairLight := pairPeer{
|
||||||
|
Name: "ulc node",
|
||||||
|
PM: pmLight,
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("generate key err:", err)
|
||||||
|
}
|
||||||
|
peerPairLight.Key = key
|
||||||
|
peerPairLight.Node = enode.NewV4(&key.PublicKey, net.IP{}, 35000, 35000)
|
||||||
|
return peerPairLight
|
||||||
|
}
|
||||||
|
|
@ -35,7 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/hashicorp/golang-lru"
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -50,6 +50,7 @@ type LightChain struct {
|
||||||
hc *core.HeaderChain
|
hc *core.HeaderChain
|
||||||
indexerConfig *IndexerConfig
|
indexerConfig *IndexerConfig
|
||||||
chainDb ethdb.Database
|
chainDb ethdb.Database
|
||||||
|
engine consensus.Engine
|
||||||
odr OdrBackend
|
odr OdrBackend
|
||||||
chainFeed event.Feed
|
chainFeed event.Feed
|
||||||
chainSideFeed event.Feed
|
chainSideFeed event.Feed
|
||||||
|
|
@ -57,20 +58,18 @@ type LightChain struct {
|
||||||
scope event.SubscriptionScope
|
scope event.SubscriptionScope
|
||||||
genesisBlock *types.Block
|
genesisBlock *types.Block
|
||||||
|
|
||||||
mu 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
|
||||||
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
|
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
|
||||||
blockCache *lru.Cache // Cache for the most recent entire blocks
|
blockCache *lru.Cache // Cache for the most recent entire blocks
|
||||||
|
|
||||||
|
chainmu sync.RWMutex // protects header inserts
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
running int32 // running must be called automically
|
wg sync.WaitGroup
|
||||||
// procInterrupt must be atomically called
|
|
||||||
procInterrupt int32 // interrupt signaler for block processing
|
|
||||||
wg sync.WaitGroup
|
|
||||||
|
|
||||||
engine consensus.Engine
|
// Atomic boolean switches:
|
||||||
|
running int32 // whether LightChain is running or stopped
|
||||||
|
procInterrupt int32 // interrupts chain insert
|
||||||
|
disableCheckFreq int32 // disables header verification
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLightChain returns a fully initialised light chain using information
|
// NewLightChain returns a fully initialised light chain using information
|
||||||
|
|
@ -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]
|
||||||
|
|
@ -355,6 +354,9 @@ func (self *LightChain) postChainEvents(events []interface{}) {
|
||||||
// In the case of a light chain, InsertHeaderChain also creates and posts light
|
// In the case of a light chain, InsertHeaderChain also creates and posts light
|
||||||
// chain events when necessary.
|
// chain events when necessary.
|
||||||
func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
||||||
|
if atomic.LoadInt32(&self.disableCheckFreq) == 1 {
|
||||||
|
checkFreq = 0
|
||||||
|
}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
|
if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
|
|
@ -362,19 +364,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 +437,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 +479,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() {
|
||||||
|
|
@ -533,3 +529,13 @@ func (self *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscri
|
||||||
func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||||
return self.scope.Track(new(event.Feed).Subscribe(ch))
|
return self.scope.Track(new(event.Feed).Subscribe(ch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DisableCheckFreq disables header validation. This is used for ultralight mode.
|
||||||
|
func (self *LightChain) DisableCheckFreq() {
|
||||||
|
atomic.StoreInt32(&self.disableCheckFreq, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableCheckFreq enables header validation.
|
||||||
|
func (self *LightChain) EnableCheckFreq() {
|
||||||
|
atomic.StoreInt32(&self.disableCheckFreq, 0)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -312,8 +312,9 @@ func (r *PrefixedRegistry) UnregisterAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultRegistry = NewRegistry()
|
DefaultRegistry = NewRegistry()
|
||||||
EphemeralRegistry = NewRegistry()
|
EphemeralRegistry = NewRegistry()
|
||||||
|
AccountingRegistry = NewRegistry() // registry used in swarm
|
||||||
)
|
)
|
||||||
|
|
||||||
// Call the given function for each registered metric.
|
// Call the given function for each registered metric.
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
|
||||||
for i := 0; i < 2; i += 1 {
|
for i := 0; i < 2; i += 1 {
|
||||||
select {
|
select {
|
||||||
case <-taskCh:
|
case <-taskCh:
|
||||||
case <-time.NewTimer(time.Second).C:
|
case <-time.NewTimer(2 * time.Second).C:
|
||||||
t.Error("new task timeout")
|
t.Error("new task timeout")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ type NodeConfig struct {
|
||||||
|
|
||||||
// Listening address of pprof server.
|
// Listening address of pprof server.
|
||||||
PprofAddress string
|
PprofAddress string
|
||||||
|
|
||||||
|
// Ultra Light client options
|
||||||
|
ULC *eth.ULCConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultNodeConfig contains the default node configuration values to use if all
|
// defaultNodeConfig contains the default node configuration values to use if all
|
||||||
|
|
@ -131,6 +134,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
||||||
MaxPeers: config.MaxPeers,
|
MaxPeers: config.MaxPeers,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
rawStack, err := node.New(nodeConf)
|
rawStack, err := node.New(nodeConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,29 @@ type Config struct {
|
||||||
// for ephemeral nodes).
|
// for ephemeral nodes).
|
||||||
HTTPPort int `toml:",omitempty"`
|
HTTPPort int `toml:",omitempty"`
|
||||||
|
|
||||||
|
// GraphQLHost is the host interface on which to start the GraphQL server. If this
|
||||||
|
// field is empty, no GraphQL API endpoint will be started.
|
||||||
|
GraphQLHost string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// GraphQLPort is the TCP port number on which to start the GraphQL server. The
|
||||||
|
// default zero value is/ valid and will pick a port number randomly (useful
|
||||||
|
// for ephemeral nodes).
|
||||||
|
GraphQLPort int `toml:",omitempty"`
|
||||||
|
|
||||||
|
// GraphQLCors is the Cross-Origin Resource Sharing header to send to requesting
|
||||||
|
// clients. Please be aware that CORS is a browser enforced security, it's fully
|
||||||
|
// useless for custom HTTP clients.
|
||||||
|
GraphQLCors []string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// GraphQLVirtualHosts is the list of virtual hostnames which are allowed on incoming requests.
|
||||||
|
// This is by default {'localhost'}. Using this prevents attacks like
|
||||||
|
// DNS rebinding, which bypasses SOP by simply masquerading as being within the same
|
||||||
|
// origin. These attacks do not utilize CORS, since they are not cross-domain.
|
||||||
|
// By explicitly checking the Host-header, the server will not allow requests
|
||||||
|
// made against the server with a malicious host domain.
|
||||||
|
// Requests using ip address directly are not affected
|
||||||
|
GraphQLVirtualHosts []string `toml:",omitempty"`
|
||||||
|
|
||||||
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
|
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
|
||||||
// clients. Please be aware that CORS is a browser enforced security, it's fully
|
// clients. Please be aware that CORS is a browser enforced security, it's fully
|
||||||
// useless for custom HTTP clients.
|
// useless for custom HTTP clients.
|
||||||
|
|
@ -213,6 +236,15 @@ func (c *Config) HTTPEndpoint() string {
|
||||||
return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort)
|
return fmt.Sprintf("%s:%d", c.HTTPHost, c.HTTPPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphQLEndpoint resolves a GraphQL endpoint based on the configured host interface
|
||||||
|
// and port parameters.
|
||||||
|
func (c *Config) GraphQLEndpoint() string {
|
||||||
|
if c.GraphQLHost == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%d", c.GraphQLHost, c.GraphQLPort)
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultHTTPEndpoint returns the HTTP endpoint used by default.
|
// DefaultHTTPEndpoint returns the HTTP endpoint used by default.
|
||||||
func DefaultHTTPEndpoint() string {
|
func DefaultHTTPEndpoint() string {
|
||||||
config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort}
|
config := &Config{HTTPHost: DefaultHTTPHost, HTTPPort: DefaultHTTPPort}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
|
DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
|
||||||
DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
|
DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
|
||||||
DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
|
DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
|
||||||
DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
|
DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
|
||||||
|
DefaultGraphQLHost = "localhost" // Default host interface for the GraphQL server
|
||||||
|
DefaultGraphQLPort = 8547 // Default TCP port for the GraphQL server
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultConfig contains reasonable default settings.
|
// DefaultConfig contains reasonable default settings.
|
||||||
|
|
|
||||||
|
|
@ -27,23 +27,21 @@ var (
|
||||||
// All metrics are cumulative
|
// All metrics are cumulative
|
||||||
|
|
||||||
// total amount of units credited
|
// total amount of units credited
|
||||||
mBalanceCredit metrics.Counter
|
mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", metrics.AccountingRegistry)
|
||||||
// total amount of units debited
|
// total amount of units debited
|
||||||
mBalanceDebit metrics.Counter
|
mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", metrics.AccountingRegistry)
|
||||||
// total amount of bytes credited
|
// total amount of bytes credited
|
||||||
mBytesCredit metrics.Counter
|
mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", metrics.AccountingRegistry)
|
||||||
// total amount of bytes debited
|
// total amount of bytes debited
|
||||||
mBytesDebit metrics.Counter
|
mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", metrics.AccountingRegistry)
|
||||||
// total amount of credited messages
|
// total amount of credited messages
|
||||||
mMsgCredit metrics.Counter
|
mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", metrics.AccountingRegistry)
|
||||||
// total amount of debited messages
|
// total amount of debited messages
|
||||||
mMsgDebit metrics.Counter
|
mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", metrics.AccountingRegistry)
|
||||||
// how many times local node had to drop remote peers
|
// how many times local node had to drop remote peers
|
||||||
mPeerDrops metrics.Counter
|
mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", metrics.AccountingRegistry)
|
||||||
// how many times local node overdrafted and dropped
|
// how many times local node overdrafted and dropped
|
||||||
mSelfDrops metrics.Counter
|
mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", metrics.AccountingRegistry)
|
||||||
|
|
||||||
MetricsRegistry metrics.Registry
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Prices defines how prices are being passed on to the accounting instance
|
// Prices defines how prices are being passed on to the accounting instance
|
||||||
|
|
@ -110,24 +108,13 @@ func NewAccounting(balance Balance, po Prices) *Accounting {
|
||||||
return ah
|
return ah
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupAccountingMetrics creates a separate registry for p2p accounting metrics;
|
// SetupAccountingMetrics uses a separate registry for p2p accounting metrics;
|
||||||
// this registry should be independent of any other metrics as it persists at different endpoints.
|
// this registry should be independent of any other metrics as it persists at different endpoints.
|
||||||
// It also instantiates the given metrics and starts the persisting go-routine which
|
// It also starts the persisting go-routine which
|
||||||
// at the passed interval writes the metrics to a LevelDB
|
// at the passed interval writes the metrics to a LevelDB
|
||||||
func SetupAccountingMetrics(reportInterval time.Duration, path string) *AccountingMetrics {
|
func SetupAccountingMetrics(reportInterval time.Duration, path string) *AccountingMetrics {
|
||||||
// create an empty registry
|
|
||||||
MetricsRegistry = metrics.NewRegistry()
|
|
||||||
// instantiate the metrics
|
|
||||||
mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", MetricsRegistry)
|
|
||||||
mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", MetricsRegistry)
|
|
||||||
mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", MetricsRegistry)
|
|
||||||
mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", MetricsRegistry)
|
|
||||||
mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", MetricsRegistry)
|
|
||||||
mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", MetricsRegistry)
|
|
||||||
mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", MetricsRegistry)
|
|
||||||
mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", MetricsRegistry)
|
|
||||||
// create the DB and start persisting
|
// create the DB and start persisting
|
||||||
return NewAccountingMetrics(MetricsRegistry, reportInterval, path)
|
return NewAccountingMetrics(metrics.AccountingRegistry, reportInterval, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send takes a peer, a size and a msg and
|
// Send takes a peer, a size and a msg and
|
||||||
|
|
|
||||||
|
|
@ -423,3 +423,17 @@ func (p *Peer) Handshake(ctx context.Context, hs interface{}, verify func(interf
|
||||||
}
|
}
|
||||||
return rhs, nil
|
return rhs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasCap returns true if Peer has a capability
|
||||||
|
// with provided name.
|
||||||
|
func (p *Peer) HasCap(capName string) (yes bool) {
|
||||||
|
if p == nil || p.Peer == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, c := range p.Caps() {
|
||||||
|
if c.Name == capName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,9 +142,9 @@ func newProtocol(pp *p2ptest.TestPeerPool) func(*p2p.Peer, p2p.MsgReadWriter) er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func protocolTester(t *testing.T, pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester {
|
func protocolTester(pp *p2ptest.TestPeerPool) *p2ptest.ProtocolTester {
|
||||||
conf := adapters.RandomNodeConfig()
|
conf := adapters.RandomNodeConfig()
|
||||||
return p2ptest.NewProtocolTester(t, conf.ID, 2, newProtocol(pp))
|
return p2ptest.NewProtocolTester(conf.ID, 2, newProtocol(pp))
|
||||||
}
|
}
|
||||||
|
|
||||||
func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
|
func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchange {
|
||||||
|
|
@ -173,7 +173,7 @@ func protoHandshakeExchange(id enode.ID, proto *protoHandshake) []p2ptest.Exchan
|
||||||
|
|
||||||
func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
|
func runProtoHandshake(t *testing.T, proto *protoHandshake, errs ...error) {
|
||||||
pp := p2ptest.NewTestPeerPool()
|
pp := p2ptest.NewTestPeerPool()
|
||||||
s := protocolTester(t, pp)
|
s := protocolTester(pp)
|
||||||
// TODO: make this more than one handshake
|
// TODO: make this more than one handshake
|
||||||
node := s.Nodes[0]
|
node := s.Nodes[0]
|
||||||
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil {
|
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), proto)...); err != nil {
|
||||||
|
|
@ -250,7 +250,7 @@ func TestProtocolHook(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := adapters.RandomNodeConfig()
|
conf := adapters.RandomNodeConfig()
|
||||||
tester := p2ptest.NewProtocolTester(t, conf.ID, 2, runFunc)
|
tester := p2ptest.NewProtocolTester(conf.ID, 2, runFunc)
|
||||||
err := tester.TestExchanges(p2ptest.Exchange{
|
err := tester.TestExchanges(p2ptest.Exchange{
|
||||||
Expects: []p2ptest.Expect{
|
Expects: []p2ptest.Expect{
|
||||||
{
|
{
|
||||||
|
|
@ -269,8 +269,11 @@ func TestProtocolHook(t *testing.T) {
|
||||||
if !testHook.send {
|
if !testHook.send {
|
||||||
t.Fatal("Expected a send message, but it is not")
|
t.Fatal("Expected a send message, but it is not")
|
||||||
}
|
}
|
||||||
if testHook.peer == nil || testHook.peer.ID() != tester.Nodes[0].ID() {
|
if testHook.peer == nil {
|
||||||
t.Fatal("Expected peer ID to be set correctly, but it is not")
|
t.Fatal("Expected peer to be set, is nil")
|
||||||
|
}
|
||||||
|
if peerId := testHook.peer.ID(); peerId != tester.Nodes[0].ID() && peerId != tester.Nodes[1].ID() {
|
||||||
|
t.Fatalf("Expected peer ID to be set correctly, but it is not (got %v, exp %v or %v", peerId, tester.Nodes[0].ID(), tester.Nodes[1].ID())
|
||||||
}
|
}
|
||||||
if testHook.size != 11 { //11 is the length of the encoded message
|
if testHook.size != 11 { //11 is the length of the encoded message
|
||||||
t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
|
t.Fatalf("Expected size to be %d, but it is %d ", 1, testHook.size)
|
||||||
|
|
@ -389,7 +392,7 @@ func moduleHandshakeExchange(id enode.ID, resp uint) []p2ptest.Exchange {
|
||||||
|
|
||||||
func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
|
func runModuleHandshake(t *testing.T, resp uint, errs ...error) {
|
||||||
pp := p2ptest.NewTestPeerPool()
|
pp := p2ptest.NewTestPeerPool()
|
||||||
s := protocolTester(t, pp)
|
s := protocolTester(pp)
|
||||||
node := s.Nodes[0]
|
node := s.Nodes[0]
|
||||||
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil {
|
if err := s.TestExchanges(protoHandshakeExchange(node.ID(), &protoHandshake{42, "420"})...); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -469,7 +472,7 @@ func testMultiPeerSetup(a, b enode.ID) []p2ptest.Exchange {
|
||||||
|
|
||||||
func runMultiplePeers(t *testing.T, peer int, errs ...error) {
|
func runMultiplePeers(t *testing.T, peer int, errs ...error) {
|
||||||
pp := p2ptest.NewTestPeerPool()
|
pp := p2ptest.NewTestPeerPool()
|
||||||
s := protocolTester(t, pp)
|
s := protocolTester(pp)
|
||||||
|
|
||||||
if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil {
|
if err := s.TestExchanges(testMultiPeerSetup(s.Nodes[0].ID(), s.Nodes[1].ID())...); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -39,25 +39,31 @@ func TestReporter(t *testing.T) {
|
||||||
|
|
||||||
//setup the metrics
|
//setup the metrics
|
||||||
log.Debug("Setting up metrics first time")
|
log.Debug("Setting up metrics first time")
|
||||||
reportInterval := 5 * time.Millisecond
|
reportInterval := 2 * time.Millisecond
|
||||||
metrics := SetupAccountingMetrics(reportInterval, filepath.Join(dir, "test.db"))
|
metrics := SetupAccountingMetrics(reportInterval, filepath.Join(dir, "test.db"))
|
||||||
log.Debug("Done.")
|
log.Debug("Done.")
|
||||||
|
|
||||||
//do some metrics
|
//change metrics
|
||||||
mBalanceCredit.Inc(12)
|
mBalanceCredit.Inc(12)
|
||||||
mBytesCredit.Inc(34)
|
mBytesCredit.Inc(34)
|
||||||
mMsgDebit.Inc(9)
|
mMsgDebit.Inc(9)
|
||||||
|
|
||||||
|
//store expected metrics
|
||||||
|
expectedBalanceCredit := mBalanceCredit.Count()
|
||||||
|
expectedBytesCredit := mBytesCredit.Count()
|
||||||
|
expectedMsgDebit := mMsgDebit.Count()
|
||||||
|
|
||||||
//give the reporter time to write the metrics to DB
|
//give the reporter time to write the metrics to DB
|
||||||
time.Sleep(20 * time.Millisecond)
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
|
||||||
//set the metrics to nil - this effectively simulates the node having shut down...
|
|
||||||
mBalanceCredit = nil
|
|
||||||
mBytesCredit = nil
|
|
||||||
mMsgDebit = nil
|
|
||||||
//close the DB also, or we can't create a new one
|
//close the DB also, or we can't create a new one
|
||||||
metrics.Close()
|
metrics.Close()
|
||||||
|
|
||||||
|
//clear the metrics - this effectively simulates the node having shut down...
|
||||||
|
mBalanceCredit.Clear()
|
||||||
|
mBytesCredit.Clear()
|
||||||
|
mMsgDebit.Clear()
|
||||||
|
|
||||||
//setup the metrics again
|
//setup the metrics again
|
||||||
log.Debug("Setting up metrics second time")
|
log.Debug("Setting up metrics second time")
|
||||||
metrics = SetupAccountingMetrics(reportInterval, filepath.Join(dir, "test.db"))
|
metrics = SetupAccountingMetrics(reportInterval, filepath.Join(dir, "test.db"))
|
||||||
|
|
@ -65,13 +71,13 @@ func TestReporter(t *testing.T) {
|
||||||
log.Debug("Done.")
|
log.Debug("Done.")
|
||||||
|
|
||||||
//now check the metrics, they should have the same value as before "shutdown"
|
//now check the metrics, they should have the same value as before "shutdown"
|
||||||
if mBalanceCredit.Count() != 12 {
|
if mBalanceCredit.Count() != expectedBalanceCredit {
|
||||||
t.Fatalf("Expected counter to be %d, but is %d", 12, mBalanceCredit.Count())
|
t.Fatalf("Expected counter to be %d, but is %d", expectedBalanceCredit, mBalanceCredit.Count())
|
||||||
}
|
}
|
||||||
if mBytesCredit.Count() != 34 {
|
if mBytesCredit.Count() != expectedBytesCredit {
|
||||||
t.Fatalf("Expected counter to be %d, but is %d", 23, mBytesCredit.Count())
|
t.Fatalf("Expected counter to be %d, but is %d", expectedBytesCredit, mBytesCredit.Count())
|
||||||
}
|
}
|
||||||
if mMsgDebit.Count() != 9 {
|
if mMsgDebit.Count() != expectedMsgDebit {
|
||||||
t.Fatalf("Expected counter to be %d, but is %d", 9, mMsgDebit.Count())
|
t.Fatalf("Expected counter to be %d, but is %d", expectedMsgDebit, mMsgDebit.Count())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,24 +168,28 @@ func (net *Network) Start(id enode.ID) error {
|
||||||
// snapshots
|
// snapshots
|
||||||
func (net *Network) startWithSnapshots(id enode.ID, snapshots map[string][]byte) error {
|
func (net *Network) startWithSnapshots(id enode.ID, snapshots map[string][]byte) error {
|
||||||
net.lock.Lock()
|
net.lock.Lock()
|
||||||
defer net.lock.Unlock()
|
|
||||||
|
|
||||||
node := net.getNode(id)
|
node := net.getNode(id)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
|
net.lock.Unlock()
|
||||||
return fmt.Errorf("node %v does not exist", id)
|
return fmt.Errorf("node %v does not exist", id)
|
||||||
}
|
}
|
||||||
if node.Up {
|
if node.Up {
|
||||||
|
net.lock.Unlock()
|
||||||
return fmt.Errorf("node %v already up", id)
|
return fmt.Errorf("node %v already up", id)
|
||||||
}
|
}
|
||||||
log.Trace("Starting node", "id", id, "adapter", net.nodeAdapter.Name())
|
log.Trace("Starting node", "id", id, "adapter", net.nodeAdapter.Name())
|
||||||
if err := node.Start(snapshots); err != nil {
|
if err := node.Start(snapshots); err != nil {
|
||||||
|
net.lock.Unlock()
|
||||||
log.Warn("Node startup failed", "id", id, "err", err)
|
log.Warn("Node startup failed", "id", id, "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
node.Up = true
|
node.Up = true
|
||||||
log.Info("Started node", "id", id)
|
log.Info("Started node", "id", id)
|
||||||
|
ev := NewEvent(node)
|
||||||
|
net.lock.Unlock()
|
||||||
|
|
||||||
net.events.Send(NewEvent(node))
|
net.events.Send(ev)
|
||||||
|
|
||||||
// subscribe to peer events
|
// subscribe to peer events
|
||||||
client, err := node.Client()
|
client, err := node.Client()
|
||||||
|
|
@ -210,12 +214,14 @@ func (net *Network) watchPeerEvents(id enode.ID, events chan *p2p.PeerEvent, sub
|
||||||
// assume the node is now down
|
// assume the node is now down
|
||||||
net.lock.Lock()
|
net.lock.Lock()
|
||||||
defer net.lock.Unlock()
|
defer net.lock.Unlock()
|
||||||
|
|
||||||
node := net.getNode(id)
|
node := net.getNode(id)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
node.Up = false
|
node.Up = false
|
||||||
net.events.Send(NewEvent(node))
|
ev := NewEvent(node)
|
||||||
|
net.events.Send(ev)
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -254,9 +260,11 @@ func (net *Network) Stop(id enode.ID) error {
|
||||||
net.lock.Lock()
|
net.lock.Lock()
|
||||||
node := net.getNode(id)
|
node := net.getNode(id)
|
||||||
if node == nil {
|
if node == nil {
|
||||||
|
net.lock.Unlock()
|
||||||
return fmt.Errorf("node %v does not exist", id)
|
return fmt.Errorf("node %v does not exist", id)
|
||||||
}
|
}
|
||||||
if !node.Up {
|
if !node.Up {
|
||||||
|
net.lock.Unlock()
|
||||||
return fmt.Errorf("node %v already down", id)
|
return fmt.Errorf("node %v already down", id)
|
||||||
}
|
}
|
||||||
node.Up = false
|
node.Up = false
|
||||||
|
|
@ -270,7 +278,10 @@ func (net *Network) Stop(id enode.ID) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Info("Stopped node", "id", id, "err", err)
|
log.Info("Stopped node", "id", id, "err", err)
|
||||||
net.events.Send(ControlEvent(node))
|
net.lock.Lock()
|
||||||
|
ev := ControlEvent(node)
|
||||||
|
net.lock.Unlock()
|
||||||
|
net.events.Send(ev)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
|
@ -52,7 +51,7 @@ type ProtocolTester struct {
|
||||||
// NewProtocolTester constructs a new ProtocolTester
|
// NewProtocolTester constructs a new ProtocolTester
|
||||||
// it takes as argument the pivot node id, the number of dummy peers and the
|
// it takes as argument the pivot node id, the number of dummy peers and the
|
||||||
// protocol run function called on a peer connection by the p2p server
|
// protocol run function called on a peer connection by the p2p server
|
||||||
func NewProtocolTester(t *testing.T, id enode.ID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
func NewProtocolTester(id enode.ID, n int, run func(*p2p.Peer, p2p.MsgReadWriter) error) *ProtocolTester {
|
||||||
services := adapters.Services{
|
services := adapters.Services{
|
||||||
"test": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
"test": func(ctx *adapters.ServiceContext) (node.Service, error) {
|
||||||
return &testNode{run}, nil
|
return &testNode{run}, nil
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ 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: nil,
|
ConstantinopleBlock: big.NewInt(7280000),
|
||||||
|
PetersburgBlock: big.NewInt(7280000),
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +68,7 @@ var (
|
||||||
EIP158Block: big.NewInt(10),
|
EIP158Block: big.NewInt(10),
|
||||||
ByzantiumBlock: big.NewInt(1700000),
|
ByzantiumBlock: big.NewInt(1700000),
|
||||||
ConstantinopleBlock: big.NewInt(4230000),
|
ConstantinopleBlock: big.NewInt(4230000),
|
||||||
|
PetersburgBlock: big.NewInt(4939394),
|
||||||
Ethash: new(EthashConfig),
|
Ethash: new(EthashConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +93,7 @@ var (
|
||||||
EIP158Block: big.NewInt(3),
|
EIP158Block: big.NewInt(3),
|
||||||
ByzantiumBlock: big.NewInt(1035301),
|
ByzantiumBlock: big.NewInt(1035301),
|
||||||
ConstantinopleBlock: big.NewInt(3660663),
|
ConstantinopleBlock: big.NewInt(3660663),
|
||||||
|
PetersburgBlock: big.NewInt(9999999), //TODO! Insert Rinkeby block number
|
||||||
Clique: &CliqueConfig{
|
Clique: &CliqueConfig{
|
||||||
Period: 15,
|
Period: 15,
|
||||||
Epoch: 30000,
|
Epoch: 30000,
|
||||||
|
|
@ -111,16 +114,16 @@ var (
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
||||||
|
|
||||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||||
//
|
//
|
||||||
// This configuration is intentionally not using keyed fields to force anyone
|
// This configuration is intentionally not using keyed fields to force anyone
|
||||||
// adding flags to the config to also have to set these fields.
|
// adding flags to the config to also have to set these fields.
|
||||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
|
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
|
||||||
|
|
||||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
||||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -158,6 +161,7 @@ type ChainConfig struct {
|
||||||
|
|
||||||
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
|
ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
|
||||||
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
|
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
|
||||||
|
PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople)
|
||||||
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
|
EWASMBlock *big.Int `json:"ewasmBlock,omitempty"` // EWASM switch block (nil = no fork, 0 = already activated)
|
||||||
|
|
||||||
// Various consensus engines
|
// Various consensus engines
|
||||||
|
|
@ -195,7 +199,7 @@ func (c *ChainConfig) String() string {
|
||||||
default:
|
default:
|
||||||
engine = "unknown"
|
engine = "unknown"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Engine: %v}",
|
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v ConstantinopleFix: %v Engine: %v}",
|
||||||
c.ChainID,
|
c.ChainID,
|
||||||
c.HomesteadBlock,
|
c.HomesteadBlock,
|
||||||
c.DAOForkBlock,
|
c.DAOForkBlock,
|
||||||
|
|
@ -205,6 +209,7 @@ func (c *ChainConfig) String() string {
|
||||||
c.EIP158Block,
|
c.EIP158Block,
|
||||||
c.ByzantiumBlock,
|
c.ByzantiumBlock,
|
||||||
c.ConstantinopleBlock,
|
c.ConstantinopleBlock,
|
||||||
|
c.PetersburgBlock,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +249,13 @@ func (c *ChainConfig) IsConstantinople(num *big.Int) bool {
|
||||||
return isForked(c.ConstantinopleBlock, num)
|
return isForked(c.ConstantinopleBlock, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsPetersburg returns whether num is either
|
||||||
|
// - equal to or greater than the PetersburgBlock fork block,
|
||||||
|
// - OR is nil, and Constantinople is active
|
||||||
|
func (c *ChainConfig) IsPetersburg(num *big.Int) bool {
|
||||||
|
return isForked(c.PetersburgBlock, num) || c.PetersburgBlock == nil && isForked(c.ConstantinopleBlock, num)
|
||||||
|
}
|
||||||
|
|
||||||
// IsEWASM returns whether num represents a block number after the EWASM fork
|
// IsEWASM returns whether num represents a block number after the EWASM fork
|
||||||
func (c *ChainConfig) IsEWASM(num *big.Int) bool {
|
func (c *ChainConfig) IsEWASM(num *big.Int) bool {
|
||||||
return isForked(c.EWASMBlock, num)
|
return isForked(c.EWASMBlock, num)
|
||||||
|
|
@ -314,6 +326,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
|
||||||
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
|
if isForkIncompatible(c.ConstantinopleBlock, newcfg.ConstantinopleBlock, head) {
|
||||||
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
|
return newCompatError("Constantinople fork block", c.ConstantinopleBlock, newcfg.ConstantinopleBlock)
|
||||||
}
|
}
|
||||||
|
if isForkIncompatible(c.PetersburgBlock, newcfg.PetersburgBlock, head) {
|
||||||
|
return newCompatError("ConstantinopleFix fork block", c.PetersburgBlock, newcfg.PetersburgBlock)
|
||||||
|
}
|
||||||
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
|
if isForkIncompatible(c.EWASMBlock, newcfg.EWASMBlock, head) {
|
||||||
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
|
return newCompatError("ewasm fork block", c.EWASMBlock, newcfg.EWASMBlock)
|
||||||
}
|
}
|
||||||
|
|
@ -381,9 +396,9 @@ func (err *ConfigCompatError) Error() string {
|
||||||
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
||||||
// phases.
|
// phases.
|
||||||
type Rules struct {
|
type Rules struct {
|
||||||
ChainID *big.Int
|
ChainID *big.Int
|
||||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||||
IsByzantium, IsConstantinople bool
|
IsByzantium, IsConstantinople, IsPetersburg bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules ensures c's ChainID is not nil.
|
// Rules ensures c's ChainID is not nil.
|
||||||
|
|
@ -400,5 +415,6 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
|
||||||
IsEIP158: c.IsEIP158(num),
|
IsEIP158: c.IsEIP158(num),
|
||||||
IsByzantium: c.IsByzantium(num),
|
IsByzantium: c.IsByzantium(num),
|
||||||
IsConstantinople: c.IsConstantinople(num),
|
IsConstantinople: c.IsConstantinople(num),
|
||||||
|
IsPetersburg: c.IsPetersburg(num),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ const (
|
||||||
NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
|
NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
|
||||||
NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
|
NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
|
||||||
|
|
||||||
JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero.
|
JumpdestGas uint64 = 1 // Once per JUMPDEST operation.
|
||||||
EpochDuration uint64 = 30000 // Duration between proof-of-work epochs.
|
EpochDuration uint64 = 30000 // Duration between proof-of-work epochs.
|
||||||
CallGas uint64 = 40 // Once per CALL operation & message call transaction.
|
CallGas uint64 = 40 // Once per CALL operation & message call transaction.
|
||||||
CreateDataGas uint64 = 200 //
|
CreateDataGas uint64 = 200 //
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ func (t *httpReadWriteNopCloser) Close() error {
|
||||||
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
||||||
//
|
//
|
||||||
// Deprecated: Server implements http.Handler
|
// Deprecated: Server implements http.Handler
|
||||||
func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server {
|
func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv http.Handler) *http.Server {
|
||||||
// Wrap the CORS-handler within a host-handler
|
// Wrap the CORS-handler within a host-handler
|
||||||
handler := newCorsHandler(srv, cors)
|
handler := newCorsHandler(srv, cors)
|
||||||
handler = newVHostHandler(vhosts, handler)
|
handler = newVHostHandler(vhosts, handler)
|
||||||
|
|
@ -284,7 +284,7 @@ func validateRequest(r *http.Request) (int, error) {
|
||||||
return http.StatusUnsupportedMediaType, err
|
return http.StatusUnsupportedMediaType, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
|
func newCorsHandler(srv http.Handler, allowedOrigins []string) http.Handler {
|
||||||
// disable CORS support if user has not specified a custom CORS configuration
|
// disable CORS support if user has not specified a custom CORS configuration
|
||||||
if len(allowedOrigins) == 0 {
|
if len(allowedOrigins) == 0 {
|
||||||
return srv
|
return srv
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ type Config struct {
|
||||||
DeliverySkipCheck bool
|
DeliverySkipCheck bool
|
||||||
MaxStreamPeerServers int
|
MaxStreamPeerServers int
|
||||||
LightNodeEnabled bool
|
LightNodeEnabled bool
|
||||||
|
BootnodeMode bool
|
||||||
SyncUpdateDelay time.Duration
|
SyncUpdateDelay time.Duration
|
||||||
SwapAPI string
|
SwapAPI string
|
||||||
Cors string
|
Cors string
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
gethmetrics "github.com/ethereum/go-ethereum/metrics"
|
gethmetrics "github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/metrics/influxdb"
|
"github.com/ethereum/go-ethereum/metrics/influxdb"
|
||||||
"github.com/ethereum/go-ethereum/swarm/log"
|
"github.com/ethereum/go-ethereum/swarm/log"
|
||||||
"gopkg.in/urfave/cli.v1"
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -31,6 +31,10 @@ var (
|
||||||
Name: "metrics.influxdb.export",
|
Name: "metrics.influxdb.export",
|
||||||
Usage: "Enable metrics export/push to an external InfluxDB database",
|
Usage: "Enable metrics export/push to an external InfluxDB database",
|
||||||
}
|
}
|
||||||
|
MetricsEnableInfluxDBAccountingExportFlag = cli.BoolFlag{
|
||||||
|
Name: "metrics.influxdb.accounting",
|
||||||
|
Usage: "Enable accounting metrics export/push to an external InfluxDB database",
|
||||||
|
}
|
||||||
MetricsInfluxDBEndpointFlag = cli.StringFlag{
|
MetricsInfluxDBEndpointFlag = cli.StringFlag{
|
||||||
Name: "metrics.influxdb.endpoint",
|
Name: "metrics.influxdb.endpoint",
|
||||||
Usage: "Metrics InfluxDB endpoint",
|
Usage: "Metrics InfluxDB endpoint",
|
||||||
|
|
@ -51,14 +55,14 @@ var (
|
||||||
Usage: "Metrics InfluxDB password",
|
Usage: "Metrics InfluxDB password",
|
||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
// The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
// Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
||||||
// It is used so that we can group all nodes and average a measurement across all of them, but also so
|
// For example `host` tag could be used so that we can group all nodes and average a measurement
|
||||||
// that we can select a specific node and inspect its measurements.
|
// across all of them, but also so that we can select a specific node and inspect its measurements.
|
||||||
// https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
|
// https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
|
||||||
MetricsInfluxDBHostTagFlag = cli.StringFlag{
|
MetricsInfluxDBTagsFlag = cli.StringFlag{
|
||||||
Name: "metrics.influxdb.host.tag",
|
Name: "metrics.influxdb.tags",
|
||||||
Usage: "Metrics InfluxDB `host` tag attached to all measurements",
|
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
||||||
Value: "localhost",
|
Value: "host=localhost",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -66,33 +70,39 @@ var (
|
||||||
var Flags = []cli.Flag{
|
var Flags = []cli.Flag{
|
||||||
utils.MetricsEnabledFlag,
|
utils.MetricsEnabledFlag,
|
||||||
MetricsEnableInfluxDBExportFlag,
|
MetricsEnableInfluxDBExportFlag,
|
||||||
|
MetricsEnableInfluxDBAccountingExportFlag,
|
||||||
MetricsInfluxDBEndpointFlag,
|
MetricsInfluxDBEndpointFlag,
|
||||||
MetricsInfluxDBDatabaseFlag,
|
MetricsInfluxDBDatabaseFlag,
|
||||||
MetricsInfluxDBUsernameFlag,
|
MetricsInfluxDBUsernameFlag,
|
||||||
MetricsInfluxDBPasswordFlag,
|
MetricsInfluxDBPasswordFlag,
|
||||||
MetricsInfluxDBHostTagFlag,
|
MetricsInfluxDBTagsFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
func Setup(ctx *cli.Context) {
|
func Setup(ctx *cli.Context) {
|
||||||
if gethmetrics.Enabled {
|
if gethmetrics.Enabled {
|
||||||
log.Info("Enabling swarm metrics collection")
|
log.Info("Enabling swarm metrics collection")
|
||||||
var (
|
var (
|
||||||
enableExport = ctx.GlobalBool(MetricsEnableInfluxDBExportFlag.Name)
|
endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name)
|
||||||
endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name)
|
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
||||||
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
||||||
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
||||||
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
enableExport = ctx.GlobalBool(MetricsEnableInfluxDBExportFlag.Name)
|
||||||
hosttag = ctx.GlobalString(MetricsInfluxDBHostTagFlag.Name)
|
enableAccountingExport = ctx.GlobalBool(MetricsEnableInfluxDBAccountingExportFlag.Name)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start system runtime metrics collection
|
// Start system runtime metrics collection
|
||||||
go gethmetrics.CollectProcessMetrics(2 * time.Second)
|
go gethmetrics.CollectProcessMetrics(2 * time.Second)
|
||||||
|
|
||||||
|
tagsMap := utils.SplitTagsFlag(ctx.GlobalString(MetricsInfluxDBTagsFlag.Name))
|
||||||
|
|
||||||
if enableExport {
|
if enableExport {
|
||||||
log.Info("Enabling swarm metrics export to InfluxDB")
|
log.Info("Enabling swarm metrics export to InfluxDB")
|
||||||
go influxdb.InfluxDBWithTags(gethmetrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "swarm.", map[string]string{
|
go influxdb.InfluxDBWithTags(gethmetrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "swarm.", tagsMap)
|
||||||
"host": hosttag,
|
}
|
||||||
})
|
|
||||||
|
if enableAccountingExport {
|
||||||
|
log.Info("Exporting swarm accounting metrics to InfluxDB")
|
||||||
|
go influxdb.InfluxDBWithTags(gethmetrics.AccountingRegistry, 10*time.Second, endpoint, database, username, password, "accounting.", tagsMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/swarm/log"
|
"github.com/ethereum/go-ethereum/swarm/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/pot"
|
"github.com/ethereum/go-ethereum/swarm/pot"
|
||||||
|
sv "github.com/ethereum/go-ethereum/swarm/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -168,82 +169,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 there is an empty bin, depth is surely passed
|
||||||
|
pastDepth = true
|
||||||
|
saturation[0] = append(saturation[0], lastPO)
|
||||||
}
|
}
|
||||||
if size < minsize {
|
lastPO = po + 1
|
||||||
bpo = append(bpo, po)
|
// past radius, depth is surely passed
|
||||||
minsize = size
|
if po >= radius {
|
||||||
|
pastDepth = true
|
||||||
}
|
}
|
||||||
return size > 0 && po < depth
|
// 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
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return f(func(val pot.Val) bool {
|
cur := 0
|
||||||
e := val.(*entry)
|
curPO := bins[0]
|
||||||
c := k.callable(e)
|
k.addrs.EachBin(k.base, Pof, curPO, func(po, _ int, f func(func(pot.Val) bool) bool) bool {
|
||||||
if c {
|
curPO = bins[cur]
|
||||||
a = e.BzzAddr
|
// 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 !c
|
// 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)
|
||||||
|
if k.callable(e) {
|
||||||
|
suggestedPeer = e.BzzAddr
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
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 +432,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 +461,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
|
||||||
|
|
@ -506,6 +553,9 @@ func (k *Kademlia) string() string {
|
||||||
var rows []string
|
var rows []string
|
||||||
|
|
||||||
rows = append(rows, "=========================================================================")
|
rows = append(rows, "=========================================================================")
|
||||||
|
if len(sv.GitCommit) > 0 {
|
||||||
|
rows = append(rows, fmt.Sprintf("commit hash: %s", sv.GitCommit))
|
||||||
|
}
|
||||||
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3]))
|
rows = append(rows, fmt.Sprintf("%v KΛÐΞMLIΛ hive: queen's address: %x", time.Now().UTC().Format(time.UnixDate), k.BaseAddr()[:3]))
|
||||||
rows = append(rows, fmt.Sprintf("population: %d (%d), NeighbourhoodSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.NeighbourhoodSize, k.MinBinSize, k.MaxBinSize))
|
rows = append(rows, fmt.Sprintf("population: %d (%d), NeighbourhoodSize: %d, MinBinSize: %d, MaxBinSize: %d", k.conns.Size(), k.addrs.Size(), k.NeighbourhoodSize, k.MinBinSize, k.MaxBinSize))
|
||||||
|
|
||||||
|
|
@ -627,21 +677,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 +792,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))
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ type BzzConfig struct {
|
||||||
HiveParams *HiveParams
|
HiveParams *HiveParams
|
||||||
NetworkID uint64
|
NetworkID uint64
|
||||||
LightNode bool
|
LightNode bool
|
||||||
|
BootnodeMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bzz is the swarm protocol bundle
|
// Bzz is the swarm protocol bundle
|
||||||
|
|
@ -87,7 +88,7 @@ type Bzz struct {
|
||||||
// * overlay driver
|
// * overlay driver
|
||||||
// * peer store
|
// * peer store
|
||||||
func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *protocols.Spec, streamerRun func(*BzzPeer) error) *Bzz {
|
func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *protocols.Spec, streamerRun func(*BzzPeer) error) *Bzz {
|
||||||
return &Bzz{
|
bzz := &Bzz{
|
||||||
Hive: NewHive(config.HiveParams, kad, store),
|
Hive: NewHive(config.HiveParams, kad, store),
|
||||||
NetworkID: config.NetworkID,
|
NetworkID: config.NetworkID,
|
||||||
LightNode: config.LightNode,
|
LightNode: config.LightNode,
|
||||||
|
|
@ -96,6 +97,13 @@ func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *p
|
||||||
streamerRun: streamerRun,
|
streamerRun: streamerRun,
|
||||||
streamerSpec: streamerSpec,
|
streamerSpec: streamerSpec,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.BootnodeMode {
|
||||||
|
bzz.streamerRun = nil
|
||||||
|
bzz.streamerSpec = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return bzz
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateLocalAddr updates underlayaddress of the running node
|
// UpdateLocalAddr updates underlayaddress of the running node
|
||||||
|
|
@ -168,7 +176,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 +221,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 +311,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"
|
||||||
|
|
@ -82,7 +83,7 @@ func newBzzBaseTester(t *testing.T, n int, addr *BzzAddr, spec *protocols.Spec,
|
||||||
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
|
return srv(&BzzPeer{Peer: protocols.NewPeer(p, rw, spec), BzzAddr: NewAddr(p.Node())})
|
||||||
}
|
}
|
||||||
|
|
||||||
s := p2ptest.NewProtocolTester(t, addr.ID(), n, protocol)
|
s := p2ptest.NewProtocolTester(addr.ID(), n, protocol)
|
||||||
|
|
||||||
for _, node := range s.Nodes {
|
for _, node := range s.Nodes {
|
||||||
cs[node.ID().String()] = make(chan bool)
|
cs[node.ID().String()] = make(chan bool)
|
||||||
|
|
@ -115,9 +116,9 @@ func newBzz(addr *BzzAddr, lightNode bool) *Bzz {
|
||||||
return bzz
|
return bzz
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBzzHandshakeTester(t *testing.T, n int, addr *BzzAddr, lightNode bool) *bzzTester {
|
func newBzzHandshakeTester(n int, addr *BzzAddr, lightNode bool) *bzzTester {
|
||||||
bzz := newBzz(addr, lightNode)
|
bzz := newBzz(addr, lightNode)
|
||||||
pt := p2ptest.NewProtocolTester(t, addr.ID(), n, bzz.runBzz)
|
pt := p2ptest.NewProtocolTester(addr.ID(), n, bzz.runBzz)
|
||||||
|
|
||||||
return &bzzTester{
|
return &bzzTester{
|
||||||
addr: addr,
|
addr: addr,
|
||||||
|
|
@ -165,7 +166,7 @@ func correctBzzHandshake(addr *BzzAddr, lightNode bool) *HandshakeMsg {
|
||||||
func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
|
func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
|
||||||
lightNode := false
|
lightNode := false
|
||||||
addr := RandomAddr()
|
addr := RandomAddr()
|
||||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
s := newBzzHandshakeTester(1, addr, lightNode)
|
||||||
node := s.Nodes[0]
|
node := s.Nodes[0]
|
||||||
|
|
||||||
err := s.testHandshake(
|
err := s.testHandshake(
|
||||||
|
|
@ -182,7 +183,7 @@ func TestBzzHandshakeNetworkIDMismatch(t *testing.T) {
|
||||||
func TestBzzHandshakeVersionMismatch(t *testing.T) {
|
func TestBzzHandshakeVersionMismatch(t *testing.T) {
|
||||||
lightNode := false
|
lightNode := false
|
||||||
addr := RandomAddr()
|
addr := RandomAddr()
|
||||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
s := newBzzHandshakeTester(1, addr, lightNode)
|
||||||
node := s.Nodes[0]
|
node := s.Nodes[0]
|
||||||
|
|
||||||
err := s.testHandshake(
|
err := s.testHandshake(
|
||||||
|
|
@ -199,7 +200,7 @@ func TestBzzHandshakeVersionMismatch(t *testing.T) {
|
||||||
func TestBzzHandshakeSuccess(t *testing.T) {
|
func TestBzzHandshakeSuccess(t *testing.T) {
|
||||||
lightNode := false
|
lightNode := false
|
||||||
addr := RandomAddr()
|
addr := RandomAddr()
|
||||||
s := newBzzHandshakeTester(t, 1, addr, lightNode)
|
s := newBzzHandshakeTester(1, addr, lightNode)
|
||||||
node := s.Nodes[0]
|
node := s.Nodes[0]
|
||||||
|
|
||||||
err := s.testHandshake(
|
err := s.testHandshake(
|
||||||
|
|
@ -224,7 +225,8 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
||||||
for _, test := range lightNodeTests {
|
for _, test := range lightNodeTests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
randomAddr := RandomAddr()
|
randomAddr := RandomAddr()
|
||||||
pt := newBzzHandshakeTester(t, 1, randomAddr, false)
|
pt := newBzzHandshakeTester(1, randomAddr, false)
|
||||||
|
|
||||||
node := pt.Nodes[0]
|
node := pt.Nodes[0]
|
||||||
addr := NewAddr(node)
|
addr := NewAddr(node)
|
||||||
|
|
||||||
|
|
@ -237,8 +239,14 @@ func TestBzzHandshakeLightNode(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pt.bzz.handshakes[node.ID()].LightNode != test.lightNode {
|
select {
|
||||||
t.Fatalf("peer LightNode flag is %v, should be %v", pt.bzz.handshakes[node.ID()].LightNode, test.lightNode)
|
|
||||||
|
case <-pt.bzz.handshakes[node.ID()].done:
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
case <-time.After(10 * time.Second):
|
||||||
|
t.Fatal("test timeout")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWaitTillHealthy(t *testing.T) {
|
func TestWaitTillHealthy(t *testing.T) {
|
||||||
t.Skip("WaitTillHealthy depends on discovery, which relies on a reliable SuggestPeer, which is not reliable")
|
|
||||||
|
|
||||||
sim := New(map[string]ServiceFunc{
|
sim := New(map[string]ServiceFunc{
|
||||||
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
|
||||||
addr := network.NewAddr(ctx.Config.Node())
|
addr := network.NewAddr(ctx.Config.Node())
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
go func(i, k int) {
|
|
||||||
defer wg.Done()
|
|
||||||
net.Connect(ids[i], ids[k])
|
|
||||||
}(i, k)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
wg.Wait()
|
err := net.ConnectNodesChain(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
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))
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ var (
|
||||||
//It also provides a documentation on the steps needed by frontends
|
//It also provides a documentation on the steps needed by frontends
|
||||||
//to use the simulations
|
//to use the simulations
|
||||||
func TestOverlaySim(t *testing.T) {
|
func TestOverlaySim(t *testing.T) {
|
||||||
t.Skip("Test is flaky, see: https://github.com/ethersphere/go-ethereum/issues/592")
|
|
||||||
//start the simulation
|
//start the simulation
|
||||||
log.Info("Start simulation backend")
|
log.Info("Start simulation backend")
|
||||||
//get the simulation networ; needed to subscribe for up events
|
//get the simulation networ; needed to subscribe for up events
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -67,7 +66,7 @@ func init() {
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStreamerTester(t *testing.T, registryOptions *RegistryOptions) (*p2ptest.ProtocolTester, *Registry, *storage.LocalStore, func(), error) {
|
func newStreamerTester(registryOptions *RegistryOptions) (*p2ptest.ProtocolTester, *Registry, *storage.LocalStore, func(), error) {
|
||||||
// setup
|
// setup
|
||||||
addr := network.RandomAddr() // tested peers peer address
|
addr := network.RandomAddr() // tested peers peer address
|
||||||
to := network.NewKademlia(addr.OAddr, network.NewKadParams())
|
to := network.NewKademlia(addr.OAddr, network.NewKadParams())
|
||||||
|
|
@ -102,7 +101,7 @@ func newStreamerTester(t *testing.T, registryOptions *RegistryOptions) (*p2ptest
|
||||||
streamer.Close()
|
streamer.Close()
|
||||||
removeDataDir()
|
removeDataDir()
|
||||||
}
|
}
|
||||||
protocolTester := p2ptest.NewProtocolTester(t, addr.ID(), 1, streamer.runProtocol)
|
protocolTester := p2ptest.NewProtocolTester(addr.ID(), 1, streamer.runProtocol)
|
||||||
|
|
||||||
err = waitForPeers(streamer, 1*time.Second, 1)
|
err = waitForPeers(streamer, 1*time.Second, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -255,8 +255,15 @@ func (d *Delivery) RequestFromPeers(ctx context.Context, req *network.Request) (
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
sp = d.getPeer(id)
|
sp = d.getPeer(id)
|
||||||
|
// sp is nil, when we encounter a peer that is not registered for delivery, i.e. doesn't support the `stream` protocol
|
||||||
if sp == nil {
|
if sp == nil {
|
||||||
//log.Warn("Delivery.RequestFromPeers: peer not found", "id", id)
|
return true
|
||||||
|
}
|
||||||
|
// nodes that do not provide stream protocol
|
||||||
|
// should not be requested, e.g. bootnodes
|
||||||
|
if !p.HasCap("stream") {
|
||||||
|
// TODO: if we have no errors, delete this if
|
||||||
|
log.Error("Delivery.RequestFromPeers: peer doesn't have stream cap. we should have returned at sp == nil")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
spID = &id
|
spID = &id
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
|
||||||
Retrieval: RetrievalClientOnly,
|
Retrieval: RetrievalClientOnly,
|
||||||
Syncing: SyncingDisabled,
|
Syncing: SyncingDisabled,
|
||||||
}
|
}
|
||||||
tester, streamer, _, teardown, err := newStreamerTester(t, regOpts)
|
tester, streamer, _, teardown, err := newStreamerTester(regOpts)
|
||||||
defer teardown()
|
defer teardown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -97,7 +97,7 @@ func TestStreamerRetrieveRequest(t *testing.T) {
|
||||||
//Test requesting a chunk from a peer then issuing a "empty" OfferedHashesMsg (no hashes available yet)
|
//Test requesting a chunk from a peer then issuing a "empty" OfferedHashesMsg (no hashes available yet)
|
||||||
//Should time out as the peer does not have the chunk (no syncing happened previously)
|
//Should time out as the peer does not have the chunk (no syncing happened previously)
|
||||||
func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
||||||
tester, streamer, _, teardown, err := newStreamerTester(t, &RegistryOptions{
|
tester, streamer, _, teardown, err := newStreamerTester(&RegistryOptions{
|
||||||
Retrieval: RetrievalEnabled,
|
Retrieval: RetrievalEnabled,
|
||||||
Syncing: SyncingDisabled, //do no syncing
|
Syncing: SyncingDisabled, //do no syncing
|
||||||
})
|
})
|
||||||
|
|
@ -169,7 +169,7 @@ func TestStreamerUpstreamRetrieveRequestMsgExchangeWithoutStore(t *testing.T) {
|
||||||
// upstream request server receives a retrieve Request and responds with
|
// upstream request server receives a retrieve Request and responds with
|
||||||
// offered hashes or delivery if skipHash is set to true
|
// offered hashes or delivery if skipHash is set to true
|
||||||
func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
func TestStreamerUpstreamRetrieveRequestMsgExchange(t *testing.T) {
|
||||||
tester, streamer, localStore, teardown, err := newStreamerTester(t, &RegistryOptions{
|
tester, streamer, localStore, teardown, err := newStreamerTester(&RegistryOptions{
|
||||||
Retrieval: RetrievalEnabled,
|
Retrieval: RetrievalEnabled,
|
||||||
Syncing: SyncingDisabled,
|
Syncing: SyncingDisabled,
|
||||||
})
|
})
|
||||||
|
|
@ -285,7 +285,7 @@ func TestRequestFromPeers(t *testing.T) {
|
||||||
addr := network.RandomAddr()
|
addr := network.RandomAddr()
|
||||||
to := network.NewKademlia(addr.OAddr, network.NewKadParams())
|
to := network.NewKademlia(addr.OAddr, network.NewKadParams())
|
||||||
delivery := NewDelivery(to, nil)
|
delivery := NewDelivery(to, nil)
|
||||||
protocolsPeer := protocols.NewPeer(p2p.NewPeer(dummyPeerID, "dummy", nil), nil, nil)
|
protocolsPeer := protocols.NewPeer(p2p.NewPeer(dummyPeerID, "dummy", []p2p.Cap{{Name: "stream"}}), nil, nil)
|
||||||
peer := network.NewPeer(&network.BzzPeer{
|
peer := network.NewPeer(&network.BzzPeer{
|
||||||
BzzAddr: network.RandomAddr(),
|
BzzAddr: network.RandomAddr(),
|
||||||
LightNode: false,
|
LightNode: false,
|
||||||
|
|
@ -359,7 +359,7 @@ func TestRequestFromPeersWithLightNode(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
|
func TestStreamerDownstreamChunkDeliveryMsgExchange(t *testing.T) {
|
||||||
tester, streamer, localStore, teardown, err := newStreamerTester(t, &RegistryOptions{
|
tester, streamer, localStore, teardown, err := newStreamerTester(&RegistryOptions{
|
||||||
Retrieval: RetrievalDisabled,
|
Retrieval: RetrievalDisabled,
|
||||||
Syncing: SyncingDisabled,
|
Syncing: SyncingDisabled,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue