OSDN Git Service

Merge pull request #1666 from Bytom/dev
[bytom/bytom.git] / protocol / state / blockindex_test.go
1 package state
2
3 import (
4         "math"
5         "math/big"
6         "reflect"
7         "testing"
8
9         "github.com/davecgh/go-spew/spew"
10
11         "github.com/bytom/consensus"
12         "github.com/bytom/consensus/difficulty"
13         "github.com/bytom/protocol/bc"
14         "github.com/bytom/protocol/bc/types"
15         "github.com/bytom/testutil"
16 )
17
18 func stringToBigInt(s string, base int) *big.Int {
19         result, _ := new(big.Int).SetString(s, base)
20         return result
21 }
22
23 func TestNewBlockNode(t *testing.T) {
24         cases := []struct {
25                 blockHeader   *types.BlockHeader
26                 parentNode    *BlockNode
27                 wantBlockNode *BlockNode
28         }{
29                 {
30                         blockHeader: &types.BlockHeader{
31                                 Height:    uint64(0),
32                                 Timestamp: 0,
33                                 Bits:      1000,
34                         },
35                         parentNode: &BlockNode{
36                                 WorkSum: &big.Int{},
37                         },
38                         wantBlockNode: &BlockNode{
39                                 Bits:    1000,
40                                 Hash:    testutil.MustDecodeHash("f1a5a6ddebad7285928a07ce1534104a8d1cd435fc80e90bb9f0034bbe5f8109"),
41                                 Seed:    consensus.InitialSeed,
42                                 WorkSum: new(big.Int).SetInt64(0),
43                                 Parent: &BlockNode{
44                                         WorkSum: &big.Int{},
45                                 },
46                         },
47                 },
48                 {
49                         blockHeader: &types.BlockHeader{
50                                 Height:    uint64(100),
51                                 Timestamp: 0,
52                                 Bits:      10000000000,
53                         },
54                         parentNode: &BlockNode{
55                                 WorkSum: new(big.Int).SetInt64(100),
56                         },
57                         wantBlockNode: &BlockNode{
58                                 Bits:    10000000000,
59                                 Hash:    testutil.MustDecodeHash("b14067726f09d74da89aeb97ca1b15a8b95760b47a0d71549b0aa5ab8c5e724f"),
60                                 Seed:    consensus.InitialSeed,
61                                 Height:  uint64(100),
62                                 WorkSum: stringToBigInt("193956598387464313942329958138505708296934647681139973265423088790474254103", 10),
63                                 Parent: &BlockNode{
64                                         WorkSum: new(big.Int).SetInt64(100),
65                                 },
66                         },
67                 },
68                 {
69                         blockHeader: &types.BlockHeader{
70                                 Height:    uint64(100),
71                                 Timestamp: 0,
72                                 Bits:      10000000000,
73                         },
74                         parentNode: &BlockNode{
75                                 WorkSum: new(big.Int).SetInt64(math.MaxInt64),
76                         },
77                         wantBlockNode: &BlockNode{
78                                 Bits:    10000000000,
79                                 Hash:    testutil.MustDecodeHash("b14067726f09d74da89aeb97ca1b15a8b95760b47a0d71549b0aa5ab8c5e724f"),
80                                 Seed:    consensus.InitialSeed,
81                                 Height:  uint64(100),
82                                 WorkSum: stringToBigInt("193956598387464313942329958138505708296934647681139973274646460827329029810", 10),
83                                 Parent: &BlockNode{
84                                         WorkSum: new(big.Int).SetInt64(math.MaxInt64),
85                                 },
86                         },
87                 },
88         }
89
90         for i, c := range cases {
91                 blockNode, err := NewBlockNode(c.blockHeader, c.parentNode)
92                 if err != nil {
93                         t.Fatal(err)
94                 }
95
96                 if !reflect.DeepEqual(blockNode, c.wantBlockNode) {
97                         t.Fatal("NewBlockNode test error, index:", i, "want:", spew.Sdump(c.wantBlockNode), "got:", spew.Sdump(blockNode))
98                 }
99         }
100 }
101
102 func TestCalcPastMedianTime(t *testing.T) {
103         cases := []struct {
104                 Timestamps []uint64
105                 MedianTime uint64
106         }{
107                 {
108                         Timestamps: []uint64{1},
109                         MedianTime: 1,
110                 },
111                 {
112                         Timestamps: []uint64{1, 2},
113                         MedianTime: 2,
114                 },
115                 {
116                         Timestamps: []uint64{1, 3, 2},
117                         MedianTime: 2,
118                 },
119                 {
120                         Timestamps: []uint64{1, 3, 2, 3},
121                         MedianTime: 3,
122                 },
123                 {
124                         Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9},
125                         MedianTime: 6,
126                 },
127                 {
128                         Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9, 11, 11, 11, 14},
129                         MedianTime: 10,
130                 },
131         }
132
133         for idx, c := range cases {
134                 var parentNode *BlockNode
135                 for i := range c.Timestamps {
136                         blockHeader := &types.BlockHeader{
137                                 Height:    uint64(i),
138                                 Timestamp: c.Timestamps[i],
139                         }
140
141                         blockNode, err := NewBlockNode(blockHeader, parentNode)
142                         if err != nil {
143                                 t.Fatal(err)
144                         }
145                         parentNode = blockNode
146                 }
147
148                 medianTime := parentNode.CalcPastMedianTime()
149                 if medianTime != c.MedianTime {
150                         t.Fatalf("calc median timestamp failed, index: %d, expected: %d, have: %d", idx, c.MedianTime, medianTime)
151                 }
152         }
153 }
154
155 func TestCalcNextBits(t *testing.T) {
156         targetTimeSpan := uint64(consensus.BlocksPerRetarget * consensus.TargetSecondsPerBlock)
157         cases := []struct {
158                 parentNode  *BlockNode
159                 currentNode *BlockNode
160                 bits        uint64
161         }{
162                 {
163                         currentNode: &BlockNode{
164                                 Height: 0,
165                                 Bits:   1000,
166                         },
167                         bits: 1000,
168                 },
169                 {
170                         currentNode: &BlockNode{
171                                 Height: consensus.BlocksPerRetarget - 1,
172                                 Bits:   1000,
173                         },
174                         bits: 1000,
175                 },
176                 {
177                         parentNode: &BlockNode{
178                                 Height:    0,
179                                 Timestamp: 0,
180                         },
181                         currentNode: &BlockNode{
182                                 Height:    consensus.BlocksPerRetarget,
183                                 Bits:      difficulty.BigToCompact(big.NewInt(1000)),
184                                 Timestamp: targetTimeSpan,
185                         },
186                         bits: difficulty.BigToCompact(big.NewInt(1000)),
187                 },
188                 {
189                         parentNode: &BlockNode{
190                                 Height:    0,
191                                 Timestamp: 0,
192                         },
193                         currentNode: &BlockNode{
194                                 Height:    consensus.BlocksPerRetarget,
195                                 Bits:      difficulty.BigToCompact(big.NewInt(1000)),
196                                 Timestamp: targetTimeSpan * 2,
197                         },
198                         bits: difficulty.BigToCompact(big.NewInt(2000)),
199                 },
200         }
201
202         for i, c := range cases {
203                 c.currentNode.Parent = c.parentNode
204                 bits := c.currentNode.CalcNextBits()
205                 if bits != c.bits {
206                         t.Fatalf("calc next bit failed, index: %d, expected: %d, have: %d", i, c.bits, bits)
207                 }
208         }
209 }
210
211 func TestCalcNextSeed(t *testing.T) {
212         cases := []struct {
213                 node *BlockNode
214                 seed *bc.Hash
215         }{
216                 {
217                         node: &BlockNode{
218                                 Height: 0,
219                         },
220                         seed: consensus.InitialSeed,
221                 },
222                 {
223                         node: &BlockNode{
224                                 Height: consensus.SeedPerRetarget - 1,
225                                 Seed:   &bc.Hash{V1: 100},
226                         },
227                         seed: &bc.Hash{V1: 100},
228                 },
229                 {
230                         node: &BlockNode{
231                                 Height: consensus.SeedPerRetarget,
232                                 Seed:   &bc.Hash{V2: 200},
233                                 Hash:   bc.Hash{V3: 300},
234                         },
235                         seed: &bc.Hash{V3: 300},
236                 },
237         }
238
239         for i, c := range cases {
240                 seed := c.node.CalcNextSeed()
241                 if *seed != *c.seed {
242                         t.Fatalf("calc next seed failed, index: %d, expected: %v, have: %v", i, c.seed, seed)
243                 }
244         }
245 }
246
247 func TestSetMainChain(t *testing.T) {
248         blockIndex := NewBlockIndex()
249         var lastNode *BlockNode
250         for i := uint64(0); i < 4; i++ {
251                 node := &BlockNode{
252                         Height: i,
253                         Hash:   bc.Hash{V0: i},
254                         Parent: lastNode,
255                 }
256                 blockIndex.AddNode(node)
257                 lastNode = node
258         }
259
260         tailNode := lastNode
261         blockIndex.SetMainChain(lastNode)
262         for lastNode.Parent != nil {
263                 if !blockIndex.InMainchain(lastNode.Hash) {
264                         t.Fatalf("block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash)
265                 }
266                 lastNode = lastNode.Parent
267         }
268
269         // fork and set main chain
270         forkHeight := uint64(1)
271         lastNode = blockIndex.nodeByHeight(forkHeight)
272         for i := uint64(1); i <= 3; i++ {
273                 node := &BlockNode{
274                         Height: lastNode.Height + 1,
275                         Hash:   bc.Hash{V1: uint64(i)},
276                         Parent: lastNode,
277                 }
278                 blockIndex.AddNode(node)
279                 lastNode = node
280         }
281
282         bestNode := lastNode
283         blockIndex.SetMainChain(lastNode)
284         for lastNode.Parent != nil {
285                 if !blockIndex.InMainchain(lastNode.Hash) {
286                         t.Fatalf("after fork, block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash)
287                 }
288                 lastNode = lastNode.Parent
289         }
290
291         if bestNode != blockIndex.BestNode() {
292                 t.Fatalf("check best node failed")
293         }
294
295         for tailNode.Parent != nil && tailNode.Height > forkHeight {
296                 if blockIndex.InMainchain(tailNode.Hash) {
297                         t.Fatalf("old chain block %d, hash %v still in main chain", tailNode.Height, tailNode.Hash)
298                 }
299                 tailNode = tailNode.Parent
300         }
301 }
302
303 // MockBlockIndex will mock a empty BlockIndex
304 func MockBlockIndex() *BlockIndex {
305         return &BlockIndex{
306                 index:     make(map[bc.Hash]*BlockNode),
307                 mainChain: make([]*BlockNode, 0, 2),
308         }
309 }
310
311 func TestSetMainChainExtendCap(t *testing.T) {
312         blockIndex := MockBlockIndex()
313         var lastNode *BlockNode
314
315         cases := []struct {
316                 start   uint64
317                 stop    uint64
318                 wantLen int
319                 wantCap int
320         }{
321                 {
322                         start:   0,
323                         stop:    500,
324                         wantLen: 500,
325                         wantCap: 500 + approxNodesPerDay,
326                 },
327                 {
328                         start:   500,
329                         stop:    1000,
330                         wantLen: 1000,
331                         wantCap: 500 + approxNodesPerDay,
332                 },
333                 {
334                         start:   1000,
335                         stop:    2000,
336                         wantLen: 2000,
337                         wantCap: 2000 + approxNodesPerDay,
338                 },
339         }
340
341         for num, c := range cases {
342                 for i := c.start; i < c.stop; i++ {
343                         node := &BlockNode{
344                                 Height: i,
345                                 Hash:   bc.Hash{V0: i},
346                                 Parent: lastNode,
347                         }
348                         blockIndex.AddNode(node)
349                         lastNode = node
350                 }
351                 blockIndex.SetMainChain(lastNode)
352                 if c.wantLen != len(blockIndex.mainChain) || c.wantCap != cap(blockIndex.mainChain) {
353                         t.Fatalf("SetMainChain extended capacity error, index: %d, got len: %d, got cap: %d, want len: %d, want cap: %d", num, len(blockIndex.mainChain), cap(blockIndex.mainChain), c.wantLen, c.wantCap)
354                 }
355         }
356
357         for i := 0; i < len(blockIndex.mainChain); i++ {
358                 if blockIndex.mainChain[i] != blockIndex.index[blockIndex.mainChain[i].Hash] {
359                         t.Fatal("SetMainChain extended capacity error, index:", i, "want:", spew.Sdump(blockIndex.mainChain[i]), "got:", spew.Sdump(blockIndex.index[blockIndex.mainChain[i].Hash]))
360                 }
361         }
362 }