github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/bft/backend/backend_test.go (about)

     1  package backend
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ecdsa"
     6  	"sort"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/quickchainproject/quickchain/common"
    12  	"github.com/quickchainproject/quickchain/consensus/bft"
    13  	"github.com/quickchainproject/quickchain/consensus/bft/validator"
    14  	"github.com/quickchainproject/quickchain/core/types"
    15  	"github.com/quickchainproject/quickchain/crypto"
    16  )
    17  
    18  func TestSign(t *testing.T) {
    19  	b := newBackend()
    20  	data := []byte("Here is a string....")
    21  	sig, err := b.Sign(data)
    22  	if err != nil {
    23  		t.Errorf("error mismatch: have %v, want nil", err)
    24  	}
    25  	//Check signature recover
    26  	hashData := crypto.Keccak256([]byte(data))
    27  	pubkey, _ := crypto.Ecrecover(hashData, sig)
    28  	var signer common.Address
    29  	copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
    30  	if signer != getAddress() {
    31  		t.Errorf("address mismatch: have %v, want %s", signer.Hex(), getAddress().Hex())
    32  	}
    33  }
    34  
    35  func TestCheckSignature(t *testing.T) {
    36  	key, _ := generatePrivateKey()
    37  	data := []byte("Here is a string....")
    38  	hashData := crypto.Keccak256([]byte(data))
    39  	sig, _ := crypto.Sign(hashData, key)
    40  	b := newBackend()
    41  	a := getAddress()
    42  	err := b.CheckSignature(data, a, sig)
    43  	if err != nil {
    44  		t.Errorf("error mismatch: have %v, want nil", err)
    45  	}
    46  	a = getInvalidAddress()
    47  	err = b.CheckSignature(data, a, sig)
    48  	if err != errInvalidSignature {
    49  		t.Errorf("error mismatch: have %v, want %v", err, errInvalidSignature)
    50  	}
    51  }
    52  
    53  func TestCheckValidatorSignature(t *testing.T) {
    54  	vset, keys := newTestValidatorSet(5)
    55  
    56  	// 1. Positive test: sign with validator's key should succeed
    57  	data := []byte("dummy data")
    58  	hashData := crypto.Keccak256([]byte(data))
    59  	for i, k := range keys {
    60  		// Sign
    61  		sig, err := crypto.Sign(hashData, k)
    62  		if err != nil {
    63  			t.Errorf("error mismatch: have %v, want nil", err)
    64  		}
    65  		// CheckValidatorSignature should succeed
    66  		addr, err := bft.CheckValidatorSignature(vset, data, sig)
    67  		if err != nil {
    68  			t.Errorf("error mismatch: have %v, want nil", err)
    69  		}
    70  		validator := vset.GetByIndex(uint64(i))
    71  		if addr != validator.Address() {
    72  			t.Errorf("validator address mismatch: have %v, want %v", addr, validator.Address())
    73  		}
    74  	}
    75  
    76  	// 2. Negative test: sign with any key other than validator's key should return error
    77  	key, err := crypto.GenerateKey()
    78  	if err != nil {
    79  		t.Errorf("error mismatch: have %v, want nil", err)
    80  	}
    81  	// Sign
    82  	sig, err := crypto.Sign(hashData, key)
    83  	if err != nil {
    84  		t.Errorf("error mismatch: have %v, want nil", err)
    85  	}
    86  
    87  	// CheckValidatorSignature should return ErrUnauthorizedAddress
    88  	addr, err := bft.CheckValidatorSignature(vset, data, sig)
    89  	if err != bft.ErrUnauthorizedAddress {
    90  		t.Errorf("error mismatch: have %v, want %v", err, bft.ErrUnauthorizedAddress)
    91  	}
    92  	emptyAddr := common.Address{}
    93  	if addr != emptyAddr {
    94  		t.Errorf("address mismatch: have %v, want %v", addr, emptyAddr)
    95  	}
    96  }
    97  
    98  func TestCommit(t *testing.T) {
    99  	backend := newBackend()
   100  
   101  	commitCh := make(chan *types.Block)
   102  	// Case: it's a proposer, so the backend.commit will receive channel result from backend.Commit function
   103  	testCases := []struct {
   104  		expectedErr       error
   105  		expectedSignature [][]byte
   106  		expectedBlock     func() *types.Block
   107  	}{
   108  		{
   109  			// normal case
   110  			nil,
   111  			[][]byte{append([]byte{1}, bytes.Repeat([]byte{0x00}, types.BFTExtraSeal-1)...)},
   112  			func() *types.Block {
   113  				chain, engine := newBlockChain(1)
   114  				block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   115  				expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
   116  				return expectedBlock
   117  			},
   118  		},
   119  		{
   120  			// invalid signature
   121  			errInvalidCommittedSeals,
   122  			nil,
   123  			func() *types.Block {
   124  				chain, engine := newBlockChain(1)
   125  				block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
   126  				expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
   127  				return expectedBlock
   128  			},
   129  		},
   130  	}
   131  
   132  	for _, test := range testCases {
   133  		expBlock := test.expectedBlock()
   134  		go func() {
   135  			select {
   136  			case result := <-backend.commitCh:
   137  				commitCh <- result
   138  				return
   139  			}
   140  		}()
   141  
   142  		backend.proposedBlockHash = expBlock.Hash()
   143  		if err := backend.Commit(expBlock, test.expectedSignature); err != nil {
   144  			if err != test.expectedErr {
   145  				t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr)
   146  			}
   147  		}
   148  
   149  		if test.expectedErr == nil {
   150  			// to avoid race condition is occurred by goroutine
   151  			select {
   152  			case result := <-commitCh:
   153  				if result.Hash() != expBlock.Hash() {
   154  					t.Errorf("hash mismatch: have %v, want %v", result.Hash(), expBlock.Hash())
   155  				}
   156  			case <-time.After(10 * time.Second):
   157  				t.Fatal("timeout")
   158  			}
   159  		}
   160  	}
   161  }
   162  
   163  func TestGetProposer(t *testing.T) {
   164  	chain, engine := newBlockChain(1)
   165  	block := makeBlock(chain, engine, chain.Genesis())
   166  	chain.InsertChain(types.Blocks{block})
   167  	expected := engine.GetProposer(1)
   168  	actual := engine.Address()
   169  	if actual != expected {
   170  		t.Errorf("proposer mismatch: have %v, want %v", actual.Hex(), expected.Hex())
   171  	}
   172  }
   173  
   174  /**
   175   * SimpleBackend
   176   * Private key: bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1
   177   * Public key: 04a2bfb0f7da9e1b9c0c64e14f87e8fb82eb0144e97c25fe3a977a921041a50976984d18257d2495e7bfd3d4b280220217f429287d25ecdf2b0d7c0f7aae9aa624
   178   * Address: 0x70524d664ffe731100208a0154e556f9bb679ae6
   179   */
   180  func getAddress() common.Address {
   181  	return common.HexToAddress("0x70524d664ffe731100208a0154e556f9bb679ae6")
   182  }
   183  
   184  func getInvalidAddress() common.Address {
   185  	return common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b")
   186  }
   187  
   188  func generatePrivateKey() (*ecdsa.PrivateKey, error) {
   189  	key := "bb047e5940b6d83354d9432db7c449ac8fca2248008aaa7271369880f9f11cc1"
   190  	return crypto.HexToECDSA(key)
   191  }
   192  
   193  func newTestValidatorSet(n int) (bft.ValidatorSet, []*ecdsa.PrivateKey) {
   194  	// generate validators
   195  	keys := make(Keys, n)
   196  	addrs := make([]common.Address, n)
   197  	for i := 0; i < n; i++ {
   198  		privateKey, _ := crypto.GenerateKey()
   199  		keys[i] = privateKey
   200  		addrs[i] = crypto.PubkeyToAddress(privateKey.PublicKey)
   201  	}
   202  	vset := validator.NewSet(addrs, bft.RoundRobin)
   203  	sort.Sort(keys) //Keys need to be sorted by its public key address
   204  	return vset, keys
   205  }
   206  
   207  type Keys []*ecdsa.PrivateKey
   208  
   209  func (slice Keys) Len() int {
   210  	return len(slice)
   211  }
   212  
   213  func (slice Keys) Less(i, j int) bool {
   214  	return strings.Compare(crypto.PubkeyToAddress(slice[i].PublicKey).String(), crypto.PubkeyToAddress(slice[j].PublicKey).String()) < 0
   215  }
   216  
   217  func (slice Keys) Swap(i, j int) {
   218  	slice[i], slice[j] = slice[j], slice[i]
   219  }
   220  
   221  func newBackend() (b *backend) {
   222  	_, b = newBlockChain(4)
   223  	key, _ := generatePrivateKey()
   224  	b.privateKey = key
   225  	return
   226  }