Update Callisto code from upstream.

This commit is contained in:
Yohan Graterol 2017-12-14 23:42:40 -05:00
commit 11f7a9603e
56 changed files with 1691 additions and 396 deletions

View file

@ -129,7 +129,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
return string(code), nil return string(code), nil
} }
// For all others just return as is for now // For all others just return as is for now
return string(buffer.Bytes()), nil return buffer.String(), nil
} }
// bindType is a set of type binders that convert Solidity types to some supported // bindType is a set of type binders that convert Solidity types to some supported

View file

@ -368,11 +368,11 @@ func TestUnmarshal(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else { } else {
if bytes.Compare(p0, p0Exp) != 0 { if !bytes.Equal(p0, p0Exp) {
t.Errorf("unexpected value unpacked: want %x, got %x", p0Exp, p0) t.Errorf("unexpected value unpacked: want %x, got %x", p0Exp, p0)
} }
if bytes.Compare(p1[:], p1Exp) != 0 { if !bytes.Equal(p1[:], p1Exp) {
t.Errorf("unexpected value unpacked: want %x, got %x", p1Exp, p1) t.Errorf("unexpected value unpacked: want %x, got %x", p1Exp, p1)
} }
} }

View file

@ -260,8 +260,7 @@ func NewTree(hasher BaseHasher, segmentSize, segmentCount int) *Tree {
for d := 1; d <= depth(segmentCount); d++ { for d := 1; d <= depth(segmentCount); d++ {
nodes := make([]*Node, count) nodes := make([]*Node, count)
for i := 0; i < len(nodes); i++ { for i := 0; i < len(nodes); i++ {
var parent *Node parent := prevlevel[i/2]
parent = prevlevel[i/2]
t := NewNode(level, i, parent) t := NewNode(level, i, parent)
nodes[i] = t nodes[i] = t
} }

View file

@ -319,8 +319,8 @@ func doLint(cmdline []string) {
packages = flag.CommandLine.Args() packages = flag.CommandLine.Args()
} }
// Get metalinter and install all supported linters // Get metalinter and install all supported linters
build.MustRun(goTool("get", "gopkg.in/alecthomas/gometalinter.v1")) build.MustRun(goTool("get", "gopkg.in/alecthomas/gometalinter.v2"))
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v1"), "--install") build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), "--install")
// Run fast linters batched together // Run fast linters batched together
configs := []string{ configs := []string{
@ -332,12 +332,12 @@ func doLint(cmdline []string) {
"--enable=goconst", "--enable=goconst",
"--min-occurrences=6", // for goconst "--min-occurrences=6", // for goconst
} }
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v1"), append(configs, packages...)...) build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...)
// Run slow linters one by one // Run slow linters one by one
for _, linter := range []string{"unconvert", "gosimple"} { for _, linter := range []string{"unconvert", "gosimple"} {
configs = []string{"--vendor", "--deadline=10m", "--disable-all", "--enable=" + linter} configs = []string{"--vendor", "--deadline=10m", "--disable-all", "--enable=" + linter}
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v1"), append(configs, packages...)...) build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...)
} }
} }

View file

@ -506,7 +506,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
// Send an error if too frequent funding, othewise a success // Send an error if too frequent funding, othewise a success
if !fund { if !fund {
if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { // nolint: gosimple
log.Warn("Failed to send funding error to client", "err", err) log.Warn("Failed to send funding error to client", "err", err)
return return
} }

View file

@ -26,7 +26,7 @@ import (
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/core" "github.com/EthereumCommonwealth/go-callisto/core"
"github.com/EthereumCommonwealth/go-callisto/ethdb" "github.com/EthereumCommonwealth/go-callisto/ethdb"
"github.com/EthereumCommonwealth/go-callisto/params" //"github.com/EthereumCommonwealth/go-callisto/params"
) )
// Genesis block for nodes which don't care about the DAO fork (i.e. not configured) // Genesis block for nodes which don't care about the DAO fork (i.e. not configured)
@ -89,7 +89,7 @@ func TestDAOForkBlockNewChain(t *testing.T) {
expectVote bool expectVote bool
}{ }{
// Test DAO Default Mainnet // Test DAO Default Mainnet
{"", params.MainnetChainConfig.DAOForkBlock, true}, //{"", params.MainnetChainConfig.DAOForkBlock, true},
// test DAO Init Old Privnet // test DAO Init Old Privnet
{daoOldGenesis, nil, false}, {daoOldGenesis, nil, false},
// test DAO Default No Fork Privnet // test DAO Default No Fork Privnet

View file

@ -42,7 +42,7 @@ var customGenesisTests = []struct {
"timestamp" : "0x00" "timestamp" : "0x00"
}`, }`,
query: "eth.getBlock(0).nonce", query: "eth.getBlock(0).nonce",
result: "0x0000000000000042", result: "0x0000000000000000",
}, },
// Genesis file with an empty chain configuration (ensure missing fields work) // Genesis file with an empty chain configuration (ensure missing fields work)
{ {
@ -80,7 +80,7 @@ var customGenesisTests = []struct {
}, },
}`, }`,
query: "eth.getBlock(0).nonce", query: "eth.getBlock(0).nonce",
result: "0x0000000000000042", result: "0x0000000000000000",
}, },
} }

File diff suppressed because one or more lines are too long

321
cmd/swarm/config.go Normal file
View file

@ -0,0 +1,321 @@
// Copyright 2017 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 (
"errors"
"fmt"
"io"
"os"
"reflect"
"strconv"
"unicode"
cli "gopkg.in/urfave/cli.v1"
"github.com/EthereumCommonwealth/go-callisto/cmd/utils"
"github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/log"
"github.com/EthereumCommonwealth/go-callisto/node"
"github.com/naoina/toml"
bzzapi "github.com/EthereumCommonwealth/go-callisto/swarm/api"
)
var (
//flag definition for the dumpconfig command
DumpConfigCommand = cli.Command{
Action: utils.MigrateFlags(dumpConfig),
Name: "dumpconfig",
Usage: "Show configuration values",
ArgsUsage: "",
Flags: app.Flags,
Category: "MISCELLANEOUS COMMANDS",
Description: `The dumpconfig command shows configuration values.`,
}
//flag definition for the config file command
SwarmTomlConfigPathFlag = cli.StringFlag{
Name: "config",
Usage: "TOML configuration file",
}
)
//constants for environment variables
const (
SWARM_ENV_CHEQUEBOOK_ADDR = "SWARM_CHEQUEBOOK_ADDR"
SWARM_ENV_ACCOUNT = "SWARM_ACCOUNT"
SWARM_ENV_LISTEN_ADDR = "SWARM_LISTEN_ADDR"
SWARM_ENV_PORT = "SWARM_PORT"
SWARM_ENV_NETWORK_ID = "SWARM_NETWORK_ID"
SWARM_ENV_SWAP_ENABLE = "SWARM_SWAP_ENABLE"
SWARM_ENV_SWAP_API = "SWARM_SWAP_API"
SWARM_ENV_SYNC_ENABLE = "SWARM_SYNC_ENABLE"
SWARM_ENV_ENS_API = "SWARM_ENS_API"
SWARM_ENV_ENS_ADDR = "SWARM_ENS_ADDR"
SWARM_ENV_CORS = "SWARM_CORS"
SWARM_ENV_BOOTNODES = "SWARM_BOOTNODES"
GETH_ENV_DATADIR = "GETH_DATADIR"
)
// These settings ensure that TOML keys use the same names as Go struct fields.
var tomlSettings = toml.Config{
NormFieldName: func(rt reflect.Type, key string) string {
return key
},
FieldToKey: func(rt reflect.Type, field string) string {
return field
},
MissingField: func(rt reflect.Type, field string) error {
link := ""
if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" {
link = fmt.Sprintf(", check github.com/EthereumCommonwealth/go-callisto/swarm/api/config.go for available fields")
}
return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
},
}
//before booting the swarm node, build the configuration
func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
//check for deprecated flags
checkDeprecated(ctx)
//start by creating a default config
config = bzzapi.NewDefaultConfig()
//first load settings from config file (if provided)
config, err = configFileOverride(config, ctx)
//override settings provided by environment variables
config = envVarsOverride(config)
//override settings provided by command line
config = cmdLineOverride(config, ctx)
return
}
//finally, after the configuration build phase is finished, initialize
func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context) {
//at this point, all vars should be set in the Config
//get the account for the provided swarm account
prvkey := getAccount(config.BzzAccount, ctx, stack)
//set the resolved config path (geth --datadir)
config.Path = stack.InstanceDir()
//finally, initialize the configuration
config.Init(prvkey)
//configuration phase completed here
log.Debug("Starting Swarm with the following parameters:")
//after having created the config, print it to screen
log.Debug(printConfig(config))
}
//override the current config with whatever is in the config file, if a config file has been provided
func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config, error) {
var err error
//only do something if the -config flag has been set
if ctx.GlobalIsSet(SwarmTomlConfigPathFlag.Name) {
var filepath string
if filepath = ctx.GlobalString(SwarmTomlConfigPathFlag.Name); filepath == "" {
utils.Fatalf("Config file flag provided with invalid file path")
}
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
defer f.Close()
//decode the TOML file into a Config struct
//note that we are decoding into the existing defaultConfig;
//if an entry is not present in the file, the default entry is kept
err = tomlSettings.NewDecoder(f).Decode(&config)
// Add file name to errors that have a line number.
if _, ok := err.(*toml.LineError); ok {
err = errors.New(filepath + ", " + err.Error())
}
}
return config, err
}
//override 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
func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
currentConfig.BzzAccount = keyid
}
if chbookaddr := ctx.GlobalString(ChequebookAddrFlag.Name); chbookaddr != "" {
currentConfig.Contract = common.HexToAddress(chbookaddr)
}
if networkid := ctx.GlobalString(SwarmNetworkIdFlag.Name); networkid != "" {
if id, _ := strconv.Atoi(networkid); id != 0 {
currentConfig.NetworkId = uint64(id)
}
}
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
if datadir := ctx.GlobalString(utils.DataDirFlag.Name); datadir != "" {
currentConfig.Path = datadir
}
}
bzzport := ctx.GlobalString(SwarmPortFlag.Name)
if len(bzzport) > 0 {
currentConfig.Port = bzzport
}
if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
currentConfig.ListenAddr = bzzaddr
}
if ctx.GlobalIsSet(SwarmSwapEnabledFlag.Name) {
currentConfig.SwapEnabled = true
}
if ctx.GlobalIsSet(SwarmSyncEnabledFlag.Name) {
currentConfig.SyncEnabled = true
}
currentConfig.SwapApi = ctx.GlobalString(SwarmSwapAPIFlag.Name)
if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
//EnsApi can be set to "", so can't check for empty string, as it is allowed!
if ctx.GlobalIsSet(EnsAPIFlag.Name) {
currentConfig.EnsApi = ctx.GlobalString(EnsAPIFlag.Name)
}
if ensaddr := ctx.GlobalString(EnsAddrFlag.Name); ensaddr != "" {
currentConfig.EnsRoot = common.HexToAddress(ensaddr)
}
if cors := ctx.GlobalString(CorsStringFlag.Name); cors != "" {
currentConfig.Cors = cors
}
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
currentConfig.BootNodes = ctx.GlobalString(utils.BootnodesFlag.Name)
}
return currentConfig
}
//override the current config with whatver is provided in environment variables
//most values are not allowed a zero value (empty string), if not otherwise noted
func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
currentConfig.BzzAccount = keyid
}
if chbookaddr := os.Getenv(SWARM_ENV_CHEQUEBOOK_ADDR); chbookaddr != "" {
currentConfig.Contract = common.HexToAddress(chbookaddr)
}
if networkid := os.Getenv(SWARM_ENV_NETWORK_ID); networkid != "" {
if id, _ := strconv.Atoi(networkid); id != 0 {
currentConfig.NetworkId = uint64(id)
}
}
if datadir := os.Getenv(GETH_ENV_DATADIR); datadir != "" {
currentConfig.Path = datadir
}
bzzport := os.Getenv(SWARM_ENV_PORT)
if len(bzzport) > 0 {
currentConfig.Port = bzzport
}
if bzzaddr := os.Getenv(SWARM_ENV_LISTEN_ADDR); bzzaddr != "" {
currentConfig.ListenAddr = bzzaddr
}
if swapenable := os.Getenv(SWARM_ENV_SWAP_ENABLE); swapenable != "" {
if swap, err := strconv.ParseBool(swapenable); err != nil {
currentConfig.SwapEnabled = swap
}
}
if syncenable := os.Getenv(SWARM_ENV_SYNC_ENABLE); syncenable != "" {
if sync, err := strconv.ParseBool(syncenable); err != nil {
currentConfig.SyncEnabled = sync
}
}
if swapapi := os.Getenv(SWARM_ENV_SWAP_API); swapapi != "" {
currentConfig.SwapApi = swapapi
}
if currentConfig.SwapEnabled && currentConfig.SwapApi == "" {
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
//EnsApi can be set to "", so can't check for empty string, as it is allowed
if ensapi, exists := os.LookupEnv(SWARM_ENV_ENS_API); exists {
currentConfig.EnsApi = ensapi
}
if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" {
currentConfig.EnsRoot = common.HexToAddress(ensaddr)
}
if cors := os.Getenv(SWARM_ENV_CORS); cors != "" {
currentConfig.Cors = cors
}
if bootnodes := os.Getenv(SWARM_ENV_BOOTNODES); bootnodes != "" {
currentConfig.BootNodes = bootnodes
}
return currentConfig
}
// dumpConfig is the dumpconfig command.
// writes a default config to STDOUT
func dumpConfig(ctx *cli.Context) error {
cfg, err := buildConfig(ctx)
if err != nil {
utils.Fatalf(fmt.Sprintf("Uh oh - dumpconfig triggered an error %v", err))
}
comment := ""
out, err := tomlSettings.Marshal(&cfg)
if err != nil {
return err
}
io.WriteString(os.Stdout, comment)
os.Stdout.Write(out)
return nil
}
//deprecated flags checked here
func checkDeprecated(ctx *cli.Context) {
// exit if the deprecated --ethapi flag is set
if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
}
}
//print a Config as string
func printConfig(config *bzzapi.Config) string {
out, err := tomlSettings.Marshal(&config)
if err != nil {
return (fmt.Sprintf("Something is not right with the configuration: %v", err))
}
return string(out)
}

