Skip to content

Commit 09ea540

Browse files
committed
consensus/istanbul: add block lock tests
1 parent 7daa006 commit 09ea540

File tree

7 files changed

+242
-11
lines changed

7 files changed

+242
-11
lines changed

consensus/istanbul/backend/backend_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,44 @@ func TestCommit(t *testing.T) {
177177
}
178178
}
179179

180+
func TestHasBlock(t *testing.T) {
181+
chain, engine := newBlockChain(1)
182+
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
183+
finalBlock, _ := engine.Seal(chain, block, nil)
184+
chain.InsertChain(types.Blocks{finalBlock})
185+
if engine.HasBlock(block.Hash(), finalBlock.Number()) {
186+
t.Errorf("error mismatch: have true, want false")
187+
}
188+
if !engine.HasBlock(finalBlock.Hash(), finalBlock.Number()) {
189+
t.Errorf("error mismatch: have false, want true")
190+
}
191+
}
192+
193+
func TestGetProposer(t *testing.T) {
194+
chain, engine := newBlockChain(1)
195+
block := makeBlock(chain, engine, chain.Genesis())
196+
chain.InsertChain(types.Blocks{block})
197+
expected := engine.GetProposer(1)
198+
actual := engine.Address()
199+
if actual != expected {
200+
t.Errorf("proposer mismatch: have %v, want %v", actual.Hex(), expected.Hex())
201+
}
202+
}
203+
204+
func TestParentValidators(t *testing.T) {
205+
chain, engine := newBlockChain(1)
206+
block := makeBlock(chain, engine, chain.Genesis())
207+
chain.InsertChain(types.Blocks{block})
208+
expected := engine.Validators(block).List()
209+
//Block without seal will make empty validator set
210+
block = makeBlockWithoutSeal(chain, engine, block)
211+
chain.InsertChain(types.Blocks{block})
212+
actual := engine.ParentValidators(block).List()
213+
if len(expected) != len(actual) || expected[0] != actual[0] {
214+
t.Errorf("validator set mismatch: have %v, want %v", actual, expected)
215+
}
216+
}
217+
180218
/**
181219
* SimpleBackend
182220
* Private key: bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1

consensus/istanbul/core/commit_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ OUTER:
178178
if err != test.expectedErr {
179179
t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
180180
}
181+
if r0.current.IsHashLocked() {
182+
t.Errorf("block should not be locked")
183+
}
181184
continue OUTER
182185
}
183186
}
@@ -191,7 +194,9 @@ OUTER:
191194
if r0.current.Commits.Size() > 2*r0.valSet.F() {
192195
t.Errorf("the size of commit messages should be less than %v", 2*r0.valSet.F()+1)
193196
}
194-
197+
if r0.current.IsHashLocked() {
198+
t.Errorf("block should not be locked")
199+
}
195200
continue
196201
}
197202

@@ -214,6 +219,9 @@ OUTER:
214219
if signedCount <= 2*r0.valSet.F() {
215220
t.Errorf("the expected signed count should be larger than %v, but got %v", 2*r0.valSet.F(), signedCount)
216221
}
222+
if !r0.current.IsHashLocked() {
223+
t.Errorf("block should be locked")
224+
}
217225
}
218226
}
219227

consensus/istanbul/core/core.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,14 @@ func (c *core) startNewRound(newView *istanbul.View, roundChange bool) {
188188
// Clear invalid round change messages
189189
c.roundChangeSet = newRoundChangeSet(c.valSet)
190190
// New snapshot for new round
191-
c.updateRoundState(newView, c.valSet)
191+
c.updateRoundState(newView, c.valSet, roundChange)
192192
// Calculate new proposer
193193
c.valSet.CalcProposer(c.lastProposer, newView.Round.Uint64())
194194
c.waitingForRoundChange = false
195195
c.setState(StateAcceptRequest)
196196
if roundChange && c.isProposer() {
197197
// If it is locked, propose the old proposal
198-
if c.current.IsHashLocked() {
198+
if c.current != nil && c.current.IsHashLocked() {
199199
r := &istanbul.Request{
200200
Proposal: c.current.Proposal(), //c.current.Proposal would be the locked proposal by previous proposer, see updateRoundState
201201
}
@@ -218,17 +218,17 @@ func (c *core) catchUpRound(view *istanbul.View) {
218218
c.waitingForRoundChange = true
219219

220220
//Needs to keep block lock for round catching up
221-
c.updateRoundState(view, c.valSet)
221+
c.updateRoundState(view, c.valSet, true)
222222
c.roundChangeSet.Clear(view.Round)
223223
c.newRoundChangeTimer()
224224

225225
logger.Trace("Catch up round", "new_round", view.Round, "new_seq", view.Sequence, "new_proposer", c.valSet)
226226
}
227227

228228
// updateRoundState updates round state by checking if locking block is necessary
229-
func (c *core) updateRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet) {
230-
// Lock only if both keepLock is true and it is locked
231-
if c.current.IsHashLocked() {
229+
func (c *core) updateRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, roundChange bool) {
230+
// Lock only if both roundChange is true and it is locked
231+
if roundChange && c.current != nil && c.current.IsHashLocked() {
232232
c.current = newRoundState(view, validatorSet, c.current.GetLockedHash(), c.current.Preprepare)
233233
} else {
234234
c.current = newRoundState(view, validatorSet, common.Hash{}, nil)

consensus/istanbul/core/prepare_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ OUTER:
201201
if err != test.expectedErr {
202202
t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
203203
}
204+
if r0.current.IsHashLocked() {
205+
t.Errorf("block should not be locked")
206+
}
204207
continue OUTER
205208
}
206209
}
@@ -214,6 +217,9 @@ OUTER:
214217
if r0.current.Prepares.Size() > 2*r0.valSet.F() {
215218
t.Errorf("the size of prepare messages should be less than %v", 2*r0.valSet.F()+1)
216219
}
220+
if r0.current.IsHashLocked() {
221+
t.Errorf("block should not be locked")
222+
}
217223

218224
continue
219225
}
@@ -246,6 +252,9 @@ OUTER:
246252
if !reflect.DeepEqual(m, expectedSubject) {
247253
t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject)
248254
}
255+
if !r0.current.IsHashLocked() {
256+
t.Errorf("block should be locked")
257+
}
249258
}
250259
}
251260

consensus/istanbul/core/preprepare_test.go

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestHandlePreprepare(t *testing.T) {
3939
system *testSystem
4040
expectedRequest istanbul.Proposal
4141
expectedErr error
42+
existingBlock bool
4243
}{
4344
{
4445
// normal case
@@ -56,6 +57,7 @@ func TestHandlePreprepare(t *testing.T) {
5657
}(),
5758
newTestProposal(),
5859
nil,
60+
false,
5961
},
6062
{
6163
// future message
@@ -84,6 +86,7 @@ func TestHandlePreprepare(t *testing.T) {
8486
}(),
8587
makeBlock(1),
8688
errFutureMessage,
89+
false,
8790
},
8891
{
8992
// non-proposer
@@ -105,6 +108,7 @@ func TestHandlePreprepare(t *testing.T) {
105108
}(),
106109
makeBlock(1),
107110
errNotFromProposer,
111+
false,
108112
},
109113
{
110114
// ErrInvalidMessage
@@ -124,6 +128,27 @@ func TestHandlePreprepare(t *testing.T) {
124128
}(),
125129
makeBlock(1),
126130
errOldMessage,
131+
false,
132+
},
133+
{
134+
// ErrInvalidMessage
135+
func() *testSystem {
136+
sys := NewTestSystemWithBackend(N, F)
137+
138+
for i, backend := range sys.backends {
139+
c := backend.engine.(*core)
140+
c.valSet = backend.peers
141+
if i != 0 {
142+
c.state = StatePreprepared
143+
c.current.SetSequence(big.NewInt(10))
144+
c.current.SetRound(big.NewInt(10))
145+
}
146+
}
147+
return sys
148+
}(),
149+
makeBlock(5), //only height 5 will retrun true on backend.HasBlock, see testSystemBackend.HashBlock
150+
nil,
151+
true,
127152
},
128153
}
129154

@@ -167,7 +192,7 @@ OUTER:
167192
t.Errorf("state mismatch: have %v, want %v", c.state, StatePreprepared)
168193
}
169194

170-
if !reflect.DeepEqual(c.current.Subject().View, curView) {
195+
if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) {
171196
t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView)
172197
}
173198

@@ -178,17 +203,116 @@ OUTER:
178203
t.Errorf("error mismatch: have %v, want nil", err)
179204
}
180205

181-
if decodedMsg.Code != msgPrepare {
182-
t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, msgPrepare)
206+
expectedCode := msgPrepare
207+
if test.existingBlock {
208+
expectedCode = msgCommit
209+
}
210+
if decodedMsg.Code != expectedCode {
211+
t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode)
183212
}
213+
184214
var subject *istanbul.Subject
185215
err = decodedMsg.Decode(&subject)
186216
if err != nil {
187217
t.Errorf("error mismatch: have %v, want nil", err)
188218
}
189-
if !reflect.DeepEqual(subject, c.current.Subject()) {
219+
if !test.existingBlock && !reflect.DeepEqual(subject, c.current.Subject()) {
190220
t.Errorf("subject mismatch: have %v, want %v", subject, c.current.Subject())
191221
}
222+
223+
}
224+
}
225+
}
226+
227+
func TestHandlePreprepareWithLock(t *testing.T) {
228+
N := uint64(4) // replica 0 is primary, it will send messages to others
229+
F := uint64(1) // F does not affect tests
230+
proposal := newTestProposal()
231+
mismatchProposal := makeBlock(10)
232+
newSystem := func() *testSystem {
233+
sys := NewTestSystemWithBackend(N, F)
234+
235+
for i, backend := range sys.backends {
236+
c := backend.engine.(*core)
237+
c.valSet = backend.peers
238+
if i != 0 {
239+
c.state = StateAcceptRequest
240+
}
241+
c.roundChangeSet = newRoundChangeSet(c.valSet)
242+
}
243+
return sys
244+
}
245+
246+
testCases := []struct {
247+
system *testSystem
248+
proposal istanbul.Proposal
249+
lockProposal istanbul.Proposal
250+
}{
251+
{
252+
newSystem(),
253+
proposal,
254+
proposal,
255+
},
256+
{
257+
newSystem(),
258+
proposal,
259+
mismatchProposal,
260+
},
261+
}
262+
263+
for _, test := range testCases {
264+
test.system.Run(false)
265+
v0 := test.system.backends[0]
266+
r0 := v0.engine.(*core)
267+
curView := r0.currentView()
268+
preprepare := &istanbul.Preprepare{
269+
View: curView,
270+
Proposal: test.proposal,
271+
}
272+
lockPreprepare := &istanbul.Preprepare{
273+
View: curView,
274+
Proposal: test.lockProposal,
275+
}
276+
277+
for i, v := range test.system.backends {
278+
// i == 0 is primary backend, it is responsible for send preprepare messages to others.
279+
if i == 0 {
280+
continue
281+
}
282+
283+
c := v.engine.(*core)
284+
c.current.SetPreprepare(lockPreprepare)
285+
c.current.LockHash()
286+
m, _ := Encode(preprepare)
287+
_, val := r0.valSet.GetByAddress(v0.Address())
288+
if err := c.handlePreprepare(&message{
289+
Code: msgPreprepare,
290+
Msg: m,
291+
Address: v0.Address(),
292+
}, val); err != nil {
293+
t.Errorf("error mismatch: have %v, want nil", err)
294+
}
295+
if test.proposal == test.lockProposal {
296+
if c.state != StatePrepared {
297+
t.Errorf("state mismatch: have %v, want %v", c.state, StatePrepared)
298+
}
299+
if !reflect.DeepEqual(curView, c.currentView()) {
300+
t.Errorf("view mismatch: have %v, want %v", c.currentView(), curView)
301+
}
302+
} else {
303+
// Should stay at StateAcceptRequest
304+
if c.state != StateAcceptRequest {
305+
t.Errorf("state mismatch: have %v, want %v", c.state, StateAcceptRequest)
306+
}
307+
// Should have triggered a round change
308+
expectedView := &istanbul.View{
309+
Sequence: curView.Sequence,
310+
Round: big.NewInt(1),
311+
}
312+
if !reflect.DeepEqual(expectedView, c.currentView()) {
313+
t.Errorf("view mismatch: have %v, want %v", c.currentView(), expectedView)
314+
}
315+
}
192316
}
193317
}
194318
}

consensus/istanbul/core/roundstate_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
package core
1818

1919
import (
20+
"math/big"
2021
"sync"
22+
"testing"
2123

24+
"github.com/ethereum/go-ethereum/common"
2225
"github.com/ethereum/go-ethereum/consensus/istanbul"
2326
)
2427

@@ -33,3 +36,39 @@ func newTestRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet)
3336
mu: new(sync.RWMutex),
3437
}
3538
}
39+
40+
func TestLockHash(t *testing.T) {
41+
sys := NewTestSystemWithBackend(1, 0)
42+
rs := newTestRoundState(
43+
&istanbul.View{
44+
Round: big.NewInt(0),
45+
Sequence: big.NewInt(0),
46+
},
47+
sys.backends[0].peers,
48+
)
49+
if !common.EmptyHash(rs.GetLockedHash()) {
50+
t.Errorf("error mismatch: have %v, want empty", rs.GetLockedHash())
51+
}
52+
if rs.IsHashLocked() {
53+
t.Error("IsHashLocked should return false")
54+
}
55+
56+
// Lock
57+
expected := rs.Proposal().Hash()
58+
rs.LockHash()
59+
if expected != rs.GetLockedHash() {
60+
t.Errorf("error mismatch: have %v, want %v", rs.GetLockedHash(), expected)
61+
}
62+
if !rs.IsHashLocked() {
63+
t.Error("IsHashLocked should return true")
64+
}
65+
66+
// Unlock
67+
rs.UnlockHash()
68+
if !common.EmptyHash(rs.GetLockedHash()) {
69+
t.Errorf("error mismatch: have %v, want empty", rs.GetLockedHash())
70+
}
71+
if rs.IsHashLocked() {
72+
t.Error("IsHashLocked should return false")
73+
}
74+
}

0 commit comments

Comments
 (0)