Merge pull request #18172 from holiman/puppeth_converter
cmd/puppeth: implement chainspec converters
This commit is contained in:
commit
f74077b4c2
@ -20,35 +20,41 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
math2 "github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cppEthereumGenesisSpec represents the genesis specification format used by the
|
// alethGenesisSpec represents the genesis specification format used by the
|
||||||
// C++ Ethereum implementation.
|
// C++ Ethereum implementation.
|
||||||
type cppEthereumGenesisSpec struct {
|
type alethGenesisSpec struct {
|
||||||
SealEngine string `json:"sealEngine"`
|
SealEngine string `json:"sealEngine"`
|
||||||
Params struct {
|
Params struct {
|
||||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"`
|
||||||
|
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||||
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
||||||
|
DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"`
|
||||||
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
||||||
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
||||||
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
||||||
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
||||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
|
||||||
ChainID hexutil.Uint64 `json:"chainID"`
|
|
||||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
|
||||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||||
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
||||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
TieBreakingGas bool `json:"tieBreakingGas"`
|
||||||
|
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"`
|
||||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"`
|
||||||
BlockReward *hexutil.Big `json:"blockReward"`
|
BlockReward *hexutil.Big `json:"blockReward"`
|
||||||
|
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||||
|
ChainID hexutil.Uint64 `json:"chainID"`
|
||||||
|
AllowFutureBlocks bool `json:"allowFutureBlocks""`
|
||||||
} `json:"params"`
|
} `json:"params"`
|
||||||
|
|
||||||
Genesis struct {
|
Genesis struct {
|
||||||
@ -62,57 +68,68 @@ type cppEthereumGenesisSpec struct {
|
|||||||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||||
} `json:"genesis"`
|
} `json:"genesis"`
|
||||||
|
|
||||||
Accounts map[common.Address]*cppEthereumGenesisSpecAccount `json:"accounts"`
|
Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// cppEthereumGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
// alethGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
||||||
// contract definition.
|
// contract definition.
|
||||||
type cppEthereumGenesisSpecAccount struct {
|
type alethGenesisSpecAccount struct {
|
||||||
Balance *hexutil.Big `json:"balance"`
|
Balance *math2.HexOrDecimal256 `json:"balance"`
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
Nonce uint64 `json:"nonce,omitempty"`
|
||||||
Precompiled *cppEthereumGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// cppEthereumGenesisSpecBuiltin is the precompiled contract definition.
|
// alethGenesisSpecBuiltin is the precompiled contract definition.
|
||||||
type cppEthereumGenesisSpecBuiltin struct {
|
type alethGenesisSpecBuiltin struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
||||||
Linear *cppEthereumGenesisSpecLinearPricing `json:"linear,omitempty"`
|
Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type cppEthereumGenesisSpecLinearPricing struct {
|
type alethGenesisSpecLinearPricing struct {
|
||||||
Base uint64 `json:"base"`
|
Base uint64 `json:"base"`
|
||||||
Word uint64 `json:"word"`
|
Word uint64 `json:"word"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// newCppEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific
|
// newAlethGenesisSpec converts a go-ethereum genesis block into a Aleth-specific
|
||||||
// chain specification format.
|
// chain specification format.
|
||||||
func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEthereumGenesisSpec, error) {
|
func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSpec, error) {
|
||||||
// Only ethash is currently supported between go-ethereum and cpp-ethereum
|
// Only ethash is currently supported between go-ethereum and aleth
|
||||||
if genesis.Config.Ethash == nil {
|
if genesis.Config.Ethash == nil {
|
||||||
return nil, errors.New("unsupported consensus engine")
|
return nil, errors.New("unsupported consensus engine")
|
||||||
}
|
}
|
||||||
// Reconstruct the chain spec in Parity's format
|
// Reconstruct the chain spec in Aleth format
|
||||||
spec := &cppEthereumGenesisSpec{
|
spec := &alethGenesisSpec{
|
||||||
SealEngine: "Ethash",
|
SealEngine: "Ethash",
|
||||||
}
|
}
|
||||||
|
// Some defaults
|
||||||
spec.Params.AccountStartNonce = 0
|
spec.Params.AccountStartNonce = 0
|
||||||
|
spec.Params.TieBreakingGas = false
|
||||||
|
spec.Params.AllowFutureBlocks = false
|
||||||
|
spec.Params.DaoHardforkBlock = 0
|
||||||
|
|
||||||
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
|
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
|
||||||
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
|
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
|
||||||
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
|
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
|
||||||
spec.Params.ByzantiumForkBlock = (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())
|
|
||||||
spec.Params.ConstantinopleForkBlock = (hexutil.Uint64)(math.MaxUint64)
|
// Byzantium
|
||||||
|
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||||
|
spec.setByzantium(num)
|
||||||
|
}
|
||||||
|
// Constantinople
|
||||||
|
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||||
|
spec.setConstantinople(num)
|
||||||
|
}
|
||||||
|
|
||||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||||
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||||
|
|
||||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||||
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxUint64)
|
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxInt64)
|
||||||
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||||
spec.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
spec.Params.DifficultyBoundDivisor = (*math2.HexOrDecimal256)(params.DifficultyBoundDivisor)
|
||||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||||
spec.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit)
|
||||||
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
||||||
|
|
||||||
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||||
@ -126,77 +143,104 @@ func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEther
|
|||||||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||||
|
|
||||||
spec.Accounts = make(map[common.Address]*cppEthereumGenesisSpecAccount)
|
|
||||||
for address, account := range genesis.Alloc {
|
for address, account := range genesis.Alloc {
|
||||||
spec.Accounts[address] = &cppEthereumGenesisSpecAccount{
|
spec.setAccount(address, account)
|
||||||
Balance: (*hexutil.Big)(account.Balance),
|
|
||||||
Nonce: account.Nonce,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spec.Accounts[common.BytesToAddress([]byte{1})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
|
||||||
Name: "ecrecover", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 3000},
|
|
||||||
}
|
|
||||||
spec.Accounts[common.BytesToAddress([]byte{2})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
|
||||||
Name: "sha256", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 60, Word: 12},
|
|
||||||
}
|
|
||||||
spec.Accounts[common.BytesToAddress([]byte{3})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
|
||||||
Name: "ripemd160", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 600, Word: 120},
|
|
||||||
}
|
|
||||||
spec.Accounts[common.BytesToAddress([]byte{4})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
|
||||||
Name: "identity", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 15, Word: 3},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spec.setPrecompile(1, &alethGenesisSpecBuiltin{Name: "ecrecover",
|
||||||
|
Linear: &alethGenesisSpecLinearPricing{Base: 3000}})
|
||||||
|
spec.setPrecompile(2, &alethGenesisSpecBuiltin{Name: "sha256",
|
||||||
|
Linear: &alethGenesisSpecLinearPricing{Base: 60, Word: 12}})
|
||||||
|
spec.setPrecompile(3, &alethGenesisSpecBuiltin{Name: "ripemd160",
|
||||||
|
Linear: &alethGenesisSpecLinearPricing{Base: 600, Word: 120}})
|
||||||
|
spec.setPrecompile(4, &alethGenesisSpecBuiltin{Name: "identity",
|
||||||
|
Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}})
|
||||||
if genesis.Config.ByzantiumBlock != nil {
|
if genesis.Config.ByzantiumBlock != nil {
|
||||||
spec.Accounts[common.BytesToAddress([]byte{5})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp",
|
||||||
Name: "modexp", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||||
}
|
spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add",
|
||||||
spec.Accounts[common.BytesToAddress([]byte{6})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||||
Name: "alt_bn128_G1_add", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 500},
|
Linear: &alethGenesisSpecLinearPricing{Base: 500}})
|
||||||
}
|
spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul",
|
||||||
spec.Accounts[common.BytesToAddress([]byte{7})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||||
Name: "alt_bn128_G1_mul", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 40000},
|
Linear: &alethGenesisSpecLinearPricing{Base: 40000}})
|
||||||
}
|
spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product",
|
||||||
spec.Accounts[common.BytesToAddress([]byte{8})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||||
Name: "alt_bn128_pairing_product", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpecBuiltin) {
|
||||||
|
if spec.Accounts == nil {
|
||||||
|
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||||
|
}
|
||||||
|
spec.Accounts[common.UnprefixedAddress(common.BytesToAddress([]byte{address}))].Precompiled = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *alethGenesisSpec) setAccount(address common.Address, account core.GenesisAccount) {
|
||||||
|
if spec.Accounts == nil {
|
||||||
|
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
a, exist := spec.Accounts[common.UnprefixedAddress(address)]
|
||||||
|
if !exist {
|
||||||
|
a = &alethGenesisSpecAccount{}
|
||||||
|
spec.Accounts[common.UnprefixedAddress(address)] = a
|
||||||
|
}
|
||||||
|
a.Balance = (*math2.HexOrDecimal256)(account.Balance)
|
||||||
|
a.Nonce = account.Nonce
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *alethGenesisSpec) setByzantium(num *big.Int) {
|
||||||
|
spec.Params.ByzantiumForkBlock = hexutil.Uint64(num.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *alethGenesisSpec) setConstantinople(num *big.Int) {
|
||||||
|
spec.Params.ConstantinopleForkBlock = hexutil.Uint64(num.Uint64())
|
||||||
|
}
|
||||||
|
|
||||||
// parityChainSpec is the chain specification format used by Parity.
|
// parityChainSpec is the chain specification format used by Parity.
|
||||||
type parityChainSpec struct {
|
type parityChainSpec struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Datadir string `json:"dataDir"`
|
||||||
Engine struct {
|
Engine struct {
|
||||||
Ethash struct {
|
Ethash struct {
|
||||||
Params struct {
|
Params struct {
|
||||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||||
BlockReward *hexutil.Big `json:"blockReward"`
|
BlockReward map[string]string `json:"blockReward"`
|
||||||
HomesteadTransition uint64 `json:"homesteadTransition"`
|
DifficultyBombDelays map[string]string `json:"difficultyBombDelays"`
|
||||||
EIP150Transition uint64 `json:"eip150Transition"`
|
HomesteadTransition hexutil.Uint64 `json:"homesteadTransition"`
|
||||||
EIP160Transition uint64 `json:"eip160Transition"`
|
EIP100bTransition hexutil.Uint64 `json:"eip100bTransition"`
|
||||||
EIP161abcTransition uint64 `json:"eip161abcTransition"`
|
|
||||||
EIP161dTransition uint64 `json:"eip161dTransition"`
|
|
||||||
EIP649Reward *hexutil.Big `json:"eip649Reward"`
|
|
||||||
EIP100bTransition uint64 `json:"eip100bTransition"`
|
|
||||||
EIP649Transition uint64 `json:"eip649Transition"`
|
|
||||||
} `json:"params"`
|
} `json:"params"`
|
||||||
} `json:"Ethash"`
|
} `json:"Ethash"`
|
||||||
} `json:"engine"`
|
} `json:"engine"`
|
||||||
|
|
||||||
Params struct {
|
Params struct {
|
||||||
|
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||||
MaxCodeSize uint64 `json:"maxCodeSize"`
|
ChainID hexutil.Uint64 `json:"chainID"`
|
||||||
EIP155Transition uint64 `json:"eip155Transition"`
|
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
||||||
EIP98Transition uint64 `json:"eip98Transition"`
|
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
||||||
EIP86Transition uint64 `json:"eip86Transition"`
|
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
||||||
EIP140Transition uint64 `json:"eip140Transition"`
|
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
||||||
EIP211Transition uint64 `json:"eip211Transition"`
|
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
||||||
EIP214Transition uint64 `json:"eip214Transition"`
|
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
||||||
EIP658Transition uint64 `json:"eip658Transition"`
|
EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
|
||||||
|
EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
|
||||||
|
EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
|
||||||
|
EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
|
||||||
|
EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
|
||||||
|
EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
|
||||||
|
EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
|
||||||
|
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
|
||||||
|
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
|
||||||
|
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
|
||||||
} `json:"params"`
|
} `json:"params"`
|
||||||
|
|
||||||
Genesis struct {
|
Genesis struct {
|
||||||
@ -216,21 +260,21 @@ type parityChainSpec struct {
|
|||||||
} `json:"genesis"`
|
} `json:"genesis"`
|
||||||
|
|
||||||
Nodes []string `json:"nodes"`
|
Nodes []string `json:"nodes"`
|
||||||
Accounts map[common.Address]*parityChainSpecAccount `json:"accounts"`
|
Accounts map[common.UnprefixedAddress]*parityChainSpecAccount `json:"accounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// parityChainSpecAccount is the prefunded genesis account and/or precompiled
|
// parityChainSpecAccount is the prefunded genesis account and/or precompiled
|
||||||
// contract definition.
|
// contract definition.
|
||||||
type parityChainSpecAccount struct {
|
type parityChainSpecAccount struct {
|
||||||
Balance *hexutil.Big `json:"balance"`
|
Balance math2.HexOrDecimal256 `json:"balance"`
|
||||||
Nonce uint64 `json:"nonce,omitempty"`
|
Nonce math2.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||||
Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"`
|
Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// parityChainSpecBuiltin is the precompiled contract definition.
|
// parityChainSpecBuiltin is the precompiled contract definition.
|
||||||
type parityChainSpecBuiltin struct {
|
type parityChainSpecBuiltin struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
ActivateAt uint64 `json:"activate_at,omitempty"`
|
ActivateAt math2.HexOrDecimal64 `json:"activate_at,omitempty"`
|
||||||
Pricing *parityChainSpecPricing `json:"pricing,omitempty"`
|
Pricing *parityChainSpecPricing `json:"pricing,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,32 +311,49 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
|||||||
spec := &parityChainSpec{
|
spec := &parityChainSpec{
|
||||||
Name: network,
|
Name: network,
|
||||||
Nodes: bootnodes,
|
Nodes: bootnodes,
|
||||||
|
Datadir: strings.ToLower(network),
|
||||||
}
|
}
|
||||||
|
spec.Engine.Ethash.Params.BlockReward = make(map[string]string)
|
||||||
|
spec.Engine.Ethash.Params.DifficultyBombDelays = make(map[string]string)
|
||||||
|
// Frontier
|
||||||
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||||
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
||||||
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
||||||
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
spec.Engine.Ethash.Params.BlockReward["0x0"] = hexutil.EncodeBig(ethash.FrontierBlockReward)
|
||||||
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP150Transition = genesis.Config.EIP150Block.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP160Transition = genesis.Config.EIP155Block.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP161abcTransition = genesis.Config.EIP158Block.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP161dTransition = genesis.Config.EIP158Block.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP649Reward = (*hexutil.Big)(ethash.ByzantiumBlockReward)
|
|
||||||
spec.Engine.Ethash.Params.EIP100bTransition = genesis.Config.ByzantiumBlock.Uint64()
|
|
||||||
spec.Engine.Ethash.Params.EIP649Transition = genesis.Config.ByzantiumBlock.Uint64()
|
|
||||||
|
|
||||||
|
// Homestead
|
||||||
|
spec.Engine.Ethash.Params.HomesteadTransition = hexutil.Uint64(genesis.Config.HomesteadBlock.Uint64())
|
||||||
|
|
||||||
|
// Tangerine Whistle : 150
|
||||||
|
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-608.md
|
||||||
|
spec.Params.EIP150Transition = hexutil.Uint64(genesis.Config.EIP150Block.Uint64())
|
||||||
|
|
||||||
|
// Spurious Dragon: 155, 160, 161, 170
|
||||||
|
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md
|
||||||
|
spec.Params.EIP155Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||||
|
spec.Params.EIP160Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||||
|
spec.Params.EIP161abcTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||||
|
spec.Params.EIP161dTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||||
|
|
||||||
|
// Byzantium
|
||||||
|
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||||
|
spec.setByzantium(num)
|
||||||
|
}
|
||||||
|
// Constantinople
|
||||||
|
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||||
|
spec.setConstantinople(num)
|
||||||
|
}
|
||||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||||
|
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||||
spec.Params.MaxCodeSize = params.MaxCodeSize
|
spec.Params.MaxCodeSize = params.MaxCodeSize
|
||||||
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()
|
// geth has it set from zero
|
||||||
spec.Params.EIP98Transition = math.MaxUint64
|
spec.Params.MaxCodeSizeTransition = 0
|
||||||
spec.Params.EIP86Transition = math.MaxUint64
|
|
||||||
spec.Params.EIP140Transition = genesis.Config.ByzantiumBlock.Uint64()
|
// Disable this one
|
||||||
spec.Params.EIP211Transition = genesis.Config.ByzantiumBlock.Uint64()
|
spec.Params.EIP98Transition = math.MaxInt64
|
||||||
spec.Params.EIP214Transition = genesis.Config.ByzantiumBlock.Uint64()
|
|
||||||
spec.Params.EIP658Transition = genesis.Config.ByzantiumBlock.Uint64()
|
|
||||||
|
|
||||||
spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||||
binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce)
|
binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce)
|
||||||
@ -305,42 +366,77 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
|||||||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||||
|
|
||||||
spec.Accounts = make(map[common.Address]*parityChainSpecAccount)
|
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||||
for address, account := range genesis.Alloc {
|
for address, account := range genesis.Alloc {
|
||||||
spec.Accounts[address] = &parityChainSpecAccount{
|
bal := math2.HexOrDecimal256(*account.Balance)
|
||||||
Balance: (*hexutil.Big)(account.Balance),
|
|
||||||
Nonce: account.Nonce,
|
spec.Accounts[common.UnprefixedAddress(address)] = &parityChainSpecAccount{
|
||||||
|
Balance: bal,
|
||||||
|
Nonce: math2.HexOrDecimal64(account.Nonce),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spec.Accounts[common.BytesToAddress([]byte{1})].Builtin = &parityChainSpecBuiltin{
|
spec.setPrecompile(1, &parityChainSpecBuiltin{Name: "ecrecover",
|
||||||
Name: "ecrecover", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}},
|
Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}})
|
||||||
}
|
|
||||||
spec.Accounts[common.BytesToAddress([]byte{2})].Builtin = &parityChainSpecBuiltin{
|
spec.setPrecompile(2, &parityChainSpecBuiltin{
|
||||||
Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}},
|
Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}},
|
||||||
}
|
})
|
||||||
spec.Accounts[common.BytesToAddress([]byte{3})].Builtin = &parityChainSpecBuiltin{
|
spec.setPrecompile(3, &parityChainSpecBuiltin{
|
||||||
Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}},
|
Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}},
|
||||||
}
|
})
|
||||||
spec.Accounts[common.BytesToAddress([]byte{4})].Builtin = &parityChainSpecBuiltin{
|
spec.setPrecompile(4, &parityChainSpecBuiltin{
|
||||||
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
|
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
|
||||||
}
|
})
|
||||||
if genesis.Config.ByzantiumBlock != nil {
|
if genesis.Config.ByzantiumBlock != nil {
|
||||||
spec.Accounts[common.BytesToAddress([]byte{5})].Builtin = &parityChainSpecBuiltin{
|
blnum := math2.HexOrDecimal64(genesis.Config.ByzantiumBlock.Uint64())
|
||||||
Name: "modexp", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
spec.setPrecompile(5, &parityChainSpecBuiltin{
|
||||||
}
|
Name: "modexp", ActivateAt: blnum, Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
||||||
spec.Accounts[common.BytesToAddress([]byte{6})].Builtin = &parityChainSpecBuiltin{
|
})
|
||||||
Name: "alt_bn128_add", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
spec.setPrecompile(6, &parityChainSpecBuiltin{
|
||||||
}
|
Name: "alt_bn128_add", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
||||||
spec.Accounts[common.BytesToAddress([]byte{7})].Builtin = &parityChainSpecBuiltin{
|
})
|
||||||
Name: "alt_bn128_mul", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
spec.setPrecompile(7, &parityChainSpecBuiltin{
|
||||||
}
|
Name: "alt_bn128_mul", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
||||||
spec.Accounts[common.BytesToAddress([]byte{8})].Builtin = &parityChainSpecBuiltin{
|
})
|
||||||
Name: "alt_bn128_pairing", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
spec.setPrecompile(8, &parityChainSpecBuiltin{
|
||||||
}
|
Name: "alt_bn128_pairing", ActivateAt: blnum, Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBuiltin) {
|
||||||
|
if spec.Accounts == nil {
|
||||||
|
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||||
|
}
|
||||||
|
a := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
|
||||||
|
if _, exist := spec.Accounts[a]; !exist {
|
||||||
|
spec.Accounts[a] = &parityChainSpecAccount{}
|
||||||
|
}
|
||||||
|
spec.Accounts[a].Builtin = data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *parityChainSpec) setByzantium(num *big.Int) {
|
||||||
|
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward)
|
||||||
|
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
|
||||||
|
n := hexutil.Uint64(num.Uint64())
|
||||||
|
spec.Engine.Ethash.Params.EIP100bTransition = n
|
||||||
|
spec.Params.EIP140Transition = n
|
||||||
|
spec.Params.EIP211Transition = n
|
||||||
|
spec.Params.EIP214Transition = n
|
||||||
|
spec.Params.EIP658Transition = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spec *parityChainSpec) setConstantinople(num *big.Int) {
|
||||||
|
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward)
|
||||||
|
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000)
|
||||||
|
n := hexutil.Uint64(num.Uint64())
|
||||||
|
spec.Params.EIP145Transition = n
|
||||||
|
spec.Params.EIP1014Transition = n
|
||||||
|
spec.Params.EIP1052Transition = n
|
||||||
|
spec.Params.EIP1283Transition = n
|
||||||
|
}
|
||||||
|
|
||||||
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
||||||
// Python Ethereum implementation.
|
// Python Ethereum implementation.
|
||||||
type pyEthereumGenesisSpec struct {
|
type pyEthereumGenesisSpec struct {
|
||||||
|
109
cmd/puppeth/genesis_test.go
Normal file
109
cmd/puppeth/genesis_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of go-ethereum.
|
||||||
|
//
|
||||||
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests the go-ethereum to Aleth chainspec conversion for the Stureby testnet.
|
||||||
|
func TestAlethSturebyConverter(t *testing.T) {
|
||||||
|
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read file: %v", err)
|
||||||
|
}
|
||||||
|
var genesis core.Genesis
|
||||||
|
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||||
|
t.Fatalf("failed parsing genesis: %v", err)
|
||||||
|
}
|
||||||
|
spec, err := newAlethGenesisSpec("stureby", &genesis)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed creating chainspec: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expBlob, err := ioutil.ReadFile("testdata/stureby_aleth.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read file: %v", err)
|
||||||
|
}
|
||||||
|
expspec := &alethGenesisSpec{}
|
||||||
|
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||||
|
t.Fatalf("failed parsing genesis: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(expspec, spec) {
|
||||||
|
t.Errorf("chainspec mismatch")
|
||||||
|
c := spew.ConfigState{
|
||||||
|
DisablePointerAddresses: true,
|
||||||
|
SortKeys: true,
|
||||||
|
}
|
||||||
|
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||||
|
got := strings.Split(c.Sdump(spec), "\n")
|
||||||
|
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||||
|
if exp[i] != got[i] {
|
||||||
|
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests the go-ethereum to Parity chainspec conversion for the Stureby testnet.
|
||||||
|
func TestParitySturebyConverter(t *testing.T) {
|
||||||
|
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read file: %v", err)
|
||||||
|
}
|
||||||
|
var genesis core.Genesis
|
||||||
|
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||||
|
t.Fatalf("failed parsing genesis: %v", err)
|
||||||
|
}
|
||||||
|
spec, err := newParityChainSpec("Stureby", &genesis, []string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed creating chainspec: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expBlob, err := ioutil.ReadFile("testdata/stureby_parity.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read file: %v", err)
|
||||||
|
}
|
||||||
|
expspec := &parityChainSpec{}
|
||||||
|
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||||
|
t.Fatalf("failed parsing genesis: %v", err)
|
||||||
|
}
|
||||||
|
expspec.Nodes = []string{}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expspec, spec) {
|
||||||
|
t.Errorf("chainspec mismatch")
|
||||||
|
c := spew.ConfigState{
|
||||||
|
DisablePointerAddresses: true,
|
||||||
|
SortKeys: true,
|
||||||
|
}
|
||||||
|
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||||
|
got := strings.Split(c.Sdump(spec), "\n")
|
||||||
|
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||||
|
if exp[i] != got[i] {
|
||||||
|
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -640,7 +640,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
|
|||||||
files[filepath.Join(workdir, network+".json")] = genesis
|
files[filepath.Join(workdir, network+".json")] = genesis
|
||||||
|
|
||||||
if conf.Genesis.Config.Ethash != nil {
|
if conf.Genesis.Config.Ethash != nil {
|
||||||
cppSpec, err := newCppEthereumGenesisSpec(network, conf.Genesis)
|
cppSpec, err := newAlethGenesisSpec(network, conf.Genesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -43,18 +43,23 @@ func main() {
|
|||||||
Usage: "log level to emit to the screen",
|
Usage: "log level to emit to the screen",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Action = func(c *cli.Context) error {
|
app.Before = func(c *cli.Context) error {
|
||||||
// Set up the logger to print everything and the random generator
|
// Set up the logger to print everything and the random generator
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||||
rand.Seed(time.Now().UnixNano())
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
app.Action = runWizard
|
||||||
|
app.Run(os.Args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runWizard start the wizard and relinquish control to it.
|
||||||
|
func runWizard(c *cli.Context) error {
|
||||||
network := c.String("network")
|
network := c.String("network")
|
||||||
if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network {
|
if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network {
|
||||||
log.Crit("No spaces, hyphens or capital letters allowed in network name")
|
log.Crit("No spaces, hyphens or capital letters allowed in network name")
|
||||||
}
|
}
|
||||||
// Start the wizard and relinquish control
|
|
||||||
makeWizard(c.String("network")).run()
|
makeWizard(c.String("network")).run()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
app.Run(os.Args)
|
|
||||||
}
|
|
||||||
|
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"sealEngine":"Ethash",
|
||||||
|
"params":{
|
||||||
|
"accountStartNonce":"0x00",
|
||||||
|
"maximumExtraDataSize":"0x20",
|
||||||
|
"homesteadForkBlock":"0x2710",
|
||||||
|
"daoHardforkBlock":"0x00",
|
||||||
|
"EIP150ForkBlock":"0x3a98",
|
||||||
|
"EIP158ForkBlock":"0x59d8",
|
||||||
|
"byzantiumForkBlock":"0x7530",
|
||||||
|
"constantinopleForkBlock":"0x9c40",
|
||||||
|
"minGasLimit":"0x1388",
|
||||||
|
"maxGasLimit":"0x7fffffffffffffff",
|
||||||
|
"tieBreakingGas":false,
|
||||||
|
"gasLimitBoundDivisor":"0x0400",
|
||||||
|
"minimumDifficulty":"0x20000",
|
||||||
|
"difficultyBoundDivisor":"0x0800",
|
||||||
|
"durationLimit":"0x0d",
|
||||||
|
"blockReward":"0x4563918244F40000",
|
||||||
|
"networkID":"0x4cb2e",
|
||||||
|
"chainID":"0x4cb2e",
|
||||||
|
"allowFutureBlocks":false
|
||||||
|
},
|
||||||
|
"genesis":{
|
||||||
|
"nonce":"0x0000000000000000",
|
||||||
|
"difficulty":"0x20000",
|
||||||
|
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"author":"0x0000000000000000000000000000000000000000",
|
||||||
|
"timestamp":"0x59a4e76d",
|
||||||
|
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||||
|
"gasLimit":"0x47b760"
|
||||||
|
},
|
||||||
|
"accounts":{
|
||||||
|
"0000000000000000000000000000000000000001":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"ecrecover",
|
||||||
|
"linear":{
|
||||||
|
"base":3000,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000002":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"sha256",
|
||||||
|
"linear":{
|
||||||
|
"base":60,
|
||||||
|
"word":12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000003":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"ripemd160",
|
||||||
|
"linear":{
|
||||||
|
"base":600,
|
||||||
|
"word":120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000004":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"identity",
|
||||||
|
"linear":{
|
||||||
|
"base":15,
|
||||||
|
"word":3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000005":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"modexp",
|
||||||
|
"startingBlock":"0x7530"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000006":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"alt_bn128_G1_add",
|
||||||
|
"startingBlock":"0x7530",
|
||||||
|
"linear":{
|
||||||
|
"base":500,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000007":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"alt_bn128_G1_mul",
|
||||||
|
"startingBlock":"0x7530",
|
||||||
|
"linear":{
|
||||||
|
"base":40000,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000008":{
|
||||||
|
"balance":"1",
|
||||||
|
"precompiled":{
|
||||||
|
"name":"alt_bn128_pairing_product",
|
||||||
|
"startingBlock":"0x7530"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"ethash":{},
|
||||||
|
"chainId": 314158,
|
||||||
|
"homesteadBlock": 10000,
|
||||||
|
"eip150Block": 15000,
|
||||||
|
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"eip155Block": 23000,
|
||||||
|
"eip158Block": 23000,
|
||||||
|
"byzantiumBlock": 30000,
|
||||||
|
"constantinopleBlock": 40000
|
||||||
|
},
|
||||||
|
"nonce": "0x0",
|
||||||
|
"timestamp": "0x59a4e76d",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||||
|
"gasLimit": "0x47b760",
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"alloc": {
|
||||||
|
"0000000000000000000000000000000000000001": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000002": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000003": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000004": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000005": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000006": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000007": {
|
||||||
|
"balance": "0x01"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000008": {
|
||||||
|
"balance": "0x01"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
{
|
||||||
|
"name":"Stureby",
|
||||||
|
"dataDir":"stureby",
|
||||||
|
"engine":{
|
||||||
|
"Ethash":{
|
||||||
|
"params":{
|
||||||
|
"minimumDifficulty":"0x20000",
|
||||||
|
"difficultyBoundDivisor":"0x800",
|
||||||
|
"durationLimit":"0xd",
|
||||||
|
"blockReward":{
|
||||||
|
"0x0":"0x4563918244f40000",
|
||||||
|
"0x7530":"0x29a2241af62c0000",
|
||||||
|
"0x9c40":"0x1bc16d674ec80000"
|
||||||
|
},
|
||||||
|
"homesteadTransition":"0x2710",
|
||||||
|
"eip100bTransition":"0x7530",
|
||||||
|
"difficultyBombDelays":{
|
||||||
|
"0x7530":"0x2dc6c0",
|
||||||
|
"0x9c40":"0x1e8480"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"params":{
|
||||||
|
"accountStartNonce":"0x0",
|
||||||
|
"maximumExtraDataSize":"0x20",
|
||||||
|
"gasLimitBoundDivisor":"0x400",
|
||||||
|
"minGasLimit":"0x1388",
|
||||||
|
"networkID":"0x4cb2e",
|
||||||
|
"chainID":"0x4cb2e",
|
||||||
|
"maxCodeSize":"0x6000",
|
||||||
|
"maxCodeSizeTransition":"0x0",
|
||||||
|
"eip98Transition": "0x7fffffffffffffff",
|
||||||
|
"eip150Transition":"0x3a98",
|
||||||
|
"eip160Transition":"0x59d8",
|
||||||
|
"eip161abcTransition":"0x59d8",
|
||||||
|
"eip161dTransition":"0x59d8",
|
||||||
|
"eip155Transition":"0x59d8",
|
||||||
|
"eip140Transition":"0x7530",
|
||||||
|
"eip211Transition":"0x7530",
|
||||||
|
"eip214Transition":"0x7530",
|
||||||
|
"eip658Transition":"0x7530",
|
||||||
|
"eip145Transition":"0x9c40",
|
||||||
|
"eip1014Transition":"0x9c40",
|
||||||
|
"eip1052Transition":"0x9c40",
|
||||||
|
"eip1283Transition":"0x9c40"
|
||||||
|
},
|
||||||
|
"genesis":{
|
||||||
|
"seal":{
|
||||||
|
"ethereum":{
|
||||||
|
"nonce":"0x0000000000000000",
|
||||||
|
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"difficulty":"0x20000",
|
||||||
|
"author":"0x0000000000000000000000000000000000000000",
|
||||||
|
"timestamp":"0x59a4e76d",
|
||||||
|
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||||
|
"gasLimit":"0x47b760"
|
||||||
|
},
|
||||||
|
"nodes":[
|
||||||
|
"enode://dfa7aca3f5b635fbfe7d0b20575f25e40d9e27b4bfbb3cf74364a42023ad9f25c1a4383bcc8cced86ee511a7d03415345a4df05be37f1dff040e4c780699f1c0@168.61.153.255:31303",
|
||||||
|
"enode://ef441b20dd70aeabf0eac35c3b8a2854e5ce04db0e30be9152ea9fd129359dcbb3f803993303ff5781c755dfd7223f3fe43505f583cccb740949407677412ba9@40.74.91.252:31303",
|
||||||
|
"enode://953b5ea1c8987cf46008232a0160324fd00d41320ecf00e23af86ec8f5396b19eb57ddab37c78141be56f62e9077de4f4dfa0747fa768ed8c8531bbfb1046237@40.70.214.166:31303",
|
||||||
|
"enode://276e613dd4b277a66591e565711e6c8bb107f0905248a9f8f8228c1a87992e156e5114bb9937c02824a9d9d25f76340442cf86e2028bf5293cae19904fb2b98e@35.178.251.52:30303",
|
||||||
|
"enode://064c820d41e52ed7d426ac64b60506c2998235bedc7e67cb497c6faf7bb4fc54fe56fc82d0add3180b747c0c4f40a1108a6f84d7d0629ed606d504528e61cc57@3.8.5.3:30303",
|
||||||
|
"enode://90069fdabcc5e684fa5d59430bebbb12755d9362dfe5006a1485b13d71a78a3812d36e74dd7d88e50b51add01e097ea80f16263aeaa4f0230db6c79e2a97e7ca@217.29.191.142:30303",
|
||||||
|
"enode://0aac74b7fd28726275e466acb5e03bc88a95927e9951eb66b5efb239b2f798ada0690853b2f2823fe4efa408f0f3d4dd258430bc952a5ff70677b8625b3e3b14@40.115.33.57:40404",
|
||||||
|
"enode://0b96415a10f835106d83e090a0528eed5e7887e5c802a6d084e9f1993a9d0fc713781e6e4101f6365e9b91259712f291acc0a9e6e667e22023050d602c36fbe2@40.115.33.57:40414"
|
||||||
|
],
|
||||||
|
"accounts":{
|
||||||
|
"0000000000000000000000000000000000000001":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"ecrecover",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":3000,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000002":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"sha256",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":60,
|
||||||
|
"word":12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000003":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"ripemd160",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":600,
|
||||||
|
"word":120
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000004":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"identity",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":15,
|
||||||
|
"word":3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000005":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"modexp",
|
||||||
|
"activate_at":"0x7530",
|
||||||
|
"pricing":{
|
||||||
|
"modexp":{
|
||||||
|
"divisor":20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000006":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"alt_bn128_add",
|
||||||
|
"activate_at":"0x7530",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":500,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000007":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"alt_bn128_mul",
|
||||||
|
"activate_at":"0x7530",
|
||||||
|
"pricing":{
|
||||||
|
"linear":{
|
||||||
|
"base":40000,
|
||||||
|
"word":0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000008":{
|
||||||
|
"balance":"1",
|
||||||
|
"nonce":"0",
|
||||||
|
"builtin":{
|
||||||
|
"name":"alt_bn128_pairing",
|
||||||
|
"activate_at":"0x7530",
|
||||||
|
"pricing":{
|
||||||
|
"alt_bn128_pairing":{
|
||||||
|
"base":100000,
|
||||||
|
"pair":80000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
@ -118,6 +119,47 @@ func (w *wizard) readDefaultString(def string) string {
|
|||||||
return def
|
return def
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readDefaultYesNo reads a single line from stdin, trimming if from spaces and
|
||||||
|
// interpreting it as a 'yes' or a 'no'. If an empty line is entered, the default
|
||||||
|
// value is returned.
|
||||||
|
func (w *wizard) readDefaultYesNo(def bool) bool {
|
||||||
|
for {
|
||||||
|
fmt.Printf("> ")
|
||||||
|
text, err := w.in.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to read user input", "err", err)
|
||||||
|
}
|
||||||
|
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
if text == "y" || text == "yes" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if text == "n" || text == "no" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
log.Error("Invalid input, expected 'y', 'yes', 'n', 'no' or empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// readURL reads a single line from stdin, trimming if from spaces and trying to
|
||||||
|
// interpret it as a URL (http, https or file).
|
||||||
|
func (w *wizard) readURL() *url.URL {
|
||||||
|
for {
|
||||||
|
fmt.Printf("> ")
|
||||||
|
text, err := w.in.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to read user input", "err", err)
|
||||||
|
}
|
||||||
|
uri, err := url.Parse(strings.TrimSpace(text))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Invalid input, expected URL", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// readInt reads a single line from stdin, trimming if from spaces, enforcing it
|
// readInt reads a single line from stdin, trimming if from spaces, enforcing it
|
||||||
// to parse into an integer.
|
// to parse into an integer.
|
||||||
func (w *wizard) readInt() int {
|
func (w *wizard) readInt() int {
|
||||||
|
@ -137,14 +137,14 @@ func (w *wizard) deployDashboard() {
|
|||||||
if w.conf.ethstats != "" {
|
if w.conf.ethstats != "" {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
|
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
|
||||||
infos.trusted = w.readDefaultString("y") == "y"
|
infos.trusted = w.readDefaultYesNo(true)
|
||||||
}
|
}
|
||||||
// Try to deploy the dashboard container on the host
|
// Try to deploy the dashboard container on the host
|
||||||
nocache := false
|
nocache := false
|
||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
|
if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
|
||||||
log.Error("Failed to deploy dashboard container", "err", err)
|
log.Error("Failed to deploy dashboard container", "err", err)
|
||||||
|
@ -67,11 +67,11 @@ func (w *wizard) deployEthstats() {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
||||||
if w.readDefaultString("y") != "y" {
|
if !w.readDefaultYesNo(true) {
|
||||||
// The user might want to clear the entire list, although generally probably not
|
// The user might want to clear the entire list, although generally probably not
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
||||||
if w.readDefaultString("n") != "n" {
|
if w.readDefaultYesNo(false) {
|
||||||
infos.banned = nil
|
infos.banned = nil
|
||||||
}
|
}
|
||||||
// Offer the user to explicitly add/remove certain IP addresses
|
// Offer the user to explicitly add/remove certain IP addresses
|
||||||
@ -106,7 +106,7 @@ func (w *wizard) deployEthstats() {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
trusted := make([]string, 0, len(w.servers))
|
trusted := make([]string, 0, len(w.servers))
|
||||||
for _, client := range w.servers {
|
for _, client := range w.servers {
|
||||||
|
@ -100,7 +100,7 @@ func (w *wizard) deployExplorer() {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil {
|
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil {
|
||||||
log.Error("Failed to deploy explorer container", "err", err)
|
log.Error("Failed to deploy explorer container", "err", err)
|
||||||
|
@ -81,7 +81,7 @@ func (w *wizard) deployFaucet() {
|
|||||||
if infos.captchaToken != "" {
|
if infos.captchaToken != "" {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
|
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
|
||||||
if w.readDefaultString("y") != "y" {
|
if !w.readDefaultYesNo(true) {
|
||||||
infos.captchaToken, infos.captchaSecret = "", ""
|
infos.captchaToken, infos.captchaSecret = "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ func (w *wizard) deployFaucet() {
|
|||||||
// No previous authorization (or old one discarded)
|
// No previous authorization (or old one discarded)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
|
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
|
||||||
if w.readDefaultString("n") == "n" {
|
if !w.readDefaultYesNo(false) {
|
||||||
log.Warn("Users will be able to requests funds via automated scripts")
|
log.Warn("Users will be able to requests funds via automated scripts")
|
||||||
} else {
|
} else {
|
||||||
// Captcha protection explicitly requested, read the site and secret keys
|
// Captcha protection explicitly requested, read the site and secret keys
|
||||||
@ -132,7 +132,7 @@ func (w *wizard) deployFaucet() {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
|
fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||||
if w.readDefaultString("y") != "y" {
|
if !w.readDefaultYesNo(true) {
|
||||||
infos.node.keyJSON, infos.node.keyPass = "", ""
|
infos.node.keyJSON, infos.node.keyPass = "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ func (w *wizard) deployFaucet() {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||||
log.Error("Failed to deploy faucet container", "err", err)
|
log.Error("Failed to deploy faucet container", "err", err)
|
||||||
|
@ -20,9 +20,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -45,6 +49,7 @@ func (w *wizard) makeGenesis() {
|
|||||||
EIP155Block: big.NewInt(3),
|
EIP155Block: big.NewInt(3),
|
||||||
EIP158Block: big.NewInt(3),
|
EIP158Block: big.NewInt(3),
|
||||||
ByzantiumBlock: big.NewInt(4),
|
ByzantiumBlock: big.NewInt(4),
|
||||||
|
ConstantinopleBlock: big.NewInt(5),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
// Figure out which consensus engine to choose
|
// Figure out which consensus engine to choose
|
||||||
@ -114,10 +119,14 @@ func (w *wizard) makeGenesis() {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)")
|
||||||
|
if w.readDefaultYesNo(true) {
|
||||||
// Add a batch of precompile balances to avoid them getting deleted
|
// Add a batch of precompile balances to avoid them getting deleted
|
||||||
for i := int64(0); i < 256; i++ {
|
for i := int64(0); i < 256; i++ {
|
||||||
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Query the user for some custom extras
|
// Query the user for some custom extras
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
|
fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
|
||||||
@ -130,53 +139,130 @@ func (w *wizard) makeGenesis() {
|
|||||||
w.conf.flush()
|
w.conf.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// importGenesis imports a Geth genesis spec into puppeth.
|
||||||
|
func (w *wizard) importGenesis() {
|
||||||
|
// Request the genesis JSON spec URL from the user
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Where's the genesis file? (local file or http/https url)")
|
||||||
|
url := w.readURL()
|
||||||
|
|
||||||
|
// Convert the various allowed URLs to a reader stream
|
||||||
|
var reader io.Reader
|
||||||
|
|
||||||
|
switch url.Scheme {
|
||||||
|
case "http", "https":
|
||||||
|
// Remote web URL, retrieve it via an HTTP client
|
||||||
|
res, err := http.Get(url.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to retrieve remote genesis", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
reader = res.Body
|
||||||
|
|
||||||
|
case "":
|
||||||
|
// Schemaless URL, interpret as a local file
|
||||||
|
file, err := os.Open(url.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to open local genesis", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
reader = file
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Parse the genesis file and inject it successful
|
||||||
|
var genesis core.Genesis
|
||||||
|
if err := json.NewDecoder(reader).Decode(&genesis); err != nil {
|
||||||
|
log.Error("Invalid genesis spec: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Imported genesis block")
|
||||||
|
|
||||||
|
w.conf.Genesis = &genesis
|
||||||
|
w.conf.flush()
|
||||||
|
}
|
||||||
|
|
||||||
// manageGenesis permits the modification of chain configuration parameters in
|
// manageGenesis permits the modification of chain configuration parameters in
|
||||||
// a genesis config and the export of the entire genesis spec.
|
// a genesis config and the export of the entire genesis spec.
|
||||||
func (w *wizard) manageGenesis() {
|
func (w *wizard) manageGenesis() {
|
||||||
// Figure out whether to modify or export the genesis
|
// Figure out whether to modify or export the genesis
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println(" 1. Modify existing fork rules")
|
fmt.Println(" 1. Modify existing fork rules")
|
||||||
fmt.Println(" 2. Export genesis configuration")
|
fmt.Println(" 2. Export genesis configurations")
|
||||||
fmt.Println(" 3. Remove genesis configuration")
|
fmt.Println(" 3. Remove genesis configuration")
|
||||||
|
|
||||||
choice := w.read()
|
choice := w.read()
|
||||||
switch {
|
switch choice {
|
||||||
case choice == "1":
|
case "1":
|
||||||
// Fork rule updating requested, iterate over each fork
|
// Fork rule updating requested, iterate over each fork
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
|
fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
|
||||||
w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
|
w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should EIP150 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
||||||
w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
|
w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should EIP155 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
||||||
w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
|
w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should EIP158 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
||||||
w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
|
w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
|
fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
|
||||||
w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
|
w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
|
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
|
||||||
|
|
||||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||||
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
||||||
|
|
||||||
case choice == "2":
|
case "2":
|
||||||
// Save whatever genesis configuration we currently have
|
// Save whatever genesis configuration we currently have
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network)
|
fmt.Printf("Which folder to save the genesis specs into? (default = current)\n")
|
||||||
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network)
|
||||||
if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil {
|
|
||||||
log.Error("Failed to save genesis file", "err", err)
|
|
||||||
}
|
|
||||||
log.Info("Exported existing genesis block")
|
|
||||||
|
|
||||||
case choice == "3":
|
folder := w.readDefaultString(".")
|
||||||
|
if err := os.MkdirAll(folder, 0755); err != nil {
|
||||||
|
log.Error("Failed to create spec folder", "folder", folder, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
||||||
|
|
||||||
|
// Export the native genesis spec used by puppeth and Geth
|
||||||
|
gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network))
|
||||||
|
if err := ioutil.WriteFile((gethJson), out, 0644); err != nil {
|
||||||
|
log.Error("Failed to save genesis file", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Saved native genesis chain spec", "path", gethJson)
|
||||||
|
|
||||||
|
// Export the genesis spec used by Aleth (formerly C++ Ethereum)
|
||||||
|
if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil {
|
||||||
|
log.Error("Failed to create Aleth chain spec", "err", err)
|
||||||
|
} else {
|
||||||
|
saveGenesis(folder, w.network, "aleth", spec)
|
||||||
|
}
|
||||||
|
// Export the genesis spec used by Parity
|
||||||
|
if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil {
|
||||||
|
log.Error("Failed to create Parity chain spec", "err", err)
|
||||||
|
} else {
|
||||||
|
saveGenesis(folder, w.network, "parity", spec)
|
||||||
|
}
|
||||||
|
// Export the genesis spec used by Harmony (formerly EthereumJ
|
||||||
|
saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
|
||||||
|
|
||||||
|
case "3":
|
||||||
// Make sure we don't have any services running
|
// Make sure we don't have any services running
|
||||||
if len(w.conf.servers()) > 0 {
|
if len(w.conf.servers()) > 0 {
|
||||||
log.Error("Genesis reset requires all services and servers torn down")
|
log.Error("Genesis reset requires all services and servers torn down")
|
||||||
@ -186,8 +272,20 @@ func (w *wizard) manageGenesis() {
|
|||||||
|
|
||||||
w.conf.Genesis = nil
|
w.conf.Genesis = nil
|
||||||
w.conf.flush()
|
w.conf.flush()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Error("That's not something I can do")
|
log.Error("That's not something I can do")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file.
|
||||||
|
func saveGenesis(folder, network, client string, spec interface{}) {
|
||||||
|
path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
|
||||||
|
|
||||||
|
out, _ := json.Marshal(spec)
|
||||||
|
if err := ioutil.WriteFile(path, out, 0644); err != nil {
|
||||||
|
log.Error("Failed to save genesis file", "client", client, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("Saved genesis chain spec", "client", client, "path", path)
|
||||||
|
}
|
||||||
|
@ -131,7 +131,20 @@ func (w *wizard) run() {
|
|||||||
|
|
||||||
case choice == "2":
|
case choice == "2":
|
||||||
if w.conf.Genesis == nil {
|
if w.conf.Genesis == nil {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("What would you like to do? (default = create)")
|
||||||
|
fmt.Println(" 1. Create new genesis from scratch")
|
||||||
|
fmt.Println(" 2. Import already existing genesis")
|
||||||
|
|
||||||
|
choice := w.read()
|
||||||
|
switch {
|
||||||
|
case choice == "" || choice == "1":
|
||||||
w.makeGenesis()
|
w.makeGenesis()
|
||||||
|
case choice == "2":
|
||||||
|
w.importGenesis()
|
||||||
|
default:
|
||||||
|
log.Error("That's not something I can do")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
w.manageGenesis()
|
w.manageGenesis()
|
||||||
}
|
}
|
||||||
@ -149,7 +162,6 @@ func (w *wizard) run() {
|
|||||||
} else {
|
} else {
|
||||||
w.manageComponents()
|
w.manageComponents()
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Error("That's not something I can do")
|
log.Error("That's not something I can do")
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,12 @@ func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (str
|
|||||||
// Reverse proxy is not running, offer to deploy a new one
|
// Reverse proxy is not running, offer to deploy a new one
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
|
fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
|
||||||
if w.readDefaultString("y") == "y" {
|
if w.readDefaultYesNo(true) {
|
||||||
nocache := false
|
nocache := false
|
||||||
if proxy != nil {
|
if proxy != nil {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployNginx(client, w.network, port, nocache); err != nil {
|
if out, err := deployNginx(client, w.network, port, nocache); err != nil {
|
||||||
log.Error("Failed to deploy reverse-proxy", "err", err)
|
log.Error("Failed to deploy reverse-proxy", "err", err)
|
||||||
|
@ -126,7 +126,7 @@ func (w *wizard) deployNode(boot bool) {
|
|||||||
} else {
|
} else {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
|
fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||||
if w.readDefaultString("y") != "y" {
|
if !w.readDefaultYesNo(true) {
|
||||||
infos.keyJSON, infos.keyPass = "", ""
|
infos.keyJSON, infos.keyPass = "", ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ func (w *wizard) deployNode(boot bool) {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||||
log.Error("Failed to deploy Ethereum node container", "err", err)
|
log.Error("Failed to deploy Ethereum node container", "err", err)
|
||||||
|
@ -96,7 +96,7 @@ func (w *wizard) deployWallet() {
|
|||||||
if existed {
|
if existed {
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
|
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
|
||||||
nocache = w.readDefaultString("n") != "n"
|
nocache = w.readDefaultYesNo(false)
|
||||||
}
|
}
|
||||||
if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||||
log.Error("Failed to deploy wallet container", "err", err)
|
log.Error("Failed to deploy wallet container", "err", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user