459
cmd/swarm/config_test.go Normal file
View file

@ -0,0 +1,459 @@
// Copyright 2017 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"
"io"
"io/ioutil"
"os"
"os/exec"
"testing"
"time"
"github.com/EthereumCommonwealth/go-callisto/rpc"
"github.com/EthereumCommonwealth/go-callisto/swarm"
"github.com/EthereumCommonwealth/go-callisto/swarm/api"
"github.com/docker/docker/pkg/reexec"
)
func TestDumpConfig(t *testing.T) {
swarm := runSwarm(t, "dumpconfig")
defaultConf := api.NewDefaultConfig()
out, err := tomlSettings.Marshal(&defaultConf)
if err != nil {
t.Fatal(err)
}
swarm.Expect(string(out))
swarm.ExpectExit()
}
func TestFailsSwapEnabledNoSwapApi(t *testing.T) {
flags := []string{
fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42",
fmt.Sprintf("--%s", SwarmPortFlag.Name), "54545",
fmt.Sprintf("--%s", SwarmSwapEnabledFlag.Name),
}
swarm := runSwarm(t, flags...)
swarm.Expect("Fatal: " + SWARM_ERR_SWAP_SET_NO_API + "\n")
swarm.ExpectExit()
}
func TestFailsNoBzzAccount(t *testing.T) {
flags := []string{
fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42",
fmt.Sprintf("--%s", SwarmPortFlag.Name), "54545",
}
swarm := runSwarm(t, flags...)
swarm.Expect("Fatal: " + SWARM_ERR_NO_BZZACCOUNT + "\n")
swarm.ExpectExit()
}
func TestCmdLineOverrides(t *testing.T) {
dir, err := ioutil.TempDir("", "bzztest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
conf, account := getTestAccount(t, dir)
node := &testNode{Dir: dir}
// assign ports
httpPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
flags := []string{
fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "42",
fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort,
fmt.Sprintf("--%s", SwarmSyncEnabledFlag.Name),
fmt.Sprintf("--%s", CorsStringFlag.Name), "*",
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
fmt.Sprintf("--%s", EnsAPIFlag.Name), "",
"--datadir", dir,
"--ipcpath", conf.IPCPath,
}
node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase)
defer func() {
if t.Failed() {
node.Shutdown()
}
}()
// wait for the node to start
for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
node.Client, err = rpc.Dial(conf.IPCEndpoint())
if err == nil {
break
}
}
if node.Client == nil {
t.Fatal(err)
}
// load info
var info swarm.Info
if err := node.Client.Call(&info, "bzz_info"); err != nil {
t.Fatal(err)
}
if info.Port != httpPort {
t.Fatalf("Expected port to be %s, got %s", httpPort, info.Port)
}
if info.NetworkId != 42 {
t.Fatalf("Expected network ID to be %d, got %d", 42, info.NetworkId)
}
if !info.SyncEnabled {
t.Fatal("Expected Sync to be enabled, but is false")
}
if info.Cors != "*" {
t.Fatalf("Expected Cors flag to be set to %s, got %s", "*", info.Cors)
}
node.Shutdown()
}
func TestFileOverrides(t *testing.T) {
// assign ports
httpPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
//create a config file
//first, create a default conf
defaultConf := api.NewDefaultConfig()
//change some values in order to test if they have been loaded
defaultConf.SyncEnabled = true
defaultConf.NetworkId = 54
defaultConf.Port = httpPort
defaultConf.StoreParams.DbCapacity = 9000000
defaultConf.ChunkerParams.Branches = 64
defaultConf.HiveParams.CallInterval = 6000000000
defaultConf.Swap.Params.Strategy.AutoCashInterval = 600 * time.Second
defaultConf.SyncParams.KeyBufferSize = 512
//create a TOML string
out, err := tomlSettings.Marshal(&defaultConf)
if err != nil {
t.Fatalf("Error creating TOML file in TestFileOverride: %v", err)
}
//create file
f, err := ioutil.TempFile("", "testconfig.toml")
if err != nil {
t.Fatalf("Error writing TOML file in TestFileOverride: %v", err)
}
//write file
_, err = f.WriteString(string(out))
if err != nil {
t.Fatalf("Error writing TOML file in TestFileOverride: %v", err)
}
f.Sync()
dir, err := ioutil.TempDir("", "bzztest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
conf, account := getTestAccount(t, dir)
node := &testNode{Dir: dir}
flags := []string{
fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(),
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
"--ens-api", "",
"--ipcpath", conf.IPCPath,
"--datadir", dir,
}
node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase)
defer func() {
if t.Failed() {
node.Shutdown()
}
}()
// wait for the node to start
for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
node.Client, err = rpc.Dial(conf.IPCEndpoint())
if err == nil {
break
}
}
if node.Client == nil {
t.Fatal(err)
}
// load info
var info swarm.Info
if err := node.Client.Call(&info, "bzz_info"); err != nil {
t.Fatal(err)
}
if info.Port != httpPort {
t.Fatalf("Expected port to be %s, got %s", httpPort, info.Port)
}
if info.NetworkId != 54 {
t.Fatalf("Expected network ID to be %d, got %d", 54, info.NetworkId)
}
if !info.SyncEnabled {
t.Fatal("Expected Sync to be enabled, but is false")
}
if info.StoreParams.DbCapacity != 9000000 {
t.Fatalf("Expected network ID to be %d, got %d", 54, info.NetworkId)
}
if info.ChunkerParams.Branches != 64 {
t.Fatalf("Expected chunker params branches to be %d, got %d", 64, info.ChunkerParams.Branches)
}
if info.HiveParams.CallInterval != 6000000000 {
t.Fatalf("Expected HiveParams CallInterval to be %d, got %d", uint64(6000000000), uint64(info.HiveParams.CallInterval))
}
if info.Swap.Params.Strategy.AutoCashInterval != 600*time.Second {
t.Fatalf("Expected SwapParams AutoCashInterval to be %ds, got %d", 600, info.Swap.Params.Strategy.AutoCashInterval)
}
if info.SyncParams.KeyBufferSize != 512 {
t.Fatalf("Expected info.SyncParams.KeyBufferSize to be %d, got %d", 512, info.SyncParams.KeyBufferSize)
}
node.Shutdown()
}
func TestEnvVars(t *testing.T) {
// assign ports
httpPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
envVars := os.Environ()
envVars = append(envVars, fmt.Sprintf("%s=%s", SwarmPortFlag.EnvVar, httpPort))
envVars = append(envVars, fmt.Sprintf("%s=%s", SwarmNetworkIdFlag.EnvVar, "999"))
envVars = append(envVars, fmt.Sprintf("%s=%s", CorsStringFlag.EnvVar, "*"))
envVars = append(envVars, fmt.Sprintf("%s=%s", SwarmSyncEnabledFlag.EnvVar, "true"))
dir, err := ioutil.TempDir("", "bzztest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
conf, account := getTestAccount(t, dir)
node := &testNode{Dir: dir}
flags := []string{
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
"--ens-api", "",
"--datadir", dir,
"--ipcpath", conf.IPCPath,
}
//node.Cmd = runSwarm(t,flags...)
//node.Cmd.cmd.Env = envVars
//the above assignment does not work, so we need a custom Cmd here in order to pass envVars:
cmd := &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{"swarm-test"}, flags...),
Stderr: os.Stderr,
Stdout: os.Stdout,
}
cmd.Env = envVars
//stdout, err := cmd.StdoutPipe()
//if err != nil {
// t.Fatal(err)
//}
//stdout = bufio.NewReader(stdout)
var stdin io.WriteCloser
if stdin, err = cmd.StdinPipe(); err != nil {
t.Fatal(err)
}
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
//cmd.InputLine(testPassphrase)
io.WriteString(stdin, testPassphrase+"\n")
defer func() {
if t.Failed() {
node.Shutdown()
cmd.Process.Kill()
}
}()
// wait for the node to start
for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
node.Client, err = rpc.Dial(conf.IPCEndpoint())
if err == nil {
break
}
}
if node.Client == nil {
t.Fatal(err)
}
// load info
var info swarm.Info
if err := node.Client.Call(&info, "bzz_info"); err != nil {
t.Fatal(err)
}
if info.Port != httpPort {
t.Fatalf("Expected port to be %s, got %s", httpPort, info.Port)
}
if info.NetworkId != 999 {
t.Fatalf("Expected network ID to be %d, got %d", 999, info.NetworkId)
}
if info.Cors != "*" {
t.Fatalf("Expected Cors flag to be set to %s, got %s", "*", info.Cors)
}
if !info.SyncEnabled {
t.Fatal("Expected Sync to be enabled, but is false")
}
node.Shutdown()
cmd.Process.Kill()
}
func TestCmdLineOverridesFile(t *testing.T) {
// assign ports
httpPort, err := assignTCPPort()
if err != nil {
t.Fatal(err)
}
//create a config file
//first, create a default conf
defaultConf := api.NewDefaultConfig()
//change some values in order to test if they have been loaded
defaultConf.SyncEnabled = false
defaultConf.NetworkId = 54
defaultConf.Port = "8588"
defaultConf.StoreParams.DbCapacity = 9000000
defaultConf.ChunkerParams.Branches = 64
defaultConf.HiveParams.CallInterval = 6000000000
defaultConf.Swap.Params.Strategy.AutoCashInterval = 600 * time.Second
defaultConf.SyncParams.KeyBufferSize = 512
//create a TOML file
out, err := tomlSettings.Marshal(&defaultConf)
if err != nil {
t.Fatalf("Error creating TOML file in TestFileOverride: %v", err)
}
//write file
f, err := ioutil.TempFile("", "testconfig.toml")
if err != nil {
t.Fatalf("Error writing TOML file in TestFileOverride: %v", err)
}
//write file
_, err = f.WriteString(string(out))
if err != nil {
t.Fatalf("Error writing TOML file in TestFileOverride: %v", err)
}
f.Sync()
dir, err := ioutil.TempDir("", "bzztest")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
conf, account := getTestAccount(t, dir)
node := &testNode{Dir: dir}
expectNetworkId := uint64(77)
flags := []string{
fmt.Sprintf("--%s", SwarmNetworkIdFlag.Name), "77",
fmt.Sprintf("--%s", SwarmPortFlag.Name), httpPort,
fmt.Sprintf("--%s", SwarmSyncEnabledFlag.Name),
fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(),
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
"--ens-api", "",
"--datadir", dir,
"--ipcpath", conf.IPCPath,
}
node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase)
defer func() {
if t.Failed() {
node.Shutdown()
}
}()
// wait for the node to start
for start := time.Now(); time.Since(start) < 10*time.Second; time.Sleep(50 * time.Millisecond) {
node.Client, err = rpc.Dial(conf.IPCEndpoint())
if err == nil {
break
}
}
if node.Client == nil {
t.Fatal(err)
}
// load info
var info swarm.Info
if err := node.Client.Call(&info, "bzz_info"); err != nil {
t.Fatal(err)
}
if info.Port != httpPort {
t.Fatalf("Expected port to be %s, got %s", httpPort, info.Port)
}
if info.NetworkId != expectNetworkId {
t.Fatalf("Expected network ID to be %d, got %d", expectNetworkId, info.NetworkId)
}
if !info.SyncEnabled {
t.Fatal("Expected Sync to be enabled, but is false")
}
if info.StoreParams.DbCapacity != 9000000 {
t.Fatalf("Expected network ID to be %d, got %d", 54, info.NetworkId)
}
if info.ChunkerParams.Branches != 64 {
t.Fatalf("Expected chunker params branches to be %d, got %d", 64, info.ChunkerParams.Branches)
}
if info.HiveParams.CallInterval != 6000000000 {
t.Fatalf("Expected HiveParams CallInterval to be %d, got %d", uint64(6000000000), uint64(info.HiveParams.CallInterval))
}
if info.Swap.Params.Strategy.AutoCashInterval != 600*time.Second {
t.Fatalf("Expected SwapParams AutoCashInterval to be %ds, got %d", 600, info.Swap.Params.Strategy.AutoCashInterval)
}
if info.SyncParams.KeyBufferSize != 512 {
t.Fatalf("Expected info.SyncParams.KeyBufferSize to be %d, got %d", 512, info.SyncParams.KeyBufferSize)
}
node.Shutdown()
}

View file

@ -48,6 +48,7 @@ import (
"github.com/EthereumCommonwealth/go-callisto/rpc" "github.com/EthereumCommonwealth/go-callisto/rpc"
"github.com/EthereumCommonwealth/go-callisto/swarm" "github.com/EthereumCommonwealth/go-callisto/swarm"
bzzapi "github.com/EthereumCommonwealth/go-callisto/swarm/api" bzzapi "github.com/EthereumCommonwealth/go-callisto/swarm/api"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -66,49 +67,58 @@ var (
var ( var (
ChequebookAddrFlag = cli.StringFlag{ ChequebookAddrFlag = cli.StringFlag{
Name: "chequebook", Name: "chequebook",
Usage: "chequebook contract address", Usage: "chequebook contract address",
EnvVar: SWARM_ENV_CHEQUEBOOK_ADDR,
} }
SwarmAccountFlag = cli.StringFlag{ SwarmAccountFlag = cli.StringFlag{
Name: "bzzaccount", Name: "bzzaccount",
Usage: "Swarm account key file", Usage: "Swarm account key file",
EnvVar: SWARM_ENV_ACCOUNT,
} }
SwarmListenAddrFlag = cli.StringFlag{ SwarmListenAddrFlag = cli.StringFlag{
Name: "httpaddr", Name: "httpaddr",
Usage: "Swarm HTTP API listening interface", Usage: "Swarm HTTP API listening interface",
EnvVar: SWARM_ENV_LISTEN_ADDR,
} }
SwarmPortFlag = cli.StringFlag{ SwarmPortFlag = cli.StringFlag{
Name: "bzzport", Name: "bzzport",
Usage: "Swarm local http api port", Usage: "Swarm local http api port",
EnvVar: SWARM_ENV_PORT,
} }
SwarmNetworkIdFlag = cli.IntFlag{ SwarmNetworkIdFlag = cli.IntFlag{
Name: "bzznetworkid", Name: "bzznetworkid",
Usage: "Network identifier (integer, default 3=swarm testnet)", Usage: "Network identifier (integer, default 3=swarm testnet)",
EnvVar: SWARM_ENV_NETWORK_ID,
} }
SwarmConfigPathFlag = cli.StringFlag{ SwarmConfigPathFlag = cli.StringFlag{
Name: "bzzconfig", Name: "bzzconfig",
Usage: "Swarm config file path (datadir/bzz)", Usage: "DEPRECATED: please use --config path/to/TOML-file",
} }
SwarmSwapEnabledFlag = cli.BoolFlag{ SwarmSwapEnabledFlag = cli.BoolFlag{
Name: "swap", Name: "swap",
Usage: "Swarm SWAP enabled (default false)", Usage: "Swarm SWAP enabled (default false)",
EnvVar: SWARM_ENV_SWAP_ENABLE,
} }
SwarmSwapAPIFlag = cli.StringFlag{ SwarmSwapAPIFlag = cli.StringFlag{
Name: "swap-api", Name: "swap-api",
Usage: "URL of the Ethereum API provider to use to settle SWAP payments", Usage: "URL of the Ethereum API provider to use to settle SWAP payments",
EnvVar: SWARM_ENV_SWAP_API,
} }
SwarmSyncEnabledFlag = cli.BoolTFlag{ SwarmSyncEnabledFlag = cli.BoolTFlag{
Name: "sync", Name: "sync",
Usage: "Swarm Syncing enabled (default true)", Usage: "Swarm Syncing enabled (default true)",
EnvVar: SWARM_ENV_SYNC_ENABLE,
} }
EnsAPIFlag = cli.StringFlag{ EnsAPIFlag = cli.StringFlag{
Name: "ens-api", Name: "ens-api",
Usage: "URL of the Ethereum API provider to use for ENS record lookups", Usage: "URL of the Ethereum API provider to use for ENS record lookups",
Value: node.DefaultIPCEndpoint("geth"), EnvVar: SWARM_ENV_ENS_API,
} }
EnsAddrFlag = cli.StringFlag{ EnsAddrFlag = cli.StringFlag{
Name: "ens-addr", Name: "ens-addr",
Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)", Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
EnvVar: SWARM_ENV_ENS_ADDR,
} }
SwarmApiFlag = cli.StringFlag{ SwarmApiFlag = cli.StringFlag{
Name: "bzzapi", Name: "bzzapi",
@ -136,8 +146,9 @@ var (
Usage: "force mime type", Usage: "force mime type",
} }
CorsStringFlag = cli.StringFlag{ CorsStringFlag = cli.StringFlag{
Name: "corsdomain", Name: "corsdomain",
Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')", Usage: "Domain on which to send Access-Control-Allow-Origin header (multiple domains can be supplied separated by a ',')",
EnvVar: SWARM_ENV_CORS,
} }
// the following flags are deprecated and should be removed in the future // the following flags are deprecated and should be removed in the future
@ -147,6 +158,12 @@ var (
} }
) )
//declare a few constant error messages, useful for later error check comparisons in test
var (
SWARM_ERR_NO_BZZACCOUNT = "bzzaccount option is required but not set; check your config file, command line or environment variables"
SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set"
)
var defaultNodeConfig = node.DefaultConfig 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.
@ -302,6 +319,8 @@ Remove corrupt entries from a local chunk database.
DEPRECATED: use 'swarm db clean'. DEPRECATED: use 'swarm db clean'.
`, `,
}, },
// See config.go
DumpConfigCommand,
} }
sort.Sort(cli.CommandsByName(app.Commands)) sort.Sort(cli.CommandsByName(app.Commands))
@ -325,6 +344,7 @@ DEPRECATED: use 'swarm db clean'.
CorsStringFlag, CorsStringFlag,
EnsAPIFlag, EnsAPIFlag,
EnsAddrFlag, EnsAddrFlag,
SwarmTomlConfigPathFlag,
SwarmConfigPathFlag, SwarmConfigPathFlag,
SwarmSwapEnabledFlag, SwarmSwapEnabledFlag,
SwarmSwapAPIFlag, SwarmSwapAPIFlag,
@ -377,19 +397,32 @@ func version(ctx *cli.Context) error {
} }
func bzzd(ctx *cli.Context) error { func bzzd(ctx *cli.Context) error {
// exit if the deprecated --ethapi flag is set //build a valid bzzapi.Config from all available sources:
if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" { //default config, file config, command line and env vars
utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.") bzzconfig, err := buildConfig(ctx)
if err != nil {
utils.Fatalf("unable to configure swarm: %v", err)
} }
cfg := defaultNodeConfig cfg := defaultNodeConfig
//geth only supports --datadir via command line
//in order to be consistent within swarm, if we pass --datadir via environment variable
//or via config file, we get the same directory for geth and swarm
if _, err := os.Stat(bzzconfig.Path); err == nil {
cfg.DataDir = bzzconfig.Path
}
//setup the ethereum node
utils.SetNodeConfig(ctx, &cfg) utils.SetNodeConfig(ctx, &cfg)
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)
} }
//a few steps need to be done after the config phase is completed,
registerBzzService(ctx, stack) //due to overriding behavior
initSwarmNode(bzzconfig, stack, ctx)
//register BZZ as node.Service in the ethereum node
registerBzzService(bzzconfig, ctx, stack)
//start the node
utils.StartNode(stack) utils.StartNode(stack)
go func() { go func() {
@ -401,13 +434,12 @@ func bzzd(ctx *cli.Context) error {
stack.Stop() stack.Stop()
}() }()
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
// Add bootnodes as initial peers. // Add bootnodes as initial peers.
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { if bzzconfig.BootNodes != "" {
bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",") bootnodes := strings.Split(bzzconfig.BootNodes, ",")
injectBootnodes(stack.Server(), bootnodes) injectBootnodes(stack.Server(), bootnodes)
} else { } else {
if networkId == 3 { if bzzconfig.NetworkId == 3 {
injectBootnodes(stack.Server(), testbetBootNodes) injectBootnodes(stack.Server(), testbetBootNodes)
} }
} }
@ -448,61 +480,31 @@ func detectEnsAddr(client *rpc.Client) (common.Address, error) {
} }
} }
func registerBzzService(ctx *cli.Context, stack *node.Node) { func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.Node) {
prvkey := getAccount(ctx, stack)
chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
if bzzdir == "" {
bzzdir = stack.InstanceDir()
}
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
if err != nil {
utils.Fatalf("unable to configure swarm: %v", err)
}
bzzport := ctx.GlobalString(SwarmPortFlag.Name)
if len(bzzport) > 0 {
bzzconfig.Port = bzzport
}
if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" {
bzzconfig.ListenAddr = bzzaddr
}
swapEnabled := ctx.GlobalBool(SwarmSwapEnabledFlag.Name)
syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabledFlag.Name)
swapapi := ctx.GlobalString(SwarmSwapAPIFlag.Name)
if swapEnabled && swapapi == "" {
utils.Fatalf("SWAP is enabled but --swap-api is not set")
}
ensapi := ctx.GlobalString(EnsAPIFlag.Name)
ensAddr := ctx.GlobalString(EnsAddrFlag.Name)
cors := ctx.GlobalString(CorsStringFlag.Name)
//define the swarm service boot function
boot := func(ctx *node.ServiceContext) (node.Service, error) { boot := func(ctx *node.ServiceContext) (node.Service, error) {
var swapClient *ethclient.Client var swapClient *ethclient.Client
if swapapi != "" { var err error
log.Info("connecting to SWAP API", "url", swapapi) if bzzconfig.SwapApi != "" {
swapClient, err = ethclient.Dial(swapapi) log.Info("connecting to SWAP API", "url", bzzconfig.SwapApi)
swapClient, err = ethclient.Dial(bzzconfig.SwapApi)
if err != nil { if err != nil {
return nil, fmt.Errorf("error connecting to SWAP API %s: %s", swapapi, err) return nil, fmt.Errorf("error connecting to SWAP API %s: %s", bzzconfig.SwapApi, err)
} }
} }
var ensClient *ethclient.Client var ensClient *ethclient.Client
if ensapi != "" { if bzzconfig.EnsApi != "" {
log.Info("connecting to ENS API", "url", ensapi) log.Info("connecting to ENS API", "url", bzzconfig.EnsApi)
client, err := rpc.Dial(ensapi) client, err := rpc.Dial(bzzconfig.EnsApi)
if err != nil { if err != nil {
return nil, fmt.Errorf("error connecting to ENS API %s: %s", ensapi, err) return nil, fmt.Errorf("error connecting to ENS API %s: %s", bzzconfig.EnsApi, err)
} }
ensClient = ethclient.NewClient(client) ensClient = ethclient.NewClient(client)
if ensAddr != "" { //no ENS root address set yet
bzzconfig.EnsRoot = common.HexToAddress(ensAddr) if bzzconfig.EnsRoot == (common.Address{}) {
} else {
ensAddr, err := detectEnsAddr(client) ensAddr, err := detectEnsAddr(client)
if err == nil { if err == nil {
bzzconfig.EnsRoot = ensAddr bzzconfig.EnsRoot = ensAddr
@ -512,21 +514,21 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
} }
} }
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, swapEnabled, syncEnabled, cors) return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors)
} }
//register within the ethereum node
if err := stack.Register(boot); err != nil { if err := stack.Register(boot); err != nil {
utils.Fatalf("Failed to register the Swarm service: %v", err) utils.Fatalf("Failed to register the Swarm service: %v", err)
} }
} }
func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey { func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
keyid := ctx.GlobalString(SwarmAccountFlag.Name) //an account is mandatory
if bzzaccount == "" {
if keyid == "" { utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT)
utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
} }
// Try to load the arg as a hex key file. // Try to load the arg as a hex key file.
if key, err := crypto.LoadECDSA(keyid); err == nil { if key, err := crypto.LoadECDSA(bzzaccount); err == nil {
log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey)) log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
return key return key
} }
@ -534,7 +536,7 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
am := stack.AccountManager() am := stack.AccountManager()
ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore) ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
return decryptStoreAccount(ks, keyid, utils.MakePasswordList(ctx)) return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
} }
func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey { func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
@ -552,7 +554,7 @@ func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []stri
utils.Fatalf("Can't find swarm account key %s", account) utils.Fatalf("Can't find swarm account key %s", account)
} }
if err != nil { if err != nil {
utils.Fatalf("Can't find swarm account key: %v", err) utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account)
} }
keyjson, err := ioutil.ReadFile(a.URL.Path) keyjson, err := ioutil.ReadFile(a.URL.Path)
if err != nil { if err != nil {

View file

@ -26,13 +26,14 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/docker/pkg/reexec" "github.com/EthereumCommonwealth/go-callisto/accounts"
"github.com/EthereumCommonwealth/go-callisto/accounts/keystore" "github.com/EthereumCommonwealth/go-callisto/accounts/keystore"
"github.com/EthereumCommonwealth/go-callisto/internal/cmdtest" "github.com/EthereumCommonwealth/go-callisto/internal/cmdtest"
"github.com/EthereumCommonwealth/go-callisto/node" "github.com/EthereumCommonwealth/go-callisto/node"
"github.com/EthereumCommonwealth/go-callisto/p2p" "github.com/EthereumCommonwealth/go-callisto/p2p"
"github.com/EthereumCommonwealth/go-callisto/rpc" "github.com/EthereumCommonwealth/go-callisto/rpc"
"github.com/EthereumCommonwealth/go-callisto/swarm" "github.com/EthereumCommonwealth/go-callisto/swarm"
"github.com/docker/docker/pkg/reexec"
) )
func init() { func init() {
@ -156,9 +157,9 @@ type testNode struct {
const testPassphrase = "swarm-test-passphrase" const testPassphrase = "swarm-test-passphrase"
func newTestNode(t *testing.T, dir string) *testNode { func getTestAccount(t *testing.T, dir string) (conf *node.Config, account accounts.Account) {
// create key // create key
conf := &node.Config{ conf = &node.Config{
DataDir: dir, DataDir: dir,
IPCPath: "bzzd.ipc", IPCPath: "bzzd.ipc",
NoUSB: true, NoUSB: true,
@ -167,18 +168,24 @@ func newTestNode(t *testing.T, dir string) *testNode {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
account, err := n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase) account, err = n.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore).NewAccount(testPassphrase)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
node := &testNode{Dir: dir}
// use a unique IPCPath when running tests on Windows // use a unique IPCPath when running tests on Windows
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String()) conf.IPCPath = fmt.Sprintf("bzzd-%s.ipc", account.Address.String())
} }
return conf, account
}
func newTestNode(t *testing.T, dir string) *testNode {
conf, account := getTestAccount(t, dir)
node := &testNode{Dir: dir}
// assign ports // assign ports
httpPort, err := assignTCPPort() httpPort, err := assignTCPPort()
if err != nil { if err != nil {

View file

@ -746,6 +746,12 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
if err != nil || index < 0 { if err != nil || index < 0 {
return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account) return accounts.Account{}, fmt.Errorf("invalid account address or index %q", account)
} }
log.Warn("-------------------------------------------------------------------")
log.Warn("Referring to accounts by order in the keystore folder is dangerous!")
log.Warn("This functionality is deprecated and will be removed in the future!")
log.Warn("Please use explicit addresses! (can search via `geth account list`)")
log.Warn("-------------------------------------------------------------------")
accs := ks.Accounts() accs := ks.Accounts()
if len(accs) <= index { if len(accs) <= index {
return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs)) return accounts.Account{}, fmt.Errorf("index %d higher than number of accounts %d", index, len(accs))
@ -762,15 +768,6 @@ func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
Fatalf("Option %q: %v", EtherbaseFlag.Name, err) Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
} }
cfg.Etherbase = account.Address cfg.Etherbase = account.Address
return
}
accounts := ks.Accounts()
if (cfg.Etherbase == common.Address{}) {
if len(accounts) > 0 {
cfg.Etherbase = accounts[0].Address
} else {
log.Warn("No etherbase set and no accounts found as default")
}
} }
} }

View file

@ -630,7 +630,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch
} }
} }
// Sweet, the protocol permits us to sign the block, wait for our time // Sweet, the protocol permits us to sign the block, wait for our time
delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
if header.Difficulty.Cmp(diffNoTurn) == 0 { if header.Difficulty.Cmp(diffNoTurn) == 0 {
// It's not our turn explicitly to sign, delay it a bit // It's not our turn explicitly to sign, delay it a bit
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime

View file

@ -41,6 +41,7 @@ var (
FrontierBlockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block FrontierBlockReward *big.Int = big.NewInt(5e+18) // Block reward in wei for successfully mining a block
ByzantiumBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium ByzantiumBlockReward *big.Int = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium
maxUncles = 2 // Maximum number of uncles allowed in a single block maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTime = 15 * time.Second
) )
// Various error messages to mark blocks invalid. These should be private to // Various error messages to mark blocks invalid. These should be private to
@ -233,7 +234,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
return errLargeBlockTime return errLargeBlockTime
} }
} else { } else {
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 { if header.Time.Cmp(big.NewInt(time.Now().Add(allowedFutureBlockTime).Unix())) > 0 {
return consensus.ErrFutureBlock return consensus.ErrFutureBlock
} }
} }
@ -305,14 +306,14 @@ func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Heade
// Some weird constants to avoid constant memory allocs for them. // Some weird constants to avoid constant memory allocs for them.
var ( var (
expDiffPeriod = big.NewInt(100000) expDiffPeriod = big.NewInt(100000)
expDiffPeriodCallisto = big.NewInt(300000) expDiffPeriodCallisto = big.NewInt(300000)
big1 = big.NewInt(1) big1 = big.NewInt(1)
big2 = big.NewInt(2) big2 = big.NewInt(2)
big9 = big.NewInt(9) big9 = big.NewInt(9)
big10 = big.NewInt(10) big10 = big.NewInt(10)
bigMinus99 = big.NewInt(-99) bigMinus99 = big.NewInt(-99)
big2999999 = big.NewInt(2999999) big2999999 = big.NewInt(2999999)
) )
// calcDifficultyCallisto is the difficulty adjustment algorithm. It returns // calcDifficultyCallisto is the difficulty adjustment algorithm. It returns

View file

@ -164,7 +164,7 @@ func TestWelcome(t *testing.T) {
tester.console.Welcome() tester.console.Welcome()
output := string(tester.output.Bytes()) output := tester.output.String()
if want := "Welcome"; !strings.Contains(output, want) { if want := "Welcome"; !strings.Contains(output, want) {
t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want) t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want)
} }
@ -188,7 +188,7 @@ func TestEvaluate(t *testing.T) {
defer tester.Close(t) defer tester.Close(t)
tester.console.Evaluate("2 + 2") tester.console.Evaluate("2 + 2")
if output := string(tester.output.Bytes()); !strings.Contains(output, "4") { if output := tester.output.String(); !strings.Contains(output, "4") {
t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
} }
} }
@ -218,7 +218,7 @@ func TestInteractive(t *testing.T) {
case <-time.After(time.Second): case <-time.After(time.Second):
t.Fatalf("secondary prompt timeout") t.Fatalf("secondary prompt timeout")
} }
if output := string(tester.output.Bytes()); !strings.Contains(output, "4") { if output := tester.output.String(); !strings.Contains(output, "4") {
t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") t.Fatalf("statement evaluation failed: have %s, want %s", output, "4")
} }
} }
@ -230,7 +230,7 @@ func TestPreload(t *testing.T) {
defer tester.Close(t) defer tester.Close(t)
tester.console.Evaluate("preloaded") tester.console.Evaluate("preloaded")
if output := string(tester.output.Bytes()); !strings.Contains(output, "some-preloaded-string") { if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") {
t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string") t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string")
} }
} }
@ -243,7 +243,7 @@ func TestExecute(t *testing.T) {
tester.console.Execute("exec.js") tester.console.Execute("exec.js")
tester.console.Evaluate("execed") tester.console.Evaluate("execed")
if output := string(tester.output.Bytes()); !strings.Contains(output, "some-executed-string") { if output := tester.output.String(); !strings.Contains(output, "some-executed-string") {
t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string") t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string")
} }
} }
@ -275,7 +275,7 @@ func TestPrettyPrint(t *testing.T) {
string: ` + two + ` string: ` + two + `
} }
` `
if output := string(tester.output.Bytes()); output != want { if output := tester.output.String(); output != want {
t.Fatalf("pretty print mismatch: have %s, want %s", output, want) t.Fatalf("pretty print mismatch: have %s, want %s", output, want)
} }
} }
@ -287,7 +287,7 @@ func TestPrettyError(t *testing.T) {
tester.console.Evaluate("throw 'hello'") tester.console.Evaluate("throw 'hello'")
want := jsre.ErrorColor("hello") + "\n" want := jsre.ErrorColor("hello") + "\n"
if output := string(tester.output.Bytes()); output != want { if output := tester.output.String(); output != want {
t.Fatalf("pretty error mismatch: have %s, want %s", output, want) t.Fatalf("pretty error mismatch: have %s, want %s", output, want)
} }
} }

View file

@ -114,10 +114,7 @@ func PrintDisassembled(code string) error {
fmt.Printf("%06v: %v\n", it.PC(), it.Op()) fmt.Printf("%06v: %v\n", it.PC(), it.Op())
} }
} }
if err := it.Error(); err != nil { return it.Error()
return err
}
return nil
} }
// Return all disassembled EVM instructions in human-readable format. // Return all disassembled EVM instructions in human-readable format.

View file

@ -237,10 +237,7 @@ func (c *Compiler) pushBin(v interface{}) {
// isPush returns whether the string op is either any of // isPush returns whether the string op is either any of
// push(N). // push(N).
func isPush(op string) bool { func isPush(op string) bool {
if op == "push" { return op == "push"
return true
}
return false
} }
// isJump returns whether the string op is jump(i) // isJump returns whether the string op is jump(i)

View file

@ -213,6 +213,8 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
return g.Config return g.Config
case ghash == params.MainnetGenesisHash: case ghash == params.MainnetGenesisHash:
return params.MainnetChainConfig return params.MainnetChainConfig
case ghash == params.CallistoMainnetGenesisHash:
return params.CallistoMainnetChainConfig
case ghash == params.TestnetGenesisHash: case ghash == params.TestnetGenesisHash:
return params.TestnetChainConfig return params.TestnetChainConfig
default: default:

File diff suppressed because one or more lines are too long

View file

@ -72,8 +72,8 @@ func TestSetupGenesis(t *testing.T) {
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, nil) return SetupGenesisBlock(db, nil)
}, },
wantHash: params.MainnetGenesisHash, wantHash: params.CallistoMainnetGenesisHash,
wantConfig: params.MainnetChainConfig, wantConfig: params.CallistoMainnetChainConfig,
}, },
{ {
name: "mainnet block in DB, genesis == nil", name: "mainnet block in DB, genesis == nil",

View file

@ -79,7 +79,7 @@ func ToECDSA(d []byte) (*ecdsa.PrivateKey, error) {
return toECDSA(d, true) return toECDSA(d, true)
} }
// ToECDSAUnsafe blidly converts a binary blob to a private key. It should almost // ToECDSAUnsafe blindly converts a binary blob to a private key. It should almost
// never be used unless you are sure the input is valid and want to avoid hitting // never be used unless you are sure the input is valid and want to avoid hitting
// errors due to bad origin encoding (0 prefixes cut off). // errors due to bad origin encoding (0 prefixes cut off).
func ToECDSAUnsafe(d []byte) *ecdsa.PrivateKey { func ToECDSAUnsafe(d []byte) *ecdsa.PrivateKey {
@ -98,6 +98,9 @@ func toECDSA(d []byte, strict bool) (*ecdsa.PrivateKey, error) {
} }
priv.D = new(big.Int).SetBytes(d) priv.D = new(big.Int).SetBytes(d)
priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d) priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
if priv.PublicKey.X == nil {
return nil, errors.New("invalid private key")
}
return priv, nil return priv, nil
} }

View file

@ -40,6 +40,15 @@ func TestKeccak256Hash(t *testing.T) {
checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp) checkhash(t, "Sha3-256-array", func(in []byte) []byte { h := Keccak256Hash(in); return h[:] }, msg, exp)
} }
func TestToECDSAErrors(t *testing.T) {
if _, err := HexToECDSA("0000000000000000000000000000000000000000000000000000000000000000"); err == nil {
t.Fatal("HexToECDSA should've returned error")
}
if _, err := HexToECDSA("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); err == nil {
t.Fatal("HexToECDSA should've returned error")
}
}
func BenchmarkSha3(b *testing.B) { func BenchmarkSha3(b *testing.B) {
a := []byte("hello world") a := []byte("hello world")
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View file

@ -452,7 +452,12 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, logConfig *vm.LogConf
} }
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root()) statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1).Root())
if err != nil { if err != nil {
return false, structLogger.StructLogs(), err switch err.(type) {
case *trie.MissingNodeError:
return false, structLogger.StructLogs(), fmt.Errorf("required historical state unavailable")
default:
return false, structLogger.StructLogs(), err
}
} }
receipts, _, usedGas, err := processor.Process(block, statedb, config) receipts, _, usedGas, err := processor.Process(block, statedb, config)
@ -518,7 +523,12 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
} }
msg, context, statedb, err := api.computeTxEnv(blockHash, int(txIndex)) msg, context, statedb, err := api.computeTxEnv(blockHash, int(txIndex))
if err != nil { if err != nil {
return nil, err switch err.(type) {
case *trie.MissingNodeError:
return nil, fmt.Errorf("required historical state unavailable")
default:
return nil, err
}
} }
// Run the transaction with tracing enabled. // Run the transaction with tracing enabled.

View file

@ -310,10 +310,17 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) {
} }
if wallets := s.AccountManager().Wallets(); len(wallets) > 0 { if wallets := s.AccountManager().Wallets(); len(wallets) > 0 {
if accounts := wallets[0].Accounts(); len(accounts) > 0 { if accounts := wallets[0].Accounts(); len(accounts) > 0 {
return accounts[0].Address, nil etherbase := accounts[0].Address
s.lock.Lock()
s.etherbase = etherbase
s.lock.Unlock()
log.Info("Etherbase automatically configured", "address", etherbase)
return etherbase, nil
} }
} }
return common.Address{}, fmt.Errorf("etherbase address must be explicitly specified") return common.Address{}, fmt.Errorf("etherbase must be explicitly specified")
} }
// set in js console via admin interface or wrapper from cli flags // set in js console via admin interface or wrapper from cli flags

View file

@ -193,7 +193,6 @@ func (s *Service) loop() {
} }
} }
close(quitCh) close(quitCh)
return
}() }()
// Loop reporting until termination // Loop reporting until termination
for { for {

View file

@ -1003,9 +1003,12 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context,
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) {
tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash) tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash)
if tx == nil { if tx == nil {
return nil, nil return nil, errors.New("unknown transaction")
} }
receipt, _, _, _ := core.GetReceipt(s.b.ChainDb(), hash) // Old receipts don't have the lookup data available receipt, _, _, _ := core.GetReceipt(s.b.ChainDb(), hash) // Old receipts don't have the lookup data available
if receipt == nil {
return nil, errors.New("unknown receipt")
}
var signer types.Signer = types.FrontierSigner{} var signer types.Signer = types.FrontierSigner{}
if tx.Protected() { if tx.Protected() {
@ -1071,7 +1074,7 @@ type SendTxArgs struct {
Nonce *hexutil.Uint64 `json:"nonce"` Nonce *hexutil.Uint64 `json:"nonce"`
} }
// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields. // setDefaults is a helper function that fills in default values for unspecified tx fields.
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
if args.Gas == nil { if args.Gas == nil {
args.Gas = (*hexutil.Big)(big.NewInt(defaultGas)) args.Gas = (*hexutil.Big)(big.NewInt(defaultGas))

View file

@ -62,6 +62,16 @@ func (bi *BigInt) SetInt64(x int64) {
bi.bigint.SetInt64(x) bi.bigint.SetInt64(x)
} }
// Sign returns:
//
// -1 if x < 0
// 0 if x == 0
// +1 if x > 0
//
func (bi *BigInt) Sign() int {
return bi.bigint.Sign()
}
// SetString sets the big int to x. // SetString sets the big int to x.
// //
// The string prefix determines the actual conversion base. A prefix of "0x" or // The string prefix determines the actual conversion base. A prefix of "0x" or

View file

@ -684,7 +684,7 @@ func (net *Network) refresh(done chan<- struct{}) {
seeds = net.nursery seeds = net.nursery
} }
if len(seeds) == 0 { if len(seeds) == 0 {
log.Trace(fmt.Sprint("no seed nodes found")) log.Trace("no seed nodes found")
close(done) close(done)
return return
} }

View file

@ -54,10 +54,10 @@ func checkClockDrift() {
howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings") howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings")
separator := strings.Repeat("-", len(warning)) separator := strings.Repeat("-", len(warning))
log.Warn(fmt.Sprint(separator)) log.Warn(separator)
log.Warn(fmt.Sprint(warning)) log.Warn(warning)
log.Warn(fmt.Sprint(howtofix)) log.Warn(howtofix)
log.Warn(fmt.Sprint(separator)) log.Warn(separator)
} else { } else {
log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift)) log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift))
} }

View file

@ -398,12 +398,12 @@ func (s *ticketStore) nextRegisterableTicket() (t *ticketRef, wait time.Duration
//s.removeExcessTickets(topic) //s.removeExcessTickets(topic)
if len(tickets.buckets) != 0 { if len(tickets.buckets) != 0 {
empty = false empty = false
if list := tickets.buckets[bucket]; list != nil {
for _, ref := range list { list := tickets.buckets[bucket]
//debugLog(fmt.Sprintf(" nrt bucket = %d node = %x sn = %v wait = %v", bucket, ref.t.node.ID[:8], ref.t.serial, time.Duration(ref.topicRegTime()-now))) for _, ref := range list {
if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() { //debugLog(fmt.Sprintf(" nrt bucket = %d node = %x sn = %v wait = %v", bucket, ref.t.node.ID[:8], ref.t.serial, time.Duration(ref.topicRegTime()-now)))
nextTicket = ref if nextTicket.t == nil || ref.topicRegTime() < nextTicket.topicRegTime() {
} nextTicket = ref
} }
} }
} }

View file

@ -0,0 +1,35 @@
// Copyright 2017 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 adapters
type SimStateStore struct {
m map[string][]byte
}
func (self *SimStateStore) Load(s string) ([]byte, error) {
return self.m[s], nil
}
func (self *SimStateStore) Save(s string, data []byte) error {
self.m[s] = data
return nil
}
func NewSimStateStore() *SimStateStore {
return &SimStateStore{
make(map[string][]byte),
}
}

View file

@ -27,6 +27,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"sync"
"github.com/EthereumCommonwealth/go-callisto/event" "github.com/EthereumCommonwealth/go-callisto/event"
"github.com/EthereumCommonwealth/go-callisto/p2p" "github.com/EthereumCommonwealth/go-callisto/p2p"
@ -263,8 +264,10 @@ func (c *Client) Send(method, path string, in, out interface{}) error {
// Server is an HTTP server providing an API to manage a simulation network // Server is an HTTP server providing an API to manage a simulation network
type Server struct { type Server struct {
router *httprouter.Router router *httprouter.Router
network *Network network *Network
mockerStop chan struct{} // when set, stops the current mocker
mockerMtx sync.Mutex // synchronises access to the mockerStop field
} }
// NewServer returns a new simulation API server // NewServer returns a new simulation API server
@ -278,6 +281,10 @@ func NewServer(network *Network) *Server {
s.GET("/", s.GetNetwork) s.GET("/", s.GetNetwork)
s.POST("/start", s.StartNetwork) s.POST("/start", s.StartNetwork)
s.POST("/stop", s.StopNetwork) s.POST("/stop", s.StopNetwork)
s.POST("/mocker/start", s.StartMocker)
s.POST("/mocker/stop", s.StopMocker)
s.GET("/mocker", s.GetMockers)
s.POST("/reset", s.ResetNetwork)
s.GET("/events", s.StreamNetworkEvents) s.GET("/events", s.StreamNetworkEvents)
s.GET("/snapshot", s.CreateSnapshot) s.GET("/snapshot", s.CreateSnapshot)
s.POST("/snapshot", s.LoadSnapshot) s.POST("/snapshot", s.LoadSnapshot)
@ -318,6 +325,59 @@ func (s *Server) StopNetwork(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
// StartMocker starts the mocker node simulation
func (s *Server) StartMocker(w http.ResponseWriter, req *http.Request) {
s.mockerMtx.Lock()
defer s.mockerMtx.Unlock()
if s.mockerStop != nil {
http.Error(w, "mocker already running", http.StatusInternalServerError)
return
}
mockerType := req.FormValue("mocker-type")
mockerFn := LookupMocker(mockerType)
if mockerFn == nil {
http.Error(w, fmt.Sprintf("unknown mocker type %q", mockerType), http.StatusBadRequest)
return
}
nodeCount, err := strconv.Atoi(req.FormValue("node-count"))
if err != nil {
http.Error(w, "invalid node-count provided", http.StatusBadRequest)
return
}
s.mockerStop = make(chan struct{})
go mockerFn(s.network, s.mockerStop, nodeCount)
w.WriteHeader(http.StatusOK)
}
// StopMocker stops the mocker node simulation
func (s *Server) StopMocker(w http.ResponseWriter, req *http.Request) {
s.mockerMtx.Lock()
defer s.mockerMtx.Unlock()
if s.mockerStop == nil {
http.Error(w, "stop channel not initialized", http.StatusInternalServerError)
return
}
close(s.mockerStop)
s.mockerStop = nil
w.WriteHeader(http.StatusOK)
}
// GetMockerList returns a list of available mockers
func (s *Server) GetMockers(w http.ResponseWriter, req *http.Request) {
list := GetMockerList()
s.JSON(w, http.StatusOK, list)
}
// ResetNetwork resets all properties of a network to its initial (empty) state
func (s *Server) ResetNetwork(w http.ResponseWriter, req *http.Request) {
s.network.Reset()
w.WriteHeader(http.StatusOK)
}
// StreamNetworkEvents streams network events as a server-sent-events stream // StreamNetworkEvents streams network events as a server-sent-events stream
func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) { func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
events := make(chan *Event) events := make(chan *Event)

192
p2p/simulations/mocker.go Normal file
View file

@ -0,0 +1,192 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package simulations simulates p2p networks.
// A mocker simulates starting and stopping real nodes in a network.
package simulations
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/EthereumCommonwealth/go-callisto/log"
"github.com/EthereumCommonwealth/go-callisto/p2p/discover"
)
//a map of mocker names to its function
var mockerList = map[string]func(net *Network, quit chan struct{}, nodeCount int){
"startStop": startStop,
"probabilistic": probabilistic,
"boot": boot,
}
//Lookup a mocker by its name, returns the mockerFn
func LookupMocker(mockerType string) func(net *Network, quit chan struct{}, nodeCount int) {
return mockerList[mockerType]
}
//Get a list of mockers (keys of the map)
//Useful for frontend to build available mocker selection
func GetMockerList() []string {
list := make([]string, 0, len(mockerList))
for k := range mockerList {
list = append(list, k)
}
return list
}
//The boot mockerFn only connects the node in a ring and doesn't do anything else
func boot(net *Network, quit chan struct{}, nodeCount int) {
_, err := connectNodesInRing(net, nodeCount)
if err != nil {
panic("Could not startup node network for mocker")
}
}
//The startStop mockerFn stops and starts nodes in a defined period (ticker)
func startStop(net *Network, quit chan struct{}, nodeCount int) {
nodes, err := connectNodesInRing(net, nodeCount)
if err != nil {
panic("Could not startup node network for mocker")
}
tick := time.NewTicker(10 * time.Second)
defer tick.Stop()
for {
select {
case <-quit:
log.Info("Terminating simulation loop")
return
case <-tick.C:
id := nodes[rand.Intn(len(nodes))]
log.Info("stopping node", "id", id)
if err := net.Stop(id); err != nil {
log.Error("error stopping node", "id", id, "err", err)
return
}
select {
case <-quit:
log.Info("Terminating simulation loop")
return
case <-time.After(3 * time.Second):
}
log.Debug("starting node", "id", id)
if err := net.Start(id); err != nil {
log.Error("error starting node", "id", id, "err", err)
return
}
}
}
}
//The probabilistic mocker func has a more probabilistic pattern
//(the implementation could probably be improved):
//nodes are connected in a ring, then a varying number of random nodes is selected,
//mocker then stops and starts them in random intervals, and continues the loop
func probabilistic(net *Network, quit chan struct{}, nodeCount int) {
nodes, err := connectNodesInRing(net, nodeCount)
if err != nil {
panic("Could not startup node network for mocker")
}
for {
select {
case <-quit:
log.Info("Terminating simulation loop")
return
default:
}
var lowid, highid int
var wg sync.WaitGroup
randWait := time.Duration(rand.Intn(5000)+1000) * time.Millisecond
rand1 := rand.Intn(nodeCount - 1)
rand2 := rand.Intn(nodeCount - 1)
if rand1 < rand2 {
lowid = rand1
highid = rand2
} else if rand1 > rand2 {
highid = rand1
lowid = rand2
} else {
if rand1 == 0 {
rand2 = 9
} else if rand1 == 9 {
rand1 = 0
}
lowid = rand1
highid = rand2
}
var steps = highid - lowid
wg.Add(steps)
for i := lowid; i < highid; i++ {
select {
case <-quit:
log.Info("Terminating simulation loop")
return
case <-time.After(randWait):
}
log.Debug(fmt.Sprintf("node %v shutting down", nodes[i]))
err := net.Stop(nodes[i])
if err != nil {
log.Error(fmt.Sprintf("Error stopping node %s", nodes[i]))
wg.Done()
continue
}
go func(id discover.NodeID) {
time.Sleep(randWait)
err := net.Start(id)
if err != nil {
log.Error(fmt.Sprintf("Error starting node %s", id))
}
wg.Done()
}(nodes[i])
}
wg.Wait()
}
}
//connect nodeCount number of nodes in a ring
func connectNodesInRing(net *Network, nodeCount int) ([]discover.NodeID, error) {
ids := make([]discover.NodeID, nodeCount)
for i := 0; i < nodeCount; i++ {
node, err := net.NewNode()
if err != nil {
log.Error("Error creating a node! %s", err)
return nil, err
}
ids[i] = node.ID()
}
for _, id := range ids {
if err := net.Start(id); err != nil {
log.Error("Error starting a node! %s", err)
return nil, err
}
log.Debug(fmt.Sprintf("node %v starting up", id))
}
for i, id := range ids {
peerID := ids[(i+1)%len(ids)]
if err := net.Connect(id, peerID); err != nil {
log.Error("Error connecting a node to a peer! %s", err)
return nil, err
}
}
return ids, nil
}

View file

@ -0,0 +1,171 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package simulations simulates p2p networks.
// A mokcer simulates starting and stopping real nodes in a network.
package simulations
import (
"encoding/json"
"net/http"
"net/url"
"strconv"
"sync"
"testing"
"time"
"github.com/EthereumCommonwealth/go-callisto/p2p/discover"
)
func TestMocker(t *testing.T) {
//start the simulation HTTP server
_, s := testHTTPServer(t)
defer s.Close()
//create a client
client := NewClient(s.URL)
//start the network
err := client.StartNetwork()
if err != nil {
t.Fatalf("Could not start test network: %s", err)
}
//stop the network to terminate
defer func() {
err = client.StopNetwork()
if err != nil {
t.Fatalf("Could not stop test network: %s", err)
}
}()
//get the list of available mocker types
resp, err := http.Get(s.URL + "/mocker")
if err != nil {
t.Fatalf("Could not get mocker list: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatalf("Invalid Status Code received, expected 200, got %d", resp.StatusCode)
}
//check the list is at least 1 in size
var mockerlist []string
err = json.NewDecoder(resp.Body).Decode(&mockerlist)
if err != nil {
t.Fatalf("Error decoding JSON mockerlist: %s", err)
}
if len(mockerlist) < 1 {
t.Fatalf("No mockers available")
}
nodeCount := 10
var wg sync.WaitGroup
events := make(chan *Event, 10)
var opts SubscribeOpts
sub, err := client.SubscribeNetwork(events, opts)
defer sub.Unsubscribe()
//wait until all nodes are started and connected
//store every node up event in a map (value is irrelevant, mimic Set datatype)
nodemap := make(map[discover.NodeID]bool)
wg.Add(1)
nodesComplete := false
connCount := 0
go func() {
for {
select {
case event := <-events:
//if the event is a node Up event only
if event.Node != nil && event.Node.Up {
//add the correspondent node ID to the map
nodemap[event.Node.Config.ID] = true
//this means all nodes got a nodeUp event, so we can continue the test
if len(nodemap) == nodeCount {
nodesComplete = true
//wait for 3s as the mocker will need time to connect the nodes
//time.Sleep( 3 *time.Second)
}
} else if event.Conn != nil && nodesComplete {
connCount += 1
if connCount == (nodeCount-1)*2 {
wg.Done()
return
}
}
case <-time.After(30 * time.Second):
wg.Done()
t.Fatalf("Timeout waiting for nodes being started up!")
}
}
}()
//take the last element of the mockerlist as the default mocker-type to ensure one is enabled
mockertype := mockerlist[len(mockerlist)-1]
//still, use hardcoded "probabilistic" one if available ;)
for _, m := range mockerlist {
if m == "probabilistic" {
mockertype = m
break
}
}
//start the mocker with nodeCount number of nodes
resp, err = http.PostForm(s.URL+"/mocker/start", url.Values{"mocker-type": {mockertype}, "node-count": {strconv.Itoa(nodeCount)}})
if err != nil {
t.Fatalf("Could not start mocker: %s", err)
}
if resp.StatusCode != 200 {
t.Fatalf("Invalid Status Code received for starting mocker, expected 200, got %d", resp.StatusCode)
}
wg.Wait()
//check there are nodeCount number of nodes in the network
nodes_info, err := client.GetNodes()
if err != nil {
t.Fatalf("Could not get nodes list: %s", err)
}
if len(nodes_info) != nodeCount {
t.Fatalf("Expected %d number of nodes, got: %d", nodeCount, len(nodes_info))
}
//stop the mocker
resp, err = http.Post(s.URL+"/mocker/stop", "", nil)
if err != nil {
t.Fatalf("Could not stop mocker: %s", err)
}
if resp.StatusCode != 200 {
t.Fatalf("Invalid Status Code received for stopping mocker, expected 200, got %d", resp.StatusCode)
}
//reset the network
_, err = http.Post(s.URL+"/reset", "", nil)
if err != nil {
t.Fatalf("Could not reset network: %s", err)
}
//now the number of nodes in the network should be zero
nodes_info, err = client.GetNodes()
if err != nil {
t.Fatalf("Could not get nodes list: %s", err)
}
if len(nodes_info) != 0 {
t.Fatalf("Expected empty list of nodes, got: %d", len(nodes_info))
}
}

View file

@ -403,9 +403,8 @@ func (self *Network) getNodeByName(name string) *Node {
func (self *Network) GetNodes() (nodes []*Node) { func (self *Network) GetNodes() (nodes []*Node) {
self.lock.Lock() self.lock.Lock()
defer self.lock.Unlock() defer self.lock.Unlock()
for _, node := range self.Nodes {
nodes = append(nodes, node) nodes = append(nodes, self.Nodes...)
}
return nodes return nodes
} }
@ -477,7 +476,7 @@ func (self *Network) InitConn(oneID, otherID discover.NodeID) (*Conn, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if time.Now().Sub(conn.initiated) < dialBanTimeout { if time.Since(conn.initiated) < dialBanTimeout {
return nil, fmt.Errorf("connection between %v and %v recently attempted", oneID, otherID) return nil, fmt.Errorf("connection between %v and %v recently attempted", oneID, otherID)
} }
if conn.Up { if conn.Up {
@ -502,6 +501,20 @@ func (self *Network) Shutdown() {
close(self.quitc) close(self.quitc)
} }
//Reset resets all network properties:
//emtpies the nodes and the connection list
func (self *Network) Reset() {
self.lock.Lock()
defer self.lock.Unlock()
//re-initialize the maps
self.connMap = make(map[string]int)
self.nodeMap = make(map[discover.NodeID]int)
self.Nodes = nil
self.Conns = nil
}
// Node is a wrapper around adapters.Node which is used to track the status // Node is a wrapper around adapters.Node which is used to track the status
// of a node in the network // of a node in the network
type Node struct { type Node struct {
@ -665,6 +678,12 @@ func (self *Network) Load(snap *Snapshot) error {
} }
} }
for _, conn := range snap.Conns { for _, conn := range snap.Conns {
if !self.GetNode(conn.One).Up || !self.GetNode(conn.Other).Up {
//in this case, at least one of the nodes of a connection is not up,
//so it would result in the snapshot `Load` to fail
continue
}
if err := self.Connect(conn.One, conn.Other); err != nil { if err := self.Connect(conn.One, conn.Other); err != nil {
return err return err
} }

View file

@ -24,6 +24,7 @@ import (
) )
var ( var (
CallistoMainnetGenesisHash = common.HexToHash("0x73ab688c08ff2062b61c301cd7abb13adf5cff3cf80e6238e129f03140183f01") // Callisto Mainnet genesis hash to enforce below configs on
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on
TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on
) )

View file

@ -98,7 +98,7 @@ func readKind(buf []byte) (k Kind, tagsize, contentsize uint64, err error) {
tagsize = 1 tagsize = 1
contentsize = uint64(b - 0x80) contentsize = uint64(b - 0x80)
// Reject strings that should've been single bytes. // Reject strings that should've been single bytes.
if contentsize == 1 && buf[1] < 128 { if contentsize == 1 && len(buf) > 1 && buf[1] < 128 {
return 0, 0, 0, ErrCanonSize return 0, 0, 0, ErrCanonSize
} }
case b < 0xC0: case b < 0xC0:

View file

@ -96,6 +96,7 @@ func TestSplit(t *testing.T) {
{input: "F90055", err: ErrCanonSize, rest: "F90055"}, {input: "F90055", err: ErrCanonSize, rest: "F90055"},
{input: "FA0002FFFF", err: ErrCanonSize, rest: "FA0002FFFF"}, {input: "FA0002FFFF", err: ErrCanonSize, rest: "FA0002FFFF"},
{input: "81", err: ErrValueTooLarge, rest: "81"},
{input: "8501010101", err: ErrValueTooLarge, rest: "8501010101"}, {input: "8501010101", err: ErrValueTooLarge, rest: "8501010101"},
{input: "C60607080902", err: ErrValueTooLarge, rest: "C60607080902"}, {input: "C60607080902", err: ErrValueTooLarge, rest: "C60607080902"},

View file

@ -67,7 +67,7 @@ func (hc *httpConn) Close() error {
// DialHTTP creates a new RPC clients that connection to an RPC server over HTTP. // DialHTTP creates a new RPC clients that connection to an RPC server over HTTP.
func DialHTTP(endpoint string) (*Client, error) { func DialHTTP(endpoint string) (*Client, error) {
req, err := http.NewRequest("POST", endpoint, nil) req, err := http.NewRequest(http.MethodPost, endpoint, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,7 +149,7 @@ func NewHTTPServer(cors []string, srv *Server) *http.Server {
// ServeHTTP serves JSON-RPC requests over HTTP. // ServeHTTP serves JSON-RPC requests over HTTP.
func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Permit dumb empty requests for remote health-checks (AWS) // Permit dumb empty requests for remote health-checks (AWS)
if r.Method == "GET" && r.ContentLength == 0 && r.URL.RawQuery == "" { if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" {
return return
} }
if code, err := validateRequest(r); err != nil { if code, err := validateRequest(r); err != nil {
@ -169,7 +169,7 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// validateRequest returns a non-zero response code and error message if the // validateRequest returns a non-zero response code and error message if the
// request is invalid. // request is invalid.
func validateRequest(r *http.Request) (int, error) { func validateRequest(r *http.Request) (int, error) {
if r.Method == "PUT" || r.Method == "DELETE" { if r.Method == http.MethodPut || r.Method == http.MethodDelete {
return http.StatusMethodNotAllowed, errors.New("method not allowed") return http.StatusMethodNotAllowed, errors.New("method not allowed")
} }
if r.ContentLength > maxHTTPRequestContentLength { if r.ContentLength > maxHTTPRequestContentLength {
@ -192,7 +192,7 @@ func newCorsHandler(srv *Server, allowedOrigins []string) http.Handler {
c := cors.New(cors.Options{ c := cors.New(cors.Options{
AllowedOrigins: allowedOrigins, AllowedOrigins: allowedOrigins,
AllowedMethods: []string{"POST", "GET"}, AllowedMethods: []string{http.MethodPost, http.MethodGet},
MaxAge: 600, MaxAge: 600,
AllowedHeaders: []string{"*"}, AllowedHeaders: []string{"*"},
}) })

View file

@ -24,25 +24,25 @@ import (
) )
func TestHTTPErrorResponseWithDelete(t *testing.T) { func TestHTTPErrorResponseWithDelete(t *testing.T) {
testHTTPErrorResponse(t, "DELETE", contentType, "", http.StatusMethodNotAllowed) testHTTPErrorResponse(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed)
} }
func TestHTTPErrorResponseWithPut(t *testing.T) { func TestHTTPErrorResponseWithPut(t *testing.T) {
testHTTPErrorResponse(t, "PUT", contentType, "", http.StatusMethodNotAllowed) testHTTPErrorResponse(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed)
} }
func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) {
body := make([]rune, maxHTTPRequestContentLength+1, maxHTTPRequestContentLength+1) body := make([]rune, maxHTTPRequestContentLength+1)
testHTTPErrorResponse(t, testHTTPErrorResponse(t,
"POST", contentType, string(body), http.StatusRequestEntityTooLarge) http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge)
} }
func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) { func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) {
testHTTPErrorResponse(t, "POST", "", "", http.StatusUnsupportedMediaType) testHTTPErrorResponse(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType)
} }
func TestHTTPErrorResponseWithValidRequest(t *testing.T) { func TestHTTPErrorResponseWithValidRequest(t *testing.T) {
testHTTPErrorResponse(t, "POST", contentType, "", 0) testHTTPErrorResponse(t, http.MethodPost, contentType, "", 0)
} }
func testHTTPErrorResponse(t *testing.T, method, contentType, body string, expected int) { func testHTTPErrorResponse(t *testing.T, method, contentType, body string, expected int) {

View file

@ -18,15 +18,15 @@ package api
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/contracts/ens" "github.com/EthereumCommonwealth/go-callisto/contracts/ens"
"github.com/EthereumCommonwealth/go-callisto/crypto" "github.com/EthereumCommonwealth/go-callisto/crypto"
"github.com/EthereumCommonwealth/go-callisto/log"
"github.com/EthereumCommonwealth/go-callisto/node"
"github.com/EthereumCommonwealth/go-callisto/swarm/network" "github.com/EthereumCommonwealth/go-callisto/swarm/network"
"github.com/EthereumCommonwealth/go-callisto/swarm/services/swap" "github.com/EthereumCommonwealth/go-callisto/swarm/services/swap"
"github.com/EthereumCommonwealth/go-callisto/swarm/storage" "github.com/EthereumCommonwealth/go-callisto/swarm/storage"
@ -46,101 +46,68 @@ type Config struct {
*network.HiveParams *network.HiveParams
Swap *swap.SwapParams Swap *swap.SwapParams
*network.SyncParams *network.SyncParams
Path string Contract common.Address
ListenAddr string EnsRoot common.Address
Port string EnsApi string
PublicKey string Path string
BzzKey string ListenAddr string
EnsRoot common.Address Port string
NetworkId uint64 PublicKey string
BzzKey string
NetworkId uint64
SwapEnabled bool
SyncEnabled bool
SwapApi string
Cors string
BzzAccount string
BootNodes string
} }
// config is agnostic to where private key is coming from //create a default config with all parameters to set to defaults
// so managing accounts is outside swarm and left to wrappers func NewDefaultConfig() (self *Config) {
func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, networkId uint64) (self *Config, err error) {
address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address
dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes()))
err = os.MkdirAll(dirpath, os.ModePerm)
if err != nil {
return
}
confpath := filepath.Join(dirpath, "config.json")
var data []byte
pubkey := crypto.FromECDSAPub(&prvKey.PublicKey)
pubkeyhex := common.ToHex(pubkey)
keyhex := crypto.Keccak256Hash(pubkey).Hex()
self = &Config{ self = &Config{
SyncParams: network.NewSyncParams(dirpath), StoreParams: storage.NewDefaultStoreParams(),
HiveParams: network.NewHiveParams(dirpath),
ChunkerParams: storage.NewChunkerParams(), ChunkerParams: storage.NewChunkerParams(),
StoreParams: storage.NewStoreParams(dirpath), HiveParams: network.NewDefaultHiveParams(),
SyncParams: network.NewDefaultSyncParams(),
Swap: swap.NewDefaultSwapParams(),
ListenAddr: DefaultHTTPListenAddr, ListenAddr: DefaultHTTPListenAddr,
Port: DefaultHTTPPort, Port: DefaultHTTPPort,
Path: dirpath, Path: node.DefaultDataDir(),
Swap: swap.DefaultSwapParams(contract, prvKey), EnsApi: node.DefaultIPCEndpoint("geth"),
PublicKey: pubkeyhex,
BzzKey: keyhex,
EnsRoot: ens.TestNetAddress, EnsRoot: ens.TestNetAddress,
NetworkId: networkId, NetworkId: network.NetworkId,
} SwapEnabled: false,
data, err = ioutil.ReadFile(confpath) SyncEnabled: true,
SwapApi: "",
// if not set in function param, then set default for swarm network, will be overwritten by config file if present BootNodes: "",
if networkId == 0 {
self.NetworkId = network.NetworkId
}
if err != nil {
if !os.IsNotExist(err) {
return
}
// file does not exist
// write out config file
err = self.Save()
if err != nil {
err = fmt.Errorf("error writing config: %v", err)
}
return
}
// file exists, deserialise
err = json.Unmarshal(data, self)
if err != nil {
return nil, fmt.Errorf("unable to parse config: %v", err)
}
// check public key
if pubkeyhex != self.PublicKey {
return nil, fmt.Errorf("public key does not match the one in the config file %v != %v", pubkeyhex, self.PublicKey)
}
if keyhex != self.BzzKey {
return nil, fmt.Errorf("bzz key does not match the one in the config file %v != %v", keyhex, self.BzzKey)
}
// if set in function param, replace id set from config file
if networkId != 0 {
self.NetworkId = networkId
}
self.Swap.SetKey(prvKey)
if (self.EnsRoot == common.Address{}) {
self.EnsRoot = ens.TestNetAddress
} }
return return
} }
func (self *Config) Save() error { //some config params need to be initialized after the complete
data, err := json.MarshalIndent(self, "", " ") //config building phase is completed (e.g. due to overriding flags)
func (self *Config) Init(prvKey *ecdsa.PrivateKey) {
address := crypto.PubkeyToAddress(prvKey.PublicKey)
self.Path = filepath.Join(self.Path, "bzz-"+common.Bytes2Hex(address.Bytes()))
err := os.MkdirAll(self.Path, os.ModePerm)
if err != nil { if err != nil {
return err log.Error(fmt.Sprintf("Error creating root swarm data directory: %v", err))
return
} }
err = os.MkdirAll(self.Path, os.ModePerm)
if err != nil { pubkey := crypto.FromECDSAPub(&prvKey.PublicKey)
return err pubkeyhex := common.ToHex(pubkey)
} keyhex := crypto.Keccak256Hash(pubkey).Hex()
confpath := filepath.Join(self.Path, "config.json")
return ioutil.WriteFile(confpath, data, os.ModePerm) self.PublicKey = pubkeyhex
self.BzzKey = keyhex
self.Swap.Init(self.Contract, prvKey)
self.SyncParams.Init(self.Path)
self.HiveParams.Init(self.Path)
self.StoreParams.Init(self.Path)
} }

View file

@ -17,109 +17,53 @@
package api package api
import ( import (
"io/ioutil" "reflect"
"os"
"path/filepath"
"strings"
"testing" "testing"
"github.com/EthereumCommonwealth/go-callisto/common" "github.com/EthereumCommonwealth/go-callisto/common"
"github.com/EthereumCommonwealth/go-callisto/crypto" "github.com/EthereumCommonwealth/go-callisto/crypto"
) )
var ( func TestConfig(t *testing.T) {
hexprvkey = "65138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c"
defaultConfig = `{
"ChunkDbPath": "` + filepath.Join("TMPDIR", "chunks") + `",
"DbCapacity": 5000000,
"CacheCapacity": 5000,
"Radius": 0,
"Branches": 128,
"Hash": "SHA3",
"CallInterval": 3000000000,
"KadDbPath": "` + filepath.Join("TMPDIR", "bzz-peers.json") + `",
"MaxProx": 8,
"ProxBinSize": 2,
"BucketSize": 4,
"PurgeInterval": 151200000000000,
"InitialRetryInterval": 42000000,
"MaxIdleInterval": 42000000000,
"ConnRetryExp": 2,
"Swap": {
"BuyAt": 20000000000,
"SellAt": 20000000000,
"PayAt": 100,
"DropAt": 10000,
"AutoCashInterval": 300000000000,
"AutoCashThreshold": 50000000000000,
"AutoDepositInterval": 300000000000,
"AutoDepositThreshold": 50000000000000,
"AutoDepositBuffer": 100000000000000,
"PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3",
"Contract": "0x0000000000000000000000000000000000000000",
"Beneficiary": "0x0d2f62485607cf38d9d795d93682a517661e513e"
},
"RequestDbPath": "` + filepath.Join("TMPDIR", "requests") + `",
"RequestDbBatchSize": 512,
"KeyBufferSize": 1024,
"SyncBatchSize": 128,
"SyncBufferSize": 128,
"SyncCacheSize": 1024,
"SyncPriorities": [
2,
1,
1,
0,
0
],
"SyncModes": [
true,
true,
true,
true,
false
],
"Path": "TMPDIR",
"ListenAddr": "127.0.0.1",
"Port": "8500",
"PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3",
"BzzKey": "0xe861964402c0b78e2d44098329b8545726f215afa737d803714a4338552fcb81",
"EnsRoot": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
"NetworkId": 323
}`
)
func TestConfigWriteRead(t *testing.T) { var hexprvkey = "65138b2aa745041b372153550584587da326ab440576b2a1191dd95cee30039c"
tmp, err := ioutil.TempDir(os.TempDir(), "bzz-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmp)
prvkey, err := crypto.HexToECDSA(hexprvkey) prvkey, err := crypto.HexToECDSA(hexprvkey)
if err != nil { if err != nil {
t.Fatalf("failed to load private key: %v", err) t.Fatalf("failed to load private key: %v", err)
} }
orig, err := NewConfig(tmp, common.Address{}, prvkey, 323)
if err != nil { one := NewDefaultConfig()
t.Fatalf("expected no error, got %v", err) two := NewDefaultConfig()
}
data, err := ioutil.ReadFile(filepath.Join(orig.Path, "config.json")) if equal := reflect.DeepEqual(one, two); !equal {
if err != nil { t.Fatal("Two default configs are not equal")
t.Fatalf("default config file cannot be read: %v", err)
}
exp := strings.Replace(defaultConfig, "TMPDIR", orig.Path, -1)
exp = strings.Replace(exp, "\\", "\\\\", -1)
if string(data) != exp {
t.Fatalf("default config mismatch:\nexpected: %v\ngot: %v", exp, string(data))
} }
conf, err := NewConfig(tmp, common.Address{}, prvkey, 323) one.Init(prvkey)
if err != nil {
t.Fatalf("expected no error, got %v", err) //the init function should set the following fields
if one.BzzKey == "" {
t.Fatal("Expected BzzKey to be set")
} }
if conf.Swap.Beneficiary.Hex() != orig.Swap.Beneficiary.Hex() { if one.PublicKey == "" {
t.Fatalf("expected beneficiary from loaded config %v to match original %v", conf.Swap.Beneficiary.Hex(), orig.Swap.Beneficiary.Hex()) t.Fatal("Expected PublicKey to be set")
} }
//the Init function should append subdirs to the given path
if one.Swap.PayProfile.Beneficiary == (common.Address{}) {
t.Fatal("Failed to correctly initialize SwapParams")
}
if one.SyncParams.RequestDbPath == one.Path {
t.Fatal("Failed to correctly initialize SyncParams")
}
if one.HiveParams.KadDbPath == one.Path {
t.Fatal("Failed to correctly initialize HiveParams")
}
if one.StoreParams.ChunkDbPath == one.Path {
t.Fatal("Failed to correctly initialize StoreParams")
}
} }

View file

@ -95,7 +95,7 @@ func mountDir(t *testing.T, api *api.Api, files map[string]fileInfo, bzzHash str
} }
// Test listMounts // Test listMounts
if found == false { if !found {
t.Fatalf("Error getting mounts information for %v: %v", mountDir, err) t.Fatalf("Error getting mounts information for %v: %v", mountDir, err)
} }
@ -185,10 +185,8 @@ func isDirEmpty(name string) bool {
defer f.Close() defer f.Close()
_, err = f.Readdirnames(1) _, err = f.Readdirnames(1)
if err == io.EOF {
return true return err == io.EOF
}
return false
} }
type testAPI struct { type testAPI struct {
@ -388,7 +386,7 @@ func (ta *testAPI) seekInMultiChunkFile(t *testing.T) {
d.Read(contents) d.Read(contents)
finfo := files["1.txt"] finfo := files["1.txt"]
if bytes.Compare(finfo.contents[:6024][5000:], contents) != 0 { if !bytes.Equal(finfo.contents[:6024][5000:], contents) {
t.Fatalf("File seek contents mismatch") t.Fatalf("File seek contents mismatch")
} }
d.Close() d.Close()

View file

@ -70,19 +70,25 @@ type HiveParams struct {
*kademlia.KadParams *kademlia.KadParams
} }
func NewHiveParams(path string) *HiveParams { //create default params
kad := kademlia.NewKadParams() func NewDefaultHiveParams() *HiveParams {
kad := kademlia.NewDefaultKadParams()
// kad.BucketSize = bucketSize // kad.BucketSize = bucketSize
// kad.MaxProx = maxProx // kad.MaxProx = maxProx
// kad.ProxBinSize = proxBinSize // kad.ProxBinSize = proxBinSize
return &HiveParams{ return &HiveParams{
CallInterval: callInterval, CallInterval: callInterval,
KadDbPath: filepath.Join(path, "bzz-peers.json"),
KadParams: kad, KadParams: kad,
} }
} }
//this can only finally be set after all config options (file, cmd line, env vars)
//have been evaluated
func (self *HiveParams) Init(path string) {
self.KadDbPath = filepath.Join(path, "bzz-peers.json")
}
func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive { func NewHive(addr common.Hash, params *HiveParams, swapEnabled, syncEnabled bool) *Hive {
kad := kademlia.New(kademlia.Address(addr), params.KadParams) kad := kademlia.New(kademlia.Address(addr), params.KadParams)
return &Hive{ return &Hive{

View file

@ -52,7 +52,7 @@ type KadParams struct {
ConnRetryExp int ConnRetryExp int
} }
func NewKadParams() *KadParams { func NewDefaultKadParams() *KadParams {
return &KadParams{ return &KadParams{
MaxProx: maxProx, MaxProx: maxProx,
ProxBinSize: proxBinSize, ProxBinSize: proxBinSize,

View file

@ -63,7 +63,7 @@ func TestOn(t *testing.T) {
if !ok1 || !ok2 { if !ok1 || !ok2 {
t.Errorf("oops") t.Errorf("oops")
} }
kad := New(addr, NewKadParams()) kad := New(addr, NewDefaultKadParams())
err := kad.On(&testNode{addr: other}, nil) err := kad.On(&testNode{addr: other}, nil)
_ = err _ = err
} }
@ -72,7 +72,7 @@ func TestBootstrap(t *testing.T) {
test := func(test *bootstrapTest) bool { test := func(test *bootstrapTest) bool {
// for any node kad.le, Target and N // for any node kad.le, Target and N
params := NewKadParams() params := NewDefaultKadParams()
params.MaxProx = test.MaxProx params.MaxProx = test.MaxProx
params.BucketSize = test.BucketSize params.BucketSize = test.BucketSize
params.ProxBinSize = test.BucketSize params.ProxBinSize = test.BucketSize
@ -127,7 +127,7 @@ func TestFindClosest(t *testing.T) {
test := func(test *FindClosestTest) bool { test := func(test *FindClosestTest) bool {
// for any node kad.le, Target and N // for any node kad.le, Target and N
params := NewKadParams() params := NewDefaultKadParams()
params.MaxProx = 7 params.MaxProx = 7
kad := New(test.Self, params) kad := New(test.Self, params)
var err error var err error
@ -198,7 +198,7 @@ var (
func TestProxAdjust(t *testing.T) { func TestProxAdjust(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
self := gen(Address{}, r).(Address) self := gen(Address{}, r).(Address)
params := NewKadParams() params := NewDefaultKadParams()
params.MaxProx = 7 params.MaxProx = 7
kad := New(self, params) kad := New(self, params)
@ -232,7 +232,7 @@ func TestSaveLoad(t *testing.T) {
r := rand.New(rand.NewSource(time.Now().UnixNano())) r := rand.New(rand.NewSource(time.Now().UnixNano()))
addresses := gen([]Address{}, r).([]Address) addresses := gen([]Address{}, r).([]Address)
self := RandomAddress() self := RandomAddress()
params := NewKadParams() params := NewDefaultKadParams()
params.MaxProx = 7 params.MaxProx = 7
kad := New(self, params) kad := New(self, params)

View file

@ -131,9 +131,8 @@ type SyncParams struct {
} }
// constructor with default values // constructor with default values
func NewSyncParams(bzzdir string) *SyncParams { func NewDefaultSyncParams() *SyncParams {
return &SyncParams{ return &SyncParams{
RequestDbPath: filepath.Join(bzzdir, "requests"),
RequestDbBatchSize: requestDbBatchSize, RequestDbBatchSize: requestDbBatchSize,
KeyBufferSize: keyBufferSize, KeyBufferSize: keyBufferSize,
SyncBufferSize: syncBufferSize, SyncBufferSize: syncBufferSize,
@ -144,6 +143,12 @@ func NewSyncParams(bzzdir string) *SyncParams {
} }
} }
//this can only finally be set after all config options (file, cmd line, env vars)
//have been evaluated
func (self *SyncParams) Init(path string) {
self.RequestDbPath = filepath.Join(path, "requests")
}
// syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding // syncer is the agent that manages content distribution/storage replication/chunk storeRequest forwarding
type syncer struct { type syncer struct {
*SyncParams // sync parameters *SyncParams // sync parameters

View file

@ -80,17 +80,10 @@ type PayProfile struct {
lock sync.RWMutex lock sync.RWMutex
} }
func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapParams { //create params with default values
pubkey := &prvkey.PublicKey func NewDefaultSwapParams() *SwapParams {
return &SwapParams{ return &SwapParams{
PayProfile: &PayProfile{ PayProfile: &PayProfile{},
PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)),
Contract: contract,
Beneficiary: crypto.PubkeyToAddress(*pubkey),
privateKey: prvkey,
publicKey: pubkey,
owner: crypto.PubkeyToAddress(*pubkey),
},
Params: &swap.Params{ Params: &swap.Params{
Profile: &swap.Profile{ Profile: &swap.Profile{
BuyAt: buyAt, BuyAt: buyAt,
@ -109,6 +102,21 @@ func DefaultSwapParams(contract common.Address, prvkey *ecdsa.PrivateKey) *SwapP
} }
} }
//this can only finally be set after all config options (file, cmd line, env vars)
//have been evaluated
func (self *SwapParams) Init(contract common.Address, prvkey *ecdsa.PrivateKey) {
pubkey := &prvkey.PublicKey
self.PayProfile = &PayProfile{
PublicKey: common.ToHex(crypto.FromECDSAPub(pubkey)),
Contract: contract,
Beneficiary: crypto.PubkeyToAddress(*pubkey),
privateKey: prvkey,
publicKey: pubkey,
owner: crypto.PubkeyToAddress(*pubkey),
}
}
// swap constructor, parameters // swap constructor, parameters
// * global chequebook, assume deployed service and // * global chequebook, assume deployed service and
// * the balance is at buffer. // * the balance is at buffer.

View file

@ -77,7 +77,7 @@ func (self *chunkerTester) Split(chunker Splitter, data io.Reader, size int64, c
key, err = chunker.Split(data, size, chunkC, swg, nil) key, err = chunker.Split(data, size, chunkC, swg, nil)
if err != nil && expectedError == nil { if err != nil && expectedError == nil {
err = errors.New(fmt.Sprintf("Split error: %v", err)) err = fmt.Errorf("Split error: %v", err)
} }
if chunkC != nil { if chunkC != nil {
@ -123,7 +123,7 @@ func (self *chunkerTester) Append(chunker Splitter, rootKey Key, data io.Reader,
key, err = chunker.Append(rootKey, data, chunkC, swg, nil) key, err = chunker.Append(rootKey, data, chunkC, swg, nil)
if err != nil && expectedError == nil { if err != nil && expectedError == nil {
err = errors.New(fmt.Sprintf("Append error: %v", err)) err = fmt.Errorf("Append error: %v", err)
} }
if chunkC != nil { if chunkC != nil {

View file

@ -57,15 +57,21 @@ type StoreParams struct {
Radius int Radius int
} }
func NewStoreParams(path string) (self *StoreParams) { //create params with default values
func NewDefaultStoreParams() (self *StoreParams) {
return &StoreParams{ return &StoreParams{
ChunkDbPath: filepath.Join(path, "chunks"),
DbCapacity: defaultDbCapacity, DbCapacity: defaultDbCapacity,
CacheCapacity: defaultCacheCapacity, CacheCapacity: defaultCacheCapacity,
Radius: defaultRadius, Radius: defaultRadius,
} }
} }
//this can only finally be set after all config options (file, cmd line, env vars)
//have been evaluated
func (self *StoreParams) Init(path string) {
self.ChunkDbPath = filepath.Join(path, "chunks")
}
// netstore contructor, takes path argument that is used to initialise dbStore, // netstore contructor, takes path argument that is used to initialise dbStore,
// the persistent (disk) storage component of LocalStore // the persistent (disk) storage component of LocalStore
// the second argument is the hive, the connection/logistics manager for the node // the second argument is the hive, the connection/logistics manager for the node

View file

@ -391,7 +391,7 @@ func (self *PyramidChunker) prepareChunks(isAppend bool, chunkLevel [][]*TreeEnt
parent := NewTreeEntry(self) parent := NewTreeEntry(self)
var unFinishedChunk *Chunk var unFinishedChunk *Chunk
if isAppend == true && len(chunkLevel[0]) != 0 { if isAppend && len(chunkLevel[0]) != 0 {
lastIndex := len(chunkLevel[0]) - 1 lastIndex := len(chunkLevel[0]) - 1
ent := chunkLevel[0][lastIndex] ent := chunkLevel[0][lastIndex]
@ -512,7 +512,7 @@ func (self *PyramidChunker) buildTree(isAppend bool, chunkLevel [][]*TreeEntry,
} }
} }
if compress == false && last == false { if !compress && !last {
return return
} }
@ -522,7 +522,7 @@ func (self *PyramidChunker) buildTree(isAppend bool, chunkLevel [][]*TreeEntry,
for lvl := int64(ent.level); lvl < endLvl; lvl++ { for lvl := int64(ent.level); lvl < endLvl; lvl++ {
lvlCount := int64(len(chunkLevel[lvl])) lvlCount := int64(len(chunkLevel[lvl]))
if lvlCount == 1 && last == true { if lvlCount == 1 && last {
copy(rootKey, chunkLevel[lvl][0].key) copy(rootKey, chunkLevel[lvl][0].key)
return return
} }
@ -540,7 +540,7 @@ func (self *PyramidChunker) buildTree(isAppend bool, chunkLevel [][]*TreeEntry,
nextLvlCount = int64(len(chunkLevel[lvl+1]) - 1) nextLvlCount = int64(len(chunkLevel[lvl+1]) - 1)
tempEntry = chunkLevel[lvl+1][nextLvlCount] tempEntry = chunkLevel[lvl+1][nextLvlCount]
} }
if isAppend == true && tempEntry != nil && tempEntry.updatePending == true { if isAppend && tempEntry != nil && tempEntry.updatePending {
updateEntry := &TreeEntry{ updateEntry := &TreeEntry{
level: int(lvl + 1), level: int(lvl + 1),
branchCount: 0, branchCount: 0,
@ -585,9 +585,9 @@ func (self *PyramidChunker) buildTree(isAppend bool, chunkLevel [][]*TreeEntry,
} }
if isAppend == false { if !isAppend {
chunkWG.Wait() chunkWG.Wait()
if compress == true { if compress {
chunkLevel[lvl] = nil chunkLevel[lvl] = nil
} }
} }
@ -599,7 +599,7 @@ func (self *PyramidChunker) enqueueTreeChunk(chunkLevel [][]*TreeEntry, ent *Tre
if ent != nil { if ent != nil {
// wait for data chunks to get over before processing the tree chunk // wait for data chunks to get over before processing the tree chunk
if last == true { if last {
chunkWG.Wait() chunkWG.Wait()
} }
@ -612,7 +612,7 @@ func (self *PyramidChunker) enqueueTreeChunk(chunkLevel [][]*TreeEntry, ent *Tre
} }
// Update or append based on weather it is a new entry or being reused // Update or append based on weather it is a new entry or being reused
if ent.updatePending == true { if ent.updatePending {
chunkWG.Wait() chunkWG.Wait()
chunkLevel[ent.level][ent.index] = ent chunkLevel[ent.level][ent.index] = ent
} else { } else {

View file

@ -220,7 +220,7 @@ func (self *Swarm) Start(srv *p2p.Server) error {
// stops all component services. // stops all component services.
func (self *Swarm) Stop() error { func (self *Swarm) Stop() error {
self.dpa.Stop() self.dpa.Stop()
self.hive.Stop() err := self.hive.Stop()
if ch := self.config.Swap.Chequebook(); ch != nil { if ch := self.config.Swap.Chequebook(); ch != nil {
ch.Stop() ch.Stop()
ch.Save() ch.Save()
@ -230,7 +230,7 @@ func (self *Swarm) Stop() error {
self.lstore.DbStore.Close() self.lstore.DbStore.Close()
} }
self.sfs.Stop() self.sfs.Stop()
return self.config.Save() return err
} }
// implements the node.Service interface // implements the node.Service interface
@ -301,7 +301,6 @@ func (self *Swarm) SetChequebook(ctx context.Context) error {
return err return err
} }
log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex())) log.Info(fmt.Sprintf("new chequebook set (%v): saving config file, resetting all connections in the hive", self.config.Swap.Contract.Hex()))
self.config.Save()
self.hive.DropAll() self.hive.DropAll()
return nil return nil
} }
@ -314,10 +313,9 @@ func NewLocalSwarm(datadir, port string) (self *Swarm, err error) {
return return
} }
config, err := api.NewConfig(datadir, common.Address{}, prvKey, network.NetworkId) config := api.NewDefaultConfig()
if err != nil { config.Path = datadir
return config.Init(prvKey)
}
config.Port = port config.Port = port
dpa, err := storage.NewLocalDPA(datadir) dpa, err := storage.NewLocalDPA(datadir)

View file

@ -124,6 +124,10 @@ func (msg *sentMessage) appendPadding(params *MessageParams) error {
if params.Src != nil { if params.Src != nil {
rawSize += signatureLength rawSize += signatureLength
} }
if params.KeySym != nil {
rawSize += AESNonceLength
}
odd := rawSize % padSizeLimit odd := rawSize % padSizeLimit
if len(params.Padding) != 0 { if len(params.Padding) != 0 {

View file

@ -416,3 +416,59 @@ func TestPadding(t *testing.T) {
singlePaddingTest(t, n) singlePaddingTest(t, n)
} }
} }
func TestPaddingAppendedToSymMessages(t *testing.T) {
params := &MessageParams{
Payload: make([]byte, 246),
KeySym: make([]byte, aesKeyLength),
}
// Simulate a message with a payload just under 256 so that
// payload + flag + aesnonce > 256. Check that the result
// is padded on the next 256 boundary.
msg := sentMessage{}
msg.Raw = make([]byte, len(params.Payload)+1+AESNonceLength)
err := msg.appendPadding(params)
if err != nil {
t.Fatalf("Error appending padding to message %v", err)
return
}
if len(msg.Raw) != 512 {
t.Errorf("Invalid size %d != 512", len(msg.Raw))
}
}
func TestPaddingAppendedToSymMessagesWithSignature(t *testing.T) {
params := &MessageParams{
Payload: make([]byte, 246),
KeySym: make([]byte, aesKeyLength),
}
pSrc, err := crypto.GenerateKey()
if err != nil {
t.Fatalf("Error creating the signature key %v", err)
return
}
params.Src = pSrc
// Simulate a message with a payload just under 256 so that
// payload + flag + aesnonce > 256. Check that the result
// is padded on the next 256 boundary.
msg := sentMessage{}
msg.Raw = make([]byte, len(params.Payload)+1+AESNonceLength+signatureLength)
err = msg.appendPadding(params)
if err != nil {
t.Fatalf("Error appending padding to message %v", err)
return
}
if len(msg.Raw) != 512 {
t.Errorf("Invalid size %d != 512", len(msg.Raw))
}
}

View file

@ -80,8 +80,7 @@ func TestWhisperBasic(t *testing.T) {
t.Fatalf("failed w.Messages.") t.Fatalf("failed w.Messages.")
} }
var derived []byte derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
derived = pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New)
if !validateSymmetricKey(derived) { if !validateSymmetricKey(derived) {
t.Fatalf("failed validateSymmetricKey with param = %v.", derived) t.Fatalf("failed validateSymmetricKey with param = %v.", derived)
} }