Skip to content

Commit bbc8e72

Browse files
authored
Merge pull request #270 from getamis/feature/update_istanbul
consensus, core, eth, miner, params: update istanbul engine
2 parents 362ecfe + 9ac24f9 commit bbc8e72

38 files changed

+495
-321
lines changed

consensus/consensus.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ type Engine interface {
9999
// Handler should be implemented is the consensus needs to handle and send peer's message
100100
type Handler interface {
101101
// NewChainHead handles a new head block comes
102-
NewChainHead(block *types.Block) error
102+
NewChainHead() error
103103

104104
// HandleMsg handles a message from peer
105105
HandleMsg(address common.Address, data p2p.Msg) (bool, error)
@@ -121,7 +121,7 @@ type Istanbul interface {
121121
Engine
122122

123123
// Start starts the engine
124-
Start(chain ChainReader, inserter func(types.Blocks) (int, error)) error
124+
Start(chain ChainReader, currentBlock func() *types.Block, hasBadBlock func(hash common.Hash) bool) error
125125

126126
// Stop stops the engine
127127
Stop() error

consensus/istanbul/backend.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package istanbul
1818

1919
import (
20+
"math/big"
2021
"time"
2122

2223
"github.com/ethereum/go-ethereum/common"
@@ -57,4 +58,16 @@ type Backend interface {
5758

5859
// LastProposal retrieves latest committed proposal and the address of proposer
5960
LastProposal() (Proposal, common.Address)
61+
62+
// HasPropsal checks if the combination of the given hash and height matches any existing blocks
63+
HasPropsal(hash common.Hash, number *big.Int) bool
64+
65+
// GetProposer returns the proposer of the given block height
66+
GetProposer(number uint64) common.Address
67+
68+
// ParentValidators returns the validator set of the given proposal's parent block
69+
ParentValidators(proposal Proposal) ValidatorSet
70+
71+
// HasBadBlock returns whether the block with the hash is a bad block
72+
HasBadProposal(hash common.Hash) bool
6073
}

consensus/istanbul/backend/backend.go

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package backend
1818

1919
import (
2020
"crypto/ecdsa"
21+
"math/big"
2122
"sync"
2223
"time"
2324

@@ -35,6 +36,11 @@ import (
3536
lru "github.com/hashicorp/golang-lru"
3637
)
3738

39+
const (
40+
// fetcherID is the ID indicates the block is from Istanbul engine
41+
fetcherID = "istanbul"
42+
)
43+
3844
// New creates an Ethereum backend for Istanbul core engine.
3945
func New(config *istanbul.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database) consensus.Istanbul {
4046
// Allocate the snapshot caches and create the engine
@@ -70,14 +76,15 @@ type backend struct {
7076
logger log.Logger
7177
db ethdb.Database
7278
chain consensus.ChainReader
73-
inserter func(types.Blocks) (int, error)
79+
currentBlock func() *types.Block
80+
hasBadBlock func(hash common.Hash) bool
7481

7582
// the channels for istanbul engine notifications
7683
commitCh chan *types.Block
7784
proposedBlockHash common.Hash
7885
sealMu sync.Mutex
7986
coreStarted bool
80-
coreMu sync.Mutex
87+
coreMu sync.RWMutex
8188

8289
// Current list of candidates we are pushing
8390
candidates map[common.Address]bool
@@ -180,16 +187,11 @@ func (sb *backend) Commit(proposal istanbul.Proposal, seals [][]byte) error {
180187
if sb.proposedBlockHash == block.Hash() {
181188
// feed block hash to Seal() and wait the Seal() result
182189
sb.commitCh <- block
183-
// TODO: how do we check the block is inserted correctly?
184190
return nil
185191
}
186-
// if I'm not a proposer, insert the block directly and broadcast NewCommittedEvent
187-
if _, err := sb.inserter(types.Blocks{block}); err != nil && err != core.ErrKnownBlock {
188-
return err
189-
}
190192

191193
if sb.broadcaster != nil {
192-
go sb.broadcaster.BroadcastBlock(block, false)
194+
sb.broadcaster.Enqueue(fetcherID, block)
193195
}
194196
return nil
195197
}
@@ -208,6 +210,22 @@ func (sb *backend) Verify(proposal istanbul.Proposal) (time.Duration, error) {
208210
sb.logger.Error("Invalid proposal, %v", proposal)
209211
return 0, errInvalidProposal
210212
}
213+
214+
// check bad block
215+
if sb.HasBadProposal(block.Hash()) {
216+
return 0, core.ErrBlacklistedHash
217+
}
218+
219+
// check block body
220+
txnHash := types.DeriveSha(block.Transactions())
221+
uncleHash := types.CalcUncleHash(block.Uncles())
222+
if txnHash != block.Header().TxHash {
223+
return 0, errMismatchTxhashes
224+
}
225+
if uncleHash != nilUncleHash {
226+
return 0, errInvalidUncleHash
227+
}
228+
211229
// verify the header of proposed block
212230
err := sb.VerifyHeader(sb.chain, block.Header(), false)
213231
// ignore errEmptyCommittedSeals error because we don't have the committed seals yet
@@ -239,6 +257,11 @@ func (sb *backend) CheckSignature(data []byte, address common.Address, sig []byt
239257
return nil
240258
}
241259

260+
// HasPropsal implements istanbul.Backend.HashBlock
261+
func (sb *backend) HasPropsal(hash common.Hash, number *big.Int) bool {
262+
return sb.chain.GetHeader(hash, number.Uint64()) != nil
263+
}
264+
242265
// GetProposer implements istanbul.Backend.GetProposer
243266
func (sb *backend) GetProposer(number uint64) common.Address {
244267
if h := sb.chain.GetHeaderByNumber(number); h != nil {
@@ -248,6 +271,14 @@ func (sb *backend) GetProposer(number uint64) common.Address {
248271
return common.Address{}
249272
}
250273

274+
// ParentValidators implements istanbul.Backend.GetParentValidators
275+
func (sb *backend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet {
276+
if block, ok := proposal.(*types.Block); ok {
277+
return sb.getValidators(block.Number().Uint64()-1, block.ParentHash())
278+
}
279+
return validator.NewSet(nil, sb.config.ProposerPolicy)
280+
}
281+
251282
func (sb *backend) getValidators(number uint64, hash common.Hash) istanbul.ValidatorSet {
252283
snap, err := sb.snapshot(sb.chain, number, hash, nil)
253284
if err != nil {
@@ -257,23 +288,25 @@ func (sb *backend) getValidators(number uint64, hash common.Hash) istanbul.Valid
257288
}
258289

259290
func (sb *backend) LastProposal() (istanbul.Proposal, common.Address) {
260-
if sb.chain == nil {
261-
sb.logger.Error("Failed to access blockchain")
262-
return nil, common.Address{}
263-
}
264-
265-
h := sb.chain.CurrentHeader()
291+
block := sb.currentBlock()
266292

267293
var proposer common.Address
268-
if h.Number.Cmp(common.Big0) > 0 {
294+
if block.Number().Cmp(common.Big0) > 0 {
269295
var err error
270-
proposer, err = sb.Author(h)
296+
proposer, err = sb.Author(block.Header())
271297
if err != nil {
272298
sb.logger.Error("Failed to get block proposer", "err", err)
273299
return nil, common.Address{}
274300
}
275301
}
276302

277303
// Return header only block here since we don't need block body
278-
return types.NewBlockWithHeader(h), proposer
304+
return block, proposer
305+
}
306+
307+
func (sb *backend) HasBadProposal(hash common.Hash) bool {
308+
if sb.hasBadBlock == nil {
309+
return false
310+
}
311+
return sb.hasBadBlock(hash)
279312
}

consensus/istanbul/backend/backend_test.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,10 @@ import (
2929
"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
3030
"github.com/ethereum/go-ethereum/core/types"
3131
"github.com/ethereum/go-ethereum/crypto"
32-
"github.com/ethereum/go-ethereum/log"
3332
)
3433

3534
func TestSign(t *testing.T) {
36-
b, _, _ := newBackend()
35+
b := newBackend()
3736
data := []byte("Here is a string....")
3837
sig, err := b.Sign(data)
3938
if err != nil {
@@ -54,7 +53,7 @@ func TestCheckSignature(t *testing.T) {
5453
data := []byte("Here is a string....")
5554
hashData := crypto.Keccak256([]byte(data))
5655
sig, _ := crypto.Sign(hashData, key)
57-
b, _, _ := newBackend()
56+
b := newBackend()
5857
a := getAddress()
5958
err := b.CheckSignature(data, a, sig)
6059
if err != nil {
@@ -68,7 +67,7 @@ func TestCheckSignature(t *testing.T) {
6867
}
6968

7069
func TestCheckValidatorSignature(t *testing.T) {
71-
_, keys, vset := newBackend()
70+
vset, keys := newTestValidatorSet(5)
7271

7372
// 1. Positive test: sign with validator's key should succeed
7473
data := []byte("dummy data")
@@ -113,7 +112,7 @@ func TestCheckValidatorSignature(t *testing.T) {
113112
}
114113

115114
func TestCommit(t *testing.T) {
116-
backend, _, _ := newBackend()
115+
backend := newBackend()
117116

118117
commitCh := make(chan *types.Block)
119118
// Case: it's a proposer, so the backend.commit will receive channel result from backend.Commit function
@@ -235,13 +234,9 @@ func (slice Keys) Swap(i, j int) {
235234
slice[i], slice[j] = slice[j], slice[i]
236235
}
237236

238-
func newBackend() (b *backend, validatorKeys Keys, validatorSet istanbul.ValidatorSet) {
237+
func newBackend() (b *backend) {
238+
_, b = newBlockChain(4)
239239
key, _ := generatePrivateKey()
240-
validatorSet, validatorKeys = newTestValidatorSet(5)
241-
b = &backend{
242-
privateKey: key,
243-
logger: log.New("backend", "simple"),
244-
commitCh: make(chan *types.Block, 1),
245-
}
240+
b.privateKey = key
246241
return
247242
}

consensus/istanbul/backend/engine.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ var (
8080
errInvalidCommittedSeals = errors.New("invalid committed seals")
8181
// errEmptyCommittedSeals is returned if the field of committed seals is zero.
8282
errEmptyCommittedSeals = errors.New("zero committed seals")
83+
// errMismatchTxhashes is returned if the TxHash in header is mismatch.
84+
errMismatchTxhashes = errors.New("mismatch transcations hashes")
8385
)
8486
var (
8587
defaultDifficulty = big.NewInt(1)
@@ -477,7 +479,7 @@ func (sb *backend) APIs(chain consensus.ChainReader) []rpc.API {
477479
}
478480

479481
// Start implements consensus.Istanbul.Start
480-
func (sb *backend) Start(chain consensus.ChainReader, inserter func(types.Blocks) (int, error)) error {
482+
func (sb *backend) Start(chain consensus.ChainReader, currentBlock func() *types.Block, hasBadBlock func(hash common.Hash) bool) error {
481483
sb.coreMu.Lock()
482484
defer sb.coreMu.Unlock()
483485
if sb.coreStarted {
@@ -492,23 +494,10 @@ func (sb *backend) Start(chain consensus.ChainReader, inserter func(types.Blocks
492494
sb.commitCh = make(chan *types.Block, 1)
493495

494496
sb.chain = chain
495-
sb.inserter = inserter
496-
497-
curHeader := chain.CurrentHeader()
498-
lastSequence := new(big.Int).Set(curHeader.Number)
499-
lastProposer := common.Address{}
500-
// should get proposer if the block is not genesis
501-
if lastSequence.Cmp(common.Big0) > 0 {
502-
p, err := sb.Author(curHeader)
503-
if err != nil {
504-
return err
505-
}
506-
lastProposer = p
507-
}
508-
// We don't need block body so we create a header only block.
509-
// The proposal is only for validator set calculation.
510-
lastProposal := types.NewBlockWithHeader(curHeader)
511-
if err := sb.core.Start(lastSequence, lastProposer, lastProposal); err != nil {
497+
sb.currentBlock = currentBlock
498+
sb.hasBadBlock = hasBadBlock
499+
500+
if err := sb.core.Start(); err != nil {
512501
return err
513502
}
514503

consensus/istanbul/backend/engine_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func newBlockChain(n int) (*core.BlockChain, *backend) {
5151
if err != nil {
5252
panic(err)
5353
}
54-
b.Start(blockchain, blockchain.InsertChain)
54+
b.Start(blockchain, blockchain.CurrentBlock, blockchain.HasBadBlock)
5555
snap, err := b.snapshot(blockchain, 0, common.Hash{}, nil)
5656
if err != nil {
5757
panic(err)
@@ -138,7 +138,7 @@ func makeBlock(chain *core.BlockChain, engine *backend, parent *types.Block) *ty
138138
func makeBlockWithoutSeal(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
139139
header := makeHeader(parent, engine.config)
140140
engine.Prepare(chain, header)
141-
state, _, _ := chain.StateAt(parent.Root())
141+
state, _,_ := chain.StateAt(parent.Root())
142142
block, _ := engine.Finalize(chain, header, state, nil, nil, nil)
143143
return block
144144
}

consensus/istanbul/backend/handler.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"github.com/ethereum/go-ethereum/common"
2323
"github.com/ethereum/go-ethereum/consensus"
2424
"github.com/ethereum/go-ethereum/consensus/istanbul"
25-
"github.com/ethereum/go-ethereum/core/types"
2625
"github.com/ethereum/go-ethereum/p2p"
2726
lru "github.com/hashicorp/golang-lru"
2827
)
@@ -93,20 +92,12 @@ func (sb *backend) SetBroadcaster(broadcaster consensus.Broadcaster) {
9392
sb.broadcaster = broadcaster
9493
}
9594

96-
func (sb *backend) NewChainHead(block *types.Block) error {
97-
sb.coreMu.Lock()
98-
defer sb.coreMu.Unlock()
95+
func (sb *backend) NewChainHead() error {
96+
sb.coreMu.RLock()
97+
defer sb.coreMu.RUnlock()
9998
if !sb.coreStarted {
10099
return istanbul.ErrStoppedEngine
101100
}
102-
p, err := sb.Author(block.Header())
103-
if err != nil {
104-
sb.logger.Error("Failed to get block proposer", "err", err)
105-
return err
106-
}
107-
go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{
108-
Proposal: block,
109-
Proposer: p,
110-
})
101+
go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{})
111102
return nil
112103
}

0 commit comments

Comments
 (0)