diff --git a/raft/backend.go b/raft/backend.go index a9e328fa1..dc3bd0bac 100644 --- a/raft/backend.go +++ b/raft/backend.go @@ -3,6 +3,7 @@ package raft import ( "sync" "time" + "crypto/ecdsa" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/core" @@ -32,6 +33,7 @@ type RaftService struct { // we need an event mux to instantiate the blockchain eventMux *event.TypeMux minter *minter + nodeKey *ecdsa.PrivateKey } func New(ctx *node.ServiceContext, chainConfig *params.ChainConfig, raftId, raftPort uint16, joinExisting bool, blockTime time.Duration, e *eth.Ethereum, startPeers []*discover.Node, datadir string) (*RaftService, error) { @@ -43,6 +45,7 @@ func New(ctx *node.ServiceContext, chainConfig *params.ChainConfig, raftId, raft accountManager: e.AccountManager(), downloader: e.Downloader(), startPeers: startPeers, + nodeKey: ctx.NodeKey(), } service.minter = newMinter(chainConfig, service, blockTime) diff --git a/raft/minter.go b/raft/minter.go index 5ece9e389..7bb7c2b2a 100644 --- a/raft/minter.go +++ b/raft/minter.go @@ -25,16 +25,22 @@ import ( "github.com/eapache/channels" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" +) + +var ( + extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for arbitrary signer vanity ) // Current state information for building the next block @@ -50,7 +56,7 @@ type minter struct { config *params.ChainConfig mu sync.Mutex mux *event.TypeMux - eth miner.Backend + eth *RaftService chain *core.BlockChain chainDb ethdb.Database coinbase common.Address @@ -66,6 +72,11 @@ type minter struct { txPreSub event.Subscription } +type extraSeal struct { + RaftId []byte // RaftID of the block minter + Signature []byte // Signature of the block minter +} + func newMinter(config *params.ChainConfig, eth *RaftService, blockTime time.Duration) *minter { minter := &minter{ config: config, @@ -318,8 +329,6 @@ func (minter *minter) mintNewBlock() { ethash.AccumulateRewards(minter.chain.Config(), work.publicState, header, nil) header.Root = work.publicState.IntermediateRoot(minter.chain.Config().IsEIP158(work.header.Number)) - // NOTE: < QuorumChain creates a signature here and puts it in header.Extra. > - allReceipts := append(publicReceipts, privateReceipts...) header.Bloom = types.CreateBloom(allReceipts) @@ -330,6 +339,14 @@ func (minter *minter) mintNewBlock() { l.BlockHash = headerHash } + //Sign the block and build the extraSeal struct + extraSealBytes := minter.buildExtraSeal(headerHash) + + // add vanity and seal to header + // NOTE: leaving vanity blank for now as a space for any future data + header.Extra = make([]byte, extraVanity+len(extraSealBytes)) + copy(header.Extra[extraVanity:], extraSealBytes) + block := types.NewBlock(header, committedTxes, nil, publicReceipts) log.Info("Generated next block", "block num", block.Number(), "num txes", txCount) @@ -407,3 +424,29 @@ func (env *work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g return publicReceipt, privateReceipt, nil } + +func (minter *minter) buildExtraSeal(headerHash common.Hash) []byte { + //Sign the headerHash + nodeKey := minter.eth.nodeKey + sig, err := crypto.Sign(headerHash.Bytes(), nodeKey) + if err != nil { + log.Warn("Block sealing failed", "err", err) + } + + //build the extraSeal struct + raftIdString := hexutil.EncodeUint64(uint64(minter.eth.raftProtocolManager.raftId)) + + var extra extraSeal + extra = extraSeal{ + RaftId: []byte(raftIdString[2:]), //remove the 0x prefix + Signature: sig, + } + + //encode to byte array for storage + extraDataBytes, err := rlp.EncodeToBytes(extra) + if err != nil { + log.Warn("Header.Extra Data Encoding failed", "err", err) + } + + return extraDataBytes +} diff --git a/raft/minter_test.go b/raft/minter_test.go new file mode 100644 index 000000000..dd60d4746 --- /dev/null +++ b/raft/minter_test.go @@ -0,0 +1,69 @@ +package raft + +import ( + "testing" + "math/big" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +func TestSignHeader(t *testing.T){ + //create only what we need to test the seal + var testRaftId uint16 = 5 + config := &node.Config{Name: "unit-test", DataDir: ""} + + nodeKey := config.NodeKey() + + raftProtocolManager := &ProtocolManager{raftId:testRaftId} + raftService := &RaftService{nodeKey: nodeKey, raftProtocolManager: raftProtocolManager} + minter := minter{eth: raftService,} + + //create some fake header to sign + fakeParentHash := common.HexToHash("0xc2c1dc1be8054808c69e06137429899d") + + header := &types.Header{ + ParentHash: fakeParentHash, + Number: big.NewInt(1), + Difficulty: big.NewInt(1), + GasLimit: new(big.Int), + GasUsed: new(big.Int), + Coinbase: minter.coinbase, + Time: big.NewInt(time.Now().UnixNano()), + } + + headerHash := header.Hash() + extraDataBytes := minter.buildExtraSeal(headerHash) + var seal *extraSeal + err := rlp.DecodeBytes(extraDataBytes[:], &seal) + if err != nil { + t.Fatalf("Unable to decode seal: %s", err.Error()) + } + + // Check raftId + sealRaftId, err := hexutil.DecodeUint64("0x"+ string(seal.RaftId)) //add the 0x prefix + if err != nil { + t.Errorf("Unable to get RaftId: %s", err.Error()) + } + if sealRaftId != uint64(testRaftId) { + t.Errorf("RaftID does not match. Expected: %d, Actual: %d", testRaftId, sealRaftId) + } + + //Identify who signed it + sig:= seal.Signature + pubKey, err := crypto.SigToPub(headerHash.Bytes(), sig) + if err != nil { + t.Fatalf("Unable to get public key from signature: %s", err.Error()) + } + + //Compare derived public key to original public key + if pubKey.X.Cmp(nodeKey.X) != 0 { + t.Errorf("Signature incorrect!") + } + +}