mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
cmd, consensus, core, eth, mobile, params: Aura proof-of-authority consensus engine implementation
added aura dir and made aura in config.go changed name of Clique struct to Aura and updated interface functions go modules added; preparing to write aura consensus adding goerli flag goerli flag added adding configs Changed signaturs to match aura namespace (#7) fix errors relating to referencing clique (#9) configuring genesis removing duplicate imports Configuring Aura config, genesis, bytecodes (#10) * go modules added; preparing to write aura consensus * adding goerli flag * goerli flag added * adding configs * configuring genesis * removing duplicate imports adding configs for aura api Engine/create (#12) * added aura to createConsensusEngine * consensus engine create Added difficulty retrival from Aura config (#11) * Added difficulty retrival from Aura config * Added default difficulty added bootnodes to bootnodes.go and added authorities array to GoerliChainConfig in params/config.go (#15) David/verify headers (#13) * Updated verifyheader logic for Aura * Fixed errors returned and comments configuring goerli cli flag fixed type differences and undefined vars (#16) * fixed type differences and undefined vars * resolved changes flag function fix Priom/gorli cli flag (#17) * go modules added; preparing to write aura consensus * adding goerli flag * goerli flag added * adding configs * core/vm: Hide read only flag from Interpreter interface (#17461) (#6) Makes Interface interface a bit more stateless and abstract. Obviously this change is dictated by EVMC design. The EVMC tries to keep the responsibility for EVM features totally inside the VMs, if feasible. This makes VM "stateless" because VM does not need to pass any information between executions, all information is included in parameters of the execute function. * configuring genesis * removing duplicate imports * adding configs for aura api * configuring goerli cli flag * flag function fix * typo fix Elizabeth/aura (#18) * added bootnodes to bootnodes.go and added authorities array to GoerliChainConfig in params/config.go * Seal() returns errUnauthorized if not authorized to sign block * verifySeal now checks if signer of block was correct signer at that turn Goerli flag keeps connecting to Mainnet (#20) * go modules added; preparing to write aura consensus * adding goerli flag * goerli flag added * adding configs * core/vm: Hide read only flag from Interpreter interface (#17461) (#6) Makes Interface interface a bit more stateless and abstract. Obviously this change is dictated by EVMC design. The EVMC tries to keep the responsibility for EVM features totally inside the VMs, if feasible. This makes VM "stateless" because VM does not need to pass any information between executions, all information is included in parameters of the execute function. * configuring genesis * removing duplicate imports * adding configs for aura api * configuring goerli cli flag * flag function fix * typo fix * goerli flag finalized * config mod Alternating config for Aura consensus mechanism (#21) * go modules added; preparing to write aura consensus * adding goerli flag * goerli flag added * adding configs * core/vm: Hide read only flag from Interpreter interface (#17461) (#6) Makes Interface interface a bit more stateless and abstract. Obviously this change is dictated by EVMC design. The EVMC tries to keep the responsibility for EVM features totally inside the VMs, if feasible. This makes VM "stateless" because VM does not need to pass any information between executions, all information is included in parameters of the execute function. * configuring genesis * removing duplicate imports * adding configs for aura api * configuring goerli cli flag * flag function fix * modifying configs type fix (#22) --goerli connects to Aura engine by default (#24) * type fix * goerli picks up aura by default goerli networkid changed to 6382 Elizabeth/aura (#23) * added bootnodes to bootnodes.go and added authorities array to GoerliChainConfig in params/config.go * Seal() returns errUnauthorized if not authorized to sign block * verifySeal now checks if signer of block was correct signer at that turn * removed snapshots from prepare and added block rewards to finalize * removed extravanity in Prepare Fix Goerli Params and Genesis (#25) * params: fix goerli aura params * params: remove period and epoch from aura config * core: fix goerli genesis * cmd/utils: fix goerli flags Fixes (#26) * aura: genesis + init-script * aura genesis Fixes (#27) * aura: genesis + init-script * aura genesis * core, params: wip on aura, still not working cmd/utils, consensus/aura, core, eth, params: gofmt Cleanup temporary files
This commit is contained in:
parent
72c820c49e
commit
50eb9d1527
16 changed files with 1395 additions and 51 deletions
|
|
@ -136,6 +136,7 @@ The export-preimages command export hash preimages to an RLP encoded stream`,
|
|||
utils.FakePoWFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
},
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ func remoteConsole(ctx *cli.Context) error {
|
|||
path = filepath.Join(path, "testnet")
|
||||
} else if ctx.GlobalBool(utils.RinkebyFlag.Name) {
|
||||
path = filepath.Join(path, "rinkeby")
|
||||
} else if ctx.GlobalBool(utils.GoerliFlag.Name) {
|
||||
path = filepath.Join(path, "goerli")
|
||||
}
|
||||
}
|
||||
endpoint = fmt.Sprintf("%s/geth.ipc", path)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ var (
|
|||
utils.DeveloperPeriodFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ var AppHelpFlagGroups = []flagGroup{
|
|||
utils.NetworkIdFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.GCModeFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/fdlimit"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/aura"
|
||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
|
@ -144,6 +145,10 @@ var (
|
|||
Name: "dev",
|
||||
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
|
||||
}
|
||||
GoerliFlag = cli.BoolFlag{
|
||||
Name: "goerli",
|
||||
Usage: "Goerli network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
DeveloperPeriodFlag = cli.IntFlag{
|
||||
Name: "dev.period",
|
||||
Usage: "Block period to use in developer mode (0 = mine only if transaction pending)",
|
||||
|
|
@ -623,6 +628,9 @@ func MakeDataDir(ctx *cli.Context) string {
|
|||
if ctx.GlobalBool(RinkebyFlag.Name) {
|
||||
return filepath.Join(path, "rinkeby")
|
||||
}
|
||||
if ctx.GlobalBool(GoerliFlag.Name) {
|
||||
return filepath.Join(path, "goerli")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
|
|
@ -677,6 +685,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
|||
urls = params.TestnetBootnodes
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyBootnodes
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
case cfg.BootstrapNodes != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
|
|
@ -704,6 +714,8 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
|||
}
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyBootnodes
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
case cfg.BootstrapNodesV5 != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
|
|
@ -971,6 +983,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
|||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
|
||||
|
|
@ -1111,7 +1125,7 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
|
|||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
// Avoid conflicting network flags
|
||||
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag)
|
||||
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
|
||||
checkExclusive(ctx, LightServFlag, SyncModeFlag, "light")
|
||||
|
||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||
|
|
@ -1196,6 +1210,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
|||
cfg.NetworkId = 4
|
||||
}
|
||||
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 6283
|
||||
}
|
||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1337
|
||||
|
|
@ -1338,6 +1357,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
|||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
Fatalf("Developer chains are ephemeral")
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
genesis = core.DefaultGoerliGenesisBlock()
|
||||
}
|
||||
return genesis
|
||||
}
|
||||
|
|
@ -1354,6 +1375,8 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
|||
var engine consensus.Engine
|
||||
if config.Clique != nil {
|
||||
engine = clique.New(config.Clique, chainDb)
|
||||
} else if config.Aura != nil {
|
||||
engine = aura.New(config.Aura, chainDb)
|
||||
} else {
|
||||
engine = ethash.NewFaker()
|
||||
if !ctx.GlobalBool(FakePoWFlag.Name) {
|
||||
|
|
|
|||
119
consensus/aura/api.go
Normal file
119
consensus/aura/api.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package aura
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// API is a user facing RPC API to allow controlling the signer and voting
|
||||
// mechanisms of the proof-of-authority scheme.
|
||||
type API struct {
|
||||
chain consensus.ChainReader
|
||||
aura *Aura
|
||||
}
|
||||
|
||||
// GetSnapshot retrieves the state snapshot at a given block.
|
||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
// Ensure we have an actually valid block and return its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
}
|
||||
return api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
}
|
||||
|
||||
// GetSnapshotAtHash retrieves the state snapshot at a given block.
|
||||
func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
|
||||
header := api.chain.GetHeaderByHash(hash)
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
}
|
||||
return api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
}
|
||||
|
||||
// GetSigners retrieves the list of authorized signers at the specified block.
|
||||
func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
// Ensure we have an actually valid block and return the signers from its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
}
|
||||
snap, err := api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snap.signers(), nil
|
||||
}
|
||||
|
||||
// GetSignersAtHash retrieves the list of authorized signers at the specified block.
|
||||
func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
|
||||
header := api.chain.GetHeaderByHash(hash)
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
}
|
||||
snap, err := api.aura.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return snap.signers(), nil
|
||||
}
|
||||
|
||||
// Proposals returns the current proposals the node tries to uphold and vote on.
|
||||
func (api *API) Proposals() map[common.Address]bool {
|
||||
api.aura.lock.RLock()
|
||||
defer api.aura.lock.RUnlock()
|
||||
|
||||
proposals := make(map[common.Address]bool)
|
||||
for address, auth := range api.aura.proposals {
|
||||
proposals[address] = auth
|
||||
}
|
||||
return proposals
|
||||
}
|
||||
|
||||
// Propose injects a new authorization proposal that the signer will attempt to
|
||||
// push through.
|
||||
func (api *API) Propose(address common.Address, auth bool) {
|
||||
api.aura.lock.Lock()
|
||||
defer api.aura.lock.Unlock()
|
||||
|
||||
api.aura.proposals[address] = auth
|
||||
}
|
||||
|
||||
// Discard drops a currently running proposal, stopping the signer from casting
|
||||
// further votes (either for or against).
|
||||
func (api *API) Discard(address common.Address) {
|
||||
api.aura.lock.Lock()
|
||||
defer api.aura.lock.Unlock()
|
||||
|
||||
delete(api.aura.proposals, address)
|
||||
}
|
||||
663
consensus/aura/aura.go
Normal file
663
consensus/aura/aura.go
Normal file
|
|
@ -0,0 +1,663 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package aura
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
const (
|
||||
checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
|
||||
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
|
||||
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||
|
||||
wiggleTime = 500 * time.Millisecond // Random delay (per signer) to allow concurrent signers
|
||||
)
|
||||
|
||||
// Clique proof-of-authority protocol constants.
|
||||
var (
|
||||
epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes
|
||||
|
||||
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
|
||||
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
|
||||
|
||||
uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
|
||||
)
|
||||
|
||||
// Various error messages to mark blocks invalid. These should be private to
|
||||
// prevent engine specific errors from being referenced in the remainder of the
|
||||
// codebase, inherently breaking if the engine is swapped out. Please put common
|
||||
// error types into the consensus package.
|
||||
var (
|
||||
// errUnknownBlock is returned when the list of signers is requested for a block
|
||||
// that is not part of the local blockchain.
|
||||
errUnknownBlock = errors.New("unknown block")
|
||||
|
||||
// errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
|
||||
// block has a beneficiary set to non-zeroes.
|
||||
errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
|
||||
|
||||
// errInvalidVote is returned if a nonce value is something else that the two
|
||||
// allowed constants of 0x00..0 or 0xff..f.
|
||||
errInvalidVote = errors.New("vote nonce not 0x00..0 or 0xff..f")
|
||||
|
||||
// errInvalidCheckpointVote is returned if a checkpoint/epoch transition block
|
||||
// has a vote nonce set to non-zeroes.
|
||||
errInvalidCheckpointVote = errors.New("vote nonce in checkpoint block non-zero")
|
||||
|
||||
// errMissingVanity is returned if a block's extra-data section is shorter than
|
||||
// 32 bytes, which is required to store the signer vanity.
|
||||
errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")
|
||||
|
||||
// errMissingSignature is returned if a block's extra-data section doesn't seem
|
||||
// to contain a 65 byte secp256k1 signature.
|
||||
errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
|
||||
|
||||
// errExtraSigners is returned if non-checkpoint block contain signer data in
|
||||
// their extra-data fields.
|
||||
errExtraSigners = errors.New("non-checkpoint block contains extra signer list")
|
||||
|
||||
// errInvalidValidatorSeal is returned if the extra data field length is not
|
||||
// equal to the length of a seal
|
||||
errInvalidExtraData = errors.New("extra data field in block header is invalid")
|
||||
|
||||
// errInvalidCheckpointSigners is returned if a checkpoint block contains an
|
||||
// invalid list of signers (i.e. non divisible by 20 bytes, or not the correct
|
||||
// ones).
|
||||
errInvalidCheckpointSigners = errors.New("invalid signer list on checkpoint block")
|
||||
|
||||
// errInvalidMixDigest is returned if a block's mix digest is non-zero.
|
||||
errInvalidMixDigest = errors.New("non-zero mix digest")
|
||||
|
||||
// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
|
||||
errInvalidUncleHash = errors.New("non empty uncle hash")
|
||||
|
||||
// errInvalidDifficulty is returned if the difficulty of a block is not either
|
||||
// of 1 or 2, or if the value does not match the turn of the signer.
|
||||
errInvalidDifficulty = errors.New("invalid difficulty")
|
||||
|
||||
// ErrInvalidTimestamp is returned if the timestamp of a block is lower than
|
||||
// the previous block's timestamp + the minimum block period.
|
||||
ErrInvalidTimestamp = errors.New("invalid timestamp")
|
||||
|
||||
// errInvalidVotingChain is returned if an authorization list is attempted to
|
||||
// be modified via out-of-range or non-contiguous headers.
|
||||
errInvalidVotingChain = errors.New("invalid voting chain")
|
||||
|
||||
// errUnauthorized is returned if a header is signed by a non-authorized entity.
|
||||
errUnauthorized = errors.New("unauthorized")
|
||||
|
||||
// errWaitTransactions is returned if an empty block is attempted to be sealed
|
||||
// on an instant chain (0 second period). It's important to refuse these as the
|
||||
// block reward is zero, so an empty block just bloats the chain... fast.
|
||||
errWaitTransactions = errors.New("waiting for transactions")
|
||||
)
|
||||
|
||||
// SignerFn is a signer callback function to request a hash to be signed by a
|
||||
// backing account.
|
||||
type SignerFn func(accounts.Account, []byte) ([]byte, error)
|
||||
|
||||
// sigHash returns the hash which is used as input for the proof-of-authority
|
||||
// signing. It is the hash of the entire header apart from the 65 byte signature
|
||||
// contained at the end of the extra data.
|
||||
//
|
||||
// Note, the method requires the extra data to be at least 65 bytes, otherwise it
|
||||
// panics. This is done to avoid accidentally using both forms (signature present
|
||||
// or not), which could be abused to produce different hashes for the same header.
|
||||
func sigHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
|
||||
rlp.Encode(hasher, []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
|
||||
// ecrecover extracts the Ethereum account address from a signed header.
|
||||
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
||||
// If the signature's already cached, return that
|
||||
hash := header.Hash()
|
||||
if address, known := sigcache.Get(hash); known {
|
||||
return address.(common.Address), nil
|
||||
}
|
||||
// Retrieve the signature from the header extra-data
|
||||
if len(header.Extra) < extraSeal {
|
||||
return common.Address{}, errMissingSignature
|
||||
}
|
||||
signature := header.Extra[len(header.Extra)-extraSeal:]
|
||||
|
||||
// Recover the public key and the Ethereum address
|
||||
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
var signer common.Address
|
||||
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
|
||||
sigcache.Add(hash, signer)
|
||||
return signer, nil
|
||||
}
|
||||
|
||||
// Clique is the proof-of-authority consensus engine proposed to support the
|
||||
// Ethereum testnet following the Ropsten attacks.
|
||||
type Aura struct {
|
||||
config *params.AuraConfig // Consensus engine configuration parameters
|
||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||
|
||||
recents *lru.ARCCache // Snapshots for recent block to speed up reorgs
|
||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||
|
||||
proposals map[common.Address]bool // Current list of proposals we are pushing
|
||||
|
||||
signer common.Address // Ethereum address of the signing key
|
||||
signFn SignerFn // Signer function to authorize hashes with
|
||||
lock sync.RWMutex // Protects the signer fields
|
||||
}
|
||||
|
||||
// New creates a Aura proof-of-authority consensus engine with the initial
|
||||
// signers set to the ones provided by the user.
|
||||
func New(config *params.AuraConfig, db ethdb.Database) *Aura {
|
||||
// Set any missing consensus parameters to their defaults
|
||||
conf := *config
|
||||
if conf.Epoch == 0 {
|
||||
conf.Epoch = epochLength
|
||||
}
|
||||
// Allocate the snapshot caches and create the engine
|
||||
recents, _ := lru.NewARC(inmemorySnapshots)
|
||||
signatures, _ := lru.NewARC(inmemorySignatures)
|
||||
|
||||
return &Aura{
|
||||
config: &conf,
|
||||
db: db,
|
||||
recents: recents,
|
||||
signatures: signatures,
|
||||
proposals: make(map[common.Address]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// Author implements consensus.Engine, returning the Ethereum address recovered
|
||||
// from the signature in the header's extra-data section.
|
||||
func (a *Aura) Author(header *types.Header) (common.Address, error) {
|
||||
return ecrecover(header, a.signatures)
|
||||
}
|
||||
|
||||
// VerifyHeader checks whether a header conforms to the consensus rules.
|
||||
func (a *Aura) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
|
||||
return a.verifyHeader(chain, header, nil)
|
||||
}
|
||||
|
||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
|
||||
// method returns a quit channel to abort the operations and a results channel to
|
||||
// retrieve the async verifications (the order is that of the input slice).
|
||||
func (a *Aura) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
||||
abort := make(chan struct{})
|
||||
results := make(chan error, len(headers))
|
||||
|
||||
go func() {
|
||||
for i, header := range headers {
|
||||
err := a.verifyHeader(chain, header, headers[:i])
|
||||
|
||||
select {
|
||||
case <-abort:
|
||||
return
|
||||
case results <- err:
|
||||
}
|
||||
}
|
||||
}()
|
||||
return abort, results
|
||||
}
|
||||
|
||||
// verifyHeader checks whether a header conforms to the consensus rules.The
|
||||
// caller may optionally pass in a batch of parents (ascending order) to avoid
|
||||
// looking those up from the database. This is useful for concurrently verifying
|
||||
// a batch of new headers.
|
||||
func (a *Aura) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
|
||||
if header.Number == nil {
|
||||
return errUnknownBlock
|
||||
}
|
||||
number := header.Number.Uint64()
|
||||
|
||||
// Don't waste time checking blocks from the future
|
||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
// TODO: Are any of these necessary checks?
|
||||
// Checkpoint blocks need to enforce zero beneficiary
|
||||
//checkpoint := (number % a.config.Epoch) == 0
|
||||
//if checkpoint && header.Coinbase != (common.Address{}) {
|
||||
// return errInvalidCheckpointBeneficiary
|
||||
//}
|
||||
// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
|
||||
//if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) {
|
||||
// return errInvalidVote
|
||||
//}
|
||||
|
||||
// Ensure that the extra-data contains a single signature
|
||||
signersBytes := len(header.Extra) - extraSeal
|
||||
if signersBytes != 0 {
|
||||
return errInvalidExtraData
|
||||
}
|
||||
// Ensure that the mix digest is zero as we don't have fork protection currently
|
||||
if header.MixDig() != (common.Hash{}) {
|
||||
return errInvalidMixDigest
|
||||
}
|
||||
// Ensure that the block doesn't contain any uncles which are meaningless in PoA
|
||||
if header.UncleHash != uncleHash {
|
||||
return errInvalidUncleHash
|
||||
}
|
||||
// Ensure that the block's difficulty is correct (it should be constant)
|
||||
if number > 0 {
|
||||
if header.Difficulty != chain.Config().Aura.Difficulty {
|
||||
return errInvalidDifficulty
|
||||
}
|
||||
}
|
||||
// If all checks passed, validate any special fields for hard forks
|
||||
if err := misc.VerifyForkHashes(chain.Config(), header, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// All basic checks passed, verify cascading fields
|
||||
return a.verifyCascadingFields(chain, header, parents)
|
||||
}
|
||||
|
||||
// verifyCascadingFields verifies all the header fields that are not standalone,
|
||||
// rather depend on a batch of previous headers. The caller may optionally pass
|
||||
// in a batch of parents (ascending order) to avoid looking those up from the
|
||||
// database. This is useful for concurrently verifying a batch of new headers.
|
||||
func (a *Aura) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
|
||||
// The genesis block is the always valid dead-end
|
||||
number := header.Number.Uint64()
|
||||
if number == 0 {
|
||||
return nil
|
||||
}
|
||||
// Ensure that the block's timestamp isn't too close to it's parent
|
||||
var parent *types.Header
|
||||
if len(parents) > 0 {
|
||||
parent = parents[len(parents)-1]
|
||||
} else {
|
||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
if parent.Time.Uint64()+a.config.Period > header.Time.Uint64() {
|
||||
return ErrInvalidTimestamp
|
||||
}
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
// snap, err := a.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// // If the block is a checkpoint block, verify the signer list
|
||||
// if number%a.config.Epoch == 0 {
|
||||
// signers := make([]byte, len(snap.Signers)*common.AddressLength)
|
||||
// for i, signer := range snap.signers() {
|
||||
// copy(signers[i*common.AddressLength:], signer[:])
|
||||
// }
|
||||
// extraSuffix := len(header.Extra) - extraSeal
|
||||
// if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
|
||||
// return errInvalidCheckpointSigners
|
||||
// }
|
||||
// }
|
||||
// All basic checks passed, verify the seal and return
|
||||
return a.verifySeal(chain, header, parents)
|
||||
}
|
||||
|
||||
// snapshot retrieves the authorization snapshot at a given point in time.
|
||||
func (a *Aura) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
|
||||
// Search for a snapshot in memory or on disk for checkpoints
|
||||
var (
|
||||
headers []*types.Header
|
||||
snap *Snapshot
|
||||
)
|
||||
for snap == nil {
|
||||
// If an in-memory snapshot was found, use that
|
||||
if s, ok := a.recents.Get(hash); ok {
|
||||
snap = s.(*Snapshot)
|
||||
break
|
||||
}
|
||||
// If an on-disk checkpoint snapshot can be found, use that
|
||||
if number%checkpointInterval == 0 {
|
||||
if s, err := loadSnapshot(a.config, a.signatures, a.db, hash); err == nil {
|
||||
log.Trace("Loaded voting snapshot from disk", "number", number, "hash", hash)
|
||||
snap = s
|
||||
break
|
||||
}
|
||||
}
|
||||
// If we're at an checkpoint block, make a snapshot if it's known
|
||||
if number%a.config.Epoch == 0 {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
hash := checkpoint.Hash()
|
||||
|
||||
signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength)
|
||||
for i := 0; i < len(signers); i++ {
|
||||
copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:])
|
||||
}
|
||||
snap = newSnapshot(a.config, a.signatures, number, hash, signers)
|
||||
if err := snap.store(a.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
|
||||
break
|
||||
}
|
||||
}
|
||||
// No snapshot for this header, gather the header and move backward
|
||||
var header *types.Header
|
||||
if len(parents) > 0 {
|
||||
// If we have explicit parents, pick from there (enforced)
|
||||
header = parents[len(parents)-1]
|
||||
if header.Hash() != hash || header.Number.Uint64() != number {
|
||||
return nil, consensus.ErrUnknownAncestor
|
||||
}
|
||||
parents = parents[:len(parents)-1]
|
||||
} else {
|
||||
// No explicit parents (or no more left), reach out to the database
|
||||
header = chain.GetHeader(hash, number)
|
||||
if header == nil {
|
||||
return nil, consensus.ErrUnknownAncestor
|
||||
}
|
||||
}
|
||||
headers = append(headers, header)
|
||||
number, hash = number-1, header.ParentHash
|
||||
}
|
||||
// Previous snapshot found, apply any pending headers on top of it
|
||||
for i := 0; i < len(headers)/2; i++ {
|
||||
headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i]
|
||||
}
|
||||
snap, err := snap.apply(headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.recents.Add(snap.Hash, snap)
|
||||
|
||||
// If we've generated a new checkpoint snapshot, save to disk
|
||||
if snap.Number%checkpointInterval == 0 && len(headers) > 0 {
|
||||
if err = snap.store(a.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Trace("Stored voting snapshot to disk", "number", snap.Number, "hash", snap.Hash)
|
||||
}
|
||||
return snap, err
|
||||
}
|
||||
|
||||
// VerifyUncles implements consensus.Engine, always returning an error for any
|
||||
// uncles as this consensus mechanism doesn't permit uncles.
|
||||
func (a *Aura) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
if len(block.Uncles()) > 0 {
|
||||
return errors.New("uncles not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySeal implements consensus.Engine, checking whether the signature contained
|
||||
// in the header satisfies the consensus protocol requirements.
|
||||
func (a *Aura) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
|
||||
return a.verifySeal(chain, header, nil)
|
||||
}
|
||||
|
||||
// verifySeal checks whether the signature contained in the header satisfies the
|
||||
// consensus protocol requirements. The method accepts an optional list of parent
|
||||
// headers that aren't yet part of the local blockchain to generate the snapshots
|
||||
// from.
|
||||
func (a *Aura) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
|
||||
// Verifying the genesis block is not supported
|
||||
number := header.Number.Uint64()
|
||||
if number == 0 {
|
||||
return errUnknownBlock
|
||||
}
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
// snap, err := a.snapshot(chain, number-1, header.ParentHash, parents)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, a.signatures)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ts := header.Time.Uint64()
|
||||
step := ts % a.config.Period
|
||||
turn := step % uint64(len(a.config.Authorities))
|
||||
if signer != a.config.Authorities[turn] {
|
||||
// not authorized to sign
|
||||
return errUnauthorized
|
||||
}
|
||||
// if _, ok := snap.Signers[signer]; !ok {
|
||||
// return errUnauthorized
|
||||
// }
|
||||
// for seen, recent := range snap.Recents {
|
||||
// if recent == signer {
|
||||
// // Signer is among recents, only fail if the current block doesn't shift it out
|
||||
// if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
|
||||
// return errUnauthorized
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
||||
//inturn := snap.inturn(header.Number.Uint64(), signer)
|
||||
//if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
|
||||
// return errInvalidDifficulty
|
||||
//}
|
||||
//if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
|
||||
// return errInvalidDifficulty
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||
// header for running the transactions on top.
|
||||
func (a *Aura) Prepare(chain consensus.ChainReader, header *types.Header) error {
|
||||
// If the block isn't a checkpoint, cast a random vote (good enough for now)
|
||||
header.Coinbase = common.Address{}
|
||||
header.Nonce = types.BlockNonce{}
|
||||
|
||||
// Set the correct difficulty
|
||||
header.Difficulty = chain.Config().Aura.Difficulty
|
||||
|
||||
// Ensure the extra data has all it's components
|
||||
if len(header.Extra) < extraVanity {
|
||||
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
|
||||
}
|
||||
//header.Extra = header.Extra[:extraVanity]
|
||||
|
||||
number := header.Number.Uint64()
|
||||
|
||||
if number%a.config.Epoch == 0 {
|
||||
//for _, signer := range snap.signers() {
|
||||
// header.Extra = append(header.Extra, signer[:]...)
|
||||
//}
|
||||
}
|
||||
header.Extra = append(header.Extra, make([]byte, extraSeal)...)
|
||||
|
||||
// Mix digest is reserved for now, set to empty
|
||||
header.MixDigest = common.Hash{}
|
||||
|
||||
// Ensure the timestamp has the correct delay
|
||||
parent := chain.GetHeader(header.ParentHash, number-1)
|
||||
if parent == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(a.config.Period))
|
||||
if header.Time.Int64() < time.Now().Unix() {
|
||||
header.Time = big.NewInt(time.Now().Unix())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
|
||||
// rewards given, and returns the final block.
|
||||
func (a *Aura) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
||||
state.AddBalance(a.signer, big.NewInt(5*(10^18)))
|
||||
// No block rewards in PoA, so the state remains as is and uncles are dropped
|
||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
header.UncleHash = types.CalcUncleHash(nil)
|
||||
|
||||
// Assemble and return the final block for sealing
|
||||
return types.NewBlock(header, txs, nil, receipts), nil
|
||||
}
|
||||
|
||||
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||
// with.
|
||||
func (a *Aura) Authorize(signer common.Address, signFn SignerFn) {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
a.signer = signer
|
||||
a.signFn = signFn
|
||||
}
|
||||
|
||||
// Seal implements consensus.Engine, attempting to create a sealed block using
|
||||
// the local signing credentials.
|
||||
func (a *Aura) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
||||
header := block.Header()
|
||||
|
||||
// Sealing the genesis block is not supported
|
||||
number := header.Number.Uint64()
|
||||
if number == 0 {
|
||||
return errUnknownBlock
|
||||
}
|
||||
// For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
|
||||
if a.config.Period == 0 && len(block.Transactions()) == 0 {
|
||||
return errWaitTransactions
|
||||
}
|
||||
// Don't hold the signer fields for the entire sealing procedure
|
||||
a.lock.RLock()
|
||||
signer, signFn := a.signer, a.signFn
|
||||
a.lock.RUnlock()
|
||||
|
||||
// check if authorized to sign
|
||||
step := uint64(time.Now().Unix()) % a.config.Period
|
||||
turn := step % uint64(len(a.config.Authorities))
|
||||
if a.signer != a.config.Authorities[turn] {
|
||||
// not authorized to sign
|
||||
return errUnauthorized
|
||||
}
|
||||
|
||||
// Bail out if we're unauthorized to sign a block
|
||||
// snap, err := a.snapshot(chain, number-1, header.ParentHash, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, authorized := snap.Signers[signer]; !authorized {
|
||||
// return errUnauthorized
|
||||
// }
|
||||
// // If we're amongst the recent signers, wait for the next block
|
||||
// for seen, recent := range snap.Recents {
|
||||
// if recent == signer {
|
||||
// // Signer is among recents, only wait if the current block doesn't shift it out
|
||||
// if limit := uint64(len(snap.Signers)/2 + 1); number < limit || seen > number-limit {
|
||||
// log.Info("Signed recently, must wait for others")
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Sweet, the protocol permits us to sign the block, wait for our time
|
||||
delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
|
||||
//if header.Difficulty.Cmp(diffNoTurn) == 0 {
|
||||
// // It's not our turn explicitly to sign, delay it a bit
|
||||
// wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
|
||||
// delay += time.Duration(rand.Int63n(int64(wiggle)))
|
||||
//
|
||||
// log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
|
||||
//}
|
||||
// Sign all the things!
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-extraSeal:], sighash)
|
||||
// Wait until sealing is terminated or delay timeout.
|
||||
log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay))
|
||||
go func() {
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case <-time.After(delay):
|
||||
}
|
||||
|
||||
select {
|
||||
case results <- block.WithSeal(header):
|
||||
default:
|
||||
log.Warn("Sealing result is not read by miner", "sealhash", a.SealHash(header))
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns difficulty constant from config
|
||||
func (a *Aura) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
|
||||
return chain.Config().Aura.Difficulty
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func (a *Aura) SealHash(header *types.Header) common.Hash {
|
||||
return sigHash(header)
|
||||
}
|
||||
|
||||
// Close implements consensus.Engine. It's a noop for clique as there is are no background threads.
|
||||
func (a *Aura) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// APIs implements consensus.Engine, returning the user facing RPC API to allow
|
||||
// controlling the signer voting.
|
||||
func (a *Aura) APIs(chain consensus.ChainReader) []rpc.API {
|
||||
return []rpc.API{{
|
||||
Namespace: "clique",
|
||||
Version: "1.0",
|
||||
Service: &API{chain: chain, aura: a},
|
||||
Public: false,
|
||||
}}
|
||||
}
|
||||
312
consensus/aura/snapshot.go
Normal file
312
consensus/aura/snapshot.go
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
// Copyright 2017 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package aura
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// Vote represents a single vote that an authorized signer made to modify the
|
||||
// list of authorizations.
|
||||
type Vote struct {
|
||||
Signer common.Address `json:"signer"` // Authorized signer that cast this vote
|
||||
Block uint64 `json:"block"` // Block number the vote was cast in (expire old votes)
|
||||
Address common.Address `json:"address"` // Account being voted on to change its authorization
|
||||
Authorize bool `json:"authorize"` // Whether to authorize or deauthorize the voted account
|
||||
}
|
||||
|
||||
// Tally is a simple vote tally to keep the current score of votes. Votes that
|
||||
// go against the proposal aren't counted since it's equivalent to not voting.
|
||||
type Tally struct {
|
||||
Authorize bool `json:"authorize"` // Whether the vote is about authorizing or kicking someone
|
||||
Votes int `json:"votes"` // Number of votes until now wanting to pass the proposal
|
||||
}
|
||||
|
||||
// Snapshot is the state of the authorization voting at a given point in time.
|
||||
type Snapshot struct {
|
||||
config *params.AuraConfig // Consensus engine parameters to fine tune behavior
|
||||
sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover
|
||||
|
||||
Number uint64 `json:"number"` // Block number where the snapshot was created
|
||||
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
|
||||
Signers map[common.Address]struct{} `json:"signers"` // Set of authorized signers at this moment
|
||||
Recents map[uint64]common.Address `json:"recents"` // Set of recent signers for spam protections
|
||||
Votes []*Vote `json:"votes"` // List of votes cast in chronological order
|
||||
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
|
||||
}
|
||||
|
||||
// signers implements the sort interface to allow sorting a list of addresses
|
||||
type signers []common.Address
|
||||
|
||||
func (s signers) Len() int { return len(s) }
|
||||
func (s signers) Less(i, j int) bool { return bytes.Compare(s[i][:], s[j][:]) < 0 }
|
||||
func (s signers) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// newSnapshot creates a new snapshot with the specified startup parameters. This
|
||||
// method does not initialize the set of recent signers, so only ever use if for
|
||||
// the genesis block.
|
||||
func newSnapshot(config *params.AuraConfig, sigcache *lru.ARCCache, number uint64, hash common.Hash, signers []common.Address) *Snapshot {
|
||||
snap := &Snapshot{
|
||||
config: config,
|
||||
sigcache: sigcache,
|
||||
Number: number,
|
||||
Hash: hash,
|
||||
Signers: make(map[common.Address]struct{}),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
for _, signer := range signers {
|
||||
snap.Signers[signer] = struct{}{}
|
||||
}
|
||||
return snap
|
||||
}
|
||||
|
||||
// loadSnapshot loads an existing snapshot from the database.
|
||||
func loadSnapshot(config *params.AuraConfig, sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) (*Snapshot, error) {
|
||||
blob, err := db.Get(append([]byte("clique-"), hash[:]...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snap := new(Snapshot)
|
||||
if err := json.Unmarshal(blob, snap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snap.config = config
|
||||
snap.sigcache = sigcache
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// store inserts the snapshot into the database.
|
||||
func (s *Snapshot) store(db ethdb.Database) error {
|
||||
blob, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Put(append([]byte("clique-"), s.Hash[:]...), blob)
|
||||
}
|
||||
|
||||
// copy creates a deep copy of the snapshot, though not the individual votes.
|
||||
func (s *Snapshot) copy() *Snapshot {
|
||||
cpy := &Snapshot{
|
||||
config: s.config,
|
||||
sigcache: s.sigcache,
|
||||
Number: s.Number,
|
||||
Hash: s.Hash,
|
||||
Signers: make(map[common.Address]struct{}),
|
||||
Recents: make(map[uint64]common.Address),
|
||||
Votes: make([]*Vote, len(s.Votes)),
|
||||
Tally: make(map[common.Address]Tally),
|
||||
}
|
||||
for signer := range s.Signers {
|
||||
cpy.Signers[signer] = struct{}{}
|
||||
}
|
||||
for block, signer := range s.Recents {
|
||||
cpy.Recents[block] = signer
|
||||
}
|
||||
for address, tally := range s.Tally {
|
||||
cpy.Tally[address] = tally
|
||||
}
|
||||
copy(cpy.Votes, s.Votes)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// validVote returns whether it makes sense to cast the specified vote in the
|
||||
// given snapshot context (e.g. don't try to add an already authorized signer).
|
||||
func (s *Snapshot) validVote(address common.Address, authorize bool) bool {
|
||||
_, signer := s.Signers[address]
|
||||
return (signer && !authorize) || (!signer && authorize)
|
||||
}
|
||||
|
||||
// cast adds a new vote into the tally.
|
||||
func (s *Snapshot) cast(address common.Address, authorize bool) bool {
|
||||
// Ensure the vote is meaningful
|
||||
if !s.validVote(address, authorize) {
|
||||
return false
|
||||
}
|
||||
// Cast the vote into an existing or new tally
|
||||
if old, ok := s.Tally[address]; ok {
|
||||
old.Votes++
|
||||
s.Tally[address] = old
|
||||
} else {
|
||||
s.Tally[address] = Tally{Authorize: authorize, Votes: 1}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// uncast removes a previously cast vote from the tally.
|
||||
func (s *Snapshot) uncast(address common.Address, authorize bool) bool {
|
||||
// If there's no tally, it's a dangling vote, just drop
|
||||
tally, ok := s.Tally[address]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Ensure we only revert counted votes
|
||||
if tally.Authorize != authorize {
|
||||
return false
|
||||
}
|
||||
// Otherwise revert the vote
|
||||
if tally.Votes > 1 {
|
||||
tally.Votes--
|
||||
s.Tally[address] = tally
|
||||
} else {
|
||||
delete(s.Tally, address)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// apply creates a new authorization snapshot by applying the given headers to
|
||||
// the original one.
|
||||
func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
|
||||
// Allow passing in no headers for cleaner code
|
||||
if len(headers) == 0 {
|
||||
return s, nil
|
||||
}
|
||||
// Sanity check that the headers can be applied
|
||||
for i := 0; i < len(headers)-1; i++ {
|
||||
if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
|
||||
return nil, errInvalidVotingChain
|
||||
}
|
||||
}
|
||||
if headers[0].Number.Uint64() != s.Number+1 {
|
||||
return nil, errInvalidVotingChain
|
||||
}
|
||||
// Iterate through the headers and create a new snapshot
|
||||
snap := s.copy()
|
||||
|
||||
for _, header := range headers {
|
||||
// Remove any votes on checkpoint blocks
|
||||
number := header.Number.Uint64()
|
||||
if number%s.config.Epoch == 0 {
|
||||
snap.Votes = nil
|
||||
snap.Tally = make(map[common.Address]Tally)
|
||||
}
|
||||
// Delete the oldest signer from the recent list to allow it signing again
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
|
||||
delete(snap.Recents, number-limit)
|
||||
}
|
||||
// Resolve the authorization key and check against signers
|
||||
signer, err := ecrecover(header, s.sigcache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := snap.Signers[signer]; !ok {
|
||||
return nil, errUnauthorized
|
||||
}
|
||||
for _, recent := range snap.Recents {
|
||||
if recent == signer {
|
||||
return nil, errUnauthorized
|
||||
}
|
||||
}
|
||||
snap.Recents[number] = signer
|
||||
|
||||
// Header authorized, discard any previous votes from the signer
|
||||
for i, vote := range snap.Votes {
|
||||
if vote.Signer == signer && vote.Address == header.Coinbase {
|
||||
// Uncast the vote from the cached tally
|
||||
snap.uncast(vote.Address, vote.Authorize)
|
||||
|
||||
// Uncast the vote from the chronological list
|
||||
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
break // only one vote allowed
|
||||
}
|
||||
}
|
||||
// Tally up the new vote from the signer
|
||||
//var authorize bool
|
||||
//switch {
|
||||
//case bytes.Equal(header.Nonce[:], nonceAuthVote):
|
||||
// authorize = true
|
||||
//case bytes.Equal(header.Nonce[:], nonceDropVote):
|
||||
// authorize = false
|
||||
//default:
|
||||
// return nil, errInvalidVote
|
||||
//}
|
||||
//if snap.cast(header.Coinbase, authorize) {
|
||||
// snap.Votes = append(snap.Votes, &Vote{
|
||||
// Signer: signer,
|
||||
// Block: number,
|
||||
// Address: header.Coinbase,
|
||||
// Authorize: authorize,
|
||||
// })
|
||||
//}
|
||||
// If the vote passed, update the list of signers
|
||||
if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
|
||||
if tally.Authorize {
|
||||
snap.Signers[header.Coinbase] = struct{}{}
|
||||
} else {
|
||||
delete(snap.Signers, header.Coinbase)
|
||||
|
||||
// Signer list shrunk, delete any leftover recent caches
|
||||
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
|
||||
delete(snap.Recents, number-limit)
|
||||
}
|
||||
// Discard any previous votes the deauthorized signer cast
|
||||
for i := 0; i < len(snap.Votes); i++ {
|
||||
if snap.Votes[i].Signer == header.Coinbase {
|
||||
// Uncast the vote from the cached tally
|
||||
snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)
|
||||
|
||||
// Uncast the vote from the chronological list
|
||||
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
// Discard any previous votes around the just changed account
|
||||
for i := 0; i < len(snap.Votes); i++ {
|
||||
if snap.Votes[i].Address == header.Coinbase {
|
||||
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
delete(snap.Tally, header.Coinbase)
|
||||
}
|
||||
}
|
||||
snap.Number += uint64(len(headers))
|
||||
snap.Hash = headers[len(headers)-1].Hash()
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
// signers retrieves the list of authorized signers in ascending order.
|
||||
func (s *Snapshot) signers() []common.Address {
|
||||
sigs := make([]common.Address, 0, len(s.Signers))
|
||||
for sig := range s.Signers {
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
sort.Sort(signers(sigs))
|
||||
return sigs
|
||||
}
|
||||
|
||||
// inturn returns if a signer at a given block height is in-turn or not.
|
||||
func (s *Snapshot) inturn(number uint64, signer common.Address) bool {
|
||||
signers, offset := s.signers(), 0
|
||||
for offset < len(signers) && signers[offset] != signer {
|
||||
offset++
|
||||
}
|
||||
return (number % uint64(len(signers))) == uint64(offset)
|
||||
}
|
||||
|
|
@ -234,6 +234,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
|||
}
|
||||
}
|
||||
root := statedb.IntermediateRoot(false)
|
||||
|
||||
head := &types.Header{
|
||||
Number: new(big.Int).SetUint64(g.Number),
|
||||
Nonce: types.EncodeNonce(g.Nonce),
|
||||
|
|
@ -247,12 +248,21 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
|||
Coinbase: g.Coinbase,
|
||||
Root: root,
|
||||
}
|
||||
|
||||
if g.Config.Aura != nil {
|
||||
for _, signature := range g.Config.Aura.Signatures {
|
||||
head.Signatures = append(head.Signatures, signature)
|
||||
}
|
||||
}
|
||||
if g.GasLimit == 0 {
|
||||
head.GasLimit = params.GenesisGasLimit
|
||||
}
|
||||
if g.Difficulty == nil {
|
||||
head.Difficulty = params.GenesisDifficulty
|
||||
}
|
||||
data, _ := rlp.EncodeToBytes(head)
|
||||
fmt.Printf("Genesis header rlp %v\n", common.Bytes2Hex(data))
|
||||
|
||||
statedb.Commit(false)
|
||||
statedb.Database().TrieDB().Commit(root, true)
|
||||
|
||||
|
|
@ -333,6 +343,19 @@ func DefaultRinkebyGenesisBlock() *Genesis {
|
|||
}
|
||||
}
|
||||
|
||||
//Need alloc data
|
||||
// DefaultGoerliGenesisBlock returns the Goerli network genesis block.
|
||||
func DefaultGoerliGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.GoerliChainConfig,
|
||||
Timestamp: 0,
|
||||
ExtraData: hexutil.MustDecode("0x"),
|
||||
GasLimit: 6000000,
|
||||
Difficulty: big.NewInt(131072),
|
||||
Alloc: decodePrealloc(goerliAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
|
||||
// be seeded with the
|
||||
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -68,6 +69,26 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
|
|||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
type Header struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner" gencodec:"required"`
|
||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
||||
Number *big.Int `json:"number" gencodec:"required"`
|
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *big.Int `json:"timestamp" gencodec:"required"`
|
||||
Extra []byte `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
||||
Signatures params.Signatures `json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
// AuraHeader represents a block header in the Aura blockchain.
|
||||
type AuraHeader struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner" gencodec:"required"`
|
||||
|
|
@ -81,8 +102,12 @@ type Header struct {
|
|||
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *big.Int `json:"timestamp" gencodec:"required"`
|
||||
Extra []byte `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
||||
Signature1 []byte `json:"signature1,omitempty"`
|
||||
Signature2 []byte `json:"signature2,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Header) MixDig() common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
|
|
@ -96,10 +121,108 @@ type headerMarshaling struct {
|
|||
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
|
||||
}
|
||||
|
||||
// DecodeRLP decodes the Ethereum
|
||||
func (h *Header) DecodeRLP(s *rlp.Stream) error {
|
||||
|
||||
var aura AuraHeader
|
||||
|
||||
// Try decoding as aura header first
|
||||
if err := s.Decode(&aura); err != nil {
|
||||
return err
|
||||
//return s.Decode(h)
|
||||
}
|
||||
//Aura decoded fine, now convert to header
|
||||
h.ParentHash = aura.ParentHash
|
||||
h.UncleHash = aura.UncleHash
|
||||
h.Coinbase = aura.Coinbase
|
||||
h.Root = aura.Root
|
||||
h.TxHash = aura.TxHash
|
||||
h.ReceiptHash = aura.ReceiptHash
|
||||
h.Bloom = aura.Bloom
|
||||
h.Difficulty = aura.Difficulty
|
||||
h.Number = aura.Number
|
||||
h.GasLimit = aura.GasLimit
|
||||
h.GasUsed = aura.GasUsed
|
||||
h.Time = aura.Time
|
||||
h.Extra = aura.Extra
|
||||
h.MixDigest = common.Hash{}
|
||||
h.Nonce = BlockNonce{}
|
||||
h.Signatures = append(h.Signatures, aura.Signature1)
|
||||
h.Signatures = append(h.Signatures, aura.Signature2)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeRLP serializes b into the Ethereum RLP block format.
|
||||
func (header *Header) EncodeRLP(w io.Writer) error {
|
||||
if false && header.Signatures != nil {
|
||||
return rlp.Encode(w, []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra,
|
||||
// Yes, this is butt-ugly
|
||||
header.Signatures[0],
|
||||
header.Signatures[1],
|
||||
})
|
||||
} else {
|
||||
return rlp.Encode(w, []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra, // Yes, this will panic if extra is too short
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
|
||||
// RLP encoding.
|
||||
func (h *Header) Hash() common.Hash {
|
||||
return rlpHash(h)
|
||||
func (header *Header) Hash() (h common.Hash) {
|
||||
//if header.Signatures == nil{
|
||||
// return rlpHash(h)
|
||||
//}
|
||||
hw := sha3.NewKeccak256()
|
||||
|
||||
rlp.Encode(hw, []interface{}{
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra,
|
||||
// Yes, this is butt-ugly
|
||||
//header.Signatures[0],
|
||||
//header.Signatures[1],
|
||||
})
|
||||
hw.Sum(h[:0])
|
||||
return h
|
||||
|
||||
}
|
||||
|
||||
// Size returns the approximate memory used by all internal contents. It is used
|
||||
|
|
@ -288,9 +411,11 @@ func (b *Block) GasUsed() uint64 { return b.header.GasUsed }
|
|||
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
|
||||
func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) }
|
||||
|
||||
func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
|
||||
func (b *Block) MixDigest() common.Hash { return b.header.MixDigest }
|
||||
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
|
||||
func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
|
||||
func (b *Block) MixDigest() common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
func (b *Block) Nonce() uint64 { return 0 }
|
||||
func (b *Block) Bloom() Bloom { return b.header.Bloom }
|
||||
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
|
||||
func (b *Block) Root() common.Hash { return b.header.Root }
|
||||
|
|
|
|||
|
|
@ -9,28 +9,29 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var _ = (*headerMarshaling)(nil)
|
||||
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (h Header) MarshalJSON() ([]byte, error) {
|
||||
type Header struct {
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner" gencodec:"required"`
|
||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
|
||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce BlockNonce `json:"nonce" gencodec:"required"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase common.Address `json:"miner" gencodec:"required"`
|
||||
Root common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
|
||||
Extra hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
Signatures params.Signatures `json:"signature,omitempty"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
}
|
||||
var enc Header
|
||||
enc.ParentHash = h.ParentHash
|
||||
|
|
@ -46,29 +47,28 @@ func (h Header) MarshalJSON() ([]byte, error) {
|
|||
enc.GasUsed = hexutil.Uint64(h.GasUsed)
|
||||
enc.Time = (*hexutil.Big)(h.Time)
|
||||
enc.Extra = h.Extra
|
||||
enc.MixDigest = h.MixDigest
|
||||
enc.Nonce = h.Nonce
|
||||
enc.Signatures = h.Signatures
|
||||
enc.Hash = h.Hash()
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||
type Header struct {
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase *common.Address `json:"miner" gencodec:"required"`
|
||||
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
|
||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
MixDigest *common.Hash `json:"mixHash" gencodec:"required"`
|
||||
Nonce *BlockNonce `json:"nonce" gencodec:"required"`
|
||||
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"`
|
||||
Coinbase *common.Address `json:"miner" gencodec:"required"`
|
||||
Root *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||
Bloom *Bloom `json:"logsBloom" gencodec:"required"`
|
||||
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"`
|
||||
Number *hexutil.Big `json:"number" gencodec:"required"`
|
||||
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||
Time *hexutil.Big `json:"timestamp" gencodec:"required"`
|
||||
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||
Signatures *params.Signatures `json:"signature,omitempty"`
|
||||
}
|
||||
var dec Header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -126,13 +126,8 @@ func (h *Header) UnmarshalJSON(input []byte) error {
|
|||
return errors.New("missing required field 'extraData' for Header")
|
||||
}
|
||||
h.Extra = *dec.Extra
|
||||
if dec.MixDigest == nil {
|
||||
return errors.New("missing required field 'mixHash' for Header")
|
||||
if dec.Signatures != nil {
|
||||
h.Signatures = *dec.Signatures
|
||||
}
|
||||
h.MixDigest = *dec.MixDigest
|
||||
if dec.Nonce == nil {
|
||||
return errors.New("missing required field 'nonce' for Header")
|
||||
}
|
||||
h.Nonce = *dec.Nonce
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/aura"
|
||||
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
|
@ -220,6 +221,8 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo
|
|||
// If proof-of-authority is requested, set it up
|
||||
if chainConfig.Clique != nil {
|
||||
return clique.New(chainConfig.Clique, db)
|
||||
} else if chainConfig.Aura != nil {
|
||||
return aura.New(chainConfig.Aura, db)
|
||||
}
|
||||
// Otherwise assume proof-of-work
|
||||
switch config.PowMode {
|
||||
|
|
@ -375,6 +378,15 @@ func (s *Ethereum) StartMining(threads int) error {
|
|||
return fmt.Errorf("signer missing: %v", err)
|
||||
}
|
||||
clique.Authorize(eb, wallet.SignHash)
|
||||
} else {
|
||||
if aura, ok := s.engine.(*aura.Aura); ok {
|
||||
wallet, e := s.accountManager.Find(accounts.Account{Address: eb})
|
||||
if wallet == nil || e != nil {
|
||||
log.Error("Etherbase account unavailable locally", "err", err)
|
||||
return fmt.Errorf("signer missing: %v", err)
|
||||
}
|
||||
aura.Authorize(eb, wallet.SignHash)
|
||||
}
|
||||
}
|
||||
// If mining is started, we can disable the transaction rejection mechanism
|
||||
// introduced to speed sync times.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,15 @@ func RinkebyGenesis() string {
|
|||
return string(enc)
|
||||
}
|
||||
|
||||
// GoerliGenesis returns the JSON spec to use for the Goerli test network
|
||||
func GoerliGenesis() string {
|
||||
enc, err := json.Marshal(core.DefaultGoerliGenesisBlock())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated
|
||||
// by the foundation running the V5 discovery protocol.
|
||||
func FoundationBootnodes() *Enodes {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,14 @@ var RinkebyBootnodes = []string{
|
|||
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303", // AKASHA
|
||||
}
|
||||
|
||||
// Goerli testnet BootNodes
|
||||
var GoerliBootnodes = []string{
|
||||
"enode://ffc0d1bcc54dee616617be06edf2b3abdd200beb3765d4475ae9fb1750d98fb0d753b30e691912bb6775b7d442b71072c91becab2141191ce73de5b6d57ac723@40.114.122.81:30303",
|
||||
"enode://5b360e4baabf9e89e42e9241d4d00e7ff7a804759049be7dde30980ccce445f1fb0f91a4b9a4a28ee371b18d679fcced3cac3d73654059dd69c4a7b772bd4abf@40.87.8.76:30303",
|
||||
"enode://b95468b5fa80293c4f2fce2df7a02a52f0b259063d99a78a571afcb69f736ead79ca73412b37cb2336da75066f02439d2e50e22dedc1b78380a6ab36ebdda3fc@40.87.4.17:30303",
|
||||
"enode://3969e0879235c0b2bcc8957fc6d62d53763fcff0e750788eae029d40f1b4fa69f4d5a952ea3d02f85eb558faaae6bd5fbd1f394867edc6728c9fe7b9959329b0@40.87.1.35:30303",
|
||||
}
|
||||
|
||||
// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
|
||||
// experimental RLPx v5 topic-discovery network.
|
||||
var DiscoveryV5Bootnodes = []string{
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ var (
|
|||
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||
TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
|
||||
GoerliGenesisHash = common.HexToHash("0x4a982649dec9992d0c83d195a81670bfcbe769436a900bab113371a25d7ad4ab")
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -79,21 +80,49 @@ var (
|
|||
},
|
||||
}
|
||||
|
||||
// GoerliChainConfig contains the chain parameters to run a node on the Goerli test network.
|
||||
GoerliChainConfig = &ChainConfig{
|
||||
ChainID: big.NewInt(6382),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: nil,
|
||||
EIP150Hash: common.HexToHash("0X0000000000000000000000000000000000000000000000000000000000000000"),
|
||||
EIP155Block: big.NewInt(0),
|
||||
EIP158Block: big.NewInt(0),
|
||||
ByzantiumBlock: big.NewInt(0),
|
||||
ConstantinopleBlock: nil,
|
||||
Aura: &AuraConfig{
|
||||
Authorities: []common.Address{
|
||||
common.HexToAddress("0x0082a7bf6aaadab094061747872243059c3c6a07"),
|
||||
common.HexToAddress("0x00faa37564140c1a5e96095f05466b9f73441e44"),
|
||||
},
|
||||
Difficulty: big.NewInt(131072),
|
||||
},
|
||||
}
|
||||
|
||||
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Ethash consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
||||
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||
|
||||
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Clique consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
|
||||
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil}
|
||||
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
|
||||
// AllAuraProtocolChanges contains every protocol change (EIPs) introduced
|
||||
// and accepted by the Ethereum core developers into the Aura consensus.
|
||||
//
|
||||
// This configuration is intentionally not using keyed fields to force anyone
|
||||
// adding flags to the config to also have to set these fields.
|
||||
AllAuraProtocolChanges = &ChainConfig{big.NewInt(5), big.NewInt(2), nil, false, big.NewInt(2), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &AuraConfig{Period: 15, Epoch: 30000, Authorities: []common.Address{common.HexToAddress("0x540a9fe3d2381016dec8ffba7235c6fb00b0f942")}, Difficulty: big.NewInt(131072)}}
|
||||
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
)
|
||||
|
||||
|
|
@ -123,6 +152,7 @@ type ChainConfig struct {
|
|||
// Various consensus engines
|
||||
Ethash *EthashConfig `json:"ethash,omitempty"`
|
||||
Clique *CliqueConfig `json:"clique,omitempty"`
|
||||
Aura *AuraConfig `json:"aura,omitempty"`
|
||||
}
|
||||
|
||||
// EthashConfig is the consensus engine configs for proof-of-work based sealing.
|
||||
|
|
@ -139,11 +169,28 @@ type CliqueConfig struct {
|
|||
Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint
|
||||
}
|
||||
|
||||
type Signature []byte
|
||||
type Signatures []Signature
|
||||
|
||||
// AuraConfig is the consensus engine configs for proof-of-authority based sealing.
|
||||
type AuraConfig struct {
|
||||
Period uint64 `json:"period"` // Number of seconds between blocks to enforce
|
||||
Epoch uint64 `json:"epoch"` // Epoch length to reset votes and checkpoint
|
||||
Authorities []common.Address `json:"authorities"` // list of addresses of authorities
|
||||
Difficulty *big.Int `json:"difficulty"` // Constant block difficulty
|
||||
Signatures Signatures `json:"signatures"`
|
||||
}
|
||||
|
||||
// String implements the stringer interface, returning the consensus engine details.
|
||||
func (c *CliqueConfig) String() string {
|
||||
return "clique"
|
||||
}
|
||||
|
||||
// String implements the stringer interface, returning the consensus engine details.
|
||||
func (c *AuraConfig) String() string {
|
||||
return "aura"
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface.
|
||||
func (c *ChainConfig) String() string {
|
||||
var engine interface{}
|
||||
|
|
@ -152,6 +199,8 @@ func (c *ChainConfig) String() string {
|
|||
engine = c.Ethash
|
||||
case c.Clique != nil:
|
||||
engine = c.Clique
|
||||
case c.Aura != nil:
|
||||
engine = c.Aura
|
||||
default:
|
||||
engine = "unknown"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue