Skip to content

Commit b389e13

Browse files
authored
Merge pull request #150 from getamis/feature/snapshot_json
consensus/istanbul: implement snapshot marshal/unmarshal
2 parents b687ef0 + d5d15af commit b389e13

File tree

6 files changed

+123
-23
lines changed

6 files changed

+123
-23
lines changed

consensus/istanbul/backend/snapshot.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/ethereum/go-ethereum/common"
2424
"github.com/ethereum/go-ethereum/consensus/istanbul"
25+
"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
2526
"github.com/ethereum/go-ethereum/core/types"
2627
"github.com/ethereum/go-ethereum/ethdb"
2728
)
@@ -50,11 +51,11 @@ type Tally struct {
5051
type Snapshot struct {
5152
Epoch uint64 // The number of blocks after which to checkpoint and reset the pending votes
5253

53-
Number uint64 `json:"number"` // Block number where the snapshot was created
54-
Hash common.Hash `json:"hash"` // Block hash where the snapshot was created
55-
Votes []*Vote `json:"votes"` // List of votes cast in chronological order
56-
Tally map[common.Address]Tally `json:"tally"` // Current vote tally to avoid recalculating
57-
ValSet istanbul.ValidatorSet `json:"validators"` // Set of authorized validators at this moment
54+
Number uint64 // Block number where the snapshot was created
55+
Hash common.Hash // Block hash where the snapshot was created
56+
Votes []*Vote // List of votes cast in chronological order
57+
Tally map[common.Address]Tally // Current vote tally to avoid recalculating
58+
ValSet istanbul.ValidatorSet // Set of authorized validators at this moment
5859
}
5960

6061
// newSnapshot create a new snapshot with the specified startup parameters. This
@@ -272,3 +273,49 @@ func (s *Snapshot) validators() []common.Address {
272273
}
273274
return validators
274275
}
276+
277+
type snapshotJSON struct {
278+
Epoch uint64 `json:"epoch"`
279+
Number uint64 `json:"number"`
280+
Hash common.Hash `json:"hash"`
281+
Votes []*Vote `json:"votes"`
282+
Tally map[common.Address]Tally `json:"tally"`
283+
284+
// for validator set
285+
Validators []common.Address `json:"validators"`
286+
Policy istanbul.ProposerPolicy `json:"policy"`
287+
}
288+
289+
func (s *Snapshot) toJSONStruct() *snapshotJSON {
290+
return &snapshotJSON{
291+
Epoch: s.Epoch,
292+
Number: s.Number,
293+
Hash: s.Hash,
294+
Votes: s.Votes,
295+
Tally: s.Tally,
296+
Validators: s.validators(),
297+
Policy: s.ValSet.Policy(),
298+
}
299+
}
300+
301+
// Unmarshal from a json byte array
302+
func (s *Snapshot) UnmarshalJSON(b []byte) error {
303+
var j snapshotJSON
304+
if err := json.Unmarshal(b, &j); err != nil {
305+
return err
306+
}
307+
308+
s.Epoch = j.Epoch
309+
s.Number = j.Number
310+
s.Hash = j.Hash
311+
s.Votes = j.Votes
312+
s.Tally = j.Tally
313+
s.ValSet = validator.NewSet(j.Validators, j.Policy)
314+
return nil
315+
}
316+
317+
// Marshal to a json byte array
318+
func (s *Snapshot) MarshalJSON() ([]byte, error) {
319+
j := s.toJSONStruct()
320+
return json.Marshal(j)
321+
}

consensus/istanbul/backend/snapshot_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import (
2020
"bytes"
2121
"crypto/ecdsa"
2222
"math/big"
23+
"reflect"
2324
"testing"
2425

2526
"github.com/ethereum/go-ethereum/common"
2627
"github.com/ethereum/go-ethereum/consensus/istanbul"
28+
"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
2729
"github.com/ethereum/go-ethereum/core"
2830
"github.com/ethereum/go-ethereum/core/types"
2931
"github.com/ethereum/go-ethereum/core/vm"
@@ -400,3 +402,54 @@ func TestVoting(t *testing.T) {
400402
}
401403
}
402404
}
405+
406+
func TestSaveAndLoad(t *testing.T) {
407+
snap := &Snapshot{
408+
Epoch: 5,
409+
Number: 10,
410+
Hash: common.HexToHash("1234567890"),
411+
Votes: []*Vote{
412+
{
413+
Validator: common.StringToAddress("1234567891"),
414+
Block: 15,
415+
Address: common.StringToAddress("1234567892"),
416+
Authorize: false,
417+
},
418+
},
419+
Tally: map[common.Address]Tally{
420+
common.StringToAddress("1234567893"): Tally{
421+
Authorize: false,
422+
Votes: 20,
423+
},
424+
},
425+
ValSet: validator.NewSet([]common.Address{
426+
common.StringToAddress("1234567894"),
427+
common.StringToAddress("1234567895"),
428+
}, istanbul.RoundRobin),
429+
}
430+
db, _ := ethdb.NewMemDatabase()
431+
err := snap.store(db)
432+
if err != nil {
433+
t.Errorf("store snapshot failed: %v", err)
434+
}
435+
436+
snap1, err := loadSnapshot(snap.Epoch, db, snap.Hash)
437+
if err != nil {
438+
t.Errorf("load snapshot failed: %v", err)
439+
}
440+
if snap.Epoch != snap1.Epoch {
441+
t.Errorf("epoch mismatch: have %v, want %v", snap1.Epoch, snap.Epoch)
442+
}
443+
if snap.Hash != snap1.Hash {
444+
t.Errorf("hash mismatch: have %v, want %v", snap1.Number, snap.Number)
445+
}
446+
if !reflect.DeepEqual(snap.Votes, snap.Votes) {
447+
t.Errorf("votes mismatch: have %v, want %v", snap1.Votes, snap.Votes)
448+
}
449+
if !reflect.DeepEqual(snap.Tally, snap.Tally) {
450+
t.Errorf("tally mismatch: have %v, want %v", snap1.Tally, snap.Tally)
451+
}
452+
if !reflect.DeepEqual(snap.ValSet, snap.ValSet) {
453+
t.Errorf("validator set mismatch: have %v, want %v", snap1.ValSet, snap.ValSet)
454+
}
455+
}

consensus/istanbul/validator.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ type ValidatorSet interface {
7171
Copy() ValidatorSet
7272
// Get the maximum number of faulty nodes
7373
F() int
74+
// Get proposer policy
75+
Policy() ProposerPolicy
7476
}
7577

7678
// ----------------------------------------------------------------------------

consensus/istanbul/validator/default.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,18 @@ func (val *defaultValidator) String() string {
4141
// ----------------------------------------------------------------------------
4242

4343
type defaultSet struct {
44-
validators istanbul.Validators
44+
validators istanbul.Validators
45+
policy istanbul.ProposerPolicy
46+
4547
proposer istanbul.Validator
4648
validatorMu sync.RWMutex
47-
48-
selector istanbul.ProposalSelector
49+
selector istanbul.ProposalSelector
4950
}
5051

51-
func newDefaultSet(addrs []common.Address, selector istanbul.ProposalSelector) *defaultSet {
52+
func newDefaultSet(addrs []common.Address, policy istanbul.ProposerPolicy) *defaultSet {
5253
valSet := &defaultSet{}
5354

55+
valSet.policy = policy
5456
// init validators
5557
valSet.validators = make([]istanbul.Validator, len(addrs))
5658
for i, addr := range addrs {
@@ -62,8 +64,10 @@ func newDefaultSet(addrs []common.Address, selector istanbul.ProposalSelector) *
6264
if valSet.Size() > 0 {
6365
valSet.proposer = valSet.GetByIndex(0)
6466
}
65-
//set proposal selector
66-
valSet.selector = selector
67+
valSet.selector = roundRobinProposer
68+
if policy == istanbul.Sticky {
69+
valSet.selector = stickyProposer
70+
}
6771

6872
return valSet
6973
}
@@ -189,7 +193,9 @@ func (valSet *defaultSet) Copy() istanbul.ValidatorSet {
189193
for _, v := range valSet.validators {
190194
addresses = append(addresses, v.Address())
191195
}
192-
return newDefaultSet(addresses, valSet.selector)
196+
return NewSet(addresses, valSet.policy)
193197
}
194198

195199
func (valSet *defaultSet) F() int { return int(math.Ceil(float64(valSet.Size())/3)) - 1 }
200+
201+
func (valSet *defaultSet) Policy() istanbul.ProposerPolicy { return valSet.policy }

consensus/istanbul/validator/default_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func testNormalValSet(t *testing.T) {
7878
val1 := New(addr1)
7979
val2 := New(addr2)
8080

81-
valSet := newDefaultSet([]common.Address{addr1, addr2}, roundRobinProposer)
81+
valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.RoundRobin)
8282
if valSet == nil {
8383
t.Errorf("the format of validator set is invalid")
8484
t.FailNow()
@@ -182,7 +182,7 @@ func testStickyProposer(t *testing.T) {
182182
val1 := New(addr1)
183183
val2 := New(addr2)
184184

185-
valSet := newDefaultSet([]common.Address{addr1, addr2}, stickyProposer)
185+
valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.Sticky)
186186

187187
// test get proposer
188188
if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) {

consensus/istanbul/validator/validator.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,7 @@ func New(addr common.Address) istanbul.Validator {
2828
}
2929

3030
func NewSet(addrs []common.Address, policy istanbul.ProposerPolicy) istanbul.ValidatorSet {
31-
switch policy {
32-
case istanbul.RoundRobin:
33-
return newDefaultSet(addrs, roundRobinProposer)
34-
case istanbul.Sticky:
35-
return newDefaultSet(addrs, stickyProposer)
36-
}
37-
38-
// use round-robin policy as default proposal policy
39-
return newDefaultSet(addrs, roundRobinProposer)
31+
return newDefaultSet(addrs, policy)
4032
}
4133

4234
func ExtractValidators(extraData []byte) []common.Address {

0 commit comments

Comments
 (0)