Implement block signing

This commit is contained in:
Luke Williams 2016-06-03 15:03:09 +10:00
parent 1a6fbacb64
commit b2d29f9e9a
7 changed files with 109 additions and 32 deletions

View file

@ -234,6 +234,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.GpobaseCorrectionFactorFlag, utils.GpobaseCorrectionFactorFlag,
utils.ExtraDataFlag, utils.ExtraDataFlag,
utils.FixedDifficultyFlag, utils.FixedDifficultyFlag,
utils.MinerPassphraseFlag,
} }
app.Flags = append(app.Flags, debug.Flags...) app.Flags = append(app.Flags, debug.Flags...)

View file

@ -21,21 +21,21 @@ import "github.com/ethereum/go-ethereum/p2p/discover"
// FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on // FrontierBootNodes are the enode URLs of the P2P bootstrap nodes running on
// the Frontier network. // the Frontier network.
var FrontierBootNodes = []*discover.Node{ var FrontierBootNodes = []*discover.Node{
// ETH/DEV Go Bootnodes // ETH/DEV Go Bootnodes
discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE // discover.MustParseNode("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303"), // IE
discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR // discover.MustParseNode("enode://de471bccee3d042261d52e9bff31458daecc406142b401d4cd848f677479f73104b9fdeb090af9583d3391b7f10cb2ba9e26865dd5fca4fcdc0fb1e3b723c786@54.94.239.50:30303"), // BR
discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG // discover.MustParseNode("enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303"), // SG
//
// ETH/DEV Cpp Bootnodes // // ETH/DEV Cpp Bootnodes
discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"), // discover.MustParseNode("enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303"),
} }
// TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the // TestNetBootNodes are the enode URLs of the P2P bootstrap nodes running on the
// Morden test network. // Morden test network.
var TestNetBootNodes = []*discover.Node{ var TestNetBootNodes = []*discover.Node{
// ETH/DEV Go Bootnodes // ETH/DEV Go Bootnodes
discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"), // discover.MustParseNode("enode://e4533109cc9bd7604e4ff6c095f7a1d807e15b38e9bfeb05d3b7c423ba86af0a9e89abbf40bd9dde4250fef114cd09270fa4e224cbeef8b7bf05a51e8260d6b8@94.242.229.4:40404"),
discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"), // discover.MustParseNode("enode://8c336ee6f03e99613ad21274f269479bf4413fb294d697ef15ab897598afb931f56beb8e97af530aee20ce2bcba5776f4a312bc168545de4d43736992c814592@94.242.229.203:30303"),
// ETH/DEV Cpp Bootnodes // ETH/DEV Cpp Bootnodes
} }

View file

@ -397,6 +397,11 @@ var (
Usage: "Specify a fixed difficulty.", Usage: "Specify a fixed difficulty.",
Value: -1, Value: -1,
} }
MinerPassphraseFlag = cli.StringFlag{
Name: "minerpass",
Usage: "Passphrase used to unlock key for signing blocks.",
Value: "",
}
) )
// MustMakeDataDir retrieves the currently requested data directory, terminating // MustMakeDataDir retrieves the currently requested data directory, terminating
@ -717,6 +722,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name), GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name),
SolcPath: ctx.GlobalString(SolcPathFlag.Name), SolcPath: ctx.GlobalString(SolcPathFlag.Name),
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name), AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
MinerPassphrase: ctx.GlobalString(MinerPassphraseFlag.Name),
} }
// Configure the Whisper service // Configure the Whisper service
shhEnable := ctx.GlobalBool(WhisperEnabledFlag.Name) shhEnable := ctx.GlobalBool(WhisperEnabledFlag.Name)

View file

@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow" "github.com/ethereum/go-ethereum/pow"
@ -35,7 +36,6 @@ var (
ExpDiffPeriod = big.NewInt(100000) ExpDiffPeriod = big.NewInt(100000)
big10 = big.NewInt(10) big10 = big.NewInt(10)
bigMinus99 = big.NewInt(-99) bigMinus99 = big.NewInt(-99)
needExtraData = []byte("hello!")
) )
// BlockValidator is responsible for validating block headers, uncles and // BlockValidator is responsible for validating block headers, uncles and
@ -199,12 +199,47 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
if v.bc.HasHeader(header.Hash()) { if v.bc.HasHeader(header.Hash()) {
return nil return nil
} }
if !bytes.Equal(parent.Extra, needExtraData) {
return ValidationError("Invalid extraData field in block header!")
}
return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false) return ValidateHeader(v.config, v.Pow, header, parent, checkPow, false)
} }
func ValidateHeaderSignature(header, parent *types.Header) error {
if header.Coinbase != parent.Coinbase {
return ValidationError("Block signature validation error: coinbase does not match parent.")
}
validator := parent.Coinbase
hash := HashOfHashes(header)
sig := header.Extra
if sig == nil || len(sig) == 0 {
return ValidationError("Rejecting unsigned block")
}
pub, err := crypto.SigToPub(hash.Bytes(), sig)
if err != nil {
return ValidationError(fmt.Sprintf("Block signature validation error: %s", err))
}
addr := crypto.PubkeyToAddress(*pub)
if addr != validator {
return ValidationError("Block signature check failed.")
}
return nil
}
func HashOfHashes(header *types.Header) common.Hash {
hashes := [][]byte{
header.ParentHash.Bytes(),
header.UncleHash.Bytes(),
header.Root.Bytes(),
header.TxHash.Bytes(),
header.ReceiptHash.Bytes(),
}
hash := crypto.Keccak256Hash(bytes.Join(hashes, []byte("")))
return hash
}
// Validates a header. Returns an error if the header is invalid. // Validates a header. Returns an error if the header is invalid.
// //
// See YP section 4.3.4. "Block Header Validity" // See YP section 4.3.4. "Block Header Validity"
@ -213,6 +248,11 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
} }
sigResult := ValidateHeaderSignature(header, parent)
if sigResult != nil {
return sigResult
}
if uncle { if uncle {
if header.Time.Cmp(common.MaxBig) == 1 { if header.Time.Cmp(common.MaxBig) == 1 {
return BlockTSTooBigErr return BlockTSTooBigErr

View file

@ -75,4 +75,5 @@ type Backend interface {
ChainDb() ethdb.Database ChainDb() ethdb.Database
DappDb() ethdb.Database DappDb() ethdb.Database
EventMux() *event.TypeMux EventMux() *event.TypeMux
MinerPassphrase() string
} }

View file

@ -102,6 +102,7 @@ type Config struct {
TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!)
FixedDifficulty int FixedDifficulty int
MinerPassphrase string
} }
type Ethereum struct { type Ethereum struct {
@ -146,6 +147,7 @@ type Ethereum struct {
netRPCService *PublicNetAPI netRPCService *PublicNetAPI
fixedDifficulty int fixedDifficulty int
minerPassphrase string
} }
func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
@ -222,6 +224,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
GpobaseStepUp: config.GpobaseStepUp, GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
httpclient: httpclient.New(config.DocRoot), httpclient: httpclient.New(config.DocRoot),
minerPassphrase: config.MinerPassphrase,
} }
switch { switch {
case config.PowTest: case config.PowTest:
@ -385,6 +388,7 @@ func (self *Ethereum) SetEtherbase(etherbase common.Address) {
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }
func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner } func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) MinerPassphrase() string { return s.minerPassphrase }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }

View file

@ -548,7 +548,16 @@ func (self *worker) commitNewWork() {
} }
// create the new block whose nonce will be mined. // create the new block whose nonce will be mined.
work.Block = types.NewBlock(header, work.txs, uncles, work.receipts) newBlock := types.NewBlock(header, work.txs, uncles, work.receipts)
sig, signError := self.signBlock(newBlock)
if signError != nil {
glog.V(logger.Info).Infoln(fmt.Sprintf("Error signing block: %s.", signError))
return
}
newHeader := newBlock.Header()
newHeader.Extra = sig
signedBlock := types.NewBlock(newHeader, work.txs, uncles, work.receipts)
work.Block = signedBlock
// We only care about logging if we're actually mining. // We only care about logging if we're actually mining.
if atomic.LoadInt32(&self.mining) == 1 { if atomic.LoadInt32(&self.mining) == 1 {
@ -558,6 +567,22 @@ func (self *worker) commitNewWork() {
self.push(work) self.push(work)
} }
func (self *worker) signBlock(block *types.Block) ([]byte, error) {
addr := block.Coinbase()
header := block.Header()
hash := core.HashOfHashes(header)
acct := accounts.Account{Address: addr}
unlockErr := self.eth.AccountManager().Unlock(acct, self.eth.MinerPassphrase())
if unlockErr != nil {
return nil, unlockErr
}
sig, err := self.eth.AccountManager().Sign(addr, hash.Bytes())
if err != nil {
return nil, err
}
return sig, nil
}
func (self *worker) commitUncle(work *Work, uncle *types.Header) error { func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
hash := uncle.Hash() hash := uncle.Hash()
if work.uncles.Has(hash) { if work.uncles.Has(hash) {