Skip to content

Commit 8f90388

Browse files
wgr523wjrjerome
authored andcommitted
Consensus V2 variable, timeout pool (#19)
* fill in XDPoS_v2 variables and processQC/TC * add timeout pool, refine engine variables * refactor type functions * solve a small pointer bug * create general pool and its test, refine engine * refine pool, add xdpos v2 config cert threshold * refine config
1 parent 80f0a96 commit 8f90388

File tree

8 files changed

+329
-121
lines changed

8 files changed

+329
-121
lines changed

consensus/XDPoS/engines/engine_v2/engine.go

+66-29
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,28 @@ type XDPoS_v2 struct {
3030
BFTQueue chan interface{}
3131
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
3232

33-
currentRound utils.Round
33+
timeoutPool *utils.Pool
34+
currentRound utils.Round
35+
highestVotedRound utils.Round
36+
highestQuorumCert *utils.QuorumCert
37+
// LockQC in XDPoS Consensus 2.0, used in voting rule
38+
lockQuorumCert *utils.QuorumCert
39+
highestTimeoutCert *utils.TimeoutCert
40+
highestCommitBlock *utils.BlockInfo
3441
}
3542

3643
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
3744
// Setup Timer
3845
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Millisecond
3946
timer := countdown.NewCountDown(duration)
40-
47+
timeoutPool := utils.NewPool(config.V2.CertThreshold)
4148
engine := &XDPoS_v2{
4249
config: config,
4350
db: db,
4451
timeoutWorker: timer,
4552
BroadcastCh: make(chan interface{}),
4653
BFTQueue: make(chan interface{}),
54+
timeoutPool: timeoutPool,
4755
}
4856
// Add callback to the timer
4957
timer.OnTimeoutFn = engine.onCountdownTimeout
@@ -62,6 +70,7 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 {
6270
// Setup Timer
6371
duration := time.Duration(config.V2.TimeoutWorkerDuration) * time.Millisecond
6472
timer := countdown.NewCountDown(duration)
73+
timeoutPool := utils.NewPool(2)
6574

6675
// Allocate the snapshot caches and create the engine
6776
fakeEngine = &XDPoS_v2{
@@ -70,6 +79,7 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 {
7079
timeoutWorker: timer,
7180
BroadcastCh: make(chan interface{}),
7281
BFTQueue: make(chan interface{}),
82+
timeoutPool: timeoutPool,
7383
}
7484
// Add callback to the timer
7585
timer.OnTimeoutFn = fakeEngine.onCountdownTimeout
@@ -171,7 +181,7 @@ func (x *XDPoS_v2) VoteHandler() {
171181
*/
172182
func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg utils.Timeout) (bool, error) {
173183
// Recover the public key and the Ethereum address
174-
pubkey, err := crypto.Ecrecover(utils.TimeoutSigHash(timeoutMsg.Round).Bytes(), timeoutMsg.Signature)
184+
pubkey, err := crypto.Ecrecover(utils.TimeoutSigHash(&timeoutMsg.Round).Bytes(), timeoutMsg.Signature)
175185
if err != nil {
176186
return false, fmt.Errorf("Error while verifying time out message: %v", err)
177187
}
@@ -187,14 +197,20 @@ func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg utils.Timeout) (bool, error)
187197
return false, fmt.Errorf("Masternodes does not contain signer address. Master node list %v, Signer address: %v", masternodes, signerAddress)
188198
}
189199

190-
func (x *XDPoS_v2) TimeoutHandler() {
191-
/*
192-
1. checkRoundNumber()
193-
2. Collect timeout (TODO)
194-
3. Genrate TC (TODO)
195-
4. processTC()
196-
5. generateSyncInfo()
197-
*/
200+
/*
201+
1. checkRoundNumber()
202+
2. Collect timeout (TODO)
203+
3. Genrate TC (TODO)
204+
4. processTC()
205+
5. generateSyncInfo()
206+
*/
207+
func (x *XDPoS_v2) TimeoutHandler(timeout *utils.Timeout) {
208+
// Collect timeout, generate TC
209+
timeoutCert := x.timeoutPool.Add(timeout)
210+
// If TC is generated
211+
if timeoutCert != nil {
212+
//TODO: processTC(),generateSyncInfo()
213+
}
198214
}
199215

200216
/*
@@ -245,29 +261,50 @@ func (x *XDPoS_v2) verifyTC(header *types.Header) error {
245261
}
246262

247263
// Update local QC variables including highestQC & lockQC, as well as update commit blockInfo before call
248-
func (x *XDPoS_v2) processQC(header *types.Header) error {
249-
/*
250-
1. Update HighestQC and LockQC
251-
2. Update commit block info (TODO)
252-
3. Check QC round >= node's currentRound. If yes, call setNewRound
253-
*/
264+
/*
265+
1. Update HighestQC and LockQC
266+
2. Update commit block info (TODO)
267+
3. Check QC round >= node's currentRound. If yes, call setNewRound
268+
*/
269+
func (x *XDPoS_v2) processQC(quorumCert *utils.QuorumCert) error {
270+
if x.highestQuorumCert == nil || quorumCert.ProposedBlockInfo.Round > x.highestQuorumCert.ProposedBlockInfo.Round {
271+
x.highestQuorumCert = quorumCert
272+
//TODO: do I need a clone?
273+
}
274+
//TODO: x.blockchain.getBlock(quorumCert.ProposedBlockInfo.Hash) then get the QC inside that block header
275+
//TODO: update lockQC
276+
//TODO: find parent and grandparent and grandgrandparent block, check round number, if so, commit grandgrandparent
277+
if quorumCert.ProposedBlockInfo.Round >= x.currentRound {
278+
x.setNewRound(quorumCert.ProposedBlockInfo.Round + 1)
279+
}
254280
return nil
255281
}
256282

257-
func (x *XDPoS_v2) processTC(header *types.Header) error {
258-
/*
259-
1. Update highestTC
260-
2. Check TC round >= node's currentRound. If yes, call setNewRound
261-
*/
283+
/*
284+
1. Update highestTC
285+
2. Check TC round >= node's currentRound. If yes, call setNewRound
286+
*/
287+
func (x *XDPoS_v2) processTC(timeoutCert *utils.TimeoutCert) error {
288+
if x.highestTimeoutCert == nil || timeoutCert.Round > x.highestTimeoutCert.Round {
289+
x.highestTimeoutCert = timeoutCert
290+
}
291+
if timeoutCert.Round >= x.currentRound {
292+
x.setNewRound(timeoutCert.Round + 1)
293+
}
262294
return nil
263295
}
264296

265-
func (x *XDPoS_v2) setNewRound() error {
266-
/*
267-
1. Set currentRound = QC round + 1 (or TC round +1)
268-
2. Reset timer
269-
3. Reset vote and timeout Pools
270-
*/
297+
/*
298+
1. Set currentRound = QC round + 1 (or TC round +1)
299+
2. Reset timer
300+
3. Reset vote and timeout Pools
301+
*/
302+
func (x *XDPoS_v2) setNewRound(round utils.Round) error {
303+
x.currentRound = round
304+
//TODO: tell miner now it's a new round and start mine if it's leader
305+
//TODO: reset timer
306+
//TODO: vote pools
307+
x.timeoutPool.Clear()
271308
return nil
272309
}
273310

@@ -308,7 +345,7 @@ func (x *XDPoS_v2) sendTimeout() error {
308345
signer, signFn := x.signer, x.signFn
309346
x.lock.RUnlock()
310347

311-
signedHash, err := signFn(accounts.Account{Address: signer}, utils.TimeoutSigHash(x.currentRound).Bytes())
348+
signedHash, err := signFn(accounts.Account{Address: signer}, utils.TimeoutSigHash(&x.currentRound).Bytes())
312349
if err != nil {
313350
return fmt.Errorf("Error while signing for timeout message")
314351
}

consensus/XDPoS/utils/pool.go

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package utils
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/XinFinOrg/XDPoSChain/common"
7+
)
8+
9+
type PoolObj interface {
10+
Hash() common.Hash
11+
PoolKey() string
12+
}
13+
type Pool struct {
14+
objList map[string]map[common.Hash]PoolObj
15+
threshold int
16+
onThresholdFn func(map[common.Hash]PoolObj) error
17+
}
18+
19+
func NewPool(threshold int) *Pool {
20+
return &Pool{
21+
objList: make(map[string]map[common.Hash]PoolObj),
22+
threshold: threshold,
23+
}
24+
}
25+
26+
func (p *Pool) Add(obj PoolObj) error {
27+
poolKey := obj.PoolKey()
28+
objListKeyed, ok := p.objList[poolKey]
29+
if !ok {
30+
p.objList[poolKey] = make(map[common.Hash]PoolObj)
31+
objListKeyed = p.objList[poolKey]
32+
}
33+
objListKeyed[obj.Hash()] = obj
34+
if len(objListKeyed) >= p.threshold {
35+
delete(p.objList, poolKey)
36+
if p.onThresholdFn != nil {
37+
return p.onThresholdFn(objListKeyed)
38+
} else {
39+
return fmt.Errorf("no call back function for pool")
40+
}
41+
}
42+
return nil
43+
}
44+
45+
func (p *Pool) Clear() {
46+
p.objList = make(map[string]map[common.Hash]PoolObj)
47+
}
48+
49+
func (p *Pool) SetThreshold(t int) {
50+
p.threshold = t
51+
}
52+
53+
func (p *Pool) SetOnThresholdFn(f func(map[common.Hash]PoolObj) error) {
54+
p.onThresholdFn = f
55+
}

consensus/XDPoS/utils/pool_test.go

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package utils
2+
3+
import (
4+
"math/big"
5+
"testing"
6+
7+
"github.com/XinFinOrg/XDPoSChain/common"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestPoolWithTimeout(t *testing.T) {
12+
assert := assert.New(t)
13+
var ret int
14+
onThresholdFn := func(po map[common.Hash]PoolObj) error {
15+
for _, m := range po {
16+
if _, ok := m.(*Timeout); ok {
17+
ret += 1
18+
} else {
19+
t.Fatalf("wrong type passed into pool: %v", m)
20+
}
21+
}
22+
return nil
23+
}
24+
25+
pool := NewPool(2) // 2 is the cert threshold
26+
ret = 0
27+
pool.SetOnThresholdFn(onThresholdFn)
28+
timeout1 := Timeout{Round: 1, Signature: []byte{1}}
29+
timeout2 := Timeout{Round: 1, Signature: []byte{2}}
30+
timeout3 := Timeout{Round: 1, Signature: []byte{3}}
31+
assert.Nil(pool.Add(&timeout1))
32+
assert.Nil(pool.Add(&timeout1))
33+
assert.Equal(ret, 0)
34+
assert.Nil(pool.Add(&timeout2))
35+
assert.Equal(ret, 2)
36+
assert.Nil(pool.Add(&timeout3))
37+
assert.Equal(ret, 2)
38+
pool = NewPool(3) // 3 is the cert size
39+
ret = 0
40+
pool.SetOnThresholdFn(onThresholdFn)
41+
assert.Nil(pool.Add(&timeout1))
42+
assert.Nil(pool.Add(&timeout2))
43+
assert.Equal(ret, 0)
44+
pool.Clear()
45+
assert.Nil(pool.Add(&timeout3))
46+
assert.Equal(ret, 0)
47+
}
48+
49+
func TestPoolWithVote(t *testing.T) {
50+
assert := assert.New(t)
51+
var ret int
52+
onThresholdFn := func(po map[common.Hash]PoolObj) error {
53+
for _, m := range po {
54+
if _, ok := m.(*Vote); ok {
55+
ret += 1
56+
} else {
57+
t.Fatalf("wrong type passed into pool: %v", m)
58+
}
59+
}
60+
return nil
61+
}
62+
63+
pool := NewPool(2) // 2 is the cert threshold
64+
ret = 0
65+
pool.SetOnThresholdFn(onThresholdFn)
66+
blockInfo1 := BlockInfo{Hash: common.BigToHash(big.NewInt(2047)), Round: 1, Number: big.NewInt(1)}
67+
blockInfo2 := BlockInfo{Hash: common.BigToHash(big.NewInt(4095)), Round: 1, Number: big.NewInt(1)}
68+
vote1 := Vote{ProposedBlockInfo: blockInfo1, Signature: []byte{1}}
69+
vote2 := Vote{ProposedBlockInfo: blockInfo2, Signature: []byte{2}}
70+
vote3 := Vote{ProposedBlockInfo: blockInfo1, Signature: []byte{3}}
71+
assert.Nil(pool.Add(&vote1))
72+
assert.Nil(pool.Add(&vote1))
73+
assert.Equal(ret, 0)
74+
assert.Nil(pool.Add(&vote2))
75+
assert.Equal(ret, 0)
76+
assert.Nil(pool.Add(&vote3))
77+
assert.Equal(ret, 2)
78+
pool = NewPool(3) // 3 is the cert size
79+
ret = 0
80+
pool.SetOnThresholdFn(onThresholdFn)
81+
assert.Nil(pool.Add(&vote1))
82+
assert.Nil(pool.Add(&vote2))
83+
assert.Nil(pool.Add(&vote3))
84+
assert.Equal(ret, 0)
85+
pool.Clear()
86+
assert.Empty(pool.objList)
87+
pool = NewPool(2) // 2 is the cert size
88+
ret = 0
89+
pool.SetOnThresholdFn(onThresholdFn)
90+
assert.Nil(pool.Add(&vote1))
91+
assert.Nil(pool.Add(&vote2))
92+
assert.Nil(pool.Add(&vote3))
93+
assert.Equal(len(pool.objList), 1) //vote for one hash is cleared, but another remains
94+
pool.Clear()
95+
assert.Empty(pool.objList)
96+
}

consensus/XDPoS/utils/types.go

+39
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
1212
"github.com/XinFinOrg/XDPoSChain/core/state"
1313
"github.com/XinFinOrg/XDPoSChain/core/types"
14+
"github.com/XinFinOrg/XDPoSChain/crypto/sha3"
15+
"github.com/XinFinOrg/XDPoSChain/rlp"
1416
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
1517
)
1618

@@ -104,3 +106,40 @@ type ExtraFields_v2 struct {
104106
Round Round
105107
QuorumCert QuorumCert
106108
}
109+
110+
func rlpHash(x interface{}) (h common.Hash) {
111+
hw := sha3.NewKeccak256()
112+
rlp.Encode(hw, x)
113+
hw.Sum(h[:0])
114+
return h
115+
}
116+
117+
func (m *Vote) Hash() common.Hash {
118+
return rlpHash(m)
119+
}
120+
121+
func (m *Timeout) Hash() common.Hash {
122+
return rlpHash(m)
123+
}
124+
125+
func (m *SyncInfo) Hash() common.Hash {
126+
return rlpHash(m)
127+
}
128+
129+
func VoteSigHash(m *BlockInfo) common.Hash {
130+
return rlpHash(m)
131+
}
132+
133+
func TimeoutSigHash(m *Round) common.Hash {
134+
return rlpHash(m)
135+
}
136+
137+
func (m *Vote) PoolKey() string {
138+
// return the voted block hash
139+
return m.ProposedBlockInfo.Hash.Hex()
140+
}
141+
142+
func (m *Timeout) PoolKey() string {
143+
// return a default pool key string
144+
return "0"
145+
}

0 commit comments

Comments
 (0)