github.com/halybang/go-ethereum@v1.0.5-0.20180325041310-3b262bc1367c/consensus/ethash/snapshot_test.go (about)

     1  // Copyright 2018 Wanchain Foundation Ltd
     2  
     3  package ethash
     4  
     5  import (
     6  	"crypto/ecdsa"
     7  	"math/big"
     8  	"math/rand"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/wanchain/go-wanchain/common"
    13  	"github.com/wanchain/go-wanchain/core/types"
    14  	"github.com/wanchain/go-wanchain/crypto"
    15  	"github.com/wanchain/go-wanchain/ethdb"
    16  )
    17  
    18  type SignerInfo struct {
    19  	private *ecdsa.PrivateKey
    20  	addr    common.Address
    21  	str     string
    22  	index   int
    23  }
    24  
    25  var (
    26  	// assert never be lower than 6
    27  	totalSigner                              = 20
    28  	signerSet                                = make(map[string]*SignerInfo)
    29  	addrStrArray                             = make([]string, 0)
    30  	addrArray                                = make([]common.Address, 0)
    31  	indexAddrStrMap                          = make(map[int]string)
    32  	unAuthorizedSigner                       = common.Address{}
    33  	unAuthorizedPrivateKey *ecdsa.PrivateKey = nil
    34  )
    35  
    36  func init() {
    37  	// generate
    38  	for i := 0; i < totalSigner; i++ {
    39  		private, _ := crypto.GenerateKey()
    40  		addr := crypto.PubkeyToAddress(private.PublicKey)
    41  		str := addr.String()
    42  		signerSet[str] = &SignerInfo{private: private, addr: addr, str: str, index: i}
    43  		addrStrArray = append(addrStrArray, str)
    44  		addrArray = append(addrArray, addr)
    45  		indexAddrStrMap[i] = str
    46  	}
    47  	unAuthorizedPrivateKey, _ = crypto.GenerateKey()
    48  	unAuthorizedSigner.Set(crypto.PubkeyToAddress(unAuthorizedPrivateKey.PublicKey))
    49  }
    50  
    51  //store and retrieve permission pow
    52  func TestStoreAndLoadEmptySnapshot(t *testing.T) {
    53  	//create a initial snapshot with only one permission signer
    54  	addr := addrStrArray[0]
    55  	genesisAddr := signerSet[addr].addr
    56  	hash := crypto.Keccak256Hash([]byte{0})
    57  	s := newSnapshot(0, hash, []common.Address{genesisAddr})
    58  
    59  	db, _ := ethdb.NewMemDatabase()
    60  	s.store(db)
    61  
    62  	sload, _ := loadSnapShot(db, hash)
    63  	if len(sload.PermissionSigners) != 1 || sload.Number != 0 ||
    64  		len(sload.UsedSigners) != 0 || sload.RecentSignersWindow.Len() != 0 {
    65  		t.Error("load snapshot failed")
    66  	}
    67  
    68  	if _, ok := sload.PermissionSigners[genesisAddr]; !ok {
    69  		t.Error("load snapshot failed")
    70  	}
    71  }
    72  
    73  //store and retrieve permission pow
    74  func TestStoreAndLoadRunningSnapshot(t *testing.T) {
    75  	hash := crypto.Keccak256Hash([]byte{0})
    76  	blockNumber := uint64(88)
    77  	s := newSnapshot(blockNumber, hash, addrArray)
    78  
    79  	usedCount := totalSigner / 2
    80  	for i := 0; i < usedCount; i++ {
    81  		s.UsedSigners[addrArray[i]] = struct{}{}
    82  	}
    83  
    84  	windowLen := (usedCount - 1) / 2
    85  	for i := windowLen - 1; i >= 0; i-- {
    86  		s.RecentSignersWindow.PushFront(addrArray[i])
    87  	}
    88  
    89  	db, _ := ethdb.NewMemDatabase()
    90  	s.store(db)
    91  
    92  	sload, _ := loadSnapShot(db, hash)
    93  	if len(sload.PermissionSigners) != totalSigner || sload.Number != blockNumber ||
    94  		len(sload.UsedSigners) != usedCount || sload.RecentSignersWindow.Len() != windowLen {
    95  		t.Error("load snapshot failed")
    96  	}
    97  
    98  	for i := 0; i < usedCount; i++ {
    99  		if _, ok := sload.PermissionSigners[addrArray[i]]; !ok {
   100  			t.Error("load snapshot failed")
   101  		}
   102  	}
   103  
   104  	i := 0
   105  	for e := s.RecentSignersWindow.Front(); e != nil; e = e.Next() {
   106  		addr := e.Value.(common.Address)
   107  		if strings.Compare(addr.String(), addrStrArray[i]) != 0 {
   108  			t.Error("error in recent window store or retrieve")
   109  		}
   110  		i++
   111  	}
   112  	if i != windowLen {
   113  		t.Error("window length is not right")
   114  	}
   115  }
   116  
   117  func sign(header *types.Header, signer common.Address) {
   118  	si := signerSet[signer.String()]
   119  	sig, _ := crypto.Sign(sigHash(header).Bytes(), si.private)
   120  	copy(header.Extra[len(header.Extra)-65:], sig)
   121  }
   122  
   123  // indexes indicated use which signer to sign header
   124  func prepareHeaders(indexes []int, blockNumbers []int) []*types.Header {
   125  	headers := make([]*types.Header, 0)
   126  	for i, n := range indexes {
   127  		signer := addrArray[n]
   128  		h := &types.Header{
   129  			Coinbase: signer,
   130  			Time:     big.NewInt(int64(blockNumbers[i]) * int64(1000)),
   131  			Number:   big.NewInt(int64(blockNumbers[i])),
   132  			Extra:    make([]byte, extraSeal+extraVanity),
   133  		}
   134  		sign(h, signer)
   135  		headers = append(headers, h)
   136  	}
   137  	return headers
   138  }
   139  
   140  func prepareUnAuthorizedSignerHeader(blockNumber int) []*types.Header {
   141  	headers := make([]*types.Header, 0)
   142  	h := &types.Header{
   143  		Coinbase: unAuthorizedSigner,
   144  		Time:     big.NewInt(int64(blockNumber) * int64(1000)),
   145  		Number:   big.NewInt(int64(blockNumber)),
   146  		Extra:    make([]byte, extraSeal+extraVanity),
   147  	}
   148  	sig, _ := crypto.Sign(sigHash(h).Bytes(), unAuthorizedPrivateKey)
   149  	copy(h.Extra[len(h.Extra)-65:], sig)
   150  	headers = append(headers, h)
   151  	return headers
   152  }
   153  
   154  func TestPPOWApplyingFixedCorrectHeaders(t *testing.T) {
   155  	hash := crypto.Keccak256Hash([]byte{0})
   156  	blockNumber := 0
   157  	s := newSnapshot(uint64(blockNumber), hash, addrArray)
   158  	blockNumber++
   159  
   160  	usingSigners := totalSigner - 3
   161  	signerIndexes := make([]int, 0)
   162  	blockNumbers := make([]int, 0)
   163  	expectWindowLen := (usingSigners - 1) / 2
   164  	for i := 0; i < usingSigners; i++ {
   165  		signerIndexes = append(signerIndexes, i)
   166  		blockNumbers = append(blockNumbers, blockNumber)
   167  		blockNumber++
   168  	}
   169  	for i := 0; i < expectWindowLen; i++ {
   170  		signerIndexes = append(signerIndexes, i)
   171  		blockNumbers = append(blockNumbers, blockNumber)
   172  		blockNumber++
   173  	}
   174  	headers := prepareHeaders(signerIndexes, blockNumbers)
   175  	_, err := s.apply(headers)
   176  	if err != nil {
   177  		t.Error("apply shouldn't be failed ")
   178  	}
   179  
   180  	for i := 0; i < usingSigners; i++ {
   181  		if _, ok := s.PermissionSigners[addrArray[i]]; !ok {
   182  			t.Error("used signer didn't record")
   183  		}
   184  	}
   185  
   186  	expectedIndex := expectWindowLen - 1
   187  	for e := s.RecentSignersWindow.Front(); e != nil; e = e.Next() {
   188  		addr := e.Value.(common.Address)
   189  		if strings.Compare(addr.String(), addrStrArray[expectedIndex]) != 0 {
   190  			t.Error("error in recent window store or retrieve")
   191  		}
   192  		expectedIndex--
   193  	}
   194  }
   195  
   196  func TestPPOWApplyingErrBlockNumberHeaders(t *testing.T) {
   197  	hash := crypto.Keccak256Hash([]byte{0})
   198  	blockNumber := 0
   199  	s := newSnapshot(uint64(blockNumber), hash, addrArray)
   200  	blockNumber++
   201  
   202  	//invalid block headers number order
   203  	invalidNumberHeaders := prepareHeaders([]int{0, 1}, []int{blockNumber + 1, blockNumber})
   204  	_, err := s.apply(invalidNumberHeaders)
   205  	if err == nil {
   206  		t.Error("apply error invalid order block number")
   207  	}
   208  }
   209  
   210  func TestPPOWApplyingUnAuthorizedHeader(t *testing.T) {
   211  	hash := crypto.Keccak256Hash([]byte{0})
   212  	blockNumber := 0
   213  	s := newSnapshot(uint64(blockNumber), hash, addrArray)
   214  	blockNumber++
   215  
   216  	invalidNumberHeaders := prepareUnAuthorizedSignerHeader(blockNumber)
   217  	_, err := s.apply(invalidNumberHeaders)
   218  	if err == nil {
   219  		t.Error("apply error invalid order block number")
   220  	}
   221  }
   222  
   223  func TestPPOWApplyingNotContinueNumberHeaders(t *testing.T) {
   224  	hash := crypto.Keccak256Hash([]byte{0})
   225  	blockNumber := 8
   226  	s := newSnapshot(uint64(blockNumber), hash, addrArray)
   227  
   228  	emptyHeaders := prepareHeaders([]int{}, []int{})
   229  	_, err := s.apply(emptyHeaders)
   230  	if err != nil {
   231  		t.Error("process empty header list failed")
   232  	}
   233  	//invalid block headers number order
   234  	invalidNumberHeaders := prepareHeaders([]int{0}, []int{blockNumber})
   235  	_, err = s.apply(invalidNumberHeaders)
   236  	if err == nil {
   237  		t.Error("apply error invalid order block number")
   238  	}
   239  	invalidNumberHeaders = prepareHeaders([]int{0}, []int{blockNumber - 1})
   240  	_, err = s.apply(invalidNumberHeaders)
   241  	if err == nil {
   242  		t.Error("apply error invalid order block number")
   243  	}
   244  	invalidNumberHeaders = prepareHeaders([]int{0}, []int{blockNumber + 2})
   245  	_, err = s.apply(invalidNumberHeaders)
   246  	if err == nil {
   247  		t.Error("apply error invalid order block number")
   248  	}
   249  }
   250  
   251  //if have error, then
   252  func internalApply(snap *Snapshot, snapNumber uint64, headers []*types.Header, state *internalState) (uint64, *internalState) {
   253  	backupState := state.copy()
   254  	if len(headers) == 0 {
   255  		return snapNumber, backupState
   256  	}
   257  
   258  	for i := 0; i < len(headers)-1; i++ {
   259  		if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 {
   260  			return snapNumber, backupState
   261  		}
   262  	}
   263  
   264  	if headers[0].Number.Uint64() != snapNumber+1 {
   265  		return snapNumber, backupState
   266  	}
   267  
   268  	for _, header := range headers {
   269  		signer, err := ecrecover(header)
   270  		if err != nil || 0 != strings.Compare(signer.String(), header.Coinbase.String()) {
   271  			return snapNumber, backupState
   272  		}
   273  
   274  		if _, ok := snap.PermissionSigners[signer]; !ok {
   275  			return snapNumber, backupState
   276  		}
   277  
   278  		for _, wSigner := range state.testWindow {
   279  			if signer == wSigner {
   280  				return snapNumber, backupState
   281  			}
   282  		}
   283  
   284  		_, ok := state.testUsedSigners[signer]
   285  		internalUpdateWindow(state, signer, ok)
   286  	}
   287  	return headers[len(headers)-1].Number.Uint64(), state
   288  }
   289  
   290  func internalUpdateWindow(state *internalState, signer common.Address, isExist bool) {
   291  	if isExist {
   292  		//if signer already presence
   293  		if (len(state.testWindow)) > 0 {
   294  			backup := make([]common.Address, 0)
   295  			backup = append(backup, signer)
   296  			backup = append(backup, state.testWindow...)
   297  			state.testWindow = make([]common.Address, len(backup)-1)
   298  			copy(state.testWindow, backup[:len(backup)-1])
   299  		}
   300  	} else {
   301  		// This is the first time the signer appear
   302  		state.testUsedSigners[signer] = struct{}{}
   303  		preWindowLen := len(state.testWindow)
   304  		newWindowLen := (len(state.testUsedSigners) - 1) / windowRatio
   305  
   306  		if newWindowLen > preWindowLen {
   307  			backup := make([]common.Address, 0)
   308  			backup = append(backup, signer)
   309  			backup = append(backup, state.testWindow...)
   310  			state.testWindow = make([]common.Address, len(backup))
   311  			copy(state.testWindow, backup)
   312  		} else {
   313  			//windowLen unchanged
   314  			if newWindowLen > 0 {
   315  				backup := make([]common.Address, 0)
   316  				backup = append(backup, signer)
   317  				backup = append(backup, state.testWindow...)
   318  				state.testWindow = make([]common.Address, len(backup)-1)
   319  				copy(state.testWindow, backup[:len(backup)-1])
   320  			}
   321  		}
   322  	}
   323  }
   324  
   325  type internalState struct {
   326  	testUsedSigners map[common.Address]struct{}
   327  	testWindow      []common.Address
   328  }
   329  
   330  func (self *internalState) copy() *internalState {
   331  	cpy := &internalState{
   332  		testUsedSigners: make(map[common.Address]struct{}),
   333  		testWindow:      make([]common.Address, 0),
   334  	}
   335  
   336  	for k := range self.testUsedSigners {
   337  		cpy.testUsedSigners[k] = struct{}{}
   338  	}
   339  
   340  	for _, e := range self.testWindow {
   341  		cpy.testWindow = append(cpy.testWindow, e)
   342  	}
   343  	return cpy
   344  }
   345  
   346  // generate x times random headers to apply
   347  func TestPPOWApplyingRandom(t *testing.T) {
   348  	tests := []struct {
   349  		headersLen int
   350  		loopTimes  int
   351  	}{
   352  		{
   353  			headersLen: 1,
   354  			loopTimes:  100,
   355  		},
   356  		{
   357  			headersLen: 2,
   358  			loopTimes:  80,
   359  		},
   360  		{
   361  			headersLen: 3,
   362  			loopTimes:  60,
   363  		},
   364  		{
   365  			headersLen: 5,
   366  			loopTimes:  50,
   367  		},
   368  	}
   369  	for _, loopInfo := range tests {
   370  		loops := totalSigner * loopInfo.loopTimes
   371  		hash := crypto.Keccak256Hash([]byte{0})
   372  		blockNumber := 0
   373  		s := newSnapshot(uint64(blockNumber), hash, addrArray)
   374  		blockNumber++
   375  
   376  		usingSigners := totalSigner - 5
   377  
   378  		state := &internalState{
   379  			testUsedSigners: make(map[common.Address]struct{}),
   380  			testWindow:      make([]common.Address, 0),
   381  		}
   382  
   383  		for ; loops > 0; loops-- {
   384  			number := s.Number + 1
   385  			indexes := make([]int, 0)
   386  			numbers := make([]int, 0)
   387  			for ri := 0; ri < loopInfo.headersLen; ri++ {
   388  				selectNumber := rand.Intn(usingSigners)
   389  				indexes = append(indexes, selectNumber)
   390  				numbers = append(numbers, int(number))
   391  				number++
   392  			}
   393  			headers := prepareHeaders(indexes, numbers)
   394  			reservedNumber := s.Number
   395  			ns, err := s.apply(headers)
   396  			if err == nil {
   397  				s = ns
   398  			}
   399  			var afterApplyNum uint64
   400  			afterApplyNum, state = internalApply(s, reservedNumber, headers, state)
   401  			if afterApplyNum != s.Number {
   402  				t.Errorf("invalid status of snapshot :snapshot num: %d compared num: %d\n", int(s.Number), int(afterApplyNum))
   403  			}
   404  		}
   405  		//compare used signers and window equal
   406  		if len(state.testUsedSigners) != len(s.UsedSigners) {
   407  			t.Error("usedSigners length incorrect")
   408  		}
   409  
   410  		for signer := range state.testUsedSigners {
   411  			if _, ok := s.UsedSigners[signer]; !ok {
   412  				t.Error("used signers element invalid")
   413  			}
   414  		}
   415  
   416  		if s.RecentSignersWindow.Len() != len(state.testWindow) {
   417  			t.Error("recent signer window len incorrect")
   418  		}
   419  
   420  		windowIndex := 0
   421  		for e := s.RecentSignersWindow.Front(); e != nil; e = e.Next() {
   422  			addr := e.Value.(common.Address)
   423  			if strings.Compare(addr.String(), state.testWindow[windowIndex].String()) != 0 {
   424  				t.Error("error in recent window signer")
   425  			}
   426  			windowIndex++
   427  		}
   428  	}
   429  }
   430  
   431  func TestIsSignerLegal(t *testing.T) {
   432  	hash := crypto.Keccak256Hash([]byte{0})
   433  	blockNumber := 0
   434  	s := newSnapshot(uint64(blockNumber), hash, addrArray)
   435  	blockNumber++
   436  
   437  	usingSigners := totalSigner - 3
   438  	signerIndexes := make([]int, 0)
   439  	blockNumbers := make([]int, 0)
   440  	for i := 0; i < usingSigners; i++ {
   441  		signerIndexes = append(signerIndexes, i)
   442  		blockNumbers = append(blockNumbers, blockNumber)
   443  		blockNumber++
   444  	}
   445  	headers := prepareHeaders(signerIndexes, blockNumbers)
   446  	s, _ = s.apply(headers)
   447  
   448  	if nil == s.isLegal4Sign(unAuthorizedSigner) {
   449  		t.Error("invalid process unauthorized signer")
   450  	}
   451  
   452  	if nil == s.isLegal4Sign(addrArray[usingSigners-1]) {
   453  		t.Error("invalid process in window signer")
   454  	}
   455  
   456  	if nil != s.isLegal4Sign(addrArray[usingSigners]) {
   457  		t.Error("invalid process valid signer")
   458  	}
   459  }