forked from cerc-io/plugeth
cmd/geth: add --config file flag (#13875)
* p2p/discover, p2p/discv5: add marshaling methods to Node * p2p/netutil: make Netlist decodable from TOML * common/math: encode nil HexOrDecimal256 as 0x0 * cmd/geth: add --config file flag * cmd/geth: add missing license header * eth: prettify Config again, fix tests * eth: use gasprice.Config instead of duplicating its fields * eth/gasprice: hide nil default from dumpconfig output * cmd/geth: hide genesis block in dumpconfig output * node: make tests compile * console: fix tests * cmd/geth: make TOML keys look exactly like Go struct fields * p2p: use discovery by default This makes the zero Config slightly more useful. It also fixes package node tests because Node detects reuse of the datadir through the NodeDatabase. * cmd/geth: make ethstats URL settable through config file * cmd/faucet: fix configuration * cmd/geth: dedup attach tests * eth: add comment for DefaultConfig * eth: pass downloader.SyncMode in Config This removes the FastSync, LightSync flags in favour of a more general SyncMode flag. * cmd/utils: remove jitvm flags * cmd/utils: make mutually exclusive flag error prettier It now reads: Fatal: flags --dev, --testnet can't be used at the same time * p2p: fix typo * node: add DefaultConfig, use it for geth * mobile: add missing NoDiscovery option * cmd/utils: drop MakeNode This exposed a couple of places that needed to be updated to use node.DefaultConfig. * node: fix typo * eth: make fast sync the default mode * cmd/utils: remove IPCApiFlag (unused) * node: remove default IPC path Set it in the frontends instead. * cmd/geth: add --syncmode * cmd/utils: make --ipcdisable and --ipcpath mutually exclusive * cmd/utils: don't enable WS, HTTP when setting addr * cmd/utils: fix --identity
This commit is contained in:
parent
b57680b0b2
commit
30d706c35e
@ -41,11 +41,13 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
@ -178,29 +180,26 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network i
|
|||||||
Name: "geth",
|
Name: "geth",
|
||||||
Version: params.Version,
|
Version: params.Version,
|
||||||
DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"),
|
DataDir: filepath.Join(os.Getenv("HOME"), ".faucet"),
|
||||||
|
P2P: p2p.Config{
|
||||||
NAT: nat.Any(),
|
NAT: nat.Any(),
|
||||||
|
NoDiscovery: true,
|
||||||
DiscoveryV5: true,
|
DiscoveryV5: true,
|
||||||
ListenAddr: fmt.Sprintf(":%d", port),
|
ListenAddr: fmt.Sprintf(":%d", port),
|
||||||
DiscoveryV5Addr: fmt.Sprintf(":%d", port+1),
|
DiscoveryV5Addr: fmt.Sprintf(":%d", port+1),
|
||||||
MaxPeers: 25,
|
MaxPeers: 25,
|
||||||
BootstrapNodesV5: enodes,
|
BootstrapNodesV5: enodes,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Assemble the Ethereum light client protocol
|
// Assemble the Ethereum light client protocol
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
return les.New(ctx, ð.Config{
|
cfg := eth.DefaultConfig
|
||||||
LightMode: true,
|
cfg.SyncMode = downloader.LightSync
|
||||||
NetworkId: network,
|
cfg.NetworkId = network
|
||||||
Genesis: genesis,
|
cfg.Genesis = genesis
|
||||||
GasPrice: big.NewInt(20 * params.Shannon),
|
return les.New(ctx, &cfg)
|
||||||
GpoBlocks: 10,
|
|
||||||
GpoPercentile: 50,
|
|
||||||
EthashCacheDir: "ethash",
|
|
||||||
EthashCachesInMem: 2,
|
|
||||||
EthashCachesOnDisk: 3,
|
|
||||||
})
|
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -179,8 +179,7 @@ nodes.
|
|||||||
)
|
)
|
||||||
|
|
||||||
func accountList(ctx *cli.Context) error {
|
func accountList(ctx *cli.Context) error {
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
|
|
||||||
var index int
|
var index int
|
||||||
for _, wallet := range stack.AccountManager().Wallets() {
|
for _, wallet := range stack.AccountManager().Wallets() {
|
||||||
for _, account := range wallet.Accounts() {
|
for _, account := range wallet.Accounts() {
|
||||||
@ -278,7 +277,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
|
|||||||
|
|
||||||
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
// accountCreate creates a new account into the keystore defined by the CLI flags.
|
||||||
func accountCreate(ctx *cli.Context) error {
|
func accountCreate(ctx *cli.Context) error {
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
@ -296,7 +295,7 @@ func accountUpdate(ctx *cli.Context) error {
|
|||||||
if len(ctx.Args()) == 0 {
|
if len(ctx.Args()) == 0 {
|
||||||
utils.Fatalf("No accounts specified to update")
|
utils.Fatalf("No accounts specified to update")
|
||||||
}
|
}
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
|
|
||||||
account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
|
account, oldPassword := unlockAccount(ctx, ks, ctx.Args().First(), 0, nil)
|
||||||
@ -317,7 +316,7 @@ func importWallet(ctx *cli.Context) error {
|
|||||||
utils.Fatalf("Could not read wallet file: %v", err)
|
utils.Fatalf("Could not read wallet file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
@ -338,7 +337,7 @@ func accountImport(ctx *cli.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to load the private key: %v", err)
|
utils.Fatalf("Failed to load the private key: %v", err)
|
||||||
}
|
}
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
|
||||||
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
|
@ -244,7 +244,7 @@ func exportChain(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func removeDB(ctx *cli.Context) error {
|
func removeDB(ctx *cli.Context) error {
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack, _ := makeConfigNode(ctx)
|
||||||
dbdir := stack.ResolvePath(utils.ChainDbName(ctx))
|
dbdir := stack.ResolvePath(utils.ChainDbName(ctx))
|
||||||
if !common.FileExist(dbdir) {
|
if !common.FileExist(dbdir) {
|
||||||
fmt.Println(dbdir, "does not exist")
|
fmt.Println(dbdir, "does not exist")
|
||||||
|
186
cmd/geth/config.go
Normal file
186
cmd/geth/config.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Copyright 2015 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 (
|
||||||
|
"bufio"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/contracts/release"
|
||||||
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/naoina/toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
dumpConfigCommand = cli.Command{
|
||||||
|
Action: dumpConfig,
|
||||||
|
Name: "dumpconfig",
|
||||||
|
Usage: "Show configuration values",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Category: "MISCELLANEOUS COMMANDS",
|
||||||
|
Description: `The dumpconfig command shows configuration values.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
configFileFlag = cli.StringFlag{
|
||||||
|
Name: "config",
|
||||||
|
Usage: "TOML configuration file",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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(", see https://godoc.org/%s#%s for available fields", rt.PkgPath(), rt.Name())
|
||||||
|
}
|
||||||
|
return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type ethstatsConfig struct {
|
||||||
|
URL string `toml:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type gethConfig struct {
|
||||||
|
Eth eth.Config
|
||||||
|
Node node.Config
|
||||||
|
Ethstats ethstatsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(file string, cfg *gethConfig) error {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
err = tomlSettings.NewDecoder(bufio.NewReader(f)).Decode(cfg)
|
||||||
|
// Add file name to errors that have a line number.
|
||||||
|
if _, ok := err.(*toml.LineError); ok {
|
||||||
|
err = errors.New(file + ", " + err.Error())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultNodeConfig() node.Config {
|
||||||
|
cfg := node.DefaultConfig
|
||||||
|
cfg.Name = clientIdentifier
|
||||||
|
cfg.Version = params.VersionWithCommit(gitCommit)
|
||||||
|
cfg.HTTPModules = append(cfg.HTTPModules, "eth")
|
||||||
|
cfg.WSModules = append(cfg.WSModules, "eth")
|
||||||
|
cfg.IPCPath = "geth.ipc"
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
|
// Load defaults.
|
||||||
|
cfg := gethConfig{
|
||||||
|
Eth: eth.DefaultConfig,
|
||||||
|
Node: defaultNodeConfig(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config file.
|
||||||
|
if file := ctx.GlobalString(configFileFlag.Name); file != "" {
|
||||||
|
if err := loadConfig(file, &cfg); err != nil {
|
||||||
|
utils.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply flags.
|
||||||
|
utils.SetNodeConfig(ctx, &cfg.Node)
|
||||||
|
stack, err := node.New(&cfg.Node)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to create the protocol stack: %v", err)
|
||||||
|
}
|
||||||
|
utils.SetEthConfig(ctx, stack, &cfg.Eth)
|
||||||
|
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
|
||||||
|
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stack, cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
|
stack, cfg := makeConfigNode(ctx)
|
||||||
|
|
||||||
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
|
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
||||||
|
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
||||||
|
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
|
||||||
|
if shhEnabled || shhAutoEnabled {
|
||||||
|
utils.RegisterShhService(stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the Ethereum Stats daemon if requested.
|
||||||
|
if cfg.Ethstats.URL != "" {
|
||||||
|
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the release oracle service so it boots along with node.
|
||||||
|
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
|
config := release.Config{
|
||||||
|
Oracle: relOracle,
|
||||||
|
Major: uint32(params.VersionMajor),
|
||||||
|
Minor: uint32(params.VersionMinor),
|
||||||
|
Patch: uint32(params.VersionPatch),
|
||||||
|
}
|
||||||
|
commit, _ := hex.DecodeString(gitCommit)
|
||||||
|
copy(config.Commit[:], commit)
|
||||||
|
return release.NewReleaseService(ctx, config)
|
||||||
|
}); err != nil {
|
||||||
|
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// dumpConfig is the dumpconfig command.
|
||||||
|
func dumpConfig(ctx *cli.Context) error {
|
||||||
|
_, cfg := makeConfigNode(ctx)
|
||||||
|
comment := ""
|
||||||
|
|
||||||
|
if cfg.Eth.Genesis != nil {
|
||||||
|
cfg.Eth.Genesis = nil
|
||||||
|
comment += "# Note: this config doesn't contain the genesis block.\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := tomlSettings.Marshal(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(os.Stdout, comment)
|
||||||
|
os.Stdout.Write(out)
|
||||||
|
return nil
|
||||||
|
}
|
@ -22,14 +22,17 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
|
||||||
|
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests that a node embedded within a console can be started up properly and
|
// Tests that a node embedded within a console can be started up properly and
|
||||||
@ -49,11 +52,7 @@ func TestConsoleWelcome(t *testing.T) {
|
|||||||
geth.setTemplateFunc("gover", runtime.Version)
|
geth.setTemplateFunc("gover", runtime.Version)
|
||||||
geth.setTemplateFunc("gethver", func() string { return params.Version })
|
geth.setTemplateFunc("gethver", func() string { return params.Version })
|
||||||
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||||
geth.setTemplateFunc("apis", func() []string {
|
geth.setTemplateFunc("apis", func() string { return ipcAPIs })
|
||||||
apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
|
|
||||||
sort.Strings(apis)
|
|
||||||
return apis
|
|
||||||
})
|
|
||||||
|
|
||||||
// Verify the actual welcome message to the required template
|
// Verify the actual welcome message to the required template
|
||||||
geth.expect(`
|
geth.expect(`
|
||||||
@ -63,7 +62,7 @@ instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
|
|||||||
coinbase: {{.Etherbase}}
|
coinbase: {{.Etherbase}}
|
||||||
at block: 0 ({{niltime}})
|
at block: 0 ({{niltime}})
|
||||||
datadir: {{.Datadir}}
|
datadir: {{.Datadir}}
|
||||||
modules:{{range apis}} {{.}}:1.0{{end}}
|
modules: {{apis}}
|
||||||
|
|
||||||
> {{.InputLine "exit"}}
|
> {{.InputLine "exit"}}
|
||||||
`)
|
`)
|
||||||
@ -89,7 +88,7 @@ func TestIPCAttachWelcome(t *testing.T) {
|
|||||||
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
|
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
||||||
testAttachWelcome(t, geth, "ipc:"+ipc)
|
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
|
||||||
|
|
||||||
geth.interrupt()
|
geth.interrupt()
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
@ -103,7 +102,7 @@ func TestHTTPAttachWelcome(t *testing.T) {
|
|||||||
"--etherbase", coinbase, "--rpc", "--rpcport", port)
|
"--etherbase", coinbase, "--rpc", "--rpcport", port)
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
||||||
testAttachWelcome(t, geth, "http://localhost:"+port)
|
testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs)
|
||||||
|
|
||||||
geth.interrupt()
|
geth.interrupt()
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
@ -118,13 +117,13 @@ func TestWSAttachWelcome(t *testing.T) {
|
|||||||
"--etherbase", coinbase, "--ws", "--wsport", port)
|
"--etherbase", coinbase, "--ws", "--wsport", port)
|
||||||
|
|
||||||
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
|
||||||
testAttachWelcome(t, geth, "ws://localhost:"+port)
|
testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs)
|
||||||
|
|
||||||
geth.interrupt()
|
geth.interrupt()
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
|
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||||
// Attach to a running geth note and terminate immediately
|
// Attach to a running geth note and terminate immediately
|
||||||
attach := runGeth(t, "attach", endpoint)
|
attach := runGeth(t, "attach", endpoint)
|
||||||
defer attach.expectExit()
|
defer attach.expectExit()
|
||||||
@ -139,16 +138,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
|
|||||||
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||||
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||||
attach.setTemplateFunc("datadir", func() string { return geth.Datadir })
|
attach.setTemplateFunc("datadir", func() string { return geth.Datadir })
|
||||||
attach.setTemplateFunc("apis", func() []string {
|
attach.setTemplateFunc("apis", func() string { return apis })
|
||||||
var apis []string
|
|
||||||
if strings.HasPrefix(endpoint, "ipc") {
|
|
||||||
apis = append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
|
|
||||||
} else {
|
|
||||||
apis = append(strings.Split(rpc.DefaultHTTPApis, ","), rpc.MetadataApi)
|
|
||||||
}
|
|
||||||
sort.Strings(apis)
|
|
||||||
return apis
|
|
||||||
})
|
|
||||||
|
|
||||||
// Verify the actual welcome message to the required template
|
// Verify the actual welcome message to the required template
|
||||||
attach.expect(`
|
attach.expect(`
|
||||||
@ -158,7 +148,7 @@ instance: Geth/v{{gethver}}/{{goos}}-{{goarch}}/{{gover}}
|
|||||||
coinbase: {{etherbase}}
|
coinbase: {{etherbase}}
|
||||||
at block: 0 ({{niltime}}){{if ipc}}
|
at block: 0 ({{niltime}}){{if ipc}}
|
||||||
datadir: {{datadir}}{{end}}
|
datadir: {{datadir}}{{end}}
|
||||||
modules:{{range apis}} {{.}}:1.0{{end}}
|
modules: {{apis}}
|
||||||
|
|
||||||
> {{.InputLine "exit" }}
|
> {{.InputLine "exit" }}
|
||||||
`)
|
`)
|
||||||
|
@ -84,27 +84,24 @@ var daoGenesisForkBlock = big.NewInt(314)
|
|||||||
// set in the database after various initialization procedures and invocations.
|
// set in the database after various initialization procedures and invocations.
|
||||||
func TestDAOForkBlockNewChain(t *testing.T) {
|
func TestDAOForkBlockNewChain(t *testing.T) {
|
||||||
for i, arg := range []struct {
|
for i, arg := range []struct {
|
||||||
testnet bool
|
|
||||||
genesis string
|
genesis string
|
||||||
expectBlock *big.Int
|
expectBlock *big.Int
|
||||||
expectVote bool
|
expectVote bool
|
||||||
}{
|
}{
|
||||||
// Test DAO Default Mainnet
|
// Test DAO Default Mainnet
|
||||||
{false, "", params.MainNetDAOForkBlock, true},
|
{"", params.MainNetDAOForkBlock, true},
|
||||||
// test DAO Default Testnet
|
|
||||||
{true, "", params.TestNetDAOForkBlock, true},
|
|
||||||
// test DAO Init Old Privnet
|
// test DAO Init Old Privnet
|
||||||
{false, daoOldGenesis, nil, false},
|
{daoOldGenesis, nil, false},
|
||||||
// test DAO Default No Fork Privnet
|
// test DAO Default No Fork Privnet
|
||||||
{false, daoNoForkGenesis, daoGenesisForkBlock, false},
|
{daoNoForkGenesis, daoGenesisForkBlock, false},
|
||||||
// test DAO Default Pro Fork Privnet
|
// test DAO Default Pro Fork Privnet
|
||||||
{false, daoProForkGenesis, daoGenesisForkBlock, true},
|
{daoProForkGenesis, daoGenesisForkBlock, true},
|
||||||
} {
|
} {
|
||||||
testDAOForkBlockNewChain(t, i, arg.testnet, arg.genesis, arg.expectBlock, arg.expectVote)
|
testDAOForkBlockNewChain(t, i, arg.genesis, arg.expectBlock, arg.expectVote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis string, expectBlock *big.Int, expectVote bool) {
|
func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBlock *big.Int, expectVote bool) {
|
||||||
// Create a temporary data directory to use and inspect later
|
// Create a temporary data directory to use and inspect later
|
||||||
datadir := tmpdir(t)
|
datadir := tmpdir(t)
|
||||||
defer os.RemoveAll(datadir)
|
defer os.RemoveAll(datadir)
|
||||||
@ -119,17 +116,11 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri
|
|||||||
} else {
|
} else {
|
||||||
// Force chain initialization
|
// Force chain initialization
|
||||||
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
|
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
|
||||||
if testnet {
|
|
||||||
args = append(args, "--testnet")
|
|
||||||
}
|
|
||||||
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
|
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
|
||||||
geth.cmd.Wait()
|
geth.cmd.Wait()
|
||||||
}
|
}
|
||||||
// Retrieve the DAO config flag from the database
|
// Retrieve the DAO config flag from the database
|
||||||
path := filepath.Join(datadir, "geth", "chaindata")
|
path := filepath.Join(datadir, "geth", "chaindata")
|
||||||
if testnet && genesis == "" {
|
|
||||||
path = filepath.Join(datadir, "testnet", "geth", "chaindata")
|
|
||||||
}
|
|
||||||
db, err := ethdb.NewLDBDatabase(path, 0, 0)
|
db, err := ethdb.NewLDBDatabase(path, 0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
||||||
@ -137,9 +128,6 @@ func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis stri
|
|||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||||
if testnet {
|
|
||||||
genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
|
||||||
}
|
|
||||||
if genesis != "" {
|
if genesis != "" {
|
||||||
genesisHash = daoGenesisHash
|
genesisHash = daoGenesisHash
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -29,17 +28,13 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
"github.com/ethereum/go-ethereum/contracts/release"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,6 +77,8 @@ func init() {
|
|||||||
versionCommand,
|
versionCommand,
|
||||||
bugCommand,
|
bugCommand,
|
||||||
licenseCommand,
|
licenseCommand,
|
||||||
|
// See config.go
|
||||||
|
dumpConfigCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
@ -99,6 +96,7 @@ func init() {
|
|||||||
utils.EthashDatasetsOnDiskFlag,
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
utils.FastSyncFlag,
|
utils.FastSyncFlag,
|
||||||
utils.LightModeFlag,
|
utils.LightModeFlag,
|
||||||
|
utils.SyncModeFlag,
|
||||||
utils.LightServFlag,
|
utils.LightServFlag,
|
||||||
utils.LightPeersFlag,
|
utils.LightPeersFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
@ -129,16 +127,12 @@ func init() {
|
|||||||
utils.WSApiFlag,
|
utils.WSApiFlag,
|
||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCApiFlag,
|
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.ExecFlag,
|
utils.ExecFlag,
|
||||||
utils.PreloadJSFlag,
|
utils.PreloadJSFlag,
|
||||||
utils.WhisperEnabledFlag,
|
utils.WhisperEnabledFlag,
|
||||||
utils.DevModeFlag,
|
utils.DevModeFlag,
|
||||||
utils.TestNetFlag,
|
utils.TestNetFlag,
|
||||||
utils.VMForceJitFlag,
|
|
||||||
utils.VMJitCacheFlag,
|
|
||||||
utils.VMEnableJitFlag,
|
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
utils.NetworkIdFlag,
|
utils.NetworkIdFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.RPCCORSDomainFlag,
|
||||||
@ -150,6 +144,7 @@ func init() {
|
|||||||
utils.GpoBlocksFlag,
|
utils.GpoBlocksFlag,
|
||||||
utils.GpoPercentileFlag,
|
utils.GpoPercentileFlag,
|
||||||
utils.ExtraDataFlag,
|
utils.ExtraDataFlag,
|
||||||
|
configFileFlag,
|
||||||
}
|
}
|
||||||
app.Flags = append(app.Flags, debug.Flags...)
|
app.Flags = append(app.Flags, debug.Flags...)
|
||||||
|
|
||||||
@ -189,52 +184,6 @@ func geth(ctx *cli.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
|
||||||
// Create the default extradata and construct the base node
|
|
||||||
var clientInfo = struct {
|
|
||||||
Version uint
|
|
||||||
Name string
|
|
||||||
GoVersion string
|
|
||||||
Os string
|
|
||||||
}{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
|
||||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to set canonical miner information", "err", err)
|
|
||||||
}
|
|
||||||
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
|
||||||
log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
|
|
||||||
extra = nil
|
|
||||||
}
|
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
|
||||||
utils.RegisterEthService(ctx, stack, extra)
|
|
||||||
|
|
||||||
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
|
||||||
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
|
||||||
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
|
|
||||||
if shhEnabled || shhAutoEnabled {
|
|
||||||
utils.RegisterShhService(stack)
|
|
||||||
}
|
|
||||||
// Add the Ethereum Stats daemon if requested
|
|
||||||
if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" {
|
|
||||||
utils.RegisterEthStatsService(stack, url)
|
|
||||||
}
|
|
||||||
// Add the release oracle service so it boots along with node.
|
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
|
||||||
config := release.Config{
|
|
||||||
Oracle: relOracle,
|
|
||||||
Major: uint32(params.VersionMajor),
|
|
||||||
Minor: uint32(params.VersionMinor),
|
|
||||||
Patch: uint32(params.VersionPatch),
|
|
||||||
}
|
|
||||||
commit, _ := hex.DecodeString(gitCommit)
|
|
||||||
copy(config.Commit[:], commit)
|
|
||||||
return release.NewReleaseService(ctx, config)
|
|
||||||
}); err != nil {
|
|
||||||
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
|
|
||||||
}
|
|
||||||
return stack
|
|
||||||
}
|
|
||||||
|
|
||||||
// startNode boots up the system node and all registered protocols, after which
|
// startNode boots up the system node and all registered protocols, after which
|
||||||
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
|
||||||
// miner.
|
// miner.
|
||||||
|
@ -115,7 +115,6 @@ var AppHelpFlagGroups = []flagGroup{
|
|||||||
utils.WSApiFlag,
|
utils.WSApiFlag,
|
||||||
utils.WSAllowedOriginsFlag,
|
utils.WSAllowedOriginsFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCApiFlag,
|
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.RPCCORSDomainFlag,
|
utils.RPCCORSDomainFlag,
|
||||||
utils.JSpathFlag,
|
utils.JSpathFlag,
|
||||||
@ -158,9 +157,6 @@ var AppHelpFlagGroups = []flagGroup{
|
|||||||
{
|
{
|
||||||
Name: "VIRTUAL MACHINE",
|
Name: "VIRTUAL MACHINE",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.VMEnableJitFlag,
|
|
||||||
utils.VMForceJitFlag,
|
|
||||||
utils.VMJitCacheFlag,
|
|
||||||
utils.VMEnableDebugFlag,
|
utils.VMEnableDebugFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -39,19 +39,16 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/swarm"
|
"github.com/ethereum/go-ethereum/swarm"
|
||||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const clientIdentifier = "swarm"
|
||||||
clientIdentifier = "swarm"
|
|
||||||
versionString = "0.2"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||||
app = utils.NewApp(gitCommit, "Ethereum Swarm")
|
|
||||||
testbetBootNodes = []string{
|
testbetBootNodes = []string{
|
||||||
"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
|
"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
|
||||||
"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
|
"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
|
||||||
@ -126,13 +123,22 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
var defaultNodeConfig = node.DefaultConfig
|
||||||
// Override flag defaults so bzzd can run alongside geth.
|
|
||||||
utils.ListenPortFlag.Value = 30399
|
|
||||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
|
||||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, swarmfs, web3"
|
|
||||||
|
|
||||||
// Set up the cli app.
|
// This init function sets defaults so cmd/swarm can run alongside geth.
|
||||||
|
func init() {
|
||||||
|
defaultNodeConfig.Name = clientIdentifier
|
||||||
|
defaultNodeConfig.Version = params.VersionWithCommit(gitCommit)
|
||||||
|
defaultNodeConfig.P2P.ListenAddr = ":30399"
|
||||||
|
defaultNodeConfig.IPCPath = "bzzd.ipc"
|
||||||
|
// Set flag defaults for --help display.
|
||||||
|
utils.ListenPortFlag.Value = 30399
|
||||||
|
}
|
||||||
|
|
||||||
|
var app = utils.NewApp(gitCommit, "Ethereum Swarm")
|
||||||
|
|
||||||
|
// This init function creates the cli.App.
|
||||||
|
func init() {
|
||||||
app.Action = bzzd
|
app.Action = bzzd
|
||||||
app.HideVersion = true // we have a command to print the version
|
app.HideVersion = true // we have a command to print the version
|
||||||
app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
|
app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
|
||||||
@ -235,7 +241,6 @@ Cleans database of corrupted entries.
|
|||||||
utils.MaxPeersFlag,
|
utils.MaxPeersFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.IPCDisabledFlag,
|
utils.IPCDisabledFlag,
|
||||||
utils.IPCApiFlag,
|
|
||||||
utils.IPCPathFlag,
|
utils.IPCPathFlag,
|
||||||
utils.PasswordFileFlag,
|
utils.PasswordFileFlag,
|
||||||
// bzzd-specific flags
|
// bzzd-specific flags
|
||||||
@ -276,7 +281,7 @@ func main() {
|
|||||||
|
|
||||||
func version(ctx *cli.Context) error {
|
func version(ctx *cli.Context) error {
|
||||||
fmt.Println(strings.Title(clientIdentifier))
|
fmt.Println(strings.Title(clientIdentifier))
|
||||||
fmt.Println("Version:", versionString)
|
fmt.Println("Version:", params.Version)
|
||||||
if gitCommit != "" {
|
if gitCommit != "" {
|
||||||
fmt.Println("Git Commit:", gitCommit)
|
fmt.Println("Git Commit:", gitCommit)
|
||||||
}
|
}
|
||||||
@ -289,9 +294,16 @@ func version(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func bzzd(ctx *cli.Context) error {
|
func bzzd(ctx *cli.Context) error {
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
cfg := defaultNodeConfig
|
||||||
|
utils.SetNodeConfig(ctx, &cfg)
|
||||||
|
stack, err := node.New(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("can't create node: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
registerBzzService(ctx, stack)
|
registerBzzService(ctx, stack)
|
||||||
utils.StartNode(stack)
|
utils.StartNode(stack)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
sigc := make(chan os.Signal, 1)
|
sigc := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigc, syscall.SIGTERM)
|
signal.Notify(sigc, syscall.SIGTERM)
|
||||||
@ -300,6 +312,7 @@ func bzzd(ctx *cli.Context) error {
|
|||||||
log.Info("Got sigterm, shutting swarm down...")
|
log.Info("Got sigterm, shutting swarm down...")
|
||||||
stack.Stop()
|
stack.Stop()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
|
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
|
||||||
// Add bootnodes as initial peers.
|
// Add bootnodes as initial peers.
|
||||||
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
|
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
|
||||||
@ -316,7 +329,6 @@ func bzzd(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
||||||
|
|
||||||
prvkey := getAccount(ctx, stack)
|
prvkey := getAccount(ctx, stack)
|
||||||
|
|
||||||
chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
|
chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -78,6 +79,58 @@ func (self DirectoryFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TextMarshaler interface {
|
||||||
|
encoding.TextMarshaler
|
||||||
|
encoding.TextUnmarshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
// textMarshalerVal turns a TextMarshaler into a flag.Value
|
||||||
|
type textMarshalerVal struct {
|
||||||
|
v TextMarshaler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v textMarshalerVal) String() string {
|
||||||
|
if v.v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
text, _ := v.v.MarshalText()
|
||||||
|
return string(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v textMarshalerVal) Set(s string) error {
|
||||||
|
return v.v.UnmarshalText([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextMarshalerFlag wraps a TextMarshaler value.
|
||||||
|
type TextMarshalerFlag struct {
|
||||||
|
Name string
|
||||||
|
Value TextMarshaler
|
||||||
|
Usage string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f TextMarshalerFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f TextMarshalerFlag) String() string {
|
||||||
|
return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Var(textMarshalerVal{f.Value}, f.Name, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalTextMarshaler returns the value of a TextMarshalerFlag from the global flag set.
|
||||||
|
func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
|
||||||
|
val := ctx.GlobalGeneric(name)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return val.(textMarshalerVal).v
|
||||||
|
}
|
||||||
|
|
||||||
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
|
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
|
||||||
// hexadecimal syntax.
|
// hexadecimal syntax.
|
||||||
type BigFlag struct {
|
type BigFlag struct {
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -38,6 +37,8 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
@ -45,12 +46,12 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
@ -121,31 +122,32 @@ var (
|
|||||||
EthashCachesInMemoryFlag = cli.IntFlag{
|
EthashCachesInMemoryFlag = cli.IntFlag{
|
||||||
Name: "ethash.cachesinmem",
|
Name: "ethash.cachesinmem",
|
||||||
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
|
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
|
||||||
Value: 2,
|
Value: eth.DefaultConfig.EthashCachesInMem,
|
||||||
}
|
}
|
||||||
EthashCachesOnDiskFlag = cli.IntFlag{
|
EthashCachesOnDiskFlag = cli.IntFlag{
|
||||||
Name: "ethash.cachesondisk",
|
Name: "ethash.cachesondisk",
|
||||||
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
|
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
|
||||||
Value: 3,
|
Value: eth.DefaultConfig.EthashCachesOnDisk,
|
||||||
}
|
}
|
||||||
EthashDatasetDirFlag = DirectoryFlag{
|
EthashDatasetDirFlag = DirectoryFlag{
|
||||||
Name: "ethash.dagdir",
|
Name: "ethash.dagdir",
|
||||||
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
|
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
|
||||||
|
Value: DirectoryString{eth.DefaultConfig.EthashDatasetDir},
|
||||||
}
|
}
|
||||||
EthashDatasetsInMemoryFlag = cli.IntFlag{
|
EthashDatasetsInMemoryFlag = cli.IntFlag{
|
||||||
Name: "ethash.dagsinmem",
|
Name: "ethash.dagsinmem",
|
||||||
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
|
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
|
||||||
Value: 1,
|
Value: eth.DefaultConfig.EthashDatasetsInMem,
|
||||||
}
|
}
|
||||||
EthashDatasetsOnDiskFlag = cli.IntFlag{
|
EthashDatasetsOnDiskFlag = cli.IntFlag{
|
||||||
Name: "ethash.dagsondisk",
|
Name: "ethash.dagsondisk",
|
||||||
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
|
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
|
||||||
Value: 2,
|
Value: eth.DefaultConfig.EthashDatasetsOnDisk,
|
||||||
}
|
}
|
||||||
NetworkIdFlag = cli.IntFlag{
|
NetworkIdFlag = cli.IntFlag{
|
||||||
Name: "networkid",
|
Name: "networkid",
|
||||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||||
Value: eth.NetworkId,
|
Value: eth.DefaultConfig.NetworkId,
|
||||||
}
|
}
|
||||||
TestNetFlag = cli.BoolFlag{
|
TestNetFlag = cli.BoolFlag{
|
||||||
Name: "testnet",
|
Name: "testnet",
|
||||||
@ -172,6 +174,13 @@ var (
|
|||||||
Name: "light",
|
Name: "light",
|
||||||
Usage: "Enable light client mode",
|
Usage: "Enable light client mode",
|
||||||
}
|
}
|
||||||
|
defaultSyncMode = eth.DefaultConfig.SyncMode
|
||||||
|
SyncModeFlag = TextMarshalerFlag{
|
||||||
|
Name: "syncmode",
|
||||||
|
Usage: `Blockchain sync mode ("fast", "full", or "light")`,
|
||||||
|
Value: &defaultSyncMode,
|
||||||
|
}
|
||||||
|
|
||||||
LightServFlag = cli.IntFlag{
|
LightServFlag = cli.IntFlag{
|
||||||
Name: "lightserv",
|
Name: "lightserv",
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
|
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
|
||||||
@ -238,19 +247,6 @@ var (
|
|||||||
Value: "",
|
Value: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
VMForceJitFlag = cli.BoolFlag{
|
|
||||||
Name: "forcejit",
|
|
||||||
Usage: "Force the JIT VM to take precedence",
|
|
||||||
}
|
|
||||||
VMJitCacheFlag = cli.IntFlag{
|
|
||||||
Name: "jitcache",
|
|
||||||
Usage: "Amount of cached JIT VM programs",
|
|
||||||
Value: 64,
|
|
||||||
}
|
|
||||||
VMEnableJitFlag = cli.BoolFlag{
|
|
||||||
Name: "jitvm",
|
|
||||||
Usage: "Enable the JIT VM",
|
|
||||||
}
|
|
||||||
VMEnableDebugFlag = cli.BoolFlag{
|
VMEnableDebugFlag = cli.BoolFlag{
|
||||||
Name: "vmdebug",
|
Name: "vmdebug",
|
||||||
Usage: "Record information useful for VM and contract debugging",
|
Usage: "Record information useful for VM and contract debugging",
|
||||||
@ -295,21 +291,15 @@ var (
|
|||||||
RPCApiFlag = cli.StringFlag{
|
RPCApiFlag = cli.StringFlag{
|
||||||
Name: "rpcapi",
|
Name: "rpcapi",
|
||||||
Usage: "API's offered over the HTTP-RPC interface",
|
Usage: "API's offered over the HTTP-RPC interface",
|
||||||
Value: rpc.DefaultHTTPApis,
|
Value: "",
|
||||||
}
|
}
|
||||||
IPCDisabledFlag = cli.BoolFlag{
|
IPCDisabledFlag = cli.BoolFlag{
|
||||||
Name: "ipcdisable",
|
Name: "ipcdisable",
|
||||||
Usage: "Disable the IPC-RPC server",
|
Usage: "Disable the IPC-RPC server",
|
||||||
}
|
}
|
||||||
IPCApiFlag = cli.StringFlag{
|
|
||||||
Name: "ipcapi",
|
|
||||||
Usage: "APIs offered over the IPC-RPC interface",
|
|
||||||
Value: rpc.DefaultIPCApis,
|
|
||||||
}
|
|
||||||
IPCPathFlag = DirectoryFlag{
|
IPCPathFlag = DirectoryFlag{
|
||||||
Name: "ipcpath",
|
Name: "ipcpath",
|
||||||
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
|
||||||
Value: DirectoryString{"geth.ipc"},
|
|
||||||
}
|
}
|
||||||
WSEnabledFlag = cli.BoolFlag{
|
WSEnabledFlag = cli.BoolFlag{
|
||||||
Name: "ws",
|
Name: "ws",
|
||||||
@ -328,7 +318,7 @@ var (
|
|||||||
WSApiFlag = cli.StringFlag{
|
WSApiFlag = cli.StringFlag{
|
||||||
Name: "wsapi",
|
Name: "wsapi",
|
||||||
Usage: "API's offered over the WS-RPC interface",
|
Usage: "API's offered over the WS-RPC interface",
|
||||||
Value: rpc.DefaultHTTPApis,
|
Value: "",
|
||||||
}
|
}
|
||||||
WSAllowedOriginsFlag = cli.StringFlag{
|
WSAllowedOriginsFlag = cli.StringFlag{
|
||||||
Name: "wsorigins",
|
Name: "wsorigins",
|
||||||
@ -412,12 +402,12 @@ var (
|
|||||||
GpoBlocksFlag = cli.IntFlag{
|
GpoBlocksFlag = cli.IntFlag{
|
||||||
Name: "gpoblocks",
|
Name: "gpoblocks",
|
||||||
Usage: "Number of recent blocks to check for gas prices",
|
Usage: "Number of recent blocks to check for gas prices",
|
||||||
Value: 10,
|
Value: eth.DefaultConfig.GPO.Blocks,
|
||||||
}
|
}
|
||||||
GpoPercentileFlag = cli.IntFlag{
|
GpoPercentileFlag = cli.IntFlag{
|
||||||
Name: "gpopercentile",
|
Name: "gpopercentile",
|
||||||
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
|
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
|
||||||
Value: 50,
|
Value: eth.DefaultConfig.GPO.Percentile,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -436,88 +426,42 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeEthashCacheDir returns the directory to use for storing the ethash cache
|
// setNodeKey creates a node key from set command line flags, either loading it
|
||||||
// dumps.
|
|
||||||
func MakeEthashCacheDir(ctx *cli.Context) string {
|
|
||||||
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) && ctx.GlobalString(EthashCacheDirFlag.Name) == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if !ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
|
|
||||||
return "ethash"
|
|
||||||
}
|
|
||||||
return ctx.GlobalString(EthashCacheDirFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeEthashDatasetDir returns the directory to use for storing the full ethash
|
|
||||||
// dataset dumps.
|
|
||||||
func MakeEthashDatasetDir(ctx *cli.Context) string {
|
|
||||||
if !ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
|
|
||||||
home := os.Getenv("HOME")
|
|
||||||
if home == "" {
|
|
||||||
if user, err := user.Current(); err == nil {
|
|
||||||
home = user.HomeDir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return filepath.Join(home, "AppData", "Ethash")
|
|
||||||
}
|
|
||||||
return filepath.Join(home, ".ethash")
|
|
||||||
}
|
|
||||||
return ctx.GlobalString(EthashDatasetDirFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeIPCPath creates an IPC path configuration from the set command line flags,
|
|
||||||
// returning an empty string if IPC was explicitly disabled, or the set path.
|
|
||||||
func MakeIPCPath(ctx *cli.Context) string {
|
|
||||||
if ctx.GlobalBool(IPCDisabledFlag.Name) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return ctx.GlobalString(IPCPathFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeNodeKey creates a node key from set command line flags, either loading it
|
|
||||||
// from a file or as a specified hex value. If neither flags were provided, this
|
// from a file or as a specified hex value. If neither flags were provided, this
|
||||||
// method returns nil and an emphemeral key is to be generated.
|
// method returns nil and an emphemeral key is to be generated.
|
||||||
func MakeNodeKey(ctx *cli.Context) *ecdsa.PrivateKey {
|
func setNodeKey(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
var (
|
var (
|
||||||
hex = ctx.GlobalString(NodeKeyHexFlag.Name)
|
hex = ctx.GlobalString(NodeKeyHexFlag.Name)
|
||||||
file = ctx.GlobalString(NodeKeyFileFlag.Name)
|
file = ctx.GlobalString(NodeKeyFileFlag.Name)
|
||||||
|
|
||||||
key *ecdsa.PrivateKey
|
key *ecdsa.PrivateKey
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch {
|
switch {
|
||||||
case file != "" && hex != "":
|
case file != "" && hex != "":
|
||||||
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
|
Fatalf("Options %q and %q are mutually exclusive", NodeKeyFileFlag.Name, NodeKeyHexFlag.Name)
|
||||||
|
|
||||||
case file != "":
|
case file != "":
|
||||||
if key, err = crypto.LoadECDSA(file); err != nil {
|
if key, err = crypto.LoadECDSA(file); err != nil {
|
||||||
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
|
Fatalf("Option %q: %v", NodeKeyFileFlag.Name, err)
|
||||||
}
|
}
|
||||||
|
cfg.PrivateKey = key
|
||||||
case hex != "":
|
case hex != "":
|
||||||
if key, err = crypto.HexToECDSA(hex); err != nil {
|
if key, err = crypto.HexToECDSA(hex); err != nil {
|
||||||
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
|
Fatalf("Option %q: %v", NodeKeyHexFlag.Name, err)
|
||||||
}
|
}
|
||||||
|
cfg.PrivateKey = key
|
||||||
}
|
}
|
||||||
return key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeNodeUserIdent creates the user identifier from CLI flags.
|
// setNodeUserIdent creates the user identifier from CLI flags.
|
||||||
func makeNodeUserIdent(ctx *cli.Context) string {
|
func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||||
var comps []string
|
|
||||||
if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
|
if identity := ctx.GlobalString(IdentityFlag.Name); len(identity) > 0 {
|
||||||
comps = append(comps, identity)
|
cfg.UserIdent = identity
|
||||||
}
|
}
|
||||||
if ctx.GlobalBool(VMEnableJitFlag.Name) {
|
|
||||||
comps = append(comps, "JIT")
|
|
||||||
}
|
|
||||||
return strings.Join(comps, "/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBootstrapNodes creates a list of bootstrap nodes from the command line
|
// setBootstrapNodes creates a list of bootstrap nodes from the command line
|
||||||
// flags, reverting to pre-configured ones if none have been specified.
|
// flags, reverting to pre-configured ones if none have been specified.
|
||||||
func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
|
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls := params.MainnetBootnodes
|
urls := params.MainnetBootnodes
|
||||||
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
||||||
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
||||||
@ -525,62 +469,68 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
|
|||||||
urls = params.TestnetBootnodes
|
urls = params.TestnetBootnodes
|
||||||
}
|
}
|
||||||
|
|
||||||
bootnodes := make([]*discover.Node, 0, len(urls))
|
cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := discover.ParseNode(url)
|
node, err := discover.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bootnodes = append(bootnodes, node)
|
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
|
||||||
}
|
}
|
||||||
return bootnodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeBootstrapNodesV5 creates a list of bootstrap nodes from the command line
|
// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line
|
||||||
// flags, reverting to pre-configured ones if none have been specified.
|
// flags, reverting to pre-configured ones if none have been specified.
|
||||||
func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node {
|
func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls := params.DiscoveryV5Bootnodes
|
urls := params.DiscoveryV5Bootnodes
|
||||||
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
if ctx.GlobalIsSet(BootnodesFlag.Name) {
|
||||||
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
|
||||||
|
} else if cfg.BootstrapNodesV5 == nil {
|
||||||
|
return // already set, don't apply defaults.
|
||||||
}
|
}
|
||||||
|
|
||||||
bootnodes := make([]*discv5.Node, 0, len(urls))
|
cfg.BootstrapNodesV5 = make([]*discv5.Node, 0, len(urls))
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := discv5.ParseNode(url)
|
node, err := discv5.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bootnodes = append(bootnodes, node)
|
cfg.BootstrapNodesV5 = append(cfg.BootstrapNodesV5, node)
|
||||||
}
|
}
|
||||||
return bootnodes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeListenAddress creates a TCP listening address string from set command
|
// setListenAddress creates a TCP listening address string from set command
|
||||||
// line flags.
|
// line flags.
|
||||||
func MakeListenAddress(ctx *cli.Context) string {
|
func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
|
if ctx.GlobalIsSet(ListenPortFlag.Name) {
|
||||||
|
cfg.ListenAddr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeDiscoveryV5Address creates a UDP listening address string from set command
|
// setDiscoveryV5Address creates a UDP listening address string from set command
|
||||||
// line flags for the V5 discovery protocol.
|
// line flags for the V5 discovery protocol.
|
||||||
func MakeDiscoveryV5Address(ctx *cli.Context) string {
|
func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
return fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
|
if ctx.GlobalIsSet(ListenPortFlag.Name) {
|
||||||
|
cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeNAT creates a port mapper from set command line flags.
|
// setNAT creates a port mapper from command line flags.
|
||||||
func MakeNAT(ctx *cli.Context) nat.Interface {
|
func setNAT(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
|
if ctx.GlobalIsSet(NATFlag.Name) {
|
||||||
natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
|
natif, err := nat.Parse(ctx.GlobalString(NATFlag.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Option %s: %v", NATFlag.Name, err)
|
Fatalf("Option %s: %v", NATFlag.Name, err)
|
||||||
}
|
}
|
||||||
return natif
|
cfg.NAT = natif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeRPCModules splits input separated by a comma and trims excessive white
|
// makeRPCModules splits input separated by a comma and trims excessive white
|
||||||
// space from the substrings.
|
// space from the substrings.
|
||||||
func MakeRPCModules(input string) []string {
|
func makeRPCModules(input string) []string {
|
||||||
result := strings.Split(input, ",")
|
result := strings.Split(input, ",")
|
||||||
for i, r := range result {
|
for i, r := range result {
|
||||||
result[i] = strings.TrimSpace(r)
|
result[i] = strings.TrimSpace(r)
|
||||||
@ -588,27 +538,63 @@ func MakeRPCModules(input string) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeHTTPRpcHost creates the HTTP RPC listener interface string from the set
|
// setHTTP creates the HTTP RPC listener interface string from the set
|
||||||
// command line flags, returning empty if the HTTP endpoint is disabled.
|
// command line flags, returning empty if the HTTP endpoint is disabled.
|
||||||
func MakeHTTPRpcHost(ctx *cli.Context) string {
|
func setHTTP(ctx *cli.Context, cfg *node.Config) {
|
||||||
if !ctx.GlobalBool(RPCEnabledFlag.Name) {
|
if ctx.GlobalBool(RPCEnabledFlag.Name) && cfg.HTTPHost == "" {
|
||||||
return ""
|
cfg.HTTPHost = "127.0.0.1"
|
||||||
|
if ctx.GlobalIsSet(RPCListenAddrFlag.Name) {
|
||||||
|
cfg.HTTPHost = ctx.GlobalString(RPCListenAddrFlag.Name)
|
||||||
}
|
}
|
||||||
return ctx.GlobalString(RPCListenAddrFlag.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeWSRpcHost creates the WebSocket RPC listener interface string from the set
|
if ctx.GlobalIsSet(RPCPortFlag.Name) {
|
||||||
// command line flags, returning empty if the HTTP endpoint is disabled.
|
cfg.HTTPPort = ctx.GlobalInt(RPCPortFlag.Name)
|
||||||
func MakeWSRpcHost(ctx *cli.Context) string {
|
}
|
||||||
if !ctx.GlobalBool(WSEnabledFlag.Name) {
|
if ctx.GlobalIsSet(RPCCORSDomainFlag.Name) {
|
||||||
return ""
|
cfg.HTTPCors = ctx.GlobalString(RPCCORSDomainFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(RPCApiFlag.Name) {
|
||||||
|
cfg.HTTPModules = makeRPCModules(ctx.GlobalString(RPCApiFlag.Name))
|
||||||
}
|
}
|
||||||
return ctx.GlobalString(WSListenAddrFlag.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
// setWS creates the WebSocket RPC listener interface string from the set
|
||||||
|
// command line flags, returning empty if the HTTP endpoint is disabled.
|
||||||
|
func setWS(ctx *cli.Context, cfg *node.Config) {
|
||||||
|
if ctx.GlobalBool(WSEnabledFlag.Name) && cfg.WSHost == "" {
|
||||||
|
cfg.WSHost = "127.0.0.1"
|
||||||
|
if ctx.GlobalIsSet(WSListenAddrFlag.Name) {
|
||||||
|
cfg.WSHost = ctx.GlobalString(WSListenAddrFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(WSPortFlag.Name) {
|
||||||
|
cfg.WSPort = ctx.GlobalInt(WSPortFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(WSAllowedOriginsFlag.Name) {
|
||||||
|
cfg.WSOrigins = ctx.GlobalString(WSAllowedOriginsFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(WSApiFlag.Name) {
|
||||||
|
cfg.WSModules = makeRPCModules(ctx.GlobalString(WSApiFlag.Name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setIPC creates an IPC path configuration from the set command line flags,
|
||||||
|
// returning an empty string if IPC was explicitly disabled, or the set path.
|
||||||
|
func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||||
|
checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalBool(IPCDisabledFlag.Name):
|
||||||
|
cfg.IPCPath = ""
|
||||||
|
case ctx.GlobalIsSet(IPCPathFlag.Name):
|
||||||
|
cfg.IPCPath = ctx.GlobalString(IPCPathFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for Geth and returns half of the allowance to assign to the database.
|
// for Geth and returns half of the allowance to assign to the database.
|
||||||
func MakeDatabaseHandles() int {
|
func makeDatabaseHandles() int {
|
||||||
if err := raiseFdLimit(2048); err != nil {
|
if err := raiseFdLimit(2048); err != nil {
|
||||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||||
}
|
}
|
||||||
@ -641,33 +627,25 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
|
|||||||
return accs[index], nil
|
return accs[index], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeEtherbase retrieves the etherbase either from the directly specified
|
// setEtherbase retrieves the etherbase either from the directly specified
|
||||||
// command line flags or from the keystore if CLI indexed.
|
// command line flags or from the keystore if CLI indexed.
|
||||||
func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
|
func setEtherbase(ctx *cli.Context, ks *keystore.KeyStore, cfg *eth.Config) {
|
||||||
accounts := ks.Accounts()
|
if ctx.GlobalIsSet(EtherbaseFlag.Name) {
|
||||||
if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
|
account, err := MakeAddress(ks, ctx.GlobalString(EtherbaseFlag.Name))
|
||||||
log.Warn("No etherbase set and no accounts found as default")
|
|
||||||
return common.Address{}
|
|
||||||
}
|
|
||||||
etherbase := ctx.GlobalString(EtherbaseFlag.Name)
|
|
||||||
if etherbase == "" {
|
|
||||||
return common.Address{}
|
|
||||||
}
|
|
||||||
// If the specified etherbase is a valid address, return it
|
|
||||||
account, err := MakeAddress(ks, etherbase)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
|
Fatalf("Option %q: %v", EtherbaseFlag.Name, err)
|
||||||
}
|
}
|
||||||
return 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeMinerExtra resolves extradata for the miner from the set command line flags
|
|
||||||
// or returns a default one composed on the client, runtime and OS metadata.
|
|
||||||
func MakeMinerExtra(extra []byte, ctx *cli.Context) []byte {
|
|
||||||
if ctx.GlobalIsSet(ExtraDataFlag.Name) {
|
|
||||||
return []byte(ctx.GlobalString(ExtraDataFlag.Name))
|
|
||||||
}
|
}
|
||||||
return extra
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakePasswordList reads password lines from the file specified by --password.
|
// MakePasswordList reads password lines from the file specified by --password.
|
||||||
@ -688,144 +666,217 @@ func MakePasswordList(ctx *cli.Context) []string {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeNode configures a node with no services from command line flags.
|
func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
setNodeKey(ctx, cfg)
|
||||||
vsn := params.Version
|
setNAT(ctx, cfg)
|
||||||
if gitCommit != "" {
|
setListenAddress(ctx, cfg)
|
||||||
vsn += "-" + gitCommit[:8]
|
setDiscoveryV5Address(ctx, cfg)
|
||||||
|
setBootstrapNodes(ctx, cfg)
|
||||||
|
setBootstrapNodesV5(ctx, cfg)
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(MaxPeersFlag.Name) {
|
||||||
|
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) {
|
||||||
|
cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) {
|
||||||
|
cfg.NoDiscovery = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're running a light client or server, force enable the v5 peer discovery unless it is explicitly disabled with --nodiscover
|
// if we're running a light client or server, force enable the v5 peer discovery
|
||||||
// note that explicitly specifying --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
|
// unless it is explicitly disabled with --nodiscover note that explicitly specifying
|
||||||
|
// --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
|
||||||
forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name)
|
forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name)
|
||||||
|
if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
|
||||||
|
cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
|
||||||
|
} else if forceV5Discovery {
|
||||||
|
cfg.DiscoveryV5 = true
|
||||||
|
}
|
||||||
|
|
||||||
config := &node.Config{
|
|
||||||
DataDir: MakeDataDir(ctx),
|
|
||||||
KeyStoreDir: ctx.GlobalString(KeyStoreDirFlag.Name),
|
|
||||||
UseLightweightKDF: ctx.GlobalBool(LightKDFFlag.Name),
|
|
||||||
PrivateKey: MakeNodeKey(ctx),
|
|
||||||
Name: name,
|
|
||||||
Version: vsn,
|
|
||||||
UserIdent: makeNodeUserIdent(ctx),
|
|
||||||
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name), // always disable v4 discovery in light client mode
|
|
||||||
DiscoveryV5: ctx.GlobalBool(DiscoveryV5Flag.Name) || forceV5Discovery,
|
|
||||||
DiscoveryV5Addr: MakeDiscoveryV5Address(ctx),
|
|
||||||
BootstrapNodes: MakeBootstrapNodes(ctx),
|
|
||||||
BootstrapNodesV5: MakeBootstrapNodesV5(ctx),
|
|
||||||
ListenAddr: MakeListenAddress(ctx),
|
|
||||||
NAT: MakeNAT(ctx),
|
|
||||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
|
||||||
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
|
|
||||||
IPCPath: MakeIPCPath(ctx),
|
|
||||||
HTTPHost: MakeHTTPRpcHost(ctx),
|
|
||||||
HTTPPort: ctx.GlobalInt(RPCPortFlag.Name),
|
|
||||||
HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name),
|
|
||||||
HTTPModules: MakeRPCModules(ctx.GlobalString(RPCApiFlag.Name)),
|
|
||||||
WSHost: MakeWSRpcHost(ctx),
|
|
||||||
WSPort: ctx.GlobalInt(WSPortFlag.Name),
|
|
||||||
WSOrigins: ctx.GlobalString(WSAllowedOriginsFlag.Name),
|
|
||||||
WSModules: MakeRPCModules(ctx.GlobalString(WSApiFlag.Name)),
|
|
||||||
}
|
|
||||||
if ctx.GlobalBool(DevModeFlag.Name) {
|
|
||||||
if !ctx.GlobalIsSet(DataDirFlag.Name) {
|
|
||||||
config.DataDir = filepath.Join(os.TempDir(), "/ethereum_dev_mode")
|
|
||||||
}
|
|
||||||
// --dev mode does not need p2p networking.
|
|
||||||
config.MaxPeers = 0
|
|
||||||
config.ListenAddr = ":0"
|
|
||||||
}
|
|
||||||
if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
|
if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
|
||||||
list, err := netutil.ParseNetlist(netrestrict)
|
list, err := netutil.ParseNetlist(netrestrict)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
|
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
|
||||||
}
|
}
|
||||||
config.NetRestrict = list
|
cfg.NetRestrict = list
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, err := node.New(config)
|
if ctx.GlobalBool(DevModeFlag.Name) {
|
||||||
if err != nil {
|
// --dev mode can't use p2p networking.
|
||||||
Fatalf("Failed to create the protocol stack: %v", err)
|
cfg.MaxPeers = 0
|
||||||
|
cfg.ListenAddr = ":0"
|
||||||
|
cfg.NoDiscovery = true
|
||||||
|
cfg.DiscoveryV5 = false
|
||||||
}
|
}
|
||||||
return stack
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterEthService configures eth.Ethereum from command line flags and adds it to the
|
// SetNodeConfig applies node-related command line flags to the config.
|
||||||
// given node.
|
func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||||
func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
SetP2PConfig(ctx, &cfg.P2P)
|
||||||
|
setIPC(ctx, cfg)
|
||||||
|
setHTTP(ctx, cfg)
|
||||||
|
setWS(ctx, cfg)
|
||||||
|
setNodeUserIdent(ctx, cfg)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalIsSet(DataDirFlag.Name):
|
||||||
|
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
|
||||||
|
case ctx.GlobalBool(DevModeFlag.Name):
|
||||||
|
cfg.DataDir = filepath.Join(os.TempDir(), "ethereum_dev_mode")
|
||||||
|
case ctx.GlobalBool(TestNetFlag.Name):
|
||||||
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
|
||||||
|
cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(LightKDFFlag.Name) {
|
||||||
|
cfg.UseLightweightKDF = ctx.GlobalBool(LightKDFFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
|
||||||
|
if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
|
||||||
|
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(GpoPercentileFlag.Name) {
|
||||||
|
cfg.Percentile = ctx.GlobalInt(GpoPercentileFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEthash(ctx *cli.Context, cfg *eth.Config) {
|
||||||
|
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
|
||||||
|
cfg.EthashCacheDir = ctx.GlobalString(EthashCacheDirFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
|
||||||
|
cfg.EthashDatasetDir = ctx.GlobalString(EthashDatasetDirFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(EthashCachesInMemoryFlag.Name) {
|
||||||
|
cfg.EthashCachesInMem = ctx.GlobalInt(EthashCachesInMemoryFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(EthashCachesOnDiskFlag.Name) {
|
||||||
|
cfg.EthashCachesOnDisk = ctx.GlobalInt(EthashCachesOnDiskFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(EthashDatasetsInMemoryFlag.Name) {
|
||||||
|
cfg.EthashDatasetsInMem = ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(EthashDatasetsOnDiskFlag.Name) {
|
||||||
|
cfg.EthashDatasetsOnDisk = ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
|
||||||
|
set := make([]string, 0, 1)
|
||||||
|
for _, flag := range flags {
|
||||||
|
if ctx.GlobalIsSet(flag.GetName()) {
|
||||||
|
set = append(set, "--"+flag.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(set) > 1 {
|
||||||
|
Fatalf("flags %v can't be used at the same time", strings.Join(set, ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags
|
||||||
networks, netFlags := 0, []cli.BoolFlag{DevModeFlag, TestNetFlag}
|
checkExclusive(ctx, DevModeFlag, TestNetFlag)
|
||||||
for _, flag := range netFlags {
|
checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag)
|
||||||
if ctx.GlobalBool(flag.Name) {
|
|
||||||
networks++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if networks > 1 {
|
|
||||||
Fatalf("The %v flags are mutually exclusive", netFlags)
|
|
||||||
}
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||||
|
setEtherbase(ctx, ks, cfg)
|
||||||
|
setGPO(ctx, &cfg.GPO)
|
||||||
|
setEthash(ctx, cfg)
|
||||||
|
|
||||||
ethConf := ð.Config{
|
switch {
|
||||||
Etherbase: MakeEtherbase(ks, ctx),
|
case ctx.GlobalIsSet(SyncModeFlag.Name):
|
||||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
|
||||||
LightMode: ctx.GlobalBool(LightModeFlag.Name),
|
case ctx.GlobalBool(FastSyncFlag.Name):
|
||||||
LightServ: ctx.GlobalInt(LightServFlag.Name),
|
cfg.SyncMode = downloader.FastSync
|
||||||
LightPeers: ctx.GlobalInt(LightPeersFlag.Name),
|
case ctx.GlobalBool(LightModeFlag.Name):
|
||||||
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
|
cfg.SyncMode = downloader.LightSync
|
||||||
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
|
}
|
||||||
DatabaseHandles: MakeDatabaseHandles(),
|
if ctx.GlobalIsSet(LightServFlag.Name) {
|
||||||
NetworkId: ctx.GlobalInt(NetworkIdFlag.Name),
|
cfg.LightServ = ctx.GlobalInt(LightServFlag.Name)
|
||||||
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
}
|
||||||
ExtraData: MakeMinerExtra(extra, ctx),
|
if ctx.GlobalIsSet(LightPeersFlag.Name) {
|
||||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
|
||||||
GasPrice: GlobalBig(ctx, GasPriceFlag.Name),
|
}
|
||||||
GpoBlocks: ctx.GlobalInt(GpoBlocksFlag.Name),
|
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
GpoPercentile: ctx.GlobalInt(GpoPercentileFlag.Name),
|
cfg.NetworkId = ctx.GlobalInt(NetworkIdFlag.Name)
|
||||||
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
|
||||||
EthashCacheDir: MakeEthashCacheDir(ctx),
|
|
||||||
EthashCachesInMem: ctx.GlobalInt(EthashCachesInMemoryFlag.Name),
|
|
||||||
EthashCachesOnDisk: ctx.GlobalInt(EthashCachesOnDiskFlag.Name),
|
|
||||||
EthashDatasetDir: MakeEthashDatasetDir(ctx),
|
|
||||||
EthashDatasetsInMem: ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name),
|
|
||||||
EthashDatasetsOnDisk: ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name),
|
|
||||||
EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override any default configs in dev mode or the test net
|
// Ethereum needs to know maxPeers to calculate the light server peer ratio.
|
||||||
|
// TODO(fjl): ensure Ethereum can get MaxPeers from node.
|
||||||
|
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(CacheFlag.Name) {
|
||||||
|
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name)
|
||||||
|
}
|
||||||
|
cfg.DatabaseHandles = makeDatabaseHandles()
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(MinerThreadsFlag.Name) {
|
||||||
|
cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(DocRootFlag.Name) {
|
||||||
|
cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(ExtraDataFlag.Name) {
|
||||||
|
cfg.ExtraData = []byte(ctx.GlobalString(ExtraDataFlag.Name))
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||||
|
cfg.GasPrice = GlobalBig(ctx, GasPriceFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(SolcPathFlag.Name) {
|
||||||
|
cfg.SolcPath = ctx.GlobalString(SolcPathFlag.Name)
|
||||||
|
}
|
||||||
|
if ctx.GlobalIsSet(VMEnableDebugFlag.Name) {
|
||||||
|
// TODO(fjl): force-enable this in --dev mode
|
||||||
|
cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override any default configs for --dev and --testnet.
|
||||||
switch {
|
switch {
|
||||||
case ctx.GlobalBool(TestNetFlag.Name):
|
case ctx.GlobalBool(TestNetFlag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
ethConf.NetworkId = 3
|
cfg.NetworkId = 3
|
||||||
}
|
}
|
||||||
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
cfg.Genesis = core.DefaultTestnetGenesisBlock()
|
||||||
case ctx.GlobalBool(DevModeFlag.Name):
|
case ctx.GlobalBool(DevModeFlag.Name):
|
||||||
ethConf.Genesis = core.DevGenesisBlock()
|
cfg.Genesis = core.DevGenesisBlock()
|
||||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||||
ethConf.GasPrice = new(big.Int)
|
cfg.GasPrice = new(big.Int)
|
||||||
}
|
}
|
||||||
ethConf.PowTest = true
|
cfg.PowTest = true
|
||||||
}
|
}
|
||||||
// Override any global options pertaining to the Ethereum protocol
|
|
||||||
|
// TODO(fjl): move trie cache generations into config
|
||||||
if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 {
|
if gen := ctx.GlobalInt(TrieCacheGenFlag.Name); gen > 0 {
|
||||||
state.MaxTrieCacheGen = uint16(gen)
|
state.MaxTrieCacheGen = uint16(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ethConf.LightMode {
|
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
|
||||||
return les.New(ctx, ethConf)
|
|
||||||
}); err != nil {
|
|
||||||
Fatalf("Failed to register the Ethereum light node service: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterEthService adds an Ethereum client to the stack.
|
||||||
|
func RegisterEthService(stack *node.Node, cfg *eth.Config) {
|
||||||
|
var err error
|
||||||
|
if cfg.SyncMode == downloader.LightSync {
|
||||||
|
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
|
return les.New(ctx, cfg)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
fullNode, err := eth.New(ctx, ethConf)
|
fullNode, err := eth.New(ctx, cfg)
|
||||||
if fullNode != nil && ethConf.LightServ > 0 {
|
if fullNode != nil && cfg.LightServ > 0 {
|
||||||
ls, _ := les.NewLesServer(fullNode, ethConf)
|
ls, _ := les.NewLesServer(fullNode, cfg)
|
||||||
fullNode.AddLesServer(ls)
|
fullNode.AddLesServer(ls)
|
||||||
}
|
}
|
||||||
return fullNode, err
|
return fullNode, err
|
||||||
}); err != nil {
|
})
|
||||||
Fatalf("Failed to register the Ethereum full node service: %v", err)
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Failed to register the Ethereum service: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,6 +906,7 @@ func RegisterEthStatsService(stack *node.Node, url string) {
|
|||||||
|
|
||||||
// SetupNetwork configures the system for either the main net or some test network.
|
// SetupNetwork configures the system for either the main net or some test network.
|
||||||
func SetupNetwork(ctx *cli.Context) {
|
func SetupNetwork(ctx *cli.Context) {
|
||||||
|
// TODO(fjl): move target gas limit into config
|
||||||
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
|
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,7 +922,7 @@ func ChainDbName(ctx *cli.Context) string {
|
|||||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
var (
|
var (
|
||||||
cache = ctx.GlobalInt(CacheFlag.Name)
|
cache = ctx.GlobalInt(CacheFlag.Name)
|
||||||
handles = MakeDatabaseHandles()
|
handles = makeDatabaseHandles()
|
||||||
name = ChainDbName(ctx)
|
name = ChainDbName(ctx)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -257,7 +257,6 @@ func initialize() {
|
|||||||
Config: p2p.Config{
|
Config: p2p.Config{
|
||||||
PrivateKey: nodeid,
|
PrivateKey: nodeid,
|
||||||
MaxPeers: maxPeers,
|
MaxPeers: maxPeers,
|
||||||
Discovery: true,
|
|
||||||
Name: common.MakeName("wnode", "5.0"),
|
Name: common.MakeName("wnode", "5.0"),
|
||||||
Protocols: shh.Protocols(),
|
Protocols: shh.Protocols(),
|
||||||
ListenAddr: *argIP,
|
ListenAddr: *argIP,
|
||||||
|
@ -51,6 +51,9 @@ func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
|
|||||||
|
|
||||||
// MarshalText implements encoding.TextMarshaler.
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
|
func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
|
||||||
|
if i == nil {
|
||||||
|
return []byte("0x0"), nil
|
||||||
|
}
|
||||||
return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
|
return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a networkless protocol stack and start an Ethereum service within
|
// Create a networkless protocol stack and start an Ethereum service within
|
||||||
stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance, NoDiscovery: true})
|
stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create node: %v", err)
|
t.Fatalf("failed to create node: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,15 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"runtime"
|
||||||
"regexp"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
@ -43,55 +44,10 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
|
|
||||||
portInUseErrRE = regexp.MustCompile("address already in use")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
// The genesis block, which is inserted if the database is empty.
|
|
||||||
// If nil, the Ethereum main net block is used.
|
|
||||||
Genesis *core.Genesis
|
|
||||||
|
|
||||||
NetworkId int // Network ID to use for selecting peers to connect to
|
|
||||||
|
|
||||||
FastSync bool // Enables the state download based fast synchronisation algorithm
|
|
||||||
LightMode bool // Running in light client mode
|
|
||||||
LightServ int // Maximum percentage of time allowed for serving LES requests
|
|
||||||
LightPeers int // Maximum number of LES client peers
|
|
||||||
MaxPeers int // Maximum number of global peers
|
|
||||||
|
|
||||||
SkipBcVersionCheck bool // e.g. blockchain export
|
|
||||||
DatabaseCache int
|
|
||||||
DatabaseHandles int
|
|
||||||
|
|
||||||
DocRoot string
|
|
||||||
PowFake bool
|
|
||||||
PowTest bool
|
|
||||||
PowShared bool
|
|
||||||
ExtraData []byte
|
|
||||||
|
|
||||||
EthashCacheDir string
|
|
||||||
EthashCachesInMem int
|
|
||||||
EthashCachesOnDisk int
|
|
||||||
EthashDatasetDir string
|
|
||||||
EthashDatasetsInMem int
|
|
||||||
EthashDatasetsOnDisk int
|
|
||||||
|
|
||||||
Etherbase common.Address
|
|
||||||
GasPrice *big.Int
|
|
||||||
MinerThreads int
|
|
||||||
SolcPath string
|
|
||||||
|
|
||||||
GpoBlocks int
|
|
||||||
GpoPercentile int
|
|
||||||
|
|
||||||
EnablePreimageRecording bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type LesServer interface {
|
type LesServer interface {
|
||||||
Start(srvr *p2p.Server)
|
Start(srvr *p2p.Server)
|
||||||
Stop()
|
Stop()
|
||||||
@ -137,6 +93,13 @@ func (s *Ethereum) AddLesServer(ls LesServer) {
|
|||||||
// New creates a new Ethereum object (including the
|
// New creates a new Ethereum object (including the
|
||||||
// initialisation of the common Ethereum object)
|
// initialisation of the common Ethereum object)
|
||||||
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
|
if config.SyncMode == downloader.LightSync {
|
||||||
|
return nil, errors.New("can't run eth.Ethereum in light sync mode, use les.LightEthereum")
|
||||||
|
}
|
||||||
|
if !config.SyncMode.IsValid() {
|
||||||
|
return nil, fmt.Errorf("invalid sync mode %d", config.SyncMode)
|
||||||
|
}
|
||||||
|
|
||||||
chainDb, err := CreateDB(ctx, config, "chaindata")
|
chainDb, err := CreateDB(ctx, config, "chaindata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -201,25 +164,41 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
|
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
|
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
|
||||||
eth.miner.SetGasPrice(config.GasPrice)
|
eth.miner.SetGasPrice(config.GasPrice)
|
||||||
eth.miner.SetExtra(config.ExtraData)
|
eth.miner.SetExtra(makeExtraData(config.ExtraData))
|
||||||
|
|
||||||
eth.ApiBackend = &EthApiBackend{eth, nil}
|
eth.ApiBackend = &EthApiBackend{eth, nil}
|
||||||
gpoParams := gasprice.Config{
|
gpoParams := config.GPO
|
||||||
Blocks: config.GpoBlocks,
|
if gpoParams.Default == nil {
|
||||||
Percentile: config.GpoPercentile,
|
gpoParams.Default = config.GasPrice
|
||||||
Default: config.GasPrice,
|
|
||||||
}
|
}
|
||||||
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
|
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
|
||||||
|
|
||||||
return eth, nil
|
return eth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeExtraData(extra []byte) []byte {
|
||||||
|
if len(extra) == 0 {
|
||||||
|
// create default extradata
|
||||||
|
extra, _ = rlp.EncodeToBytes([]interface{}{
|
||||||
|
uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch),
|
||||||
|
"geth",
|
||||||
|
runtime.Version(),
|
||||||
|
runtime.GOOS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
||||||
|
log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
|
||||||
|
extra = nil
|
||||||
|
}
|
||||||
|
return extra
|
||||||
|
}
|
||||||
|
|
||||||
// CreateDB creates the chain database.
|
// CreateDB creates the chain database.
|
||||||
func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) {
|
func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) {
|
||||||
db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles)
|
db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles)
|
||||||
@ -415,8 +394,3 @@ func (s *Ethereum) Stop() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function will wait for a shutdown and resumes main thread execution
|
|
||||||
func (s *Ethereum) WaitForShutdown() {
|
|
||||||
<-s.shutdownChan
|
|
||||||
}
|
|
||||||
|
117
eth/config.go
Normal file
117
eth/config.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2014 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 eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConfig contains default settings for use on the Ethereum main net.
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
SyncMode: downloader.FastSync,
|
||||||
|
EthashCachesInMem: 2,
|
||||||
|
EthashCachesOnDisk: 3,
|
||||||
|
EthashDatasetsInMem: 1,
|
||||||
|
EthashDatasetsOnDisk: 2,
|
||||||
|
NetworkId: 1,
|
||||||
|
LightPeers: 20,
|
||||||
|
DatabaseCache: 128,
|
||||||
|
GasPrice: big.NewInt(20 * params.Shannon),
|
||||||
|
|
||||||
|
GPO: gasprice.Config{
|
||||||
|
Blocks: 10,
|
||||||
|
Percentile: 50,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
if home == "" {
|
||||||
|
if user, err := user.Current(); err == nil {
|
||||||
|
home = user.HomeDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
DefaultConfig.EthashDatasetDir = filepath.Join(home, "AppData", "Ethash")
|
||||||
|
} else {
|
||||||
|
DefaultConfig.EthashDatasetDir = filepath.Join(home, ".ethash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate gencodec -type Config -field-override configMarshaling -formats toml -out gen_config.go
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// The genesis block, which is inserted if the database is empty.
|
||||||
|
// If nil, the Ethereum main net block is used.
|
||||||
|
Genesis *core.Genesis `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Protocol options
|
||||||
|
NetworkId int // Network ID to use for selecting peers to connect to
|
||||||
|
SyncMode downloader.SyncMode
|
||||||
|
|
||||||
|
// Light client options
|
||||||
|
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
||||||
|
LightPeers int `toml:",omitempty"` // Maximum number of LES client peers
|
||||||
|
MaxPeers int `toml:"-"` // Maximum number of global peers
|
||||||
|
|
||||||
|
// Database options
|
||||||
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
|
DatabaseHandles int `toml:"-"`
|
||||||
|
DatabaseCache int
|
||||||
|
|
||||||
|
// Mining-related options
|
||||||
|
Etherbase common.Address `toml:",omitempty"`
|
||||||
|
MinerThreads int `toml:",omitempty"`
|
||||||
|
ExtraData []byte `toml:",omitempty"`
|
||||||
|
GasPrice *big.Int
|
||||||
|
|
||||||
|
// Ethash options
|
||||||
|
EthashCacheDir string
|
||||||
|
EthashCachesInMem int
|
||||||
|
EthashCachesOnDisk int
|
||||||
|
EthashDatasetDir string
|
||||||
|
EthashDatasetsInMem int
|
||||||
|
EthashDatasetsOnDisk int
|
||||||
|
|
||||||
|
// Gas Price Oracle options
|
||||||
|
GPO gasprice.Config
|
||||||
|
|
||||||
|
// Enables tracking of SHA3 preimages in the VM
|
||||||
|
EnablePreimageRecording bool
|
||||||
|
|
||||||
|
// Miscellaneous options
|
||||||
|
SolcPath string
|
||||||
|
DocRoot string `toml:"-"`
|
||||||
|
PowFake bool `toml:"-"`
|
||||||
|
PowTest bool `toml:"-"`
|
||||||
|
PowShared bool `toml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type configMarshaling struct {
|
||||||
|
ExtraData hexutil.Bytes
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package downloader
|
package downloader
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// SyncMode represents the synchronisation mode of the downloader.
|
// SyncMode represents the synchronisation mode of the downloader.
|
||||||
type SyncMode int
|
type SyncMode int
|
||||||
|
|
||||||
@ -25,6 +27,10 @@ const (
|
|||||||
LightSync // Download only the headers and terminate afterwards
|
LightSync // Download only the headers and terminate afterwards
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (mode SyncMode) IsValid() bool {
|
||||||
|
return mode >= FullSync && mode <= LightSync
|
||||||
|
}
|
||||||
|
|
||||||
// String implements the stringer interface.
|
// String implements the stringer interface.
|
||||||
func (mode SyncMode) String() string {
|
func (mode SyncMode) String() string {
|
||||||
switch mode {
|
switch mode {
|
||||||
@ -38,3 +44,30 @@ func (mode SyncMode) String() string {
|
|||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mode SyncMode) MarshalText() ([]byte, error) {
|
||||||
|
switch mode {
|
||||||
|
case FullSync:
|
||||||
|
return []byte("full"), nil
|
||||||
|
case FastSync:
|
||||||
|
return []byte("fast"), nil
|
||||||
|
case LightSync:
|
||||||
|
return []byte("light"), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown sync mode %d", mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mode *SyncMode) UnmarshalText(text []byte) error {
|
||||||
|
switch string(text) {
|
||||||
|
case "full":
|
||||||
|
*mode = FullSync
|
||||||
|
case "fast":
|
||||||
|
*mode = FastSync
|
||||||
|
case "light":
|
||||||
|
*mode = LightSync
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -33,7 +33,7 @@ var maxPrice = big.NewInt(500 * params.Shannon)
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Blocks int
|
Blocks int
|
||||||
Percentile int
|
Percentile int
|
||||||
Default *big.Int
|
Default *big.Int `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle recommends gas prices based on the content of recent
|
// Oracle recommends gas prices based on the content of recent
|
||||||
|
186
eth/gen_config.go
Normal file
186
eth/gen_config.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package eth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c Config) MarshalTOML() (interface{}, error) {
|
||||||
|
type Config struct {
|
||||||
|
Genesis *core.Genesis `toml:",omitempty"`
|
||||||
|
NetworkId int
|
||||||
|
SyncMode downloader.SyncMode
|
||||||
|
LightServ int `toml:",omitempty"`
|
||||||
|
LightPeers int `toml:",omitempty"`
|
||||||
|
MaxPeers int `toml:"-"`
|
||||||
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
|
DatabaseHandles int `toml:"-"`
|
||||||
|
DatabaseCache int
|
||||||
|
Etherbase common.Address `toml:",omitempty"`
|
||||||
|
MinerThreads int `toml:",omitempty"`
|
||||||
|
ExtraData hexutil.Bytes `toml:",omitempty"`
|
||||||
|
GasPrice *big.Int
|
||||||
|
EthashCacheDir string
|
||||||
|
EthashCachesInMem int
|
||||||
|
EthashCachesOnDisk int
|
||||||
|
EthashDatasetDir string
|
||||||
|
EthashDatasetsInMem int
|
||||||
|
EthashDatasetsOnDisk int
|
||||||
|
GPO gasprice.Config
|
||||||
|
EnablePreimageRecording bool
|
||||||
|
SolcPath string
|
||||||
|
DocRoot string `toml:"-"`
|
||||||
|
PowFake bool `toml:"-"`
|
||||||
|
PowTest bool `toml:"-"`
|
||||||
|
PowShared bool `toml:"-"`
|
||||||
|
}
|
||||||
|
var enc Config
|
||||||
|
enc.Genesis = c.Genesis
|
||||||
|
enc.NetworkId = c.NetworkId
|
||||||
|
enc.SyncMode = c.SyncMode
|
||||||
|
enc.LightServ = c.LightServ
|
||||||
|
enc.LightPeers = c.LightPeers
|
||||||
|
enc.MaxPeers = c.MaxPeers
|
||||||
|
enc.SkipBcVersionCheck = c.SkipBcVersionCheck
|
||||||
|
enc.DatabaseHandles = c.DatabaseHandles
|
||||||
|
enc.DatabaseCache = c.DatabaseCache
|
||||||
|
enc.Etherbase = c.Etherbase
|
||||||
|
enc.MinerThreads = c.MinerThreads
|
||||||
|
enc.ExtraData = c.ExtraData
|
||||||
|
enc.GasPrice = c.GasPrice
|
||||||
|
enc.EthashCacheDir = c.EthashCacheDir
|
||||||
|
enc.EthashCachesInMem = c.EthashCachesInMem
|
||||||
|
enc.EthashCachesOnDisk = c.EthashCachesOnDisk
|
||||||
|
enc.EthashDatasetDir = c.EthashDatasetDir
|
||||||
|
enc.EthashDatasetsInMem = c.EthashDatasetsInMem
|
||||||
|
enc.EthashDatasetsOnDisk = c.EthashDatasetsOnDisk
|
||||||
|
enc.GPO = c.GPO
|
||||||
|
enc.EnablePreimageRecording = c.EnablePreimageRecording
|
||||||
|
enc.SolcPath = c.SolcPath
|
||||||
|
enc.DocRoot = c.DocRoot
|
||||||
|
enc.PowFake = c.PowFake
|
||||||
|
enc.PowTest = c.PowTest
|
||||||
|
enc.PowShared = c.PowShared
|
||||||
|
return &enc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
|
||||||
|
type Config struct {
|
||||||
|
Genesis *core.Genesis `toml:",omitempty"`
|
||||||
|
NetworkId *int
|
||||||
|
SyncMode *downloader.SyncMode
|
||||||
|
LightServ *int `toml:",omitempty"`
|
||||||
|
LightPeers *int `toml:",omitempty"`
|
||||||
|
MaxPeers *int `toml:"-"`
|
||||||
|
SkipBcVersionCheck *bool `toml:"-"`
|
||||||
|
DatabaseHandles *int `toml:"-"`
|
||||||
|
DatabaseCache *int
|
||||||
|
Etherbase *common.Address `toml:",omitempty"`
|
||||||
|
MinerThreads *int `toml:",omitempty"`
|
||||||
|
ExtraData hexutil.Bytes `toml:",omitempty"`
|
||||||
|
GasPrice *big.Int
|
||||||
|
EthashCacheDir *string
|
||||||
|
EthashCachesInMem *int
|
||||||
|
EthashCachesOnDisk *int
|
||||||
|
EthashDatasetDir *string
|
||||||
|
EthashDatasetsInMem *int
|
||||||
|
EthashDatasetsOnDisk *int
|
||||||
|
GPO *gasprice.Config
|
||||||
|
EnablePreimageRecording *bool
|
||||||
|
SolcPath *string
|
||||||
|
DocRoot *string `toml:"-"`
|
||||||
|
PowFake *bool `toml:"-"`
|
||||||
|
PowTest *bool `toml:"-"`
|
||||||
|
PowShared *bool `toml:"-"`
|
||||||
|
}
|
||||||
|
var dec Config
|
||||||
|
if err := unmarshal(&dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Genesis != nil {
|
||||||
|
c.Genesis = dec.Genesis
|
||||||
|
}
|
||||||
|
if dec.NetworkId != nil {
|
||||||
|
c.NetworkId = *dec.NetworkId
|
||||||
|
}
|
||||||
|
if dec.SyncMode != nil {
|
||||||
|
c.SyncMode = *dec.SyncMode
|
||||||
|
}
|
||||||
|
if dec.LightServ != nil {
|
||||||
|
c.LightServ = *dec.LightServ
|
||||||
|
}
|
||||||
|
if dec.LightPeers != nil {
|
||||||
|
c.LightPeers = *dec.LightPeers
|
||||||
|
}
|
||||||
|
if dec.MaxPeers != nil {
|
||||||
|
c.MaxPeers = *dec.MaxPeers
|
||||||
|
}
|
||||||
|
if dec.SkipBcVersionCheck != nil {
|
||||||
|
c.SkipBcVersionCheck = *dec.SkipBcVersionCheck
|
||||||
|
}
|
||||||
|
if dec.DatabaseHandles != nil {
|
||||||
|
c.DatabaseHandles = *dec.DatabaseHandles
|
||||||
|
}
|
||||||
|
if dec.DatabaseCache != nil {
|
||||||
|
c.DatabaseCache = *dec.DatabaseCache
|
||||||
|
}
|
||||||
|
if dec.Etherbase != nil {
|
||||||
|
c.Etherbase = *dec.Etherbase
|
||||||
|
}
|
||||||
|
if dec.MinerThreads != nil {
|
||||||
|
c.MinerThreads = *dec.MinerThreads
|
||||||
|
}
|
||||||
|
if dec.ExtraData != nil {
|
||||||
|
c.ExtraData = dec.ExtraData
|
||||||
|
}
|
||||||
|
if dec.GasPrice != nil {
|
||||||
|
c.GasPrice = dec.GasPrice
|
||||||
|
}
|
||||||
|
if dec.EthashCacheDir != nil {
|
||||||
|
c.EthashCacheDir = *dec.EthashCacheDir
|
||||||
|
}
|
||||||
|
if dec.EthashCachesInMem != nil {
|
||||||
|
c.EthashCachesInMem = *dec.EthashCachesInMem
|
||||||
|
}
|
||||||
|
if dec.EthashCachesOnDisk != nil {
|
||||||
|
c.EthashCachesOnDisk = *dec.EthashCachesOnDisk
|
||||||
|
}
|
||||||
|
if dec.EthashDatasetDir != nil {
|
||||||
|
c.EthashDatasetDir = *dec.EthashDatasetDir
|
||||||
|
}
|
||||||
|
if dec.EthashDatasetsInMem != nil {
|
||||||
|
c.EthashDatasetsInMem = *dec.EthashDatasetsInMem
|
||||||
|
}
|
||||||
|
if dec.EthashDatasetsOnDisk != nil {
|
||||||
|
c.EthashDatasetsOnDisk = *dec.EthashDatasetsOnDisk
|
||||||
|
}
|
||||||
|
if dec.GPO != nil {
|
||||||
|
c.GPO = *dec.GPO
|
||||||
|
}
|
||||||
|
if dec.EnablePreimageRecording != nil {
|
||||||
|
c.EnablePreimageRecording = *dec.EnablePreimageRecording
|
||||||
|
}
|
||||||
|
if dec.SolcPath != nil {
|
||||||
|
c.SolcPath = *dec.SolcPath
|
||||||
|
}
|
||||||
|
if dec.DocRoot != nil {
|
||||||
|
c.DocRoot = *dec.DocRoot
|
||||||
|
}
|
||||||
|
if dec.PowFake != nil {
|
||||||
|
c.PowFake = *dec.PowFake
|
||||||
|
}
|
||||||
|
if dec.PowTest != nil {
|
||||||
|
c.PowTest = *dec.PowTest
|
||||||
|
}
|
||||||
|
if dec.PowShared != nil {
|
||||||
|
c.PowShared = *dec.PowShared
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -96,7 +96,7 @@ type ProtocolManager struct {
|
|||||||
|
|
||||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||||
// with the ethereum network.
|
// with the ethereum network.
|
||||||
func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, networkId int, maxPeers int, mux *event.TypeMux, txpool txPool, engine consensus.Engine, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||||
// Create the protocol manager with the base fields
|
// Create the protocol manager with the base fields
|
||||||
manager := &ProtocolManager{
|
manager := &ProtocolManager{
|
||||||
networkId: networkId,
|
networkId: networkId,
|
||||||
@ -113,18 +113,18 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
|
|||||||
quitSync: make(chan struct{}),
|
quitSync: make(chan struct{}),
|
||||||
}
|
}
|
||||||
// Figure out whether to allow fast sync or not
|
// Figure out whether to allow fast sync or not
|
||||||
if fastSync && blockchain.CurrentBlock().NumberU64() > 0 {
|
if mode == downloader.FastSync && blockchain.CurrentBlock().NumberU64() > 0 {
|
||||||
log.Warn("Blockchain not empty, fast sync disabled")
|
log.Warn("Blockchain not empty, fast sync disabled")
|
||||||
fastSync = false
|
mode = downloader.FullSync
|
||||||
}
|
}
|
||||||
if fastSync {
|
if mode == downloader.FastSync {
|
||||||
manager.fastSync = uint32(1)
|
manager.fastSync = uint32(1)
|
||||||
}
|
}
|
||||||
// Initiate a sub-protocol for every implemented version we can handle
|
// Initiate a sub-protocol for every implemented version we can handle
|
||||||
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
|
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
|
||||||
for i, version := range ProtocolVersions {
|
for i, version := range ProtocolVersions {
|
||||||
// Skip protocol version if incompatible with the mode of operation
|
// Skip protocol version if incompatible with the mode of operation
|
||||||
if fastSync && version < eth63 {
|
if mode == downloader.FastSync && version < eth63 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Compatible; initialise the sub-protocol
|
// Compatible; initialise the sub-protocol
|
||||||
@ -159,7 +159,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
|
|||||||
return nil, errIncompatibleConfig
|
return nil, errIncompatibleConfig
|
||||||
}
|
}
|
||||||
// Construct the different synchronisation mechanisms
|
// Construct the different synchronisation mechanisms
|
||||||
manager.downloader = downloader.New(downloader.FullSync, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash,
|
manager.downloader = downloader.New(mode, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash,
|
||||||
blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead,
|
blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead,
|
||||||
blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback,
|
blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.blockchain.InsertChain, blockchain.InsertReceiptChain, blockchain.Rollback,
|
||||||
manager.removePeer)
|
manager.removePeer)
|
||||||
|
@ -44,11 +44,11 @@ func TestProtocolCompatibility(t *testing.T) {
|
|||||||
// Define the compatibility chart
|
// Define the compatibility chart
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
version uint
|
version uint
|
||||||
fastSync bool
|
mode downloader.SyncMode
|
||||||
compatible bool
|
compatible bool
|
||||||
}{
|
}{
|
||||||
{61, false, true}, {62, false, true}, {63, false, true},
|
{61, downloader.FullSync, true}, {62, downloader.FullSync, true}, {63, downloader.FullSync, true},
|
||||||
{61, true, false}, {62, true, false}, {63, true, true},
|
{61, downloader.FastSync, false}, {62, downloader.FastSync, false}, {63, downloader.FastSync, true},
|
||||||
}
|
}
|
||||||
// Make sure anything we screw up is restored
|
// Make sure anything we screw up is restored
|
||||||
backup := ProtocolVersions
|
backup := ProtocolVersions
|
||||||
@ -58,7 +58,7 @@ func TestProtocolCompatibility(t *testing.T) {
|
|||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ProtocolVersions = []uint{tt.version}
|
ProtocolVersions = []uint{tt.version}
|
||||||
|
|
||||||
pm, err := newTestProtocolManager(tt.fastSync, 0, nil, nil)
|
pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
|
||||||
if pm != nil {
|
if pm != nil {
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
|
|||||||
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
|
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
|
||||||
|
|
||||||
func testGetBlockHeaders(t *testing.T, protocol int) {
|
func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
|
|||||||
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
|
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
|
||||||
|
|
||||||
func testGetBlockBodies(t *testing.T, protocol int) {
|
func testGetBlockBodies(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
@ -339,7 +339,7 @@ func testGetNodeData(t *testing.T, protocol int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
pm := newTestProtocolManagerMust(t, false, 4, generator, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
@ -431,7 +431,7 @@ func testGetReceipt(t *testing.T, protocol int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
pm := newTestProtocolManagerMust(t, false, 4, generator, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
@ -476,7 +476,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
|||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = core.NewBlockChain(db, config, pow, evmux, vm.Config{})
|
blockchain, _ = core.NewBlockChain(db, config, pow, evmux, vm.Config{})
|
||||||
)
|
)
|
||||||
pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db)
|
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to start test protocol manager: %v", err)
|
t.Fatalf("failed to start test protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
@ -48,7 +49,7 @@ var (
|
|||||||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, and potential notification
|
// with the given number of blocks already known, and potential notification
|
||||||
// channels for different events.
|
// channels for different events.
|
||||||
func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
|
func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
|
||||||
var (
|
var (
|
||||||
evmux = new(event.TypeMux)
|
evmux = new(event.TypeMux)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
@ -65,7 +66,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pm, err := NewProtocolManager(gspec.Config, fastSync, NetworkId, 1000, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
|
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, 1000, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -77,8 +78,8 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
|
|||||||
// with the given number of blocks already known, and potential notification
|
// with the given number of blocks already known, and potential notification
|
||||||
// channels for different events. In case of an error, the constructor force-
|
// channels for different events. In case of an error, the constructor force-
|
||||||
// fails the test.
|
// fails the test.
|
||||||
func newTestProtocolManagerMust(t *testing.T, fastSync bool, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
|
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
|
||||||
pm, err := newTestProtocolManager(fastSync, blocks, generator, newtx)
|
pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
@ -172,7 +173,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
|
|||||||
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
|
func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, genesis common.Hash) {
|
||||||
msg := &statusData{
|
msg := &statusData{
|
||||||
ProtocolVersion: uint32(p.version),
|
ProtocolVersion: uint32(p.version),
|
||||||
NetworkId: uint32(NetworkId),
|
NetworkId: uint32(DefaultConfig.NetworkId),
|
||||||
TD: td,
|
TD: td,
|
||||||
CurrentBlock: head,
|
CurrentBlock: head,
|
||||||
GenesisBlock: genesis,
|
GenesisBlock: genesis,
|
||||||
|
@ -41,10 +41,7 @@ var ProtocolVersions = []uint{eth63, eth62}
|
|||||||
// Number of implemented message corresponding to different protocol versions.
|
// Number of implemented message corresponding to different protocol versions.
|
||||||
var ProtocolLengths = []uint64{17, 8}
|
var ProtocolLengths = []uint64{17, 8}
|
||||||
|
|
||||||
const (
|
const ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
|
||||||
NetworkId = 1
|
|
||||||
ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
|
|
||||||
)
|
|
||||||
|
|
||||||
// eth protocol message codes
|
// eth protocol message codes
|
||||||
const (
|
const (
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
@ -40,7 +41,7 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) }
|
|||||||
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
|
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
|
||||||
|
|
||||||
func testStatusMsgErrors(t *testing.T, protocol int) {
|
func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, false, 0, nil, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||||
td, currentBlock, genesis := pm.blockchain.Status()
|
td, currentBlock, genesis := pm.blockchain.Status()
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
|
|||||||
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
|
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: statusData{10, NetworkId, td, currentBlock, genesis},
|
code: StatusMsg, data: statusData{10, uint32(DefaultConfig.NetworkId), td, currentBlock, genesis},
|
||||||
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
|
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -62,7 +63,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
|
|||||||
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
|
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: statusData{uint32(protocol), NetworkId, td, currentBlock, common.Hash{3}},
|
code: StatusMsg, data: statusData{uint32(protocol), uint32(DefaultConfig.NetworkId), td, currentBlock, common.Hash{3}},
|
||||||
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]),
|
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -93,7 +94,7 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) }
|
|||||||
|
|
||||||
func testRecvTransactions(t *testing.T, protocol int) {
|
func testRecvTransactions(t *testing.T, protocol int) {
|
||||||
txAdded := make(chan []*types.Transaction)
|
txAdded := make(chan []*types.Transaction)
|
||||||
pm := newTestProtocolManagerMust(t, false, 0, nil, txAdded)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded)
|
||||||
pm.acceptTxs = 1 // mark synced to accept transactions
|
pm.acceptTxs = 1 // mark synced to accept transactions
|
||||||
p, _ := newTestPeer("peer", protocol, pm, true)
|
p, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
@ -120,7 +121,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) }
|
|||||||
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
|
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
|
||||||
|
|
||||||
func testSendTransactions(t *testing.T, protocol int) {
|
func testSendTransactions(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, false, 0, nil, nil)
|
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
|
|
||||||
// Fill the pool with big transactions.
|
// Fill the pool with big transactions.
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
)
|
)
|
||||||
@ -29,12 +30,12 @@ import (
|
|||||||
// imported into the blockchain.
|
// imported into the blockchain.
|
||||||
func TestFastSyncDisabling(t *testing.T) {
|
func TestFastSyncDisabling(t *testing.T) {
|
||||||
// Create a pristine protocol manager, check that fast sync is left enabled
|
// Create a pristine protocol manager, check that fast sync is left enabled
|
||||||
pmEmpty := newTestProtocolManagerMust(t, true, 0, nil, nil)
|
pmEmpty := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil)
|
||||||
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
|
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
|
||||||
t.Fatalf("fast sync disabled on pristine blockchain")
|
t.Fatalf("fast sync disabled on pristine blockchain")
|
||||||
}
|
}
|
||||||
// Create a full protocol manager, check that fast sync gets disabled
|
// Create a full protocol manager, check that fast sync gets disabled
|
||||||
pmFull := newTestProtocolManagerMust(t, true, 1024, nil, nil)
|
pmFull := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil)
|
||||||
if atomic.LoadUint32(&pmFull.fastSync) == 1 {
|
if atomic.LoadUint32(&pmFull.fastSync) == 1 {
|
||||||
t.Fatalf("fast sync not disabled on non-empty blockchain")
|
t.Fatalf("fast sync not disabled on non-empty blockchain")
|
||||||
}
|
}
|
||||||
|
@ -104,17 +104,17 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
eth.txPool = light.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain, eth.relay)
|
eth.txPool = light.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain, eth.relay)
|
||||||
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.LightMode, config.NetworkId, eth.eventMux, eth.engine, eth.blockchain, nil, chainDb, odr, relay); err != nil {
|
lightSync := config.SyncMode == downloader.LightSync
|
||||||
|
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, lightSync, config.NetworkId, eth.eventMux, eth.engine, eth.blockchain, nil, chainDb, odr, relay); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
relay.ps = eth.protocolManager.peers
|
relay.ps = eth.protocolManager.peers
|
||||||
relay.reqDist = eth.protocolManager.reqDist
|
relay.reqDist = eth.protocolManager.reqDist
|
||||||
|
|
||||||
eth.ApiBackend = &LesApiBackend{eth, nil}
|
eth.ApiBackend = &LesApiBackend{eth, nil}
|
||||||
gpoParams := gasprice.Config{
|
gpoParams := config.GPO
|
||||||
Blocks: config.GpoBlocks,
|
if gpoParams.Default == nil {
|
||||||
Percentile: config.GpoPercentile,
|
gpoParams.Default = config.GasPrice
|
||||||
Default: config.GasPrice,
|
|
||||||
}
|
}
|
||||||
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
|
eth.ApiBackend.gpo = gasprice.NewOracle(eth.ApiBackend, gpoParams)
|
||||||
return eth, nil
|
return eth, nil
|
||||||
|
@ -22,15 +22,16 @@ package geth
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
||||||
@ -112,6 +113,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
|||||||
Version: params.Version,
|
Version: params.Version,
|
||||||
DataDir: datadir,
|
DataDir: datadir,
|
||||||
KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
|
KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
|
||||||
|
P2P: p2p.Config{
|
||||||
NoDiscovery: true,
|
NoDiscovery: true,
|
||||||
DiscoveryV5: true,
|
DiscoveryV5: true,
|
||||||
DiscoveryV5Addr: ":0",
|
DiscoveryV5Addr: ":0",
|
||||||
@ -119,6 +121,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
|||||||
ListenAddr: ":0",
|
ListenAddr: ":0",
|
||||||
NAT: nat.Any(),
|
NAT: nat.Any(),
|
||||||
MaxPeers: config.MaxPeers,
|
MaxPeers: config.MaxPeers,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
rawStack, err := node.New(nodeConf)
|
rawStack, err := node.New(nodeConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -142,20 +145,13 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
|
|||||||
}
|
}
|
||||||
// Register the Ethereum protocol if requested
|
// Register the Ethereum protocol if requested
|
||||||
if config.EthereumEnabled {
|
if config.EthereumEnabled {
|
||||||
ethConf := ð.Config{
|
ethConf := eth.DefaultConfig
|
||||||
Genesis: genesis,
|
ethConf.Genesis = genesis
|
||||||
LightMode: true,
|
ethConf.SyncMode = downloader.LightSync
|
||||||
DatabaseCache: config.EthereumDatabaseCache,
|
ethConf.NetworkId = config.EthereumNetworkID
|
||||||
NetworkId: config.EthereumNetworkID,
|
ethConf.DatabaseCache = config.EthereumDatabaseCache
|
||||||
GasPrice: new(big.Int).SetUint64(20 * params.Shannon),
|
|
||||||
GpoBlocks: 10,
|
|
||||||
GpoPercentile: 50,
|
|
||||||
EthashCacheDir: "ethash",
|
|
||||||
EthashCachesInMem: 2,
|
|
||||||
EthashCachesOnDisk: 3,
|
|
||||||
}
|
|
||||||
if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
return les.New(ctx, ethConf)
|
return les.New(ctx, ðConf)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, fmt.Errorf("ethereum init: %v", err)
|
return nil, fmt.Errorf("ethereum init: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -32,10 +31,8 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -53,14 +50,14 @@ type Config struct {
|
|||||||
// Name sets the instance name of the node. It must not contain the / character and is
|
// Name sets the instance name of the node. It must not contain the / character and is
|
||||||
// used in the devp2p node identifier. The instance name of geth is "geth". If no
|
// used in the devp2p node identifier. The instance name of geth is "geth". If no
|
||||||
// value is specified, the basename of the current executable is used.
|
// value is specified, the basename of the current executable is used.
|
||||||
Name string
|
Name string `toml:"-"`
|
||||||
|
|
||||||
// UserIdent, if set, is used as an additional component in the devp2p node identifier.
|
// UserIdent, if set, is used as an additional component in the devp2p node identifier.
|
||||||
UserIdent string
|
UserIdent string `toml:",omitempty"`
|
||||||
|
|
||||||
// Version should be set to the version number of the program. It is used
|
// Version should be set to the version number of the program. It is used
|
||||||
// in the devp2p node identifier.
|
// in the devp2p node identifier.
|
||||||
Version string
|
Version string `toml:"-"`
|
||||||
|
|
||||||
// DataDir is the file system folder the node should use for any data storage
|
// DataDir is the file system folder the node should use for any data storage
|
||||||
// requirements. The configured data directory will not be directly shared with
|
// requirements. The configured data directory will not be directly shared with
|
||||||
@ -69,6 +66,9 @@ type Config struct {
|
|||||||
// in memory.
|
// in memory.
|
||||||
DataDir string
|
DataDir string
|
||||||
|
|
||||||
|
// Configuration of peer-to-peer networking.
|
||||||
|
P2P p2p.Config
|
||||||
|
|
||||||
// KeyStoreDir is the file system folder that contains private keys. The directory can
|
// KeyStoreDir is the file system folder that contains private keys. The directory can
|
||||||
// be specified as a relative path, in which case it is resolved relative to the
|
// be specified as a relative path, in which case it is resolved relative to the
|
||||||
// current directory.
|
// current directory.
|
||||||
@ -76,106 +76,55 @@ type Config struct {
|
|||||||
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of
|
// If KeyStoreDir is empty, the default location is the "keystore" subdirectory of
|
||||||
// DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory
|
// DataDir. If DataDir is unspecified and KeyStoreDir is empty, an ephemeral directory
|
||||||
// is created by New and destroyed when the node is stopped.
|
// is created by New and destroyed when the node is stopped.
|
||||||
KeyStoreDir string
|
KeyStoreDir string `toml:",omitempty"`
|
||||||
|
|
||||||
// UseLightweightKDF lowers the memory and CPU requirements of the key store
|
// UseLightweightKDF lowers the memory and CPU requirements of the key store
|
||||||
// scrypt KDF at the expense of security.
|
// scrypt KDF at the expense of security.
|
||||||
UseLightweightKDF bool
|
UseLightweightKDF bool `toml:",omitempty"`
|
||||||
|
|
||||||
// IPCPath is the requested location to place the IPC endpoint. If the path is
|
// IPCPath is the requested location to place the IPC endpoint. If the path is
|
||||||
// a simple file name, it is placed inside the data directory (or on the root
|
// a simple file name, it is placed inside the data directory (or on the root
|
||||||
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
|
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
|
||||||
// relative), then that specific path is enforced. An empty path disables IPC.
|
// relative), then that specific path is enforced. An empty path disables IPC.
|
||||||
IPCPath string
|
IPCPath string `toml:",omitempty"`
|
||||||
|
|
||||||
// This field should be a valid secp256k1 private key that will be used for both
|
|
||||||
// remote peer identification as well as network traffic encryption. If no key
|
|
||||||
// is configured, the preset one is loaded from the data dir, generating it if
|
|
||||||
// needed.
|
|
||||||
PrivateKey *ecdsa.PrivateKey
|
|
||||||
|
|
||||||
// NoDiscovery specifies whether the peer discovery mechanism should be started
|
|
||||||
// or not. Disabling is usually useful for protocol debugging (manual topology).
|
|
||||||
NoDiscovery bool
|
|
||||||
|
|
||||||
// DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
|
|
||||||
// protocol should be started or not.
|
|
||||||
DiscoveryV5 bool
|
|
||||||
|
|
||||||
// Listener address for the V5 discovery protocol UDP traffic.
|
|
||||||
DiscoveryV5Addr string
|
|
||||||
|
|
||||||
// Restrict communication to white listed IP networks.
|
|
||||||
// The whitelist only applies when non-nil.
|
|
||||||
NetRestrict *netutil.Netlist
|
|
||||||
|
|
||||||
// BootstrapNodes used to establish connectivity with the rest of the network.
|
|
||||||
BootstrapNodes []*discover.Node
|
|
||||||
|
|
||||||
// BootstrapNodesV5 used to establish connectivity with the rest of the network
|
|
||||||
// using the V5 discovery protocol.
|
|
||||||
BootstrapNodesV5 []*discv5.Node
|
|
||||||
|
|
||||||
// Network interface address on which the node should listen for inbound peers.
|
|
||||||
ListenAddr string
|
|
||||||
|
|
||||||
// If set to a non-nil value, the given NAT port mapper is used to make the
|
|
||||||
// listening port available to the Internet.
|
|
||||||
NAT nat.Interface
|
|
||||||
|
|
||||||
// If Dialer is set to a non-nil value, the given Dialer is used to dial outbound
|
|
||||||
// peer connections.
|
|
||||||
Dialer *net.Dialer
|
|
||||||
|
|
||||||
// If NoDial is true, the node will not dial any peers.
|
|
||||||
NoDial bool
|
|
||||||
|
|
||||||
// MaxPeers is the maximum number of peers that can be connected. If this is
|
|
||||||
// set to zero, then only the configured static and trusted peers can connect.
|
|
||||||
MaxPeers int
|
|
||||||
|
|
||||||
// MaxPendingPeers is the maximum number of peers that can be pending in the
|
|
||||||
// handshake phase, counted separately for inbound and outbound connections.
|
|
||||||
// Zero defaults to preset values.
|
|
||||||
MaxPendingPeers int
|
|
||||||
|
|
||||||
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
|
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
|
||||||
// field is empty, no HTTP API endpoint will be started.
|
// field is empty, no HTTP API endpoint will be started.
|
||||||
HTTPHost string
|
HTTPHost string `toml:",omitempty"`
|
||||||
|
|
||||||
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
|
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
|
||||||
// default zero value is/ valid and will pick a port number randomly (useful
|
// default zero value is/ valid and will pick a port number randomly (useful
|
||||||
// for ephemeral nodes).
|
// for ephemeral nodes).
|
||||||
HTTPPort int
|
HTTPPort int `toml:",omitempty"`
|
||||||
|
|
||||||
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
|
// HTTPCors is the Cross-Origin Resource Sharing header to send to requesting
|
||||||
// clients. Please be aware that CORS is a browser enforced security, it's fully
|
// clients. Please be aware that CORS is a browser enforced security, it's fully
|
||||||
// useless for custom HTTP clients.
|
// useless for custom HTTP clients.
|
||||||
HTTPCors string
|
HTTPCors string `toml:",omitempty"`
|
||||||
|
|
||||||
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
|
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
|
||||||
// If the module list is empty, all RPC API endpoints designated public will be
|
// If the module list is empty, all RPC API endpoints designated public will be
|
||||||
// exposed.
|
// exposed.
|
||||||
HTTPModules []string
|
HTTPModules []string `toml:",omitempty"`
|
||||||
|
|
||||||
// WSHost is the host interface on which to start the websocket RPC server. If
|
// WSHost is the host interface on which to start the websocket RPC server. If
|
||||||
// this field is empty, no websocket API endpoint will be started.
|
// this field is empty, no websocket API endpoint will be started.
|
||||||
WSHost string
|
WSHost string `toml:",omitempty"`
|
||||||
|
|
||||||
// WSPort is the TCP port number on which to start the websocket RPC server. The
|
// WSPort is the TCP port number on which to start the websocket RPC server. The
|
||||||
// default zero value is/ valid and will pick a port number randomly (useful for
|
// default zero value is/ valid and will pick a port number randomly (useful for
|
||||||
// ephemeral nodes).
|
// ephemeral nodes).
|
||||||
WSPort int
|
WSPort int `toml:",omitempty"`
|
||||||
|
|
||||||
// WSOrigins is the list of domain to accept websocket requests from. Please be
|
// WSOrigins is the list of domain to accept websocket requests from. Please be
|
||||||
// aware that the server can only act upon the HTTP request the client sends and
|
// aware that the server can only act upon the HTTP request the client sends and
|
||||||
// cannot verify the validity of the request header.
|
// cannot verify the validity of the request header.
|
||||||
WSOrigins string
|
WSOrigins string `toml:",omitempty"`
|
||||||
|
|
||||||
// WSModules is a list of API modules to expose via the websocket RPC interface.
|
// WSModules is a list of API modules to expose via the websocket RPC interface.
|
||||||
// If the module list is empty, all RPC API endpoints designated public will be
|
// If the module list is empty, all RPC API endpoints designated public will be
|
||||||
// exposed.
|
// exposed.
|
||||||
WSModules []string
|
WSModules []string `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
|
||||||
@ -326,8 +275,8 @@ func (c *Config) instanceDir() string {
|
|||||||
// data folder. If no key can be found, a new one is generated.
|
// data folder. If no key can be found, a new one is generated.
|
||||||
func (c *Config) NodeKey() *ecdsa.PrivateKey {
|
func (c *Config) NodeKey() *ecdsa.PrivateKey {
|
||||||
// Use any specifically configured key.
|
// Use any specifically configured key.
|
||||||
if c.PrivateKey != nil {
|
if c.P2P.PrivateKey != nil {
|
||||||
return c.PrivateKey
|
return c.P2P.PrivateKey
|
||||||
}
|
}
|
||||||
// Generate ephemeral key if no datadir is being used.
|
// Generate ephemeral key if no datadir is being used.
|
||||||
if c.DataDir == "" {
|
if c.DataDir == "" {
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests that datadirs can be successfully created, be them manually configured
|
// Tests that datadirs can be successfully created, be them manually configured
|
||||||
@ -109,7 +110,7 @@ func TestNodeKeyPersistency(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to generate one-shot node key: %v", err)
|
t.Fatalf("failed to generate one-shot node key: %v", err)
|
||||||
}
|
}
|
||||||
config := &Config{Name: "unit-test", DataDir: dir, PrivateKey: key}
|
config := &Config{Name: "unit-test", DataDir: dir, P2P: p2p.Config{PrivateKey: key}}
|
||||||
config.NodeKey()
|
config.NodeKey()
|
||||||
if _, err := os.Stat(filepath.Join(keyfile)); err == nil {
|
if _, err := os.Stat(filepath.Join(keyfile)); err == nil {
|
||||||
t.Fatalf("one-shot node key persisted to data directory")
|
t.Fatalf("one-shot node key persisted to data directory")
|
||||||
|
@ -21,16 +21,32 @@ import (
|
|||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultIPCSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket
|
|
||||||
DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
|
DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
|
||||||
DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
|
DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
|
||||||
DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
|
DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
|
||||||
DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
|
DefaultWSPort = 8546 // Default TCP port for the websocket RPC server
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultConfig contains reasonable default settings.
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
DataDir: DefaultDataDir(),
|
||||||
|
HTTPPort: DefaultHTTPPort,
|
||||||
|
HTTPModules: []string{"net", "web3"},
|
||||||
|
WSPort: DefaultWSPort,
|
||||||
|
WSModules: []string{"net", "web3"},
|
||||||
|
P2P: p2p.Config{
|
||||||
|
ListenAddr: ":30303",
|
||||||
|
MaxPeers: 25,
|
||||||
|
NAT: nat.Any(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultDataDir is the default data directory to use for the databases and other
|
// DefaultDataDir is the default data directory to use for the databases and other
|
||||||
// persistence requirements.
|
// persistence requirements.
|
||||||
func DefaultDataDir() string {
|
func DefaultDataDir() string {
|
||||||
|
29
node/node.go
29
node/node.go
@ -153,24 +153,17 @@ func (n *Node) Start() error {
|
|||||||
|
|
||||||
// Initialize the p2p server. This creates the node key and
|
// Initialize the p2p server. This creates the node key and
|
||||||
// discovery databases.
|
// discovery databases.
|
||||||
n.serverConfig = p2p.Config{
|
n.serverConfig = n.config.P2P
|
||||||
PrivateKey: n.config.NodeKey(),
|
n.serverConfig.PrivateKey = n.config.NodeKey()
|
||||||
Name: n.config.NodeName(),
|
n.serverConfig.Name = n.config.NodeName()
|
||||||
Discovery: !n.config.NoDiscovery,
|
if n.serverConfig.StaticNodes == nil {
|
||||||
DiscoveryV5: n.config.DiscoveryV5,
|
n.serverConfig.StaticNodes = n.config.StaticNodes()
|
||||||
DiscoveryV5Addr: n.config.DiscoveryV5Addr,
|
}
|
||||||
BootstrapNodes: n.config.BootstrapNodes,
|
if n.serverConfig.TrustedNodes == nil {
|
||||||
BootstrapNodesV5: n.config.BootstrapNodesV5,
|
n.serverConfig.TrustedNodes = n.config.TrusterNodes()
|
||||||
StaticNodes: n.config.StaticNodes(),
|
}
|
||||||
TrustedNodes: n.config.TrusterNodes(),
|
if n.serverConfig.NodeDatabase == "" {
|
||||||
NodeDatabase: n.config.NodeDB(),
|
n.serverConfig.NodeDatabase = n.config.NodeDB()
|
||||||
ListenAddr: n.config.ListenAddr,
|
|
||||||
NetRestrict: n.config.NetRestrict,
|
|
||||||
NAT: n.config.NAT,
|
|
||||||
Dialer: n.config.Dialer,
|
|
||||||
NoDial: n.config.NoDial,
|
|
||||||
MaxPeers: n.config.MaxPeers,
|
|
||||||
MaxPendingPeers: n.config.MaxPendingPeers,
|
|
||||||
}
|
}
|
||||||
running := &p2p.Server{Config: n.serverConfig}
|
running := &p2p.Server{Config: n.serverConfig}
|
||||||
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
|
log.Info("Starting peer-to-peer node", "instance", n.serverConfig.Name)
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,23 +41,8 @@ func (s *SampleService) Start(*p2p.Server) error { fmt.Println("Service starti
|
|||||||
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
func (s *SampleService) Stop() error { fmt.Println("Service stopping..."); return nil }
|
||||||
|
|
||||||
func ExampleService() {
|
func ExampleService() {
|
||||||
// Create a network node to run protocols with the default values. The below list
|
// Create a network node to run protocols with the default values.
|
||||||
// is only used to display each of the configuration options. All of these could
|
stack, err := node.New(&node.Config{})
|
||||||
// have been omitted if the default behavior is desired.
|
|
||||||
nodeConfig := &node.Config{
|
|
||||||
DataDir: "", // Empty uses ephemeral storage
|
|
||||||
PrivateKey: nil, // Nil generates a node key on the fly
|
|
||||||
Name: "", // Any textual node name is allowed
|
|
||||||
NoDiscovery: false, // Can disable discovering remote nodes
|
|
||||||
BootstrapNodes: []*discover.Node{}, // List of bootstrap nodes to use
|
|
||||||
ListenAddr: ":0", // Network interface to listen on
|
|
||||||
NAT: nil, // UPnP port mapper to use for crossing firewalls
|
|
||||||
Dialer: nil, // Custom dialer to use for establishing peer connections
|
|
||||||
NoDial: false, // Can prevent this node from dialing out
|
|
||||||
MaxPeers: 0, // Number of peers to allow
|
|
||||||
MaxPendingPeers: 0, // Number of peers allowed to handshake concurrently
|
|
||||||
}
|
|
||||||
stack, err := node.New(nodeConfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to create network node: %v", err)
|
log.Fatalf("Failed to create network node: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,8 @@ var (
|
|||||||
|
|
||||||
func testNodeConfig() *Config {
|
func testNodeConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
PrivateKey: testNodeKey,
|
|
||||||
Name: "test node",
|
Name: "test node",
|
||||||
|
P2P: p2p.Config{PrivateKey: testNodeKey},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +207,20 @@ func MustParseNode(rawurl string) *Node {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (n *Node) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(n.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (n *Node) UnmarshalText(text []byte) error {
|
||||||
|
dec, err := ParseNode(string(text))
|
||||||
|
if err == nil {
|
||||||
|
*n = *dec
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// NodeID is a unique identifier for each node.
|
// NodeID is a unique identifier for each node.
|
||||||
// The node identifier is a marshaled elliptic curve public key.
|
// The node identifier is a marshaled elliptic curve public key.
|
||||||
type NodeID [NodeIDBits / 8]byte
|
type NodeID [NodeIDBits / 8]byte
|
||||||
|
@ -215,6 +215,20 @@ func MustParseNode(rawurl string) *Node {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (n *Node) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(n.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (n *Node) UnmarshalText(text []byte) error {
|
||||||
|
dec, err := ParseNode(string(text))
|
||||||
|
if err == nil {
|
||||||
|
*n = *dec
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// type nodeQueue []*Node
|
// type nodeQueue []*Node
|
||||||
//
|
//
|
||||||
// // pushNew adds n to the end if it is not present.
|
// // pushNew adds n to the end if it is not present.
|
||||||
|
@ -84,6 +84,31 @@ func ParseNetlist(s string) (*Netlist, error) {
|
|||||||
return &l, nil
|
return &l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalTOML implements toml.MarshalerRec.
|
||||||
|
func (l Netlist) MarshalTOML() interface{} {
|
||||||
|
list := make([]string, 0, len(l))
|
||||||
|
for _, net := range l {
|
||||||
|
list = append(list, net.String())
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalTOML implements toml.UnmarshalerRec.
|
||||||
|
func (l *Netlist) UnmarshalTOML(fn func(interface{}) error) error {
|
||||||
|
var masks []string
|
||||||
|
if err := fn(&masks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, mask := range masks {
|
||||||
|
_, n, err := net.ParseCIDR(mask)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*l = append(*l, *n)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is
|
// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is
|
||||||
// intended to be used for setting up static lists.
|
// intended to be used for setting up static lists.
|
||||||
func (l *Netlist) Add(cidr string) {
|
func (l *Netlist) Add(cidr string) {
|
||||||
|
@ -58,7 +58,7 @@ var errServerStopped = errors.New("server stopped")
|
|||||||
// Config holds Server options.
|
// Config holds Server options.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// This field must be set to a valid secp256k1 private key.
|
// This field must be set to a valid secp256k1 private key.
|
||||||
PrivateKey *ecdsa.PrivateKey
|
PrivateKey *ecdsa.PrivateKey `toml:"-"`
|
||||||
|
|
||||||
// MaxPeers is the maximum number of peers that can be
|
// MaxPeers is the maximum number of peers that can be
|
||||||
// connected. It must be greater than zero.
|
// connected. It must be greater than zero.
|
||||||
@ -67,22 +67,22 @@ type Config struct {
|
|||||||
// MaxPendingPeers is the maximum number of peers that can be pending in the
|
// MaxPendingPeers is the maximum number of peers that can be pending in the
|
||||||
// handshake phase, counted separately for inbound and outbound connections.
|
// handshake phase, counted separately for inbound and outbound connections.
|
||||||
// Zero defaults to preset values.
|
// Zero defaults to preset values.
|
||||||
MaxPendingPeers int
|
MaxPendingPeers int `toml:",omitempty"`
|
||||||
|
|
||||||
// Discovery specifies whether the peer discovery mechanism should be started
|
// NoDiscovery can be used to disable the peer discovery mechanism.
|
||||||
// or not. Disabling is usually useful for protocol debugging (manual topology).
|
// Disabling is useful for protocol debugging (manual topology).
|
||||||
Discovery bool
|
NoDiscovery bool
|
||||||
|
|
||||||
// DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
|
// DiscoveryV5 specifies whether the the new topic-discovery based V5 discovery
|
||||||
// protocol should be started or not.
|
// protocol should be started or not.
|
||||||
DiscoveryV5 bool
|
DiscoveryV5 bool `toml:",omitempty"`
|
||||||
|
|
||||||
// Listener address for the V5 discovery protocol UDP traffic.
|
// Listener address for the V5 discovery protocol UDP traffic.
|
||||||
DiscoveryV5Addr string
|
DiscoveryV5Addr string `toml:",omitempty"`
|
||||||
|
|
||||||
// Name sets the node name of this server.
|
// Name sets the node name of this server.
|
||||||
// Use common.MakeName to create a name that follows existing conventions.
|
// Use common.MakeName to create a name that follows existing conventions.
|
||||||
Name string
|
Name string `toml:"-"`
|
||||||
|
|
||||||
// BootstrapNodes are used to establish connectivity
|
// BootstrapNodes are used to establish connectivity
|
||||||
// with the rest of the network.
|
// with the rest of the network.
|
||||||
@ -91,7 +91,7 @@ type Config struct {
|
|||||||
// BootstrapNodesV5 are used to establish connectivity
|
// BootstrapNodesV5 are used to establish connectivity
|
||||||
// with the rest of the network using the V5 discovery
|
// with the rest of the network using the V5 discovery
|
||||||
// protocol.
|
// protocol.
|
||||||
BootstrapNodesV5 []*discv5.Node
|
BootstrapNodesV5 []*discv5.Node `toml:",omitempty"`
|
||||||
|
|
||||||
// Static nodes are used as pre-configured connections which are always
|
// Static nodes are used as pre-configured connections which are always
|
||||||
// maintained and re-connected on disconnects.
|
// maintained and re-connected on disconnects.
|
||||||
@ -104,16 +104,16 @@ type Config struct {
|
|||||||
// Connectivity can be restricted to certain IP networks.
|
// Connectivity can be restricted to certain IP networks.
|
||||||
// If this option is set to a non-nil value, only hosts which match one of the
|
// If this option is set to a non-nil value, only hosts which match one of the
|
||||||
// IP networks contained in the list are considered.
|
// IP networks contained in the list are considered.
|
||||||
NetRestrict *netutil.Netlist
|
NetRestrict *netutil.Netlist `toml:",omitempty"`
|
||||||
|
|
||||||
// NodeDatabase is the path to the database containing the previously seen
|
// NodeDatabase is the path to the database containing the previously seen
|
||||||
// live nodes in the network.
|
// live nodes in the network.
|
||||||
NodeDatabase string
|
NodeDatabase string `toml:",omitempty"`
|
||||||
|
|
||||||
// Protocols should contain the protocols supported
|
// Protocols should contain the protocols supported
|
||||||
// by the server. Matching protocols are launched for
|
// by the server. Matching protocols are launched for
|
||||||
// each peer.
|
// each peer.
|
||||||
Protocols []Protocol
|
Protocols []Protocol `toml:"-"`
|
||||||
|
|
||||||
// If ListenAddr is set to a non-nil address, the server
|
// If ListenAddr is set to a non-nil address, the server
|
||||||
// will listen for incoming connections.
|
// will listen for incoming connections.
|
||||||
@ -126,14 +126,14 @@ type Config struct {
|
|||||||
// If set to a non-nil value, the given NAT port mapper
|
// If set to a non-nil value, the given NAT port mapper
|
||||||
// is used to make the listening port available to the
|
// is used to make the listening port available to the
|
||||||
// Internet.
|
// Internet.
|
||||||
NAT nat.Interface
|
NAT nat.Interface `toml:",omitempty"`
|
||||||
|
|
||||||
// If Dialer is set to a non-nil value, the given Dialer
|
// If Dialer is set to a non-nil value, the given Dialer
|
||||||
// is used to dial outbound peer connections.
|
// is used to dial outbound peer connections.
|
||||||
Dialer *net.Dialer
|
Dialer *net.Dialer `toml:"-"`
|
||||||
|
|
||||||
// If NoDial is true, the server will not dial any peers.
|
// If NoDial is true, the server will not dial any peers.
|
||||||
NoDial bool
|
NoDial bool `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server manages all peer connections.
|
// Server manages all peer connections.
|
||||||
@ -370,7 +370,7 @@ func (srv *Server) Start() (err error) {
|
|||||||
srv.peerOpDone = make(chan struct{})
|
srv.peerOpDone = make(chan struct{})
|
||||||
|
|
||||||
// node table
|
// node table
|
||||||
if srv.Discovery {
|
if !srv.NoDiscovery {
|
||||||
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict)
|
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -393,7 +393,7 @@ func (srv *Server) Start() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dynPeers := (srv.MaxPeers + 1) / 2
|
dynPeers := (srv.MaxPeers + 1) / 2
|
||||||
if !srv.Discovery {
|
if srv.NoDiscovery {
|
||||||
dynPeers = 0
|
dynPeers = 0
|
||||||
}
|
}
|
||||||
dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
|
dialer := newDialState(srv.StaticNodes, srv.BootstrapNodes, srv.ntab, dynPeers, srv.NetRestrict)
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package params
|
package params
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VersionMajor = 1 // Major version component of the current release
|
VersionMajor = 1 // Major version component of the current release
|
||||||
@ -33,3 +35,11 @@ var Version = func() string {
|
|||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
func VersionWithCommit(gitCommit string) string {
|
||||||
|
vsn := Version
|
||||||
|
if len(gitCommit) >= 8 {
|
||||||
|
vsn += "-" + gitCommit[:8]
|
||||||
|
}
|
||||||
|
return vsn
|
||||||
|
}
|
||||||
|
@ -32,8 +32,6 @@ const (
|
|||||||
notificationBufferSize = 10000 // max buffered notifications before codec is closed
|
notificationBufferSize = 10000 // max buffered notifications before codec is closed
|
||||||
|
|
||||||
MetadataApi = "rpc"
|
MetadataApi = "rpc"
|
||||||
DefaultIPCApis = "admin,debug,eth,miner,net,personal,shh,txpool,web3"
|
|
||||||
DefaultHTTPApis = "eth,net,web3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CodecOption specifies which type of messages this codec supports
|
// CodecOption specifies which type of messages this codec supports
|
||||||
|
19
vendor/github.com/naoina/go-stringutil/LICENSE
generated
vendored
Normal file
19
vendor/github.com/naoina/go-stringutil/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2015 Naoya Inada <naoina@kuune.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
13
vendor/github.com/naoina/go-stringutil/README.md
generated
vendored
Normal file
13
vendor/github.com/naoina/go-stringutil/README.md
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# stringutil [![Build Status](https://travis-ci.org/naoina/go-stringutil.svg?branch=master)](https://travis-ci.org/naoina/go-stringutil)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u github.com/naoina/go-stringutil
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
See https://godoc.org/github.com/naoina/go-stringutil
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
253
vendor/github.com/naoina/go-stringutil/da.go
generated
vendored
Normal file
253
vendor/github.com/naoina/go-stringutil/da.go
generated
vendored
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package stringutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
terminationCharacter = '#'
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustDoubleArray(da *doubleArray, err error) *doubleArray {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return da
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) Build(keys []string) error {
|
||||||
|
records := makeRecords(keys)
|
||||||
|
if err := da.build(records, 1, 0, make(map[int]struct{})); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type doubleArray struct {
|
||||||
|
bc []baseCheck
|
||||||
|
node []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDoubleArray(keys []string) (*doubleArray, error) {
|
||||||
|
da := &doubleArray{
|
||||||
|
bc: []baseCheck{0},
|
||||||
|
node: []int{-1}, // A start index is adjusting to 1 because 0 will be used as a mark of non-existent node.
|
||||||
|
}
|
||||||
|
if err := da.Build(keys); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return da, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// baseCheck contains BASE, CHECK and Extra flags.
|
||||||
|
// From the top, 22bits of BASE, 2bits of Extra flags and 8bits of CHECK.
|
||||||
|
//
|
||||||
|
// BASE (22bit) | Extra flags (2bit) | CHECK (8bit)
|
||||||
|
// |----------------------|--|--------|
|
||||||
|
// 32 10 8 0
|
||||||
|
type baseCheck uint32
|
||||||
|
|
||||||
|
func (bc baseCheck) Base() int {
|
||||||
|
return int(bc >> 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *baseCheck) SetBase(base int) {
|
||||||
|
*bc |= baseCheck(base) << 10
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc baseCheck) Check() byte {
|
||||||
|
return byte(bc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc *baseCheck) SetCheck(check byte) {
|
||||||
|
*bc |= baseCheck(check)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc baseCheck) IsEmpty() bool {
|
||||||
|
return bc&0xfffffcff == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) Lookup(path string) (length int) {
|
||||||
|
idx := 1
|
||||||
|
tmpIdx := idx
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
c := path[i]
|
||||||
|
tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c)
|
||||||
|
if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
idx = tmpIdx
|
||||||
|
}
|
||||||
|
if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter {
|
||||||
|
return da.node[da.bc[next].Base()]
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) LookupByBytes(path []byte) (length int) {
|
||||||
|
idx := 1
|
||||||
|
tmpIdx := idx
|
||||||
|
for i := 0; i < len(path); i++ {
|
||||||
|
c := path[i]
|
||||||
|
tmpIdx = da.nextIndex(da.bc[tmpIdx].Base(), c)
|
||||||
|
if tmpIdx >= len(da.bc) || da.bc[tmpIdx].Check() != c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
idx = tmpIdx
|
||||||
|
}
|
||||||
|
if next := da.nextIndex(da.bc[idx].Base(), terminationCharacter); next < len(da.bc) && da.bc[next].Check() == terminationCharacter {
|
||||||
|
return da.node[da.bc[next].Base()]
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) build(srcs []record, idx, depth int, usedBase map[int]struct{}) error {
|
||||||
|
sort.Stable(recordSlice(srcs))
|
||||||
|
base, siblings, leaf, err := da.arrange(srcs, idx, depth, usedBase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if leaf != nil {
|
||||||
|
da.bc[idx].SetBase(len(da.node))
|
||||||
|
da.node = append(da.node, leaf.value)
|
||||||
|
}
|
||||||
|
for _, sib := range siblings {
|
||||||
|
da.setCheck(da.nextIndex(base, sib.c), sib.c)
|
||||||
|
}
|
||||||
|
for _, sib := range siblings {
|
||||||
|
if err := da.build(srcs[sib.start:sib.end], da.nextIndex(base, sib.c), depth+1, usedBase); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) setBase(i, base int) {
|
||||||
|
da.bc[i].SetBase(base)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) setCheck(i int, check byte) {
|
||||||
|
da.bc[i].SetCheck(check)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) findEmptyIndex(start int) int {
|
||||||
|
i := start
|
||||||
|
for ; i < len(da.bc); i++ {
|
||||||
|
if da.bc[i].IsEmpty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// findBase returns good BASE.
|
||||||
|
func (da *doubleArray) findBase(siblings []sibling, start int, usedBase map[int]struct{}) (base int) {
|
||||||
|
for idx, firstChar := start+1, siblings[0].c; ; idx = da.findEmptyIndex(idx + 1) {
|
||||||
|
base = da.nextIndex(idx, firstChar)
|
||||||
|
if _, used := usedBase[base]; used {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for ; i < len(siblings); i++ {
|
||||||
|
next := da.nextIndex(base, siblings[i].c)
|
||||||
|
if len(da.bc) <= next {
|
||||||
|
da.bc = append(da.bc, make([]baseCheck, next-len(da.bc)+1)...)
|
||||||
|
}
|
||||||
|
if !da.bc[next].IsEmpty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == len(siblings) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usedBase[base] = struct{}{}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) arrange(records []record, idx, depth int, usedBase map[int]struct{}) (base int, siblings []sibling, leaf *record, err error) {
|
||||||
|
siblings, leaf, err = makeSiblings(records, depth)
|
||||||
|
if err != nil {
|
||||||
|
return -1, nil, nil, err
|
||||||
|
}
|
||||||
|
if len(siblings) < 1 {
|
||||||
|
return -1, nil, leaf, nil
|
||||||
|
}
|
||||||
|
base = da.findBase(siblings, idx, usedBase)
|
||||||
|
da.setBase(idx, base)
|
||||||
|
return base, siblings, leaf, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type sibling struct {
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
c byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (da *doubleArray) nextIndex(base int, c byte) int {
|
||||||
|
return base ^ int(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSiblings(records []record, depth int) (sib []sibling, leaf *record, err error) {
|
||||||
|
var (
|
||||||
|
pc byte
|
||||||
|
n int
|
||||||
|
)
|
||||||
|
for i, r := range records {
|
||||||
|
if len(r.key) <= depth {
|
||||||
|
leaf = &r
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c := r.key[depth]
|
||||||
|
switch {
|
||||||
|
case pc < c:
|
||||||
|
sib = append(sib, sibling{start: i, c: c})
|
||||||
|
case pc == c:
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("stringutil: BUG: records hasn't been sorted")
|
||||||
|
}
|
||||||
|
if n > 0 {
|
||||||
|
sib[n-1].end = i
|
||||||
|
}
|
||||||
|
pc = c
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
if n == 0 {
|
||||||
|
return nil, leaf, nil
|
||||||
|
}
|
||||||
|
sib[n-1].end = len(records)
|
||||||
|
return sib, leaf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type record struct {
|
||||||
|
key string
|
||||||
|
value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRecords(srcs []string) (records []record) {
|
||||||
|
termChar := string(terminationCharacter)
|
||||||
|
for _, s := range srcs {
|
||||||
|
records = append(records, record{
|
||||||
|
key: string(s + termChar),
|
||||||
|
value: utf8.RuneCountInString(s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return records
|
||||||
|
}
|
||||||
|
|
||||||
|
type recordSlice []record
|
||||||
|
|
||||||
|
func (rs recordSlice) Len() int {
|
||||||
|
return len(rs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs recordSlice) Less(i, j int) bool {
|
||||||
|
return rs[i].key < rs[j].key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs recordSlice) Swap(i, j int) {
|
||||||
|
rs[i], rs[j] = rs[j], rs[i]
|
||||||
|
}
|
320
vendor/github.com/naoina/go-stringutil/strings.go
generated
vendored
Normal file
320
vendor/github.com/naoina/go-stringutil/strings.go
generated
vendored
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
package stringutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
// Based on https://github.com/golang/lint/blob/32a87160691b3c96046c0c678fe57c5bef761456/lint.go#L702
|
||||||
|
commonInitialismMap = map[string]struct{}{
|
||||||
|
"API": struct{}{},
|
||||||
|
"ASCII": struct{}{},
|
||||||
|
"CPU": struct{}{},
|
||||||
|
"CSRF": struct{}{},
|
||||||
|
"CSS": struct{}{},
|
||||||
|
"DNS": struct{}{},
|
||||||
|
"EOF": struct{}{},
|
||||||
|
"GUID": struct{}{},
|
||||||
|
"HTML": struct{}{},
|
||||||
|
"HTTP": struct{}{},
|
||||||
|
"HTTPS": struct{}{},
|
||||||
|
"ID": struct{}{},
|
||||||
|
"IP": struct{}{},
|
||||||
|
"JSON": struct{}{},
|
||||||
|
"LHS": struct{}{},
|
||||||
|
"QPS": struct{}{},
|
||||||
|
"RAM": struct{}{},
|
||||||
|
"RHS": struct{}{},
|
||||||
|
"RPC": struct{}{},
|
||||||
|
"SLA": struct{}{},
|
||||||
|
"SMTP": struct{}{},
|
||||||
|
"SQL": struct{}{},
|
||||||
|
"SSH": struct{}{},
|
||||||
|
"TCP": struct{}{},
|
||||||
|
"TLS": struct{}{},
|
||||||
|
"TTL": struct{}{},
|
||||||
|
"UDP": struct{}{},
|
||||||
|
"UI": struct{}{},
|
||||||
|
"UID": struct{}{},
|
||||||
|
"UUID": struct{}{},
|
||||||
|
"URI": struct{}{},
|
||||||
|
"URL": struct{}{},
|
||||||
|
"UTF8": struct{}{},
|
||||||
|
"VM": struct{}{},
|
||||||
|
"XML": struct{}{},
|
||||||
|
"XSRF": struct{}{},
|
||||||
|
"XSS": struct{}{},
|
||||||
|
}
|
||||||
|
commonInitialisms = keys(commonInitialismMap)
|
||||||
|
commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
|
||||||
|
longestLen = longestLength(commonInitialisms)
|
||||||
|
shortestLen = shortestLength(commonInitialisms, longestLen)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToUpperCamelCase returns a copy of the string s with all Unicode letters mapped to their camel case.
|
||||||
|
// It will convert to upper case previous letter of '_' and first letter, and remove letter of '_'.
|
||||||
|
func ToUpperCamelCase(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
upper := true
|
||||||
|
start := 0
|
||||||
|
result := make([]byte, 0, len(s))
|
||||||
|
var runeBuf [utf8.UTFMax]byte
|
||||||
|
var initialism []byte
|
||||||
|
for _, c := range s {
|
||||||
|
if c == '_' {
|
||||||
|
upper = true
|
||||||
|
candidate := string(result[start:])
|
||||||
|
initialism = initialism[:0]
|
||||||
|
for _, r := range candidate {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
initialism = append(initialism, toUpperASCII(byte(r)))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r))
|
||||||
|
initialism = append(initialism, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length := commonInitialism.LookupByBytes(initialism); length > 0 {
|
||||||
|
result = append(result[:start], initialism...)
|
||||||
|
}
|
||||||
|
start = len(result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if upper {
|
||||||
|
if c < utf8.RuneSelf {
|
||||||
|
result = append(result, toUpperASCII(byte(c)))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(c))
|
||||||
|
result = append(result, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
upper = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if c < utf8.RuneSelf {
|
||||||
|
result = append(result, byte(c))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], c)
|
||||||
|
result = append(result, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
candidate := string(result[start:])
|
||||||
|
initialism = initialism[:0]
|
||||||
|
for _, r := range candidate {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
initialism = append(initialism, toUpperASCII(byte(r)))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], unicode.ToUpper(r))
|
||||||
|
initialism = append(initialism, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length := commonInitialism.LookupByBytes(initialism); length > 0 {
|
||||||
|
result = append(result[:start], initialism...)
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToUpperCamelCaseASCII is similar to ToUpperCamelCase, but optimized for
|
||||||
|
// only the ASCII characters.
|
||||||
|
// ToUpperCamelCaseASCII is faster than ToUpperCamelCase, but doesn't work if
|
||||||
|
// contains non-ASCII characters.
|
||||||
|
func ToUpperCamelCaseASCII(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
upper := true
|
||||||
|
start := 0
|
||||||
|
result := make([]byte, 0, len(s))
|
||||||
|
var initialism []byte
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
if c == '_' {
|
||||||
|
upper = true
|
||||||
|
candidate := result[start:]
|
||||||
|
initialism = initialism[:0]
|
||||||
|
for _, b := range candidate {
|
||||||
|
initialism = append(initialism, toUpperASCII(b))
|
||||||
|
}
|
||||||
|
if length := commonInitialism.LookupByBytes(initialism); length > 0 {
|
||||||
|
result = append(result[:start], initialism...)
|
||||||
|
}
|
||||||
|
start = len(result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if upper {
|
||||||
|
result = append(result, toUpperASCII(c))
|
||||||
|
upper = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, c)
|
||||||
|
}
|
||||||
|
candidate := result[start:]
|
||||||
|
initialism = initialism[:0]
|
||||||
|
for _, b := range candidate {
|
||||||
|
initialism = append(initialism, toUpperASCII(b))
|
||||||
|
}
|
||||||
|
if length := commonInitialism.LookupByBytes(initialism); length > 0 {
|
||||||
|
result = append(result[:start], initialism...)
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSnakeCase returns a copy of the string s with all Unicode letters mapped to their snake case.
|
||||||
|
// It will insert letter of '_' at position of previous letter of uppercase and all
|
||||||
|
// letters convert to lower case.
|
||||||
|
// ToSnakeCase does not insert '_' letter into a common initialism word like ID, URL and so on.
|
||||||
|
func ToSnakeCase(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
result := make([]byte, 0, len(s))
|
||||||
|
var runeBuf [utf8.UTFMax]byte
|
||||||
|
var j, skipCount int
|
||||||
|
for i, c := range s {
|
||||||
|
if i < skipCount {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if unicode.IsUpper(c) {
|
||||||
|
if i != 0 {
|
||||||
|
result = append(result, '_')
|
||||||
|
}
|
||||||
|
next := nextIndex(j, len(s))
|
||||||
|
if length := commonInitialism.Lookup(s[j:next]); length > 0 {
|
||||||
|
for _, r := range s[j : j+length] {
|
||||||
|
if r < utf8.RuneSelf {
|
||||||
|
result = append(result, toLowerASCII(byte(r)))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(r))
|
||||||
|
result = append(result, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += length - 1
|
||||||
|
skipCount = i + length
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c < utf8.RuneSelf {
|
||||||
|
result = append(result, toLowerASCII(byte(c)))
|
||||||
|
} else {
|
||||||
|
n := utf8.EncodeRune(runeBuf[:], unicode.ToLower(c))
|
||||||
|
result = append(result, runeBuf[:n]...)
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSnakeCaseASCII is similar to ToSnakeCase, but optimized for only the ASCII
|
||||||
|
// characters.
|
||||||
|
// ToSnakeCaseASCII is faster than ToSnakeCase, but doesn't work correctly if
|
||||||
|
// contains non-ASCII characters.
|
||||||
|
func ToSnakeCaseASCII(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
result := make([]byte, 0, len(s))
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
if isUpperASCII(c) {
|
||||||
|
if i != 0 {
|
||||||
|
result = append(result, '_')
|
||||||
|
}
|
||||||
|
if k := i + shortestLen - 1; k < len(s) && isUpperASCII(s[k]) {
|
||||||
|
if length := commonInitialism.Lookup(s[i:nextIndex(i, len(s))]); length > 0 {
|
||||||
|
for j, buf := 0, s[i:i+length]; j < len(buf); j++ {
|
||||||
|
result = append(result, toLowerASCII(buf[j]))
|
||||||
|
}
|
||||||
|
i += length - 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, toLowerASCII(c))
|
||||||
|
}
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCommonInitialism adds ss to list of common initialisms.
|
||||||
|
func AddCommonInitialism(ss ...string) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
for _, s := range ss {
|
||||||
|
commonInitialismMap[s] = struct{}{}
|
||||||
|
}
|
||||||
|
commonInitialisms = keys(commonInitialismMap)
|
||||||
|
commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
|
||||||
|
longestLen = longestLength(commonInitialisms)
|
||||||
|
shortestLen = shortestLength(commonInitialisms, longestLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DelCommonInitialism deletes ss from list of common initialisms.
|
||||||
|
func DelCommonInitialism(ss ...string) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
for _, s := range ss {
|
||||||
|
delete(commonInitialismMap, s)
|
||||||
|
}
|
||||||
|
commonInitialisms = keys(commonInitialismMap)
|
||||||
|
commonInitialism = mustDoubleArray(newDoubleArray(commonInitialisms))
|
||||||
|
longestLen = longestLength(commonInitialisms)
|
||||||
|
shortestLen = shortestLength(commonInitialisms, longestLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUpperASCII(c byte) bool {
|
||||||
|
return 'A' <= c && c <= 'Z'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLowerASCII(c byte) bool {
|
||||||
|
return 'a' <= c && c <= 'z'
|
||||||
|
}
|
||||||
|
|
||||||
|
func toUpperASCII(c byte) byte {
|
||||||
|
if isLowerASCII(c) {
|
||||||
|
return c - ('a' - 'A')
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func toLowerASCII(c byte) byte {
|
||||||
|
if isUpperASCII(c) {
|
||||||
|
return c + 'a' - 'A'
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextIndex(i, maxlen int) int {
|
||||||
|
if n := i + longestLen; n < maxlen {
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
return maxlen
|
||||||
|
}
|
||||||
|
|
||||||
|
func keys(m map[string]struct{}) []string {
|
||||||
|
result := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func shortestLength(strs []string, shortest int) int {
|
||||||
|
for _, s := range strs {
|
||||||
|
if candidate := utf8.RuneCountInString(s); candidate < shortest {
|
||||||
|
shortest = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shortest
|
||||||
|
}
|
||||||
|
|
||||||
|
func longestLength(strs []string) (longest int) {
|
||||||
|
for _, s := range strs {
|
||||||
|
if candidate := utf8.RuneCountInString(s); candidate > longest {
|
||||||
|
longest = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return longest
|
||||||
|
}
|
19
vendor/github.com/naoina/toml/LICENSE
generated
vendored
Normal file
19
vendor/github.com/naoina/toml/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2014 Naoya Inada <naoina@kuune.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
392
vendor/github.com/naoina/toml/README.md
generated
vendored
Normal file
392
vendor/github.com/naoina/toml/README.md
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
# TOML parser and encoder library for Golang [![Build Status](https://travis-ci.org/naoina/toml.png?branch=master)](https://travis-ci.org/naoina/toml)
|
||||||
|
|
||||||
|
[TOML](https://github.com/toml-lang/toml) parser and encoder library for [Golang](http://golang.org/).
|
||||||
|
|
||||||
|
This library is compatible with TOML version [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
go get -u github.com/naoina/toml
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The following TOML save as `example.toml`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# This is a TOML document. Boom.
|
||||||
|
|
||||||
|
title = "TOML Example"
|
||||||
|
|
||||||
|
[owner]
|
||||||
|
name = "Lance Uppercut"
|
||||||
|
dob = 1979-05-27T07:32:00-08:00 # First class dates? Why not?
|
||||||
|
|
||||||
|
[database]
|
||||||
|
server = "192.168.1.1"
|
||||||
|
ports = [ 8001, 8001, 8002 ]
|
||||||
|
connection_max = 5000
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||||
|
[servers.alpha]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[servers.beta]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[clients]
|
||||||
|
data = [ ["gamma", "delta"], [1, 2] ]
|
||||||
|
|
||||||
|
# Line breaks are OK when inside arrays
|
||||||
|
hosts = [
|
||||||
|
"alpha",
|
||||||
|
"omega"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Then above TOML will mapping to `tomlConfig` struct using `toml.Unmarshal`.
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/naoina/toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tomlConfig struct {
|
||||||
|
Title string
|
||||||
|
Owner struct {
|
||||||
|
Name string
|
||||||
|
Dob time.Time
|
||||||
|
}
|
||||||
|
Database struct {
|
||||||
|
Server string
|
||||||
|
Ports []int
|
||||||
|
ConnectionMax uint
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
Servers map[string]ServerInfo
|
||||||
|
Clients struct {
|
||||||
|
Data [][]interface{}
|
||||||
|
Hosts []string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerInfo struct {
|
||||||
|
IP net.IP
|
||||||
|
DC string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f, err := os.Open("example.toml")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var config Config
|
||||||
|
if err := toml.NewDecoder(f).Decode(&config); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// then to use the unmarshaled config...
|
||||||
|
fmt.Println("IP of server 'alpha':", config.Servers["alpha"].IP)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mappings
|
||||||
|
|
||||||
|
A key and value of TOML will map to the corresponding field.
|
||||||
|
The fields of struct for mapping must be exported.
|
||||||
|
|
||||||
|
The rules of the mapping of key are following:
|
||||||
|
|
||||||
|
#### Exact matching
|
||||||
|
|
||||||
|
```toml
|
||||||
|
timeout_seconds = 256
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Timeout_seconds int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Camelcase matching
|
||||||
|
|
||||||
|
```toml
|
||||||
|
server_name = "srv1"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
ServerName string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Uppercase matching
|
||||||
|
|
||||||
|
```toml
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See the following examples for the value mappings.
|
||||||
|
|
||||||
|
### String
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = "string"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integer
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = 100
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All types that can be used are following:
|
||||||
|
|
||||||
|
* int8 (from `-128` to `127`)
|
||||||
|
* int16 (from `-32768` to `32767`)
|
||||||
|
* int32 (from `-2147483648` to `2147483647`)
|
||||||
|
* int64 (from `-9223372036854775808` to `9223372036854775807`)
|
||||||
|
* int (same as `int32` on 32bit environment, or `int64` on 64bit environment)
|
||||||
|
* uint8 (from `0` to `255`)
|
||||||
|
* uint16 (from `0` to `65535`)
|
||||||
|
* uint32 (from `0` to `4294967295`)
|
||||||
|
* uint64 (from `0` to `18446744073709551615`)
|
||||||
|
* uint (same as `uint` on 32bit environment, or `uint64` on 64bit environment)
|
||||||
|
|
||||||
|
### Float
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = 3.1415
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val float32
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All types that can be used are following:
|
||||||
|
|
||||||
|
* float32
|
||||||
|
* float64
|
||||||
|
|
||||||
|
### Boolean
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = true
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Datetime
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = 2014-09-28T21:27:39Z
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val time.Time
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val = ["a", "b", "c"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val []string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Also following examples all can be mapped:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
val1 = [1, 2, 3]
|
||||||
|
val2 = [["a", "b"], ["c", "d"]]
|
||||||
|
val3 = [[1, 2, 3], ["a", "b", "c"]]
|
||||||
|
val4 = [[1, 2, 3], [["a", "b"], [true, false]]]
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Val1 []int
|
||||||
|
Val2 [][]string
|
||||||
|
Val3 [][]interface{}
|
||||||
|
Val4 [][]interface{}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server]
|
||||||
|
type = "app"
|
||||||
|
|
||||||
|
[server.development]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
|
||||||
|
[server.production]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Server map[string]Server
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use the following struct instead of map of struct.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Server struct {
|
||||||
|
Development Server
|
||||||
|
Production Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array of Tables
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[fruit]]
|
||||||
|
name = "apple"
|
||||||
|
|
||||||
|
[fruit.physical]
|
||||||
|
color = "red"
|
||||||
|
shape = "round"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "red delicious"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "granny smith"
|
||||||
|
|
||||||
|
[[fruit]]
|
||||||
|
name = "banana"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "plantain"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
Fruit []struct {
|
||||||
|
Name string
|
||||||
|
Physical struct {
|
||||||
|
Color string
|
||||||
|
Shape string
|
||||||
|
}
|
||||||
|
Variety []struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the `encoding.TextUnmarshaler` interface
|
||||||
|
|
||||||
|
Package toml supports `encoding.TextUnmarshaler` (and `encoding.TextMarshaler`). You can
|
||||||
|
use it to apply custom marshaling rules for certain types. The `UnmarshalText` method is
|
||||||
|
called with the value text found in the TOML input. TOML strings are passed unquoted.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
duration = "10s"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
import time
|
||||||
|
|
||||||
|
type Duration time.Duration
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
|
func (d *Duration) UnmarshalText(data []byte) error {
|
||||||
|
duration, err := time.ParseDuration(string(data))
|
||||||
|
if err == nil {
|
||||||
|
*d = Duration(duration)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler
|
||||||
|
func (d Duration) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(time.Duration(d).String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigWithDuration struct {
|
||||||
|
Duration Duration
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### Using the `toml.UnmarshalerRec` interface
|
||||||
|
|
||||||
|
You can also override marshaling rules specifically for TOML using the `UnmarshalerRec`
|
||||||
|
and `MarshalerRec` interfaces. These are useful if you want to control how structs or
|
||||||
|
arrays are handled. You can apply additional validation or set unexported struct fields.
|
||||||
|
|
||||||
|
Note: `encoding.TextUnmarshaler` and `encoding.TextMarshaler` should be preferred for
|
||||||
|
simple (scalar) values because they're also compatible with other formats like JSON or
|
||||||
|
YAML.
|
||||||
|
|
||||||
|
[See the UnmarshalerRec example](https://godoc.org/github.com/naoina/toml/#example_UnmarshalerRec).
|
||||||
|
|
||||||
|
### Using the `toml.Unmarshaler` interface
|
||||||
|
|
||||||
|
If you want to deal with raw TOML syntax, use the `Unmarshaler` and `Marshaler`
|
||||||
|
interfaces. Their input and output is raw TOML syntax. As such, these interfaces are
|
||||||
|
useful if you want to handle TOML at the syntax level.
|
||||||
|
|
||||||
|
[See the Unmarshaler example](https://godoc.org/github.com/naoina/toml/#example_Unmarshaler).
|
||||||
|
|
||||||
|
## API documentation
|
||||||
|
|
||||||
|
See [Godoc](http://godoc.org/github.com/naoina/toml).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
192
vendor/github.com/naoina/toml/ast/ast.go
generated
vendored
Normal file
192
vendor/github.com/naoina/toml/ast/ast.go
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Position struct {
|
||||||
|
Begin int
|
||||||
|
End int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Value interface {
|
||||||
|
Pos() int
|
||||||
|
End() int
|
||||||
|
Source() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type String struct {
|
||||||
|
Position Position
|
||||||
|
Value string
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) Pos() int {
|
||||||
|
return s.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) End() int {
|
||||||
|
return s.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *String) Source() string {
|
||||||
|
return string(s.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integer struct {
|
||||||
|
Position Position
|
||||||
|
Value string
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) Pos() int {
|
||||||
|
return i.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) End() int {
|
||||||
|
return i.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) Source() string {
|
||||||
|
return string(i.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Integer) Int() (int64, error) {
|
||||||
|
return strconv.ParseInt(i.Value, 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float struct {
|
||||||
|
Position Position
|
||||||
|
Value string
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Float) Pos() int {
|
||||||
|
return f.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Float) End() int {
|
||||||
|
return f.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Float) Source() string {
|
||||||
|
return string(f.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Float) Float() (float64, error) {
|
||||||
|
return strconv.ParseFloat(f.Value, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Boolean struct {
|
||||||
|
Position Position
|
||||||
|
Value string
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Boolean) Pos() int {
|
||||||
|
return b.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Boolean) End() int {
|
||||||
|
return b.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Boolean) Source() string {
|
||||||
|
return string(b.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Boolean) Boolean() (bool, error) {
|
||||||
|
return strconv.ParseBool(b.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Datetime struct {
|
||||||
|
Position Position
|
||||||
|
Value string
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Datetime) Pos() int {
|
||||||
|
return d.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Datetime) End() int {
|
||||||
|
return d.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Datetime) Source() string {
|
||||||
|
return string(d.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Datetime) Time() (time.Time, error) {
|
||||||
|
switch {
|
||||||
|
case !strings.Contains(d.Value, ":"):
|
||||||
|
return time.Parse("2006-01-02", d.Value)
|
||||||
|
case !strings.Contains(d.Value, "-"):
|
||||||
|
return time.Parse("15:04:05.999999999", d.Value)
|
||||||
|
default:
|
||||||
|
return time.Parse(time.RFC3339Nano, d.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Array struct {
|
||||||
|
Position Position
|
||||||
|
Value []Value
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Array) Pos() int {
|
||||||
|
return a.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Array) End() int {
|
||||||
|
return a.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Array) Source() string {
|
||||||
|
return string(a.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TableTypeNormal TableType = iota
|
||||||
|
TableTypeArray
|
||||||
|
)
|
||||||
|
|
||||||
|
var tableTypes = [...]string{
|
||||||
|
"normal",
|
||||||
|
"array",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TableType) String() string {
|
||||||
|
return tableTypes[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
type Table struct {
|
||||||
|
Position Position
|
||||||
|
Line int
|
||||||
|
Name string
|
||||||
|
Fields map[string]interface{}
|
||||||
|
Type TableType
|
||||||
|
Data []rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) Pos() int {
|
||||||
|
return t.Position.Begin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) End() int {
|
||||||
|
return t.Position.End
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Table) Source() string {
|
||||||
|
return string(t.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyValue struct {
|
||||||
|
Key string
|
||||||
|
Value Value
|
||||||
|
Line int
|
||||||
|
}
|
86
vendor/github.com/naoina/toml/config.go
generated
vendored
Normal file
86
vendor/github.com/naoina/toml/config.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
stringutil "github.com/naoina/go-stringutil"
|
||||||
|
"github.com/naoina/toml/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config contains options for encoding and decoding.
|
||||||
|
type Config struct {
|
||||||
|
// NormFieldName is used to match TOML keys to struct fields. The function runs for
|
||||||
|
// both input keys and struct field names and should return a string that makes the
|
||||||
|
// two match. You must set this field to use the decoder.
|
||||||
|
//
|
||||||
|
// Example: The function in the default config removes _ and lowercases all keys. This
|
||||||
|
// allows a key called 'api_key' to match the struct field 'APIKey' because both are
|
||||||
|
// normalized to 'apikey'.
|
||||||
|
//
|
||||||
|
// Note that NormFieldName is not used for fields which define a TOML
|
||||||
|
// key through the struct tag.
|
||||||
|
NormFieldName func(typ reflect.Type, keyOrField string) string
|
||||||
|
|
||||||
|
// FieldToKey determines the TOML key of a struct field when encoding.
|
||||||
|
// You must set this field to use the encoder.
|
||||||
|
//
|
||||||
|
// Note that FieldToKey is not used for fields which define a TOML
|
||||||
|
// key through the struct tag.
|
||||||
|
FieldToKey func(typ reflect.Type, field string) string
|
||||||
|
|
||||||
|
// MissingField, if non-nil, is called when the decoder encounters a key for which no
|
||||||
|
// matching struct field exists. The default behavior is to return an error.
|
||||||
|
MissingField func(typ reflect.Type, key string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig contains the default options for encoding and decoding.
|
||||||
|
// Snake case (i.e. 'foo_bar') is used for key names.
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
NormFieldName: defaultNormFieldName,
|
||||||
|
FieldToKey: snakeCase,
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultNormFieldName(typ reflect.Type, s string) string {
|
||||||
|
return strings.Replace(strings.ToLower(s), "_", "", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func snakeCase(typ reflect.Type, s string) string {
|
||||||
|
return stringutil.ToSnakeCase(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultMissingField(typ reflect.Type, key string) error {
|
||||||
|
return fmt.Errorf("field corresponding to `%s' is not defined in %v", key, typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new Encoder that writes to w.
|
||||||
|
// It is shorthand for DefaultConfig.NewEncoder(w).
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
return DefaultConfig.NewEncoder(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the TOML encoding of v.
|
||||||
|
// It is shorthand for DefaultConfig.Marshal(v).
|
||||||
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return DefaultConfig.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
|
||||||
|
// It is shorthand for DefaultConfig.Unmarshal(data, v).
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return DefaultConfig.Unmarshal(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
|
||||||
|
// It is shorthand for DefaultConfig.UnmarshalTable(t, v).
|
||||||
|
func UnmarshalTable(t *ast.Table, v interface{}) error {
|
||||||
|
return DefaultConfig.UnmarshalTable(t, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new Decoder that reads from r.
|
||||||
|
// It is shorthand for DefaultConfig.NewDecoder(r).
|
||||||
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
|
return DefaultConfig.NewDecoder(r)
|
||||||
|
}
|
478
vendor/github.com/naoina/toml/decode.go
generated
vendored
Normal file
478
vendor/github.com/naoina/toml/decode.go
generated
vendored
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
// Package toml encodes and decodes the TOML configuration format using reflection.
|
||||||
|
//
|
||||||
|
// This library is compatible with TOML version v0.4.0.
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/naoina/toml/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tableSeparator = '.'
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
escapeReplacer = strings.NewReplacer(
|
||||||
|
"\b", "\\n",
|
||||||
|
"\f", "\\f",
|
||||||
|
"\n", "\\n",
|
||||||
|
"\r", "\\r",
|
||||||
|
"\t", "\\t",
|
||||||
|
)
|
||||||
|
underscoreReplacer = strings.NewReplacer(
|
||||||
|
"_", "",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var timeType = reflect.TypeOf(time.Time{})
|
||||||
|
|
||||||
|
// Unmarshal parses the TOML data and stores the result in the value pointed to by v.
|
||||||
|
//
|
||||||
|
// Unmarshal will mapped to v that according to following rules:
|
||||||
|
//
|
||||||
|
// TOML strings to string
|
||||||
|
// TOML integers to any int type
|
||||||
|
// TOML floats to float32 or float64
|
||||||
|
// TOML booleans to bool
|
||||||
|
// TOML datetimes to time.Time
|
||||||
|
// TOML arrays to any type of slice
|
||||||
|
// TOML tables to struct or map
|
||||||
|
// TOML array tables to slice of struct or map
|
||||||
|
func (cfg *Config) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
table, err := Parse(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cfg.UnmarshalTable(table, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Decoder reads and decodes TOML from an input stream.
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
cfg *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new Decoder that reads from r.
|
||||||
|
// Note that it reads all from r before parsing it.
|
||||||
|
func (cfg *Config) NewDecoder(r io.Reader) *Decoder {
|
||||||
|
return &Decoder{r, cfg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode parses the TOML data from its input and stores it in the value pointed to by v.
|
||||||
|
// See the documentation for Unmarshal for details about the conversion of TOML into a Go value.
|
||||||
|
func (d *Decoder) Decode(v interface{}) error {
|
||||||
|
b, err := ioutil.ReadAll(d.r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.cfg.Unmarshal(b, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalerRec may be implemented by types to customize their behavior when being
|
||||||
|
// unmarshaled from TOML. You can use it to implement custom validation or to set
|
||||||
|
// unexported fields.
|
||||||
|
//
|
||||||
|
// UnmarshalTOML receives a function that can be called to unmarshal the original TOML
|
||||||
|
// value into a field or variable. It is safe to call the function more than once if
|
||||||
|
// necessary.
|
||||||
|
type UnmarshalerRec interface {
|
||||||
|
UnmarshalTOML(fn func(interface{}) error) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshaler can be used to capture and process raw TOML source of a table or value.
|
||||||
|
// UnmarshalTOML must copy the input if it wishes to retain it after returning.
|
||||||
|
//
|
||||||
|
// Note: this interface is retained for backwards compatibility. You probably want
|
||||||
|
// to implement encoding.TextUnmarshaler or UnmarshalerRec instead.
|
||||||
|
type Unmarshaler interface {
|
||||||
|
UnmarshalTOML(input []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalTable applies the contents of an ast.Table to the value pointed at by v.
|
||||||
|
//
|
||||||
|
// UnmarshalTable will mapped to v that according to following rules:
|
||||||
|
//
|
||||||
|
// TOML strings to string
|
||||||
|
// TOML integers to any int type
|
||||||
|
// TOML floats to float32 or float64
|
||||||
|
// TOML booleans to bool
|
||||||
|
// TOML datetimes to time.Time
|
||||||
|
// TOML arrays to any type of slice
|
||||||
|
// TOML tables to struct or map
|
||||||
|
// TOML array tables to slice of struct or map
|
||||||
|
func (cfg *Config) UnmarshalTable(t *ast.Table, v interface{}) error {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
toplevelMap := rv.Kind() == reflect.Map
|
||||||
|
if (!toplevelMap && rv.Kind() != reflect.Ptr) || rv.IsNil() {
|
||||||
|
return &invalidUnmarshalError{reflect.TypeOf(v)}
|
||||||
|
}
|
||||||
|
return unmarshalTable(cfg, rv, t, toplevelMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// used for UnmarshalerRec.
|
||||||
|
func unmarshalTableOrValue(cfg *Config, rv reflect.Value, av interface{}) error {
|
||||||
|
if (rv.Kind() != reflect.Ptr && rv.Kind() != reflect.Map) || rv.IsNil() {
|
||||||
|
return &invalidUnmarshalError{rv.Type()}
|
||||||
|
}
|
||||||
|
rv = indirect(rv)
|
||||||
|
|
||||||
|
switch av.(type) {
|
||||||
|
case *ast.KeyValue, *ast.Table, []*ast.Table:
|
||||||
|
if err := unmarshalField(cfg, rv, av); err != nil {
|
||||||
|
return lineError(fieldLineNumber(av), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
case ast.Value:
|
||||||
|
return setValue(cfg, rv, av.(ast.Value))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshalTable unmarshals the fields of a table into a struct or map.
|
||||||
|
//
|
||||||
|
// toplevelMap is true when rv is an (unadressable) map given to UnmarshalTable. In this
|
||||||
|
// (special) case, the map is used as-is instead of creating a new map.
|
||||||
|
func unmarshalTable(cfg *Config, rv reflect.Value, t *ast.Table, toplevelMap bool) error {
|
||||||
|
rv = indirect(rv)
|
||||||
|
if err, ok := setUnmarshaler(cfg, rv, t); ok {
|
||||||
|
return lineError(t.Line, err)
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case rv.Kind() == reflect.Struct:
|
||||||
|
fc := makeFieldCache(cfg, rv.Type())
|
||||||
|
for key, fieldAst := range t.Fields {
|
||||||
|
fv, fieldName, err := fc.findField(cfg, rv, key)
|
||||||
|
if err != nil {
|
||||||
|
return lineError(fieldLineNumber(fieldAst), err)
|
||||||
|
}
|
||||||
|
if fv.IsValid() {
|
||||||
|
if err := unmarshalField(cfg, fv, fieldAst); err != nil {
|
||||||
|
return lineErrorField(fieldLineNumber(fieldAst), rv.Type().String()+"."+fieldName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case rv.Kind() == reflect.Map || isEface(rv):
|
||||||
|
m := rv
|
||||||
|
if !toplevelMap {
|
||||||
|
if rv.Kind() == reflect.Interface {
|
||||||
|
m = reflect.ValueOf(make(map[string]interface{}))
|
||||||
|
} else {
|
||||||
|
m = reflect.MakeMap(rv.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elemtyp := m.Type().Elem()
|
||||||
|
for key, fieldAst := range t.Fields {
|
||||||
|
kv, err := unmarshalMapKey(m.Type().Key(), key)
|
||||||
|
if err != nil {
|
||||||
|
return lineError(fieldLineNumber(fieldAst), err)
|
||||||
|
}
|
||||||
|
fv := reflect.New(elemtyp).Elem()
|
||||||
|
if err := unmarshalField(cfg, fv, fieldAst); err != nil {
|
||||||
|
return lineError(fieldLineNumber(fieldAst), err)
|
||||||
|
}
|
||||||
|
m.SetMapIndex(kv, fv)
|
||||||
|
}
|
||||||
|
if !toplevelMap {
|
||||||
|
rv.Set(m)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return lineError(t.Line, &unmarshalTypeError{"table", "struct or map", rv.Type()})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fieldLineNumber(fieldAst interface{}) int {
|
||||||
|
switch av := fieldAst.(type) {
|
||||||
|
case *ast.KeyValue:
|
||||||
|
return av.Line
|
||||||
|
case *ast.Table:
|
||||||
|
return av.Line
|
||||||
|
case []*ast.Table:
|
||||||
|
return av[0].Line
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("BUG: unhandled node type %T", fieldAst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalField(cfg *Config, rv reflect.Value, fieldAst interface{}) error {
|
||||||
|
switch av := fieldAst.(type) {
|
||||||
|
case *ast.KeyValue:
|
||||||
|
return setValue(cfg, rv, av.Value)
|
||||||
|
case *ast.Table:
|
||||||
|
return unmarshalTable(cfg, rv, av, false)
|
||||||
|
case []*ast.Table:
|
||||||
|
rv = indirect(rv)
|
||||||
|
if err, ok := setUnmarshaler(cfg, rv, fieldAst); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var slice reflect.Value
|
||||||
|
switch {
|
||||||
|
case rv.Kind() == reflect.Slice:
|
||||||
|
slice = reflect.MakeSlice(rv.Type(), len(av), len(av))
|
||||||
|
case isEface(rv):
|
||||||
|
slice = reflect.ValueOf(make([]interface{}, len(av)))
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"array table", "slice", rv.Type()}
|
||||||
|
}
|
||||||
|
for i, tbl := range av {
|
||||||
|
vv := reflect.New(slice.Type().Elem()).Elem()
|
||||||
|
if err := unmarshalTable(cfg, vv, tbl, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slice.Index(i).Set(vv)
|
||||||
|
}
|
||||||
|
rv.Set(slice)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("BUG: unhandled AST node type %T", av))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalMapKey(typ reflect.Type, key string) (reflect.Value, error) {
|
||||||
|
rv := reflect.New(typ).Elem()
|
||||||
|
if u, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok {
|
||||||
|
return rv, u.UnmarshalText([]byte(key))
|
||||||
|
}
|
||||||
|
switch typ.Kind() {
|
||||||
|
case reflect.String:
|
||||||
|
rv.SetString(key)
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
i, err := strconv.ParseInt(key, 10, int(typ.Size()*8))
|
||||||
|
if err != nil {
|
||||||
|
return rv, convertNumError(typ.Kind(), err)
|
||||||
|
}
|
||||||
|
rv.SetInt(i)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
i, err := strconv.ParseUint(key, 10, int(typ.Size()*8))
|
||||||
|
if err != nil {
|
||||||
|
return rv, convertNumError(typ.Kind(), err)
|
||||||
|
}
|
||||||
|
rv.SetUint(i)
|
||||||
|
default:
|
||||||
|
return rv, fmt.Errorf("invalid map key type %s", typ)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setValue(cfg *Config, lhs reflect.Value, val ast.Value) error {
|
||||||
|
lhs = indirect(lhs)
|
||||||
|
if err, ok := setUnmarshaler(cfg, lhs, val); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err, ok := setTextUnmarshaler(lhs, val); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch v := val.(type) {
|
||||||
|
case *ast.Integer:
|
||||||
|
return setInt(lhs, v)
|
||||||
|
case *ast.Float:
|
||||||
|
return setFloat(lhs, v)
|
||||||
|
case *ast.String:
|
||||||
|
return setString(lhs, v)
|
||||||
|
case *ast.Boolean:
|
||||||
|
return setBoolean(lhs, v)
|
||||||
|
case *ast.Datetime:
|
||||||
|
return setDatetime(lhs, v)
|
||||||
|
case *ast.Array:
|
||||||
|
return setArray(cfg, lhs, v)
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("BUG: unhandled node type %T", v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func indirect(rv reflect.Value) reflect.Value {
|
||||||
|
for rv.Kind() == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
rv.Set(reflect.New(rv.Type().Elem()))
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
return rv
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUnmarshaler(cfg *Config, lhs reflect.Value, av interface{}) (error, bool) {
|
||||||
|
if lhs.CanAddr() {
|
||||||
|
if u, ok := lhs.Addr().Interface().(UnmarshalerRec); ok {
|
||||||
|
err := u.UnmarshalTOML(func(v interface{}) error {
|
||||||
|
return unmarshalTableOrValue(cfg, reflect.ValueOf(v), av)
|
||||||
|
})
|
||||||
|
return err, true
|
||||||
|
}
|
||||||
|
if u, ok := lhs.Addr().Interface().(Unmarshaler); ok {
|
||||||
|
return u.UnmarshalTOML(unmarshalerSource(av)), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalerSource(av interface{}) []byte {
|
||||||
|
var source []byte
|
||||||
|
switch av := av.(type) {
|
||||||
|
case []*ast.Table:
|
||||||
|
for i, tab := range av {
|
||||||
|
source = append(source, tab.Source()...)
|
||||||
|
if i != len(av)-1 {
|
||||||
|
source = append(source, '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.Value:
|
||||||
|
source = []byte(av.Source())
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("BUG: unhandled node type %T", av))
|
||||||
|
}
|
||||||
|
return source
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTextUnmarshaler(lhs reflect.Value, val ast.Value) (error, bool) {
|
||||||
|
if !lhs.CanAddr() {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
u, ok := lhs.Addr().Interface().(encoding.TextUnmarshaler)
|
||||||
|
if !ok || lhs.Type() == timeType {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
var data string
|
||||||
|
switch val := val.(type) {
|
||||||
|
case *ast.Array:
|
||||||
|
return &unmarshalTypeError{"array", "", lhs.Type()}, true
|
||||||
|
case *ast.String:
|
||||||
|
data = val.Value
|
||||||
|
default:
|
||||||
|
data = val.Source()
|
||||||
|
}
|
||||||
|
return u.UnmarshalText([]byte(data)), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func setInt(fv reflect.Value, v *ast.Integer) error {
|
||||||
|
k := fv.Kind()
|
||||||
|
switch {
|
||||||
|
case k >= reflect.Int && k <= reflect.Int64:
|
||||||
|
i, err := strconv.ParseInt(v.Value, 10, int(fv.Type().Size()*8))
|
||||||
|
if err != nil {
|
||||||
|
return convertNumError(fv.Kind(), err)
|
||||||
|
}
|
||||||
|
fv.SetInt(i)
|
||||||
|
case k >= reflect.Uint && k <= reflect.Uintptr:
|
||||||
|
i, err := strconv.ParseUint(v.Value, 10, int(fv.Type().Size()*8))
|
||||||
|
if err != nil {
|
||||||
|
return convertNumError(fv.Kind(), err)
|
||||||
|
}
|
||||||
|
fv.SetUint(i)
|
||||||
|
case isEface(fv):
|
||||||
|
i, err := strconv.ParseInt(v.Value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return convertNumError(reflect.Int64, err)
|
||||||
|
}
|
||||||
|
fv.Set(reflect.ValueOf(i))
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"integer", "", fv.Type()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFloat(fv reflect.Value, v *ast.Float) error {
|
||||||
|
f, err := v.Float()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case fv.Kind() == reflect.Float32 || fv.Kind() == reflect.Float64:
|
||||||
|
if fv.OverflowFloat(f) {
|
||||||
|
return &overflowError{fv.Kind(), v.Value}
|
||||||
|
}
|
||||||
|
fv.SetFloat(f)
|
||||||
|
case isEface(fv):
|
||||||
|
fv.Set(reflect.ValueOf(f))
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"float", "", fv.Type()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setString(fv reflect.Value, v *ast.String) error {
|
||||||
|
switch {
|
||||||
|
case fv.Kind() == reflect.String:
|
||||||
|
fv.SetString(v.Value)
|
||||||
|
case isEface(fv):
|
||||||
|
fv.Set(reflect.ValueOf(v.Value))
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"string", "", fv.Type()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBoolean(fv reflect.Value, v *ast.Boolean) error {
|
||||||
|
b, _ := v.Boolean()
|
||||||
|
switch {
|
||||||
|
case fv.Kind() == reflect.Bool:
|
||||||
|
fv.SetBool(b)
|
||||||
|
case isEface(fv):
|
||||||
|
fv.Set(reflect.ValueOf(b))
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"boolean", "", fv.Type()}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDatetime(rv reflect.Value, v *ast.Datetime) error {
|
||||||
|
t, err := v.Time()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !timeType.AssignableTo(rv.Type()) {
|
||||||
|
return &unmarshalTypeError{"datetime", "", rv.Type()}
|
||||||
|
}
|
||||||
|
rv.Set(reflect.ValueOf(t))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setArray(cfg *Config, rv reflect.Value, v *ast.Array) error {
|
||||||
|
var slicetyp reflect.Type
|
||||||
|
switch {
|
||||||
|
case rv.Kind() == reflect.Slice:
|
||||||
|
slicetyp = rv.Type()
|
||||||
|
case isEface(rv):
|
||||||
|
slicetyp = reflect.SliceOf(rv.Type())
|
||||||
|
default:
|
||||||
|
return &unmarshalTypeError{"array", "slice", rv.Type()}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.Value) == 0 {
|
||||||
|
// Ensure defined slices are always set to a non-nil value.
|
||||||
|
rv.Set(reflect.MakeSlice(slicetyp, 0, 0))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tomltyp := reflect.TypeOf(v.Value[0])
|
||||||
|
slice := reflect.MakeSlice(slicetyp, len(v.Value), len(v.Value))
|
||||||
|
typ := slicetyp.Elem()
|
||||||
|
for i, vv := range v.Value {
|
||||||
|
if i > 0 && tomltyp != reflect.TypeOf(vv) {
|
||||||
|
return errArrayMultiType
|
||||||
|
}
|
||||||
|
tmp := reflect.New(typ).Elem()
|
||||||
|
if err := setValue(cfg, tmp, vv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slice.Index(i).Set(tmp)
|
||||||
|
}
|
||||||
|
rv.Set(slice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEface(rv reflect.Value) bool {
|
||||||
|
return rv.Kind() == reflect.Interface && rv.Type().NumMethod() == 0
|
||||||
|
}
|
398
vendor/github.com/naoina/toml/encode.go
generated
vendored
Normal file
398
vendor/github.com/naoina/toml/encode.go
generated
vendored
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/naoina/toml/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tagOmitempty = "omitempty"
|
||||||
|
tagSkip = "-"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Marshal returns the TOML encoding of v.
|
||||||
|
//
|
||||||
|
// Struct values encode as TOML. Each exported struct field becomes a field of
|
||||||
|
// the TOML structure unless
|
||||||
|
// - the field's tag is "-", or
|
||||||
|
// - the field is empty and its tag specifies the "omitempty" option.
|
||||||
|
//
|
||||||
|
// The "toml" key in the struct field's tag value is the key name, followed by
|
||||||
|
// an optional comma and options. Examples:
|
||||||
|
//
|
||||||
|
// // Field is ignored by this package.
|
||||||
|
// Field int `toml:"-"`
|
||||||
|
//
|
||||||
|
// // Field appears in TOML as key "myName".
|
||||||
|
// Field int `toml:"myName"`
|
||||||
|
//
|
||||||
|
// // Field appears in TOML as key "myName" and the field is omitted from the
|
||||||
|
// // result of encoding if its value is empty.
|
||||||
|
// Field int `toml:"myName,omitempty"`
|
||||||
|
//
|
||||||
|
// // Field appears in TOML as key "field", but the field is skipped if
|
||||||
|
// // empty. Note the leading comma.
|
||||||
|
// Field int `toml:",omitempty"`
|
||||||
|
func (cfg *Config) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err := cfg.NewEncoder(buf).Encode(v)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Encoder writes TOML to an output stream.
|
||||||
|
type Encoder struct {
|
||||||
|
w io.Writer
|
||||||
|
cfg *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new Encoder that writes to w.
|
||||||
|
func (cfg *Config) NewEncoder(w io.Writer) *Encoder {
|
||||||
|
return &Encoder{w, cfg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the TOML of v to the stream.
|
||||||
|
// See the documentation for Marshal for details about the conversion of Go values to TOML.
|
||||||
|
func (e *Encoder) Encode(v interface{}) error {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
for rv.Kind() == reflect.Ptr {
|
||||||
|
if rv.IsNil() {
|
||||||
|
return &marshalNilError{rv.Type()}
|
||||||
|
}
|
||||||
|
rv = rv.Elem()
|
||||||
|
}
|
||||||
|
buf := &tableBuf{typ: ast.TableTypeNormal}
|
||||||
|
var err error
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
err = buf.structFields(e.cfg, rv)
|
||||||
|
case reflect.Map:
|
||||||
|
err = buf.mapFields(e.cfg, rv)
|
||||||
|
default:
|
||||||
|
err = &marshalTableError{rv.Type()}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return buf.writeTo(e.w, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaler can be implemented to override the encoding of TOML values. The returned text
|
||||||
|
// must be a simple TOML value (i.e. not a table) and is inserted into marshaler output.
|
||||||
|
//
|
||||||
|
// This interface exists for backwards-compatibility reasons. You probably want to
|
||||||
|
// implement encoding.TextMarshaler or MarshalerRec instead.
|
||||||
|
type Marshaler interface {
|
||||||
|
MarshalTOML() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalerRec can be implemented to override the TOML encoding of a type.
|
||||||
|
// The returned value is marshaled in place of the receiver.
|
||||||
|
type MarshalerRec interface {
|
||||||
|
MarshalTOML() (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableBuf struct {
|
||||||
|
name string // already escaped / quoted
|
||||||
|
body []byte
|
||||||
|
children []*tableBuf
|
||||||
|
typ ast.TableType
|
||||||
|
arrayDepth int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) writeTo(w io.Writer, prefix string) error {
|
||||||
|
key := b.name // TODO: escape dots
|
||||||
|
if prefix != "" {
|
||||||
|
key = prefix + "." + key
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.name != "" {
|
||||||
|
head := "[" + key + "]"
|
||||||
|
if b.typ == ast.TableTypeArray {
|
||||||
|
head = "[" + head + "]"
|
||||||
|
}
|
||||||
|
head += "\n"
|
||||||
|
if _, err := io.WriteString(w, head); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err := w.Write(b.body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, child := range b.children {
|
||||||
|
if len(b.body) > 0 || i > 0 {
|
||||||
|
if _, err := w.Write([]byte("\n")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := child.writeTo(w, key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) newChild(name string) *tableBuf {
|
||||||
|
child := &tableBuf{name: quoteName(name), typ: ast.TableTypeNormal}
|
||||||
|
if b.arrayDepth > 0 {
|
||||||
|
child.typ = ast.TableTypeArray
|
||||||
|
}
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) addChild(child *tableBuf) {
|
||||||
|
// Empty table elision: we can avoid writing a table that doesn't have any keys on its
|
||||||
|
// own. Array tables can't be elided because they define array elements (which would
|
||||||
|
// be missing if elided).
|
||||||
|
if len(child.body) == 0 && child.typ == ast.TableTypeNormal {
|
||||||
|
for _, gchild := range child.children {
|
||||||
|
gchild.name = child.name + "." + gchild.name
|
||||||
|
b.addChild(gchild)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.children = append(b.children, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error {
|
||||||
|
rt := rv.Type()
|
||||||
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
ft := rt.Field(i)
|
||||||
|
if ft.PkgPath != "" && !ft.Anonymous { // not exported
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, rest := extractTag(ft.Tag.Get(fieldTagName))
|
||||||
|
if name == tagSkip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fv := rv.Field(i)
|
||||||
|
if rest == tagOmitempty && isEmptyValue(fv) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
name = cfg.FieldToKey(rt, ft.Name)
|
||||||
|
}
|
||||||
|
if err := b.field(cfg, name, fv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mapKeyList []struct {
|
||||||
|
key string
|
||||||
|
value reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l mapKeyList) Len() int { return len(l) }
|
||||||
|
func (l mapKeyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||||
|
func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key }
|
||||||
|
|
||||||
|
func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error {
|
||||||
|
keys := rv.MapKeys()
|
||||||
|
keylist := make(mapKeyList, len(keys))
|
||||||
|
for i, key := range keys {
|
||||||
|
var err error
|
||||||
|
keylist[i].key, err = encodeMapKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
keylist[i].value = rv.MapIndex(key)
|
||||||
|
}
|
||||||
|
sort.Sort(keylist)
|
||||||
|
|
||||||
|
for _, kv := range keylist {
|
||||||
|
if err := b.field(cfg, kv.key, kv.value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error {
|
||||||
|
off := len(b.body)
|
||||||
|
b.body = append(b.body, quoteName(name)...)
|
||||||
|
b.body = append(b.body, " = "...)
|
||||||
|
isTable, err := b.value(cfg, rv, name)
|
||||||
|
if isTable {
|
||||||
|
b.body = b.body[:off] // rub out "key ="
|
||||||
|
} else {
|
||||||
|
b.body = append(b.body, '\n')
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) {
|
||||||
|
isMarshaler, isTable, err := b.marshaler(cfg, rv, name)
|
||||||
|
if isMarshaler {
|
||||||
|
return isTable, err
|
||||||
|
}
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
b.body = strconv.AppendInt(b.body, rv.Int(), 10)
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
b.body = strconv.AppendUint(b.body, rv.Uint(), 10)
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
b.body = strconv.AppendFloat(b.body, rv.Float(), 'e', -1, 64)
|
||||||
|
case reflect.Bool:
|
||||||
|
b.body = strconv.AppendBool(b.body, rv.Bool())
|
||||||
|
case reflect.String:
|
||||||
|
b.body = strconv.AppendQuote(b.body, rv.String())
|
||||||
|
case reflect.Ptr, reflect.Interface:
|
||||||
|
if rv.IsNil() {
|
||||||
|
return false, &marshalNilError{rv.Type()}
|
||||||
|
}
|
||||||
|
return b.value(cfg, rv.Elem(), name)
|
||||||
|
case reflect.Slice, reflect.Array:
|
||||||
|
rvlen := rv.Len()
|
||||||
|
if rvlen == 0 {
|
||||||
|
b.body = append(b.body, '[', ']')
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b.arrayDepth++
|
||||||
|
wroteElem := false
|
||||||
|
b.body = append(b.body, '[')
|
||||||
|
for i := 0; i < rvlen; i++ {
|
||||||
|
isTable, err := b.value(cfg, rv.Index(i), name)
|
||||||
|
if err != nil {
|
||||||
|
return isTable, err
|
||||||
|
}
|
||||||
|
wroteElem = wroteElem || !isTable
|
||||||
|
if wroteElem {
|
||||||
|
if i < rvlen-1 {
|
||||||
|
b.body = append(b.body, ',', ' ')
|
||||||
|
} else {
|
||||||
|
b.body = append(b.body, ']')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !wroteElem {
|
||||||
|
b.body = b.body[:len(b.body)-1] // rub out '['
|
||||||
|
}
|
||||||
|
b.arrayDepth--
|
||||||
|
return !wroteElem, nil
|
||||||
|
case reflect.Struct:
|
||||||
|
child := b.newChild(name)
|
||||||
|
err := child.structFields(cfg, rv)
|
||||||
|
b.addChild(child)
|
||||||
|
return true, err
|
||||||
|
case reflect.Map:
|
||||||
|
child := b.newChild(name)
|
||||||
|
err := child.mapFields(cfg, rv)
|
||||||
|
b.addChild(child)
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("toml: marshal: unsupported type %v", rv.Kind())
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) {
|
||||||
|
switch t := rv.Interface().(type) {
|
||||||
|
case encoding.TextMarshaler:
|
||||||
|
enc, err := t.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
return true, false, err
|
||||||
|
}
|
||||||
|
b.body = encodeTextMarshaler(b.body, string(enc))
|
||||||
|
return true, false, nil
|
||||||
|
case MarshalerRec:
|
||||||
|
newval, err := t.MarshalTOML()
|
||||||
|
if err != nil {
|
||||||
|
return true, false, err
|
||||||
|
}
|
||||||
|
isTable, err = b.value(cfg, reflect.ValueOf(newval), name)
|
||||||
|
return true, isTable, err
|
||||||
|
case Marshaler:
|
||||||
|
enc, err := t.MarshalTOML()
|
||||||
|
if err != nil {
|
||||||
|
return true, false, err
|
||||||
|
}
|
||||||
|
b.body = append(b.body, enc...)
|
||||||
|
return true, false, nil
|
||||||
|
}
|
||||||
|
return false, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTextMarshaler(buf []byte, v string) []byte {
|
||||||
|
// Emit the value without quotes if possible.
|
||||||
|
if v == "true" || v == "false" {
|
||||||
|
return append(buf, v...)
|
||||||
|
} else if _, err := time.Parse(time.RFC3339Nano, v); err == nil {
|
||||||
|
return append(buf, v...)
|
||||||
|
} else if _, err := strconv.ParseInt(v, 10, 64); err == nil {
|
||||||
|
return append(buf, v...)
|
||||||
|
} else if _, err := strconv.ParseUint(v, 10, 64); err == nil {
|
||||||
|
return append(buf, v...)
|
||||||
|
} else if _, err := strconv.ParseFloat(v, 64); err == nil {
|
||||||
|
return append(buf, v...)
|
||||||
|
}
|
||||||
|
return strconv.AppendQuote(buf, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeMapKey(rv reflect.Value) (string, error) {
|
||||||
|
if rv.Kind() == reflect.String {
|
||||||
|
return rv.String(), nil
|
||||||
|
}
|
||||||
|
if tm, ok := rv.Interface().(encoding.TextMarshaler); ok {
|
||||||
|
b, err := tm.MarshalText()
|
||||||
|
return string(b), err
|
||||||
|
}
|
||||||
|
switch rv.Kind() {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return strconv.FormatInt(rv.Int(), 10), nil
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return strconv.FormatUint(rv.Uint(), 10), nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("toml: invalid map key type %v", rv.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEmptyValue(v reflect.Value) bool {
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Array:
|
||||||
|
// encoding/json treats all arrays with non-zero length as non-empty. We check the
|
||||||
|
// array content here because zero-length arrays are almost never used.
|
||||||
|
len := v.Len()
|
||||||
|
for i := 0; i < len; i++ {
|
||||||
|
if !isEmptyValue(v.Index(i)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case reflect.Map, reflect.Slice, reflect.String:
|
||||||
|
return v.Len() == 0
|
||||||
|
case reflect.Bool:
|
||||||
|
return !v.Bool()
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return v.Int() == 0
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||||
|
return v.Uint() == 0
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return v.Float() == 0
|
||||||
|
case reflect.Interface, reflect.Ptr:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoteName(s string) string {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return strconv.Quote(s)
|
||||||
|
}
|
||||||
|
for _, r := range s {
|
||||||
|
if r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '-' || r == '_' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return strconv.Quote(s)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
107
vendor/github.com/naoina/toml/error.go
generated
vendored
Normal file
107
vendor/github.com/naoina/toml/error.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errArrayMultiType = errors.New("array can't contain multiple types")
|
||||||
|
)
|
||||||
|
|
||||||
|
// LineError is returned by Unmarshal, UnmarshalTable and Parse
|
||||||
|
// if the error is local to a line.
|
||||||
|
type LineError struct {
|
||||||
|
Line int
|
||||||
|
StructField string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *LineError) Error() string {
|
||||||
|
field := ""
|
||||||
|
if err.StructField != "" {
|
||||||
|
field = "(" + err.StructField + ") "
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("line %d: %s%v", err.Line, field, err.Err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineError(line int, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if _, ok := err.(*LineError); ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return &LineError{Line: line, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lineErrorField(line int, field string, err error) error {
|
||||||
|
if lerr, ok := err.(*LineError); ok {
|
||||||
|
return lerr
|
||||||
|
} else if err != nil {
|
||||||
|
err = &LineError{Line: line, StructField: field, Err: err}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type overflowError struct {
|
||||||
|
kind reflect.Kind
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *overflowError) Error() string {
|
||||||
|
return fmt.Sprintf("value %s is out of range for %v", err.v, err.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertNumError(kind reflect.Kind, err error) error {
|
||||||
|
if numerr, ok := err.(*strconv.NumError); ok && numerr.Err == strconv.ErrRange {
|
||||||
|
return &overflowError{kind, numerr.Num}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidUnmarshalError struct {
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *invalidUnmarshalError) Error() string {
|
||||||
|
if err.typ == nil {
|
||||||
|
return "toml: Unmarshal(nil)"
|
||||||
|
}
|
||||||
|
if err.typ.Kind() != reflect.Ptr {
|
||||||
|
return "toml: Unmarshal(non-pointer " + err.typ.String() + ")"
|
||||||
|
}
|
||||||
|
return "toml: Unmarshal(nil " + err.typ.String() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
type unmarshalTypeError struct {
|
||||||
|
what string
|
||||||
|
want string
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *unmarshalTypeError) Error() string {
|
||||||
|
msg := fmt.Sprintf("cannot unmarshal TOML %s into %s", err.what, err.typ)
|
||||||
|
if err.want != "" {
|
||||||
|
msg += " (need " + err.want + ")"
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
type marshalNilError struct {
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *marshalNilError) Error() string {
|
||||||
|
return fmt.Sprintf("toml: cannot marshal nil %s", err.typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
type marshalTableError struct {
|
||||||
|
typ reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *marshalTableError) Error() string {
|
||||||
|
return fmt.Sprintf("toml: cannot marshal %s as table, want struct or map type", err.typ)
|
||||||
|
}
|
376
vendor/github.com/naoina/toml/parse.go
generated
vendored
Normal file
376
vendor/github.com/naoina/toml/parse.go
generated
vendored
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/naoina/toml/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The parser is generated by github.com/pointlander/peg. To regenerate it, do:
|
||||||
|
//
|
||||||
|
// go get -u github.com/pointlander/peg
|
||||||
|
// go generate .
|
||||||
|
|
||||||
|
//go:generate peg -switch -inline parse.peg
|
||||||
|
|
||||||
|
var errParse = errors.New("invalid TOML syntax")
|
||||||
|
|
||||||
|
// Parse returns an AST representation of TOML.
|
||||||
|
// The toplevel is represented by a table.
|
||||||
|
func Parse(data []byte) (*ast.Table, error) {
|
||||||
|
d := &parseState{p: &tomlParser{Buffer: string(data)}}
|
||||||
|
d.init()
|
||||||
|
|
||||||
|
if err := d.parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.p.toml.table, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseState struct {
|
||||||
|
p *tomlParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *parseState) init() {
|
||||||
|
d.p.Init()
|
||||||
|
d.p.toml.init(d.p.buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *parseState) parse() error {
|
||||||
|
if err := d.p.Parse(); err != nil {
|
||||||
|
if err, ok := err.(*parseError); ok {
|
||||||
|
return lineError(err.Line(), errParse)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *parseState) execute() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
lerr, ok := e.(*LineError)
|
||||||
|
if !ok {
|
||||||
|
panic(e)
|
||||||
|
}
|
||||||
|
err = lerr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
d.p.Execute()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *parseError) Line() int {
|
||||||
|
tokens := []token32{e.max}
|
||||||
|
positions, p := make([]int, 2*len(tokens)), 0
|
||||||
|
for _, token := range tokens {
|
||||||
|
positions[p], p = int(token.begin), p+1
|
||||||
|
positions[p], p = int(token.end), p+1
|
||||||
|
}
|
||||||
|
for _, t := range translatePositions(e.p.buffer, positions) {
|
||||||
|
if e.p.line < t.line {
|
||||||
|
e.p.line = t.line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e.p.line
|
||||||
|
}
|
||||||
|
|
||||||
|
type stack struct {
|
||||||
|
key string
|
||||||
|
table *ast.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
type array struct {
|
||||||
|
parent *array
|
||||||
|
child *array
|
||||||
|
current *ast.Array
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
|
||||||
|
type toml struct {
|
||||||
|
table *ast.Table
|
||||||
|
line int
|
||||||
|
currentTable *ast.Table
|
||||||
|
s string
|
||||||
|
key string
|
||||||
|
val ast.Value
|
||||||
|
arr *array
|
||||||
|
stack []*stack
|
||||||
|
skip bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) init(data []rune) {
|
||||||
|
p.line = 1
|
||||||
|
p.table = p.newTable(ast.TableTypeNormal, "")
|
||||||
|
p.table.Position.End = len(data) - 1
|
||||||
|
p.table.Data = data[:len(data)-1] // truncate the end_symbol added by PEG parse generator.
|
||||||
|
p.currentTable = p.table
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) Error(err error) {
|
||||||
|
panic(lineError(p.line, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetTime(begin, end int) {
|
||||||
|
p.val = &ast.Datetime{
|
||||||
|
Position: ast.Position{Begin: begin, End: end},
|
||||||
|
Data: p.buffer[begin:end],
|
||||||
|
Value: string(p.buffer[begin:end]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetFloat64(begin, end int) {
|
||||||
|
p.val = &ast.Float{
|
||||||
|
Position: ast.Position{Begin: begin, End: end},
|
||||||
|
Data: p.buffer[begin:end],
|
||||||
|
Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetInt64(begin, end int) {
|
||||||
|
p.val = &ast.Integer{
|
||||||
|
Position: ast.Position{Begin: begin, End: end},
|
||||||
|
Data: p.buffer[begin:end],
|
||||||
|
Value: underscoreReplacer.Replace(string(p.buffer[begin:end])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetString(begin, end int) {
|
||||||
|
p.val = &ast.String{
|
||||||
|
Position: ast.Position{Begin: begin, End: end},
|
||||||
|
Data: p.buffer[begin:end],
|
||||||
|
Value: p.s,
|
||||||
|
}
|
||||||
|
p.s = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetBool(begin, end int) {
|
||||||
|
p.val = &ast.Boolean{
|
||||||
|
Position: ast.Position{Begin: begin, End: end},
|
||||||
|
Data: p.buffer[begin:end],
|
||||||
|
Value: string(p.buffer[begin:end]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) StartArray() {
|
||||||
|
if p.arr == nil {
|
||||||
|
p.arr = &array{line: p.line, current: &ast.Array{}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.arr.child = &array{parent: p.arr, line: p.line, current: &ast.Array{}}
|
||||||
|
p.arr = p.arr.child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) AddArrayVal() {
|
||||||
|
if p.arr.current == nil {
|
||||||
|
p.arr.current = &ast.Array{}
|
||||||
|
}
|
||||||
|
p.arr.current.Value = append(p.arr.current.Value, p.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetArray(begin, end int) {
|
||||||
|
p.arr.current.Position = ast.Position{Begin: begin, End: end}
|
||||||
|
p.arr.current.Data = p.buffer[begin:end]
|
||||||
|
p.val = p.arr.current
|
||||||
|
p.arr = p.arr.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetTable(buf []rune, begin, end int) {
|
||||||
|
p.setTable(p.table, buf, begin, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) setTable(parent *ast.Table, buf []rune, begin, end int) {
|
||||||
|
name := string(buf[begin:end])
|
||||||
|
names := splitTableKey(name)
|
||||||
|
parent, err := p.lookupTable(parent, names[:len(names)-1])
|
||||||
|
if err != nil {
|
||||||
|
p.Error(err)
|
||||||
|
}
|
||||||
|
last := names[len(names)-1]
|
||||||
|
tbl := p.newTable(ast.TableTypeNormal, last)
|
||||||
|
switch v := parent.Fields[last].(type) {
|
||||||
|
case nil:
|
||||||
|
parent.Fields[last] = tbl
|
||||||
|
case []*ast.Table:
|
||||||
|
p.Error(fmt.Errorf("table `%s' is in conflict with array table in line %d", name, v[0].Line))
|
||||||
|
case *ast.Table:
|
||||||
|
if (v.Position == ast.Position{}) {
|
||||||
|
// This table was created as an implicit parent.
|
||||||
|
// Replace it with the real defined table.
|
||||||
|
tbl.Fields = v.Fields
|
||||||
|
parent.Fields[last] = tbl
|
||||||
|
} else {
|
||||||
|
p.Error(fmt.Errorf("table `%s' is in conflict with table in line %d", name, v.Line))
|
||||||
|
}
|
||||||
|
case *ast.KeyValue:
|
||||||
|
p.Error(fmt.Errorf("table `%s' is in conflict with line %d", name, v.Line))
|
||||||
|
default:
|
||||||
|
p.Error(fmt.Errorf("BUG: table `%s' is in conflict but it's unknown type `%T'", last, v))
|
||||||
|
}
|
||||||
|
p.currentTable = tbl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) newTable(typ ast.TableType, name string) *ast.Table {
|
||||||
|
return &ast.Table{
|
||||||
|
Line: p.line,
|
||||||
|
Name: name,
|
||||||
|
Type: typ,
|
||||||
|
Fields: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) SetTableString(begin, end int) {
|
||||||
|
p.currentTable.Data = p.buffer[begin:end]
|
||||||
|
p.currentTable.Position.Begin = begin
|
||||||
|
p.currentTable.Position.End = end
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetArrayTable(buf []rune, begin, end int) {
|
||||||
|
p.setArrayTable(p.table, buf, begin, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) setArrayTable(parent *ast.Table, buf []rune, begin, end int) {
|
||||||
|
name := string(buf[begin:end])
|
||||||
|
names := splitTableKey(name)
|
||||||
|
parent, err := p.lookupTable(parent, names[:len(names)-1])
|
||||||
|
if err != nil {
|
||||||
|
p.Error(err)
|
||||||
|
}
|
||||||
|
last := names[len(names)-1]
|
||||||
|
tbl := p.newTable(ast.TableTypeArray, last)
|
||||||
|
switch v := parent.Fields[last].(type) {
|
||||||
|
case nil:
|
||||||
|
parent.Fields[last] = []*ast.Table{tbl}
|
||||||
|
case []*ast.Table:
|
||||||
|
parent.Fields[last] = append(v, tbl)
|
||||||
|
case *ast.Table:
|
||||||
|
p.Error(fmt.Errorf("array table `%s' is in conflict with table in line %d", name, v.Line))
|
||||||
|
case *ast.KeyValue:
|
||||||
|
p.Error(fmt.Errorf("array table `%s' is in conflict with line %d", name, v.Line))
|
||||||
|
default:
|
||||||
|
p.Error(fmt.Errorf("BUG: array table `%s' is in conflict but it's unknown type `%T'", name, v))
|
||||||
|
}
|
||||||
|
p.currentTable = tbl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) StartInlineTable() {
|
||||||
|
p.skip = false
|
||||||
|
p.stack = append(p.stack, &stack{p.key, p.currentTable})
|
||||||
|
buf := []rune(p.key)
|
||||||
|
if p.arr == nil {
|
||||||
|
p.setTable(p.currentTable, buf, 0, len(buf))
|
||||||
|
} else {
|
||||||
|
p.setArrayTable(p.currentTable, buf, 0, len(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) EndInlineTable() {
|
||||||
|
st := p.stack[len(p.stack)-1]
|
||||||
|
p.key, p.currentTable = st.key, st.table
|
||||||
|
p.stack[len(p.stack)-1] = nil
|
||||||
|
p.stack = p.stack[:len(p.stack)-1]
|
||||||
|
p.skip = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) AddLineCount(i int) {
|
||||||
|
p.line += i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetKey(buf []rune, begin, end int) {
|
||||||
|
p.key = string(buf[begin:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) AddKeyValue() {
|
||||||
|
if p.skip {
|
||||||
|
p.skip = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if val, exists := p.currentTable.Fields[p.key]; exists {
|
||||||
|
switch v := val.(type) {
|
||||||
|
case *ast.Table:
|
||||||
|
p.Error(fmt.Errorf("key `%s' is in conflict with table in line %d", p.key, v.Line))
|
||||||
|
case *ast.KeyValue:
|
||||||
|
p.Error(fmt.Errorf("key `%s' is in conflict with line %xd", p.key, v.Line))
|
||||||
|
default:
|
||||||
|
p.Error(fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", p.key, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.currentTable.Fields[p.key] = &ast.KeyValue{Key: p.key, Value: p.val, Line: p.line}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetBasicString(buf []rune, begin, end int) {
|
||||||
|
p.s = p.unquote(string(buf[begin:end]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetMultilineString() {
|
||||||
|
p.s = p.unquote(`"` + escapeReplacer.Replace(strings.TrimLeft(p.s, "\r\n")) + `"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) AddMultilineBasicBody(buf []rune, begin, end int) {
|
||||||
|
p.s += string(buf[begin:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetLiteralString(buf []rune, begin, end int) {
|
||||||
|
p.s = string(buf[begin:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) SetMultilineLiteralString(buf []rune, begin, end int) {
|
||||||
|
p.s = strings.TrimLeft(string(buf[begin:end]), "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) unquote(s string) string {
|
||||||
|
s, err := strconv.Unquote(s)
|
||||||
|
if err != nil {
|
||||||
|
p.Error(err)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *toml) lookupTable(t *ast.Table, keys []string) (*ast.Table, error) {
|
||||||
|
for _, s := range keys {
|
||||||
|
val, exists := t.Fields[s]
|
||||||
|
if !exists {
|
||||||
|
tbl := p.newTable(ast.TableTypeNormal, s)
|
||||||
|
t.Fields[s] = tbl
|
||||||
|
t = tbl
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch v := val.(type) {
|
||||||
|
case *ast.Table:
|
||||||
|
t = v
|
||||||
|
case []*ast.Table:
|
||||||
|
t = v[len(v)-1]
|
||||||
|
case *ast.KeyValue:
|
||||||
|
return nil, fmt.Errorf("key `%s' is in conflict with line %d", s, v.Line)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("BUG: key `%s' is in conflict but it's unknown type `%T'", s, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitTableKey(tk string) []string {
|
||||||
|
key := make([]byte, 0, 1)
|
||||||
|
keys := make([]string, 0, 1)
|
||||||
|
inQuote := false
|
||||||
|
for i := 0; i < len(tk); i++ {
|
||||||
|
k := tk[i]
|
||||||
|
switch {
|
||||||
|
case k == tableSeparator && !inQuote:
|
||||||
|
keys = append(keys, string(key))
|
||||||
|
key = key[:0] // reuse buffer.
|
||||||
|
case k == '"':
|
||||||
|
inQuote = !inQuote
|
||||||
|
case (k == ' ' || k == '\t') && !inQuote:
|
||||||
|
// skip.
|
||||||
|
default:
|
||||||
|
key = append(key, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keys = append(keys, string(key))
|
||||||
|
return keys
|
||||||
|
}
|
145
vendor/github.com/naoina/toml/parse.peg
generated
vendored
Normal file
145
vendor/github.com/naoina/toml/parse.peg
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
type tomlParser Peg {
|
||||||
|
toml
|
||||||
|
}
|
||||||
|
|
||||||
|
TOML <- Expression (newline Expression)* newline? !. { _ = buffer }
|
||||||
|
|
||||||
|
Expression <- (
|
||||||
|
<ws table ws comment? (wsnl keyval ws comment?)*> { p.SetTableString(begin, end) }
|
||||||
|
/ ws keyval ws comment?
|
||||||
|
/ ws comment?
|
||||||
|
/ ws
|
||||||
|
)
|
||||||
|
|
||||||
|
newline <- <[\r\n]+> { p.AddLineCount(end - begin) }
|
||||||
|
|
||||||
|
ws <- [ \t]*
|
||||||
|
wsnl <- (
|
||||||
|
[ \t]
|
||||||
|
/ <[\r\n]> { p.AddLineCount(end - begin) }
|
||||||
|
)*
|
||||||
|
|
||||||
|
comment <- '#' <[\t -\0x10FFFF]*>
|
||||||
|
|
||||||
|
keyval <- key ws '=' ws val { p.AddKeyValue() }
|
||||||
|
|
||||||
|
key <- bareKey / quotedKey
|
||||||
|
|
||||||
|
bareKey <- <[0-9A-Za-z\-_]+> { p.SetKey(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
quotedKey <- '"' <basicChar+> '"' { p.SetKey(p.buffer, begin-1, end+1) }
|
||||||
|
|
||||||
|
val <- (
|
||||||
|
<datetime> { p.SetTime(begin, end) }
|
||||||
|
/ <float> { p.SetFloat64(begin, end) }
|
||||||
|
/ <integer> { p.SetInt64(begin, end) }
|
||||||
|
/ <string> { p.SetString(begin, end) }
|
||||||
|
/ <boolean> { p.SetBool(begin, end) }
|
||||||
|
/ <array> { p.SetArray(begin, end) }
|
||||||
|
/ inlineTable
|
||||||
|
)
|
||||||
|
|
||||||
|
table <- stdTable / arrayTable
|
||||||
|
|
||||||
|
stdTable <- '[' ws <tableKey> ws ']' { p.SetTable(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
arrayTable <- '[[' ws <tableKey> ws ']]' { p.SetArrayTable(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
inlineTable <- (
|
||||||
|
'{' { p.StartInlineTable() }
|
||||||
|
ws inlineTableKeyValues ws
|
||||||
|
'}' { p.EndInlineTable() }
|
||||||
|
)
|
||||||
|
|
||||||
|
inlineTableKeyValues <- (keyval inlineTableValSep?)*
|
||||||
|
|
||||||
|
tableKey <- key (tableKeySep key)*
|
||||||
|
|
||||||
|
tableKeySep <- ws '.' ws
|
||||||
|
|
||||||
|
inlineTableValSep <- ws ',' ws
|
||||||
|
|
||||||
|
integer <- [\-+]? int
|
||||||
|
int <- [1-9] (digit / '_' digit)+ / digit
|
||||||
|
|
||||||
|
float <- integer (frac exp? / frac? exp)
|
||||||
|
frac <- '.' digit (digit / '_' digit)*
|
||||||
|
exp <- [eE] [\-+]? digit (digit / '_' digit)*
|
||||||
|
|
||||||
|
string <- (
|
||||||
|
mlLiteralString
|
||||||
|
/ literalString
|
||||||
|
/ mlBasicString
|
||||||
|
/ basicString
|
||||||
|
)
|
||||||
|
|
||||||
|
basicString <- <'"' basicChar* '"'> { p.SetBasicString(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
basicChar <- basicUnescaped / escaped
|
||||||
|
escaped <- escape ([btnfr"/\\] / 'u' hexQuad / 'U' hexQuad hexQuad)
|
||||||
|
|
||||||
|
basicUnescaped <- [ -!#-\[\]-\0x10FFFF]
|
||||||
|
|
||||||
|
escape <- '\\'
|
||||||
|
|
||||||
|
mlBasicString <- '"""' mlBasicBody '"""' { p.SetMultilineString() }
|
||||||
|
|
||||||
|
mlBasicBody <- (
|
||||||
|
<basicChar / newline> { p.AddMultilineBasicBody(p.buffer, begin, end) }
|
||||||
|
/ escape newline wsnl
|
||||||
|
)*
|
||||||
|
|
||||||
|
literalString <- "'" <literalChar*> "'" { p.SetLiteralString(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
literalChar <- [\t -&(-\0x10FFFF]
|
||||||
|
|
||||||
|
mlLiteralString <- "'''" <mlLiteralBody> "'''" { p.SetMultilineLiteralString(p.buffer, begin, end) }
|
||||||
|
|
||||||
|
mlLiteralBody <- (!"'''" (mlLiteralChar / newline))*
|
||||||
|
|
||||||
|
mlLiteralChar <- [\t -\0x10FFFF]
|
||||||
|
|
||||||
|
hexdigit <- [0-9A-Fa-f]
|
||||||
|
hexQuad <- hexdigit hexdigit hexdigit hexdigit
|
||||||
|
|
||||||
|
boolean <- 'true' / 'false'
|
||||||
|
|
||||||
|
dateFullYear <- digitQuad
|
||||||
|
dateMonth <- digitDual
|
||||||
|
dateMDay <- digitDual
|
||||||
|
timeHour <- digitDual
|
||||||
|
timeMinute <- digitDual
|
||||||
|
timeSecond <- digitDual
|
||||||
|
timeSecfrac <- '.' digit+
|
||||||
|
timeNumoffset <- [\-+] timeHour ':' timeMinute
|
||||||
|
timeOffset <- 'Z' / timeNumoffset
|
||||||
|
partialTime <- timeHour ':' timeMinute ':' timeSecond timeSecfrac?
|
||||||
|
fullDate <- dateFullYear '-' dateMonth '-' dateMDay
|
||||||
|
fullTime <- partialTime timeOffset
|
||||||
|
datetime <- (fullDate ('T' fullTime)?) / partialTime
|
||||||
|
|
||||||
|
digit <- [0-9]
|
||||||
|
digitDual <- digit digit
|
||||||
|
digitQuad <- digitDual digitDual
|
||||||
|
|
||||||
|
array <- (
|
||||||
|
'[' { p.StartArray() }
|
||||||
|
wsnl arrayValues? wsnl
|
||||||
|
']'
|
||||||
|
)
|
||||||
|
|
||||||
|
arrayValues <- (
|
||||||
|
val { p.AddArrayVal() }
|
||||||
|
(
|
||||||
|
wsnl comment?
|
||||||
|
wsnl arraySep
|
||||||
|
wsnl comment?
|
||||||
|
wsnl val { p.AddArrayVal() }
|
||||||
|
)*
|
||||||
|
wsnl arraySep?
|
||||||
|
wsnl comment?
|
||||||
|
)
|
||||||
|
|
||||||
|
arraySep <- ','
|
2556
vendor/github.com/naoina/toml/parse.peg.go
generated
vendored
Normal file
2556
vendor/github.com/naoina/toml/parse.peg.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
65
vendor/github.com/naoina/toml/util.go
generated
vendored
Normal file
65
vendor/github.com/naoina/toml/util.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fieldTagName = "toml"
|
||||||
|
|
||||||
|
// fieldCache maps normalized field names to their position in a struct.
|
||||||
|
type fieldCache struct {
|
||||||
|
named map[string]fieldInfo // fields with an explicit name in tag
|
||||||
|
auto map[string]fieldInfo // fields with auto-assigned normalized names
|
||||||
|
}
|
||||||
|
|
||||||
|
type fieldInfo struct {
|
||||||
|
index []int
|
||||||
|
name string
|
||||||
|
ignored bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFieldCache(cfg *Config, rt reflect.Type) fieldCache {
|
||||||
|
named, auto := make(map[string]fieldInfo), make(map[string]fieldInfo)
|
||||||
|
for i := 0; i < rt.NumField(); i++ {
|
||||||
|
ft := rt.Field(i)
|
||||||
|
// skip unexported fields
|
||||||
|
if ft.PkgPath != "" && !ft.Anonymous {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
col, _ := extractTag(ft.Tag.Get(fieldTagName))
|
||||||
|
info := fieldInfo{index: ft.Index, name: ft.Name, ignored: col == "-"}
|
||||||
|
if col == "" || col == "-" {
|
||||||
|
auto[cfg.NormFieldName(rt, ft.Name)] = info
|
||||||
|
} else {
|
||||||
|
named[col] = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldCache{named, auto}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc fieldCache) findField(cfg *Config, rv reflect.Value, name string) (reflect.Value, string, error) {
|
||||||
|
info, found := fc.named[name]
|
||||||
|
if !found {
|
||||||
|
info, found = fc.auto[cfg.NormFieldName(rv.Type(), name)]
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
if cfg.MissingField == nil {
|
||||||
|
return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' is not defined in %v", name, rv.Type())
|
||||||
|
} else {
|
||||||
|
return reflect.Value{}, "", cfg.MissingField(rv.Type(), name)
|
||||||
|
}
|
||||||
|
} else if info.ignored {
|
||||||
|
return reflect.Value{}, "", fmt.Errorf("field corresponding to `%s' in %v cannot be set through TOML", name, rv.Type())
|
||||||
|
}
|
||||||
|
return rv.FieldByIndex(info.index), info.name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTag(tag string) (col, rest string) {
|
||||||
|
tags := strings.SplitN(tag, ",", 2)
|
||||||
|
if len(tags) == 2 {
|
||||||
|
return strings.TrimSpace(tags[0]), strings.TrimSpace(tags[1])
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(tags[0]), ""
|
||||||
|
}
|
12
vendor/vendor.json
vendored
12
vendor/vendor.json
vendored
@ -201,6 +201,18 @@
|
|||||||
"revision": "ad45545899c7b13c020ea92b2072220eefad42b8",
|
"revision": "ad45545899c7b13c020ea92b2072220eefad42b8",
|
||||||
"revisionTime": "2015-03-14T17:03:34Z"
|
"revisionTime": "2015-03-14T17:03:34Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "2gmvVTDCks8cPhpmyDlvm0sbrXE=",
|
||||||
|
"path": "github.com/naoina/toml",
|
||||||
|
"revision": "ac014c6b6502388d89a85552b7208b8da7cfe104",
|
||||||
|
"revisionTime": "2017-04-10T21:57:17Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "xZBlSMT5o/A+EDOro6KbfHZwSNc=",
|
||||||
|
"path": "github.com/naoina/toml/ast",
|
||||||
|
"revision": "eb52202f758b98ac5b1a8eb26f36455205d688f0",
|
||||||
|
"revisionTime": "2017-04-03T15:03:10Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "R1h9XHH3dTmLq7yKL9/uW0xFwfs=",
|
"checksumSHA1": "R1h9XHH3dTmLq7yKL9/uW0xFwfs=",
|
||||||
"path": "github.com/nsf/termbox-go",
|
"path": "github.com/nsf/termbox-go",
|
||||||
|
Loading…
Reference in New Issue
Block a user