github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/state/blockindex_test.go (about)

     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/bytom/consensus"
    12  	"github.com/bytom/bytom/consensus/difficulty"
    13  	"github.com/bytom/bytom/protocol/bc"
    14  	"github.com/bytom/bytom/protocol/bc/types"
    15  	"github.com/bytom/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  }