mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
Finish flags
This commit is contained in:
parent
a9b5e5dfb8
commit
7225eec0f6
9 changed files with 389 additions and 131 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -47,3 +47,4 @@ profile.cov
|
||||||
/dashboard/assets/package-lock.json
|
/dashboard/assets/package-lock.json
|
||||||
|
|
||||||
**/yarn-error.log
|
**/yarn-error.log
|
||||||
|
./test
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,6 @@ func (f *Flagset) StringFlag(b *StringFlag) {
|
||||||
type IntFlag struct {
|
type IntFlag struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
Default int
|
|
||||||
Value *int
|
Value *int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,13 +88,12 @@ func (f *Flagset) IntFlag(i *IntFlag) {
|
||||||
Name: i.Name,
|
Name: i.Name,
|
||||||
Usage: i.Usage,
|
Usage: i.Usage,
|
||||||
})
|
})
|
||||||
f.set.IntVar(i.Value, i.Name, i.Default, i.Usage)
|
f.set.IntVar(i.Value, i.Name, *i.Value, i.Usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Uint64Flag struct {
|
type Uint64Flag struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
Default uint64
|
|
||||||
Value *uint64
|
Value *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,17 +102,19 @@ func (f *Flagset) Uint64Flag(i *Uint64Flag) {
|
||||||
Name: i.Name,
|
Name: i.Name,
|
||||||
Usage: i.Usage,
|
Usage: i.Usage,
|
||||||
})
|
})
|
||||||
f.set.Uint64Var(i.Value, i.Name, i.Default, i.Usage)
|
f.set.Uint64Var(i.Value, i.Name, *i.Value, i.Usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BigIntFlag struct {
|
type BigIntFlag struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
Default *big.Int
|
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BigIntFlag) String() string {
|
func (b *BigIntFlag) String() string {
|
||||||
|
if b.Value == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return b.Value.String()
|
return b.Value.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,15 +145,18 @@ func (f *Flagset) BigIntFlag(b *BigIntFlag) {
|
||||||
type SliceStringFlag struct {
|
type SliceStringFlag struct {
|
||||||
Name string
|
Name string
|
||||||
Usage string
|
Usage string
|
||||||
Value []string
|
Value *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *SliceStringFlag) String() string {
|
func (i *SliceStringFlag) String() string {
|
||||||
return strings.Join(i.Value, ",")
|
if i.Value == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Join(*i.Value, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *SliceStringFlag) Set(value string) error {
|
func (i *SliceStringFlag) Set(value string) error {
|
||||||
i.Value = append(i.Value, value)
|
*i.Value = append(*i.Value, strings.Split(value, ",")...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,25 +174,12 @@ type DurationFlag struct {
|
||||||
Value *time.Duration
|
Value *time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DurationFlag) String() string {
|
|
||||||
return d.Value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DurationFlag) Set(value string) error {
|
|
||||||
v, err := time.ParseDuration(value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.Value = &v
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Flagset) DurationFlag(d *DurationFlag) {
|
func (f *Flagset) DurationFlag(d *DurationFlag) {
|
||||||
f.addFlag(&FlagVar{
|
f.addFlag(&FlagVar{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
Usage: d.Usage,
|
Usage: d.Usage,
|
||||||
})
|
})
|
||||||
f.set.Var(d, d.Name, d.Usage)
|
f.set.DurationVar(d.Value, d.Name, *d.Value, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
type MapStringFlag struct {
|
type MapStringFlag struct {
|
||||||
|
|
@ -199,6 +189,9 @@ type MapStringFlag struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MapStringFlag) String() string {
|
func (m *MapStringFlag) String() string {
|
||||||
|
if m.Value == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
ls := []string{}
|
ls := []string{}
|
||||||
for k, v := range *m.Value {
|
for k, v := range *m.Value {
|
||||||
ls = append(ls, k+"="+v)
|
ls = append(ls, k+"="+v)
|
||||||
|
|
|
||||||
60
command/flagset/flagset_test.go
Normal file
60
command/flagset/flagset_test.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package flagset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFlagsetBool(t *testing.T) {
|
||||||
|
f := NewFlagSet("")
|
||||||
|
|
||||||
|
value := false
|
||||||
|
f.BoolFlag(&BoolFlag{
|
||||||
|
Name: "flag",
|
||||||
|
Value: &value,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, f.Parse([]string{"--flag", "true"}))
|
||||||
|
assert.Equal(t, value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagsetSliceString(t *testing.T) {
|
||||||
|
f := NewFlagSet("")
|
||||||
|
|
||||||
|
value := []string{}
|
||||||
|
f.SliceStringFlag(&SliceStringFlag{
|
||||||
|
Name: "flag",
|
||||||
|
Value: &value,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, f.Parse([]string{"--flag", "a,b", "--flag", "c"}))
|
||||||
|
assert.Equal(t, value, []string{"a", "b", "c"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagsetDuration(t *testing.T) {
|
||||||
|
f := NewFlagSet("")
|
||||||
|
|
||||||
|
value := time.Duration(0)
|
||||||
|
f.DurationFlag(&DurationFlag{
|
||||||
|
Name: "flag",
|
||||||
|
Value: &value,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, f.Parse([]string{"--flag", "1m"}))
|
||||||
|
assert.Equal(t, value, 1*time.Minute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFlagsetMapString(t *testing.T) {
|
||||||
|
f := NewFlagSet("")
|
||||||
|
|
||||||
|
value := map[string]string{}
|
||||||
|
f.MapStringFlag(&MapStringFlag{
|
||||||
|
Name: "flag",
|
||||||
|
Value: &value,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, f.Parse([]string{"--flag", "a=b,c=d"}))
|
||||||
|
assert.Equal(t, value, map[string]string{"a": "b", "c": "d"})
|
||||||
|
}
|
||||||
|
|
@ -3,13 +3,16 @@ package server
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
|
|
@ -30,6 +33,9 @@ type Command struct {
|
||||||
// cli configuration
|
// cli configuration
|
||||||
cliConfig *Config
|
cliConfig *Config
|
||||||
|
|
||||||
|
// final configuration
|
||||||
|
config *Config
|
||||||
|
|
||||||
configFile string
|
configFile string
|
||||||
|
|
||||||
// bor node
|
// bor node
|
||||||
|
|
@ -74,6 +80,7 @@ func (c *Command) Run(args []string) int {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
c.config = config
|
||||||
|
|
||||||
// start the logger
|
// start the logger
|
||||||
setupLogger(*config.LogLevel)
|
setupLogger(*config.LogLevel)
|
||||||
|
|
@ -121,7 +128,7 @@ func (c *Command) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// register ethash service
|
// register ethash service
|
||||||
if config.EthStats != nil {
|
if *config.EthStats != "" {
|
||||||
if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil {
|
if err := ethstats.New(stack, backend.APIBackend, backend.Engine(), *config.EthStats); err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -129,13 +136,23 @@ func (c *Command) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup account manager (only keystore)
|
// setup account manager (only keystore)
|
||||||
|
var borKeystore *keystore.KeyStore
|
||||||
{
|
{
|
||||||
keydir := stack.KeyStoreDir()
|
keydir := stack.KeyStoreDir()
|
||||||
n, p := keystore.StandardScryptN, keystore.StandardScryptP
|
n, p := keystore.StandardScryptN, keystore.StandardScryptP
|
||||||
if *config.UseLightweightKDF {
|
if *config.Accounts.UseLightweightKDF {
|
||||||
n, p = keystore.LightScryptN, keystore.LightScryptP
|
n, p = keystore.LightScryptN, keystore.LightScryptP
|
||||||
}
|
}
|
||||||
stack.AccountManager().AddBackend(keystore.NewKeyStore(keydir, n, p))
|
borKeystore = keystore.NewKeyStore(keydir, n, p)
|
||||||
|
stack.AccountManager().AddBackend(borKeystore)
|
||||||
|
}
|
||||||
|
|
||||||
|
// unlock accounts if necessary
|
||||||
|
if len(config.Accounts.Unlock) != 0 {
|
||||||
|
if err := c.unlockAccounts(borKeystore); err != nil {
|
||||||
|
c.UI.Error(fmt.Sprintf("failed to unlock: %v", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sealing (if enabled)
|
// sealing (if enabled)
|
||||||
|
|
@ -159,6 +176,49 @@ func (c *Command) Run(args []string) int {
|
||||||
return c.handleSignals()
|
return c.handleSignals()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Command) unlockAccounts(borKeystore *keystore.KeyStore) error {
|
||||||
|
// If insecure account unlocking is not allowed if node's APIs are exposed to external.
|
||||||
|
if !c.node.Config().InsecureUnlockAllowed && c.node.Config().ExtRPCEnabled() {
|
||||||
|
return fmt.Errorf("account unlock with HTTP access is forbidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// read passwords from file if possible
|
||||||
|
passwords := []string{}
|
||||||
|
if *c.config.Accounts.PasswordFile != "" {
|
||||||
|
var err error
|
||||||
|
if passwords, err = readMultilineFile(*c.config.Accounts.PasswordFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decodePassword := func(addr common.Address, index int) (string, error) {
|
||||||
|
if len(passwords) > 0 {
|
||||||
|
if index < len(passwords) {
|
||||||
|
return passwords[index], nil
|
||||||
|
}
|
||||||
|
return passwords[len(passwords)-1], nil
|
||||||
|
}
|
||||||
|
// ask for the password
|
||||||
|
return c.UI.AskSecret(fmt.Sprintf("Please give a password to unlock '%s'", addr.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, addrStr := range c.config.Accounts.Unlock {
|
||||||
|
if !common.IsHexAddress(addrStr) {
|
||||||
|
return fmt.Errorf("unlock value '%s' is not an address", addrStr)
|
||||||
|
}
|
||||||
|
acct := accounts.Account{Address: common.HexToAddress(addrStr)}
|
||||||
|
|
||||||
|
password, err := decodePassword(acct.Address, index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := borKeystore.Unlock(acct, password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("Unlocked account", "address", acct.Address.Hex())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Command) setupMetrics(config *MetricsConfig) error {
|
func (c *Command) setupMetrics(config *MetricsConfig) error {
|
||||||
metrics.Enabled = *config.Enabled
|
metrics.Enabled = *config.Enabled
|
||||||
metrics.EnabledExpensive = *config.Expensive
|
metrics.EnabledExpensive = *config.Expensive
|
||||||
|
|
@ -208,18 +268,20 @@ func (c *Command) handleSignals() int {
|
||||||
gracefulCh := make(chan struct{})
|
gracefulCh := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
c.node.Close()
|
c.node.Close()
|
||||||
|
c.node.Wait()
|
||||||
close(gracefulCh)
|
close(gracefulCh)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
for i := 10; i > 0; i-- {
|
||||||
select {
|
select {
|
||||||
case <-signalCh:
|
case <-signalCh:
|
||||||
return 1
|
log.Warn("Already shutting down, interrupt more force stop.", "times", i-1)
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
return 1
|
|
||||||
case <-gracefulCh:
|
case <-gracefulCh:
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
func setupLogger(logLevel string) {
|
func setupLogger(logLevel string) {
|
||||||
output := io.Writer(os.Stderr)
|
output := io.Writer(os.Stderr)
|
||||||
|
|
@ -239,3 +301,16 @@ func setupLogger(logLevel string) {
|
||||||
}
|
}
|
||||||
log.Root().SetHandler(glogger)
|
log.Root().SetHandler(glogger)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readMultilineFile(path string) ([]string, error) {
|
||||||
|
text, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(text), "\n")
|
||||||
|
// Sanitise DOS line endings.
|
||||||
|
for i := range lines {
|
||||||
|
lines[i] = strings.TrimRight(lines[i], "\r")
|
||||||
|
}
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/command/server/chains"
|
"github.com/ethereum/go-ethereum/command/server/chains"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
|
|
@ -27,6 +26,7 @@ import (
|
||||||
"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"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
gopsutil "github.com/shirou/gopsutil/mem"
|
gopsutil "github.com/shirou/gopsutil/mem"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -60,7 +60,6 @@ type Config struct {
|
||||||
Chain *string
|
Chain *string
|
||||||
Debug *bool
|
Debug *bool
|
||||||
Whitelist *map[string]string
|
Whitelist *map[string]string
|
||||||
UseLightweightKDF *bool
|
|
||||||
LogLevel *string
|
LogLevel *string
|
||||||
DataDir *string
|
DataDir *string
|
||||||
P2P *P2PConfig
|
P2P *P2PConfig
|
||||||
|
|
@ -76,6 +75,7 @@ type Config struct {
|
||||||
Ethstats *string
|
Ethstats *string
|
||||||
Metrics *MetricsConfig
|
Metrics *MetricsConfig
|
||||||
Cache *CacheConfig
|
Cache *CacheConfig
|
||||||
|
Accounts *AccountsConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type P2PConfig struct {
|
type P2PConfig struct {
|
||||||
|
|
@ -132,7 +132,6 @@ type JsonRPCConfig struct {
|
||||||
Modules []string
|
Modules []string
|
||||||
VHost []string
|
VHost []string
|
||||||
Cors []string
|
Cors []string
|
||||||
Bind *string
|
|
||||||
|
|
||||||
GasCap *uint64
|
GasCap *uint64
|
||||||
TxFeeCap *float64
|
TxFeeCap *float64
|
||||||
|
|
@ -146,6 +145,7 @@ type APIConfig struct {
|
||||||
Enabled *bool
|
Enabled *bool
|
||||||
Port *uint64
|
Port *uint64
|
||||||
Prefix *string
|
Prefix *string
|
||||||
|
Host *string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GpoConfig struct {
|
type GpoConfig struct {
|
||||||
|
|
@ -187,14 +187,20 @@ type CacheConfig struct {
|
||||||
TxLookupLimit *uint64
|
TxLookupLimit *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountsConfig struct {
|
||||||
|
Unlock []string
|
||||||
|
PasswordFile *string
|
||||||
|
AllowInsecureUnlock *bool
|
||||||
|
UseLightweightKDF *bool
|
||||||
|
}
|
||||||
|
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Chain: stringPtr("mainnet"),
|
Chain: stringPtr("mainnet"),
|
||||||
Debug: boolPtr(false),
|
Debug: boolPtr(false),
|
||||||
UseLightweightKDF: boolPtr(false),
|
|
||||||
Whitelist: mapStrPtr(map[string]string{}),
|
Whitelist: mapStrPtr(map[string]string{}),
|
||||||
LogLevel: stringPtr("INFO"),
|
LogLevel: stringPtr("INFO"),
|
||||||
DataDir: stringPtr(node.DefaultDataDir()),
|
DataDir: stringPtr(defaultDataDir()),
|
||||||
P2P: &P2PConfig{
|
P2P: &P2PConfig{
|
||||||
MaxPeers: uint64Ptr(30),
|
MaxPeers: uint64Ptr(30),
|
||||||
MaxPendPeers: uint64Ptr(50),
|
MaxPendPeers: uint64Ptr(50),
|
||||||
|
|
@ -235,6 +241,7 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
Sealer: &SealerConfig{
|
Sealer: &SealerConfig{
|
||||||
Enabled: boolPtr(false),
|
Enabled: boolPtr(false),
|
||||||
|
Etherbase: stringPtr(""),
|
||||||
GasCeil: uint64Ptr(8000000),
|
GasCeil: uint64Ptr(8000000),
|
||||||
GasPrice: big.NewInt(params.GWei),
|
GasPrice: big.NewInt(params.GWei),
|
||||||
ExtraData: stringPtr(""),
|
ExtraData: stringPtr(""),
|
||||||
|
|
@ -247,6 +254,7 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
JsonRPC: &JsonRPCConfig{
|
JsonRPC: &JsonRPCConfig{
|
||||||
IPCDisable: boolPtr(false),
|
IPCDisable: boolPtr(false),
|
||||||
|
IPCPath: stringPtr(""),
|
||||||
Modules: []string{"web3", "net"},
|
Modules: []string{"web3", "net"},
|
||||||
Cors: []string{"*"},
|
Cors: []string{"*"},
|
||||||
VHost: []string{"*"},
|
VHost: []string{"*"},
|
||||||
|
|
@ -256,11 +264,13 @@ func DefaultConfig() *Config {
|
||||||
Enabled: boolPtr(false),
|
Enabled: boolPtr(false),
|
||||||
Port: uint64Ptr(8545),
|
Port: uint64Ptr(8545),
|
||||||
Prefix: stringPtr(""),
|
Prefix: stringPtr(""),
|
||||||
|
Host: stringPtr("localhost"),
|
||||||
},
|
},
|
||||||
Ws: &APIConfig{
|
Ws: &APIConfig{
|
||||||
Enabled: boolPtr(false),
|
Enabled: boolPtr(false),
|
||||||
Port: uint64Ptr(8546),
|
Port: uint64Ptr(8546),
|
||||||
Prefix: stringPtr(""),
|
Prefix: stringPtr(""),
|
||||||
|
Host: stringPtr("localhost"),
|
||||||
},
|
},
|
||||||
Graphql: &APIConfig{
|
Graphql: &APIConfig{
|
||||||
Enabled: boolPtr(false),
|
Enabled: boolPtr(false),
|
||||||
|
|
@ -295,6 +305,12 @@ func DefaultConfig() *Config {
|
||||||
Preimages: boolPtr(false),
|
Preimages: boolPtr(false),
|
||||||
TxLookupLimit: uint64Ptr(2350000),
|
TxLookupLimit: uint64Ptr(2350000),
|
||||||
},
|
},
|
||||||
|
Accounts: &AccountsConfig{
|
||||||
|
Unlock: []string{},
|
||||||
|
PasswordFile: stringPtr(""),
|
||||||
|
AllowInsecureUnlock: boolPtr(false),
|
||||||
|
UseLightweightKDF: boolPtr(false),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -321,11 +337,6 @@ func (c *Config) loadChain() error {
|
||||||
c.chain = chain
|
c.chain = chain
|
||||||
|
|
||||||
// preload some default values that depend on the chain file
|
// preload some default values that depend on the chain file
|
||||||
|
|
||||||
// the genesis files defines default bootnodes
|
|
||||||
if c.P2P.Discovery.Bootnodes == nil {
|
|
||||||
c.P2P.Discovery.Bootnodes = c.chain.Bootnodes
|
|
||||||
}
|
|
||||||
if c.P2P.Discovery.DNS == nil {
|
if c.P2P.Discovery.DNS == nil {
|
||||||
c.P2P.Discovery.DNS = c.chain.DNS
|
c.P2P.Discovery.DNS = c.chain.DNS
|
||||||
}
|
}
|
||||||
|
|
@ -368,11 +379,11 @@ func (c *Config) buildEth() (*ethconfig.Config, error) {
|
||||||
n.Miner.GasCeil = *c.Sealer.GasCeil
|
n.Miner.GasCeil = *c.Sealer.GasCeil
|
||||||
n.Miner.ExtraData = []byte(*c.Sealer.ExtraData)
|
n.Miner.ExtraData = []byte(*c.Sealer.ExtraData)
|
||||||
|
|
||||||
if etherbase := c.Sealer.Etherbase; etherbase != nil {
|
if etherbase := *c.Sealer.Etherbase; etherbase != "" {
|
||||||
if !common.IsHexAddress(*etherbase) {
|
if !common.IsHexAddress(etherbase) {
|
||||||
return nil, fmt.Errorf("etherbase is not an address: %s", *etherbase)
|
return nil, fmt.Errorf("etherbase is not an address: %s", etherbase)
|
||||||
}
|
}
|
||||||
n.Miner.Etherbase = common.HexToAddress(*etherbase)
|
n.Miner.Etherbase = common.HexToAddress(etherbase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -495,7 +506,7 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
ipcPath := ""
|
ipcPath := ""
|
||||||
if !*c.JsonRPC.IPCDisable {
|
if !*c.JsonRPC.IPCDisable {
|
||||||
ipcPath = clientIdentifier + ".ipc"
|
ipcPath = clientIdentifier + ".ipc"
|
||||||
if c.JsonRPC.IPCPath != nil {
|
if *c.JsonRPC.IPCPath != "" {
|
||||||
ipcPath = *c.JsonRPC.IPCPath
|
ipcPath = *c.JsonRPC.IPCPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -503,7 +514,8 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
cfg := &node.Config{
|
cfg := &node.Config{
|
||||||
Name: clientIdentifier,
|
Name: clientIdentifier,
|
||||||
DataDir: *c.DataDir,
|
DataDir: *c.DataDir,
|
||||||
UseLightweightKDF: *c.UseLightweightKDF,
|
UseLightweightKDF: *c.Accounts.UseLightweightKDF,
|
||||||
|
InsecureUnlockAllowed: *c.Accounts.AllowInsecureUnlock,
|
||||||
Version: params.VersionWithCommit(gitCommit, gitDate),
|
Version: params.VersionWithCommit(gitCommit, gitDate),
|
||||||
IPCPath: ipcPath,
|
IPCPath: ipcPath,
|
||||||
P2P: p2p.Config{
|
P2P: p2p.Config{
|
||||||
|
|
@ -526,11 +538,11 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
// enable jsonrpc endpoints
|
// enable jsonrpc endpoints
|
||||||
{
|
{
|
||||||
if *c.JsonRPC.Http.Enabled {
|
if *c.JsonRPC.Http.Enabled {
|
||||||
cfg.HTTPHost = *c.JsonRPC.Bind
|
cfg.HTTPHost = *c.JsonRPC.Http.Host
|
||||||
cfg.HTTPPort = int(*c.JsonRPC.Http.Port)
|
cfg.HTTPPort = int(*c.JsonRPC.Http.Port)
|
||||||
}
|
}
|
||||||
if *c.JsonRPC.Ws.Enabled {
|
if *c.JsonRPC.Ws.Enabled {
|
||||||
cfg.WSHost = *c.JsonRPC.Bind
|
cfg.WSHost = *c.JsonRPC.Ws.Host
|
||||||
cfg.WSPort = int(*c.JsonRPC.Ws.Port)
|
cfg.WSPort = int(*c.JsonRPC.Ws.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -541,15 +553,13 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
}
|
}
|
||||||
cfg.P2P.NAT = natif
|
cfg.P2P.NAT = natif
|
||||||
|
|
||||||
// setup private key for DevP2P if not found
|
|
||||||
devP2PPrivKey, err := readDevP2PKey(*c.DataDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cfg.P2P.PrivateKey = devP2PPrivKey
|
|
||||||
|
|
||||||
// Discovery
|
// Discovery
|
||||||
if cfg.P2P.BootstrapNodes, err = parseBootnodes(c.P2P.Discovery.Bootnodes); err != nil {
|
// if no bootnodes are defined, use the ones from the chain file.
|
||||||
|
bootnodes := c.P2P.Discovery.Bootnodes
|
||||||
|
if len(bootnodes) == 0 {
|
||||||
|
bootnodes = c.chain.Bootnodes
|
||||||
|
}
|
||||||
|
if cfg.P2P.BootstrapNodes, err = parseBootnodes(bootnodes); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
|
if cfg.P2P.BootstrapNodesV5, err = parseBootnodes(c.P2P.Discovery.BootnodesV5); err != nil {
|
||||||
|
|
@ -572,8 +582,8 @@ func (c *Config) buildNode() (*node.Config, error) {
|
||||||
|
|
||||||
func (c *Config) Merge(cc ...*Config) error {
|
func (c *Config) Merge(cc ...*Config) error {
|
||||||
for _, elem := range cc {
|
for _, elem := range cc {
|
||||||
if err := mergo.Merge(&c, elem); err != nil {
|
if err := mergo.Merge(c, elem, mergo.WithOverride, mergo.WithAppendSlice); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to merge configurations: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -605,30 +615,24 @@ func parseBootnodes(urls []string) ([]*enode.Node, error) {
|
||||||
return dst, nil
|
return dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const devP2PKeyPath = "devp2p.key"
|
func defaultDataDir() string {
|
||||||
|
// Try to place the data folder in the user's home dir
|
||||||
func readDevP2PKey(dataDir string) (*ecdsa.PrivateKey, error) {
|
home, _ := homedir.Dir()
|
||||||
path := filepath.Join(dataDir, devP2PKeyPath)
|
if home == "" {
|
||||||
_, err := os.Stat(path)
|
// we cannot guess a stable location
|
||||||
if err != nil && !os.IsNotExist(err) {
|
return ""
|
||||||
return nil, fmt.Errorf("failed to stat (%s): %v", path, err)
|
|
||||||
}
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
if os.IsNotExist(err) {
|
case "darwin":
|
||||||
priv, err := crypto.GenerateKey()
|
return filepath.Join(home, "Library", "Bor")
|
||||||
if err != nil {
|
case "windows":
|
||||||
return nil, err
|
appdata := os.Getenv("LOCALAPPDATA")
|
||||||
|
if appdata == "" {
|
||||||
|
// Windows XP and below don't have LocalAppData.
|
||||||
|
panic("environment variable LocalAppData is undefined")
|
||||||
}
|
}
|
||||||
if err := crypto.SaveECDSA(path, priv); err != nil {
|
return filepath.Join(appdata, "Bor")
|
||||||
return nil, err
|
default:
|
||||||
|
return filepath.Join(home, ".bor")
|
||||||
}
|
}
|
||||||
return priv, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// exists
|
|
||||||
priv, err := crypto.LoadECDSA(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return priv, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
90
command/server/config_test.go
Normal file
90
command/server/config_test.go
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigDefault(t *testing.T) {
|
||||||
|
// the default config should work out of the box
|
||||||
|
config := DefaultConfig()
|
||||||
|
assert.NoError(t, config.loadChain())
|
||||||
|
|
||||||
|
_, err := config.buildNode()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = config.buildEth()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigMerge(t *testing.T) {
|
||||||
|
c0 := &Config{
|
||||||
|
Chain: stringPtr("0"),
|
||||||
|
Debug: boolPtr(false),
|
||||||
|
Whitelist: mapStrPtr(map[string]string{
|
||||||
|
"a": "b",
|
||||||
|
}),
|
||||||
|
P2P: &P2PConfig{
|
||||||
|
Discovery: &P2PDiscovery{
|
||||||
|
StaticNodes: []string{
|
||||||
|
"a",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c1 := &Config{
|
||||||
|
Chain: stringPtr("1"),
|
||||||
|
Whitelist: mapStrPtr(map[string]string{
|
||||||
|
"b": "c",
|
||||||
|
}),
|
||||||
|
P2P: &P2PConfig{
|
||||||
|
Discovery: &P2PDiscovery{
|
||||||
|
StaticNodes: []string{
|
||||||
|
"b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expected := &Config{
|
||||||
|
Chain: stringPtr("1"),
|
||||||
|
Debug: boolPtr(false),
|
||||||
|
Whitelist: mapStrPtr(map[string]string{
|
||||||
|
"a": "b",
|
||||||
|
"b": "c",
|
||||||
|
}),
|
||||||
|
P2P: &P2PConfig{
|
||||||
|
Discovery: &P2PDiscovery{
|
||||||
|
StaticNodes: []string{
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.NoError(t, c0.Merge(c1))
|
||||||
|
assert.Equal(t, c0, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dummyEnodeAddr = "enode://0cb82b395094ee4a2915e9714894627de9ed8498fb881cec6db7c65e8b9a5bd7f2f25cc84e71e89d0947e51c76e85d0847de848c7782b13c0255247a6758178c@44.232.55.71:30303"
|
||||||
|
|
||||||
|
func TestConfigBootnodesDefault(t *testing.T) {
|
||||||
|
t.Run("EmptyBootnodes", func(t *testing.T) {
|
||||||
|
// if no bootnodes are specific, we use the ones from the genesis chain
|
||||||
|
config := DefaultConfig()
|
||||||
|
assert.NoError(t, config.loadChain())
|
||||||
|
|
||||||
|
cfg, err := config.buildNode()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, cfg.P2P.BootstrapNodes)
|
||||||
|
})
|
||||||
|
t.Run("NotEmptyBootnodes", func(t *testing.T) {
|
||||||
|
// if bootnodes specific, DO NOT load the genesis bootnodes
|
||||||
|
config := DefaultConfig()
|
||||||
|
config.P2P.Discovery.Bootnodes = []string{dummyEnodeAddr}
|
||||||
|
|
||||||
|
cfg, err := config.buildNode()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, cfg.P2P.BootstrapNodes, 1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -44,11 +44,6 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Usage: `Blockchain garbage collection mode ("full", "archive")`,
|
Usage: `Blockchain garbage collection mode ("full", "archive")`,
|
||||||
Value: c.cliConfig.GcMode,
|
Value: c.cliConfig.GcMode,
|
||||||
})
|
})
|
||||||
f.BoolFlag(&flagset.BoolFlag{
|
|
||||||
Name: "lightkdf",
|
|
||||||
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
|
||||||
Value: c.cliConfig.UseLightweightKDF,
|
|
||||||
})
|
|
||||||
f.MapStringFlag(&flagset.MapStringFlag{
|
f.MapStringFlag(&flagset.MapStringFlag{
|
||||||
Name: "whitelist",
|
Name: "whitelist",
|
||||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
||||||
|
|
@ -76,7 +71,7 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
f.SliceStringFlag(&flagset.SliceStringFlag{
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
Name: "txpool.locals",
|
Name: "txpool.locals",
|
||||||
Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)",
|
Usage: "Comma separated accounts to treat as locals (no flush, priority inclusion)",
|
||||||
Value: c.cliConfig.TxPool.Locals,
|
Value: &c.cliConfig.TxPool.Locals,
|
||||||
})
|
})
|
||||||
f.BoolFlag(&flagset.BoolFlag{
|
f.BoolFlag(&flagset.BoolFlag{
|
||||||
Name: "txpool.nolocals",
|
Name: "txpool.nolocals",
|
||||||
|
|
@ -259,21 +254,32 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Value: c.cliConfig.JsonRPC.IPCPath,
|
Value: c.cliConfig.JsonRPC.IPCPath,
|
||||||
})
|
})
|
||||||
f.SliceStringFlag(&flagset.SliceStringFlag{
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
Name: "rpc.corsdomain",
|
Name: "jsonrpc.corsdomain",
|
||||||
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
|
||||||
Value: c.cliConfig.JsonRPC.Cors,
|
Value: &c.cliConfig.JsonRPC.Cors,
|
||||||
})
|
})
|
||||||
f.SliceStringFlag(&flagset.SliceStringFlag{
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
Name: "rpc.vhosts",
|
Name: "jsonrpc.vhosts",
|
||||||
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
|
||||||
Value: c.cliConfig.JsonRPC.VHost,
|
Value: &c.cliConfig.JsonRPC.VHost,
|
||||||
})
|
})
|
||||||
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
|
Name: "jsonrpc.modules",
|
||||||
|
Usage: "API's offered over the HTTP-RPC interface",
|
||||||
|
Value: &c.cliConfig.JsonRPC.Modules,
|
||||||
|
})
|
||||||
|
|
||||||
// http options
|
// http options
|
||||||
f.BoolFlag(&flagset.BoolFlag{
|
f.BoolFlag(&flagset.BoolFlag{
|
||||||
Name: "http",
|
Name: "http",
|
||||||
Usage: "Enable the HTTP-RPC server",
|
Usage: "Enable the HTTP-RPC server",
|
||||||
Value: c.cliConfig.JsonRPC.Http.Enabled,
|
Value: c.cliConfig.JsonRPC.Http.Enabled,
|
||||||
})
|
})
|
||||||
|
f.StringFlag(&flagset.StringFlag{
|
||||||
|
Name: "http.addr",
|
||||||
|
Usage: "HTTP-RPC server listening interface",
|
||||||
|
Value: c.cliConfig.JsonRPC.Http.Host,
|
||||||
|
})
|
||||||
f.Uint64Flag(&flagset.Uint64Flag{
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
Name: "http.port",
|
Name: "http.port",
|
||||||
Usage: "HTTP-RPC server listening port",
|
Usage: "HTTP-RPC server listening port",
|
||||||
|
|
@ -290,6 +296,11 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Usage: "Enable the WS-RPC server",
|
Usage: "Enable the WS-RPC server",
|
||||||
Value: c.cliConfig.JsonRPC.Ws.Enabled,
|
Value: c.cliConfig.JsonRPC.Ws.Enabled,
|
||||||
})
|
})
|
||||||
|
f.StringFlag(&flagset.StringFlag{
|
||||||
|
Name: "ws.addr",
|
||||||
|
Usage: "WS-RPC server listening interface",
|
||||||
|
Value: c.cliConfig.JsonRPC.Ws.Host,
|
||||||
|
})
|
||||||
f.Uint64Flag(&flagset.Uint64Flag{
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
Name: "ws.port",
|
Name: "ws.port",
|
||||||
Usage: "WS-RPC server listening port",
|
Usage: "WS-RPC server listening port",
|
||||||
|
|
@ -321,7 +332,7 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
f.SliceStringFlag(&flagset.SliceStringFlag{
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
Name: "bootnodes",
|
Name: "bootnodes",
|
||||||
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
|
Usage: "Comma separated enode URLs for P2P discovery bootstrap",
|
||||||
Value: c.cliConfig.P2P.Discovery.Bootnodes,
|
Value: &c.cliConfig.P2P.Discovery.Bootnodes,
|
||||||
})
|
})
|
||||||
f.Uint64Flag(&flagset.Uint64Flag{
|
f.Uint64Flag(&flagset.Uint64Flag{
|
||||||
Name: "maxpeers",
|
Name: "maxpeers",
|
||||||
|
|
@ -412,5 +423,27 @@ func (c *Command) Flags() *flagset.Flagset {
|
||||||
Value: c.cliConfig.Metrics.InfluxDB.Organization,
|
Value: c.cliConfig.Metrics.InfluxDB.Organization,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// account
|
||||||
|
f.SliceStringFlag(&flagset.SliceStringFlag{
|
||||||
|
Name: "unlock",
|
||||||
|
Usage: "Comma separated list of accounts to unlock",
|
||||||
|
Value: &c.cliConfig.Accounts.Unlock,
|
||||||
|
})
|
||||||
|
f.StringFlag(&flagset.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
Usage: "Password file to use for non-interactive password input",
|
||||||
|
Value: c.cliConfig.Accounts.PasswordFile,
|
||||||
|
})
|
||||||
|
f.BoolFlag(&flagset.BoolFlag{
|
||||||
|
Name: "allow-insecure-unlock",
|
||||||
|
Usage: "Allow insecure account unlocking when account-related RPCs are exposed by http",
|
||||||
|
Value: c.cliConfig.Accounts.AllowInsecureUnlock,
|
||||||
|
})
|
||||||
|
f.BoolFlag(&flagset.BoolFlag{
|
||||||
|
Name: "lightkdf",
|
||||||
|
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
||||||
|
Value: c.cliConfig.Accounts.UseLightweightKDF,
|
||||||
|
})
|
||||||
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
go.mod
1
go.mod
|
|
@ -51,6 +51,7 @@ require (
|
||||||
github.com/mattn/go-colorable v0.1.8
|
github.com/mattn/go-colorable v0.1.8
|
||||||
github.com/mattn/go-isatty v0.0.12
|
github.com/mattn/go-isatty v0.0.12
|
||||||
github.com/mitchellh/cli v1.1.2
|
github.com/mitchellh/cli v1.1.2
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/naoina/go-stringutil v0.1.0 // indirect
|
github.com/naoina/go-stringutil v0.1.0 // indirect
|
||||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
|
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
|
|
|
||||||
3
go.sum
3
go.sum
|
|
@ -33,7 +33,6 @@ github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSW
|
||||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc=
|
|
||||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||||
|
|
@ -331,6 +330,8 @@ github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw=
|
||||||
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
|
github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
|
||||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue