github.com/daefrom/go-dae@v1.0.1/contracts/checkpointoracle/oracle_test.go (about)

     1  // Copyright 2019 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package checkpointoracle
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/ecdsa"
    22  	"encoding/binary"
    23  	"errors"
    24  	"math/big"
    25  	"reflect"
    26  	"sort"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/daefrom/go-dae/accounts/abi/bind"
    31  	"github.com/daefrom/go-dae/accounts/abi/bind/backends"
    32  	"github.com/daefrom/go-dae/common"
    33  	"github.com/daefrom/go-dae/contracts/checkpointoracle/contract"
    34  	"github.com/daefrom/go-dae/core"
    35  	"github.com/daefrom/go-dae/crypto"
    36  	"github.com/daefrom/go-dae/params"
    37  )
    38  
    39  var (
    40  	emptyHash = [32]byte{}
    41  
    42  	checkpoint0 = params.TrustedCheckpoint{
    43  		SectionIndex: 0,
    44  		SectionHead:  common.HexToHash("0x7fa3c32f996c2bfb41a1a65b3d8ea3e0a33a1674cde43678ad6f4235e764d17d"),
    45  		CHTRoot:      common.HexToHash("0x98fc5d3de23a0fecebad236f6655533c157d26a1aedcd0852a514dc1169e6350"),
    46  		BloomRoot:    common.HexToHash("0x99b5adb52b337fe25e74c1c6d3835b896bd638611b3aebddb2317cce27a3f9fa"),
    47  	}
    48  	checkpoint1 = params.TrustedCheckpoint{
    49  		SectionIndex: 1,
    50  		SectionHead:  common.HexToHash("0x2d4dee68102125e59b0cc61b176bd89f0d12b3b91cfaf52ef8c2c82fb920c2d2"),
    51  		CHTRoot:      common.HexToHash("0x7d428008ece3b4c4ef5439f071930aad0bb75108d381308df73beadcd01ded95"),
    52  		BloomRoot:    common.HexToHash("0x652571f7736de17e7bbb427ac881474da684c6988a88bf51b10cca9a2ee148f4"),
    53  	}
    54  	checkpoint2 = params.TrustedCheckpoint{
    55  		SectionIndex: 2,
    56  		SectionHead:  common.HexToHash("0x61c0de578c0115b1dff8ef39aa600588c7c6ecb8a2f102003d7cf4c4146e9291"),
    57  		CHTRoot:      common.HexToHash("0x407a08a407a2bc3838b74ca3eb206903c9c8a186ccf5ef14af07794efff1970b"),
    58  		BloomRoot:    common.HexToHash("0x058b4161f558ce295a92925efc57f34f9210d5a30088d7475c183e0d3e58f5ac"),
    59  	}
    60  )
    61  
    62  var (
    63  	// The block frequency for creating checkpoint(only used in test)
    64  	sectionSize = big.NewInt(512)
    65  
    66  	// The number of confirmations needed to generate a checkpoint(only used in test).
    67  	processConfirms = big.NewInt(4)
    68  )
    69  
    70  // validateOperation executes the operation, watches and delivers all events fired by the backend and ensures the
    71  // correctness by assert function.
    72  func validateOperation(t *testing.T, c *contract.CheckpointOracle, backend *backends.SimulatedBackend, operation func(),
    73  	assert func(<-chan *contract.CheckpointOracleNewCheckpointVote) error, opName string) {
    74  	// Watch all events and deliver them to assert function
    75  	var (
    76  		sink   = make(chan *contract.CheckpointOracleNewCheckpointVote)
    77  		sub, _ = c.WatchNewCheckpointVote(nil, sink, nil)
    78  	)
    79  	defer func() {
    80  		// Close all subscribers
    81  		sub.Unsubscribe()
    82  	}()
    83  	operation()
    84  
    85  	// flush pending block
    86  	backend.Commit()
    87  	if err := assert(sink); err != nil {
    88  		t.Errorf("operation {%s} failed, err %s", opName, err)
    89  	}
    90  }
    91  
    92  // validateEvents checks that the correct number of contract events
    93  // fired by contract backend.
    94  func validateEvents(target int, sink interface{}) (bool, []reflect.Value) {
    95  	chanval := reflect.ValueOf(sink)
    96  	chantyp := chanval.Type()
    97  	if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
    98  		return false, nil
    99  	}
   100  	count := 0
   101  	var recv []reflect.Value
   102  	timeout := time.After(1 * time.Second)
   103  	cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
   104  	for {
   105  		chose, v, _ := reflect.Select(cases)
   106  		if chose == 1 {
   107  			// Not enough event received
   108  			return false, nil
   109  		}
   110  		count += 1
   111  		recv = append(recv, v)
   112  		if count == target {
   113  			break
   114  		}
   115  	}
   116  	done := time.After(50 * time.Millisecond)
   117  	cases = cases[:1]
   118  	cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
   119  	chose, _, _ := reflect.Select(cases)
   120  	// If chose equal 0, it means receiving redundant events.
   121  	return chose == 1, recv
   122  }
   123  
   124  func signCheckpoint(addr common.Address, privateKey *ecdsa.PrivateKey, index uint64, hash common.Hash) []byte {
   125  	// EIP 191 style signatures
   126  	//
   127  	// Arguments when calculating hash to validate
   128  	// 1: byte(0x19) - the initial 0x19 byte
   129  	// 2: byte(0) - the version byte (data with intended validator)
   130  	// 3: this - the validator address
   131  	// --  Application specific data
   132  	// 4 : checkpoint section_index(uint64)
   133  	// 5 : checkpoint hash (bytes32)
   134  	//     hash = keccak256(checkpoint_index, section_head, cht_root, bloom_root)
   135  	buf := make([]byte, 8)
   136  	binary.BigEndian.PutUint64(buf, index)
   137  	data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash.Bytes()...)...)...)
   138  	sig, _ := crypto.Sign(crypto.Keccak256(data), privateKey)
   139  	sig[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
   140  	return sig
   141  }
   142  
   143  // assertSignature verifies whether the recovered signers are equal with expected.
   144  func assertSignature(addr common.Address, index uint64, hash [32]byte, r, s [32]byte, v uint8, expect common.Address) bool {
   145  	buf := make([]byte, 8)
   146  	binary.BigEndian.PutUint64(buf, index)
   147  	data := append([]byte{0x19, 0x00}, append(addr.Bytes(), append(buf, hash[:]...)...)...)
   148  	pubkey, err := crypto.Ecrecover(crypto.Keccak256(data), append(r[:], append(s[:], v-27)...))
   149  	if err != nil {
   150  		return false
   151  	}
   152  	var signer common.Address
   153  	copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
   154  	return bytes.Equal(signer.Bytes(), expect.Bytes())
   155  }
   156  
   157  type Account struct {
   158  	key  *ecdsa.PrivateKey
   159  	addr common.Address
   160  }
   161  type Accounts []Account
   162  
   163  func (a Accounts) Len() int           { return len(a) }
   164  func (a Accounts) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   165  func (a Accounts) Less(i, j int) bool { return bytes.Compare(a[i].addr.Bytes(), a[j].addr.Bytes()) < 0 }
   166  
   167  func TestCheckpointRegister(t *testing.T) {
   168  	// Initialize test accounts
   169  	var accounts Accounts
   170  	for i := 0; i < 3; i++ {
   171  		key, _ := crypto.GenerateKey()
   172  		addr := crypto.PubkeyToAddress(key.PublicKey)
   173  		accounts = append(accounts, Account{key: key, addr: addr})
   174  	}
   175  	sort.Sort(accounts)
   176  
   177  	// Deploy registrar contract
   178  	contractBackend := backends.NewSimulatedBackend(
   179  		core.GenesisAlloc{
   180  			accounts[0].addr: {Balance: big.NewInt(10000000000000000)},
   181  			accounts[1].addr: {Balance: big.NewInt(10000000000000000)},
   182  			accounts[2].addr: {Balance: big.NewInt(10000000000000000)},
   183  		}, 10000000,
   184  	)
   185  	defer contractBackend.Close()
   186  
   187  	transactOpts, _ := bind.NewKeyedTransactorWithChainID(accounts[0].key, big.NewInt(1337))
   188  
   189  	// 3 trusted signers, threshold 2
   190  	contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2))
   191  	if err != nil {
   192  		t.Error("Failed to deploy registrar contract", err)
   193  	}
   194  	contractBackend.Commit()
   195  
   196  	// getRecent returns block height and hash of the head parent.
   197  	getRecent := func() (*big.Int, common.Hash) {
   198  		parentNumber := new(big.Int).Sub(contractBackend.Blockchain().CurrentHeader().Number, big.NewInt(1))
   199  		parentHash := contractBackend.Blockchain().CurrentHeader().ParentHash
   200  		return parentNumber, parentHash
   201  	}
   202  	// collectSig generates specified number signatures.
   203  	collectSig := func(index uint64, hash common.Hash, n int, unauthorized *ecdsa.PrivateKey) (v []uint8, r [][32]byte, s [][32]byte) {
   204  		for i := 0; i < n; i++ {
   205  			sig := signCheckpoint(contractAddr, accounts[i].key, index, hash)
   206  			if unauthorized != nil {
   207  				sig = signCheckpoint(contractAddr, unauthorized, index, hash)
   208  			}
   209  			r = append(r, common.BytesToHash(sig[:32]))
   210  			s = append(s, common.BytesToHash(sig[32:64]))
   211  			v = append(v, sig[64])
   212  		}
   213  		return v, r, s
   214  	}
   215  	// insertEmptyBlocks inserts a batch of empty blocks to blockchain.
   216  	insertEmptyBlocks := func(number int) {
   217  		for i := 0; i < number; i++ {
   218  			contractBackend.Commit()
   219  		}
   220  	}
   221  	// assert checks whether the current contract status is same with
   222  	// the expected.
   223  	assert := func(index uint64, hash [32]byte, height *big.Int) error {
   224  		lindex, lhash, lheight, err := c.GetLatestCheckpoint(nil)
   225  		if err != nil {
   226  			return err
   227  		}
   228  		if lindex != index {
   229  			return errors.New("latest checkpoint index mismatch")
   230  		}
   231  		if !bytes.Equal(lhash[:], hash[:]) {
   232  			return errors.New("latest checkpoint hash mismatch")
   233  		}
   234  		if lheight.Cmp(height) != 0 {
   235  			return errors.New("latest checkpoint height mismatch")
   236  		}
   237  		return nil
   238  	}
   239  
   240  	// Test future checkpoint registration
   241  	validateOperation(t, c, contractBackend, func() {
   242  		number, hash := getRecent()
   243  		v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
   244  		c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
   245  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   246  		return assert(0, emptyHash, big.NewInt(0))
   247  	}, "test future checkpoint registration")
   248  
   249  	insertEmptyBlocks(int(sectionSize.Uint64() + processConfirms.Uint64()))
   250  
   251  	// Test transaction replay protection
   252  	validateOperation(t, c, contractBackend, func() {
   253  		number, _ := getRecent()
   254  		v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
   255  		hash := common.HexToHash("deadbeef")
   256  		c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
   257  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   258  		return assert(0, emptyHash, big.NewInt(0))
   259  	}, "test transaction replay protection")
   260  
   261  	// Test unauthorized signature checking
   262  	validateOperation(t, c, contractBackend, func() {
   263  		number, hash := getRecent()
   264  		u, _ := crypto.GenerateKey()
   265  		v, r, s := collectSig(0, checkpoint0.Hash(), 2, u)
   266  		c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
   267  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   268  		return assert(0, emptyHash, big.NewInt(0))
   269  	}, "test unauthorized signature checking")
   270  
   271  	// Test un-multi-signature checkpoint registration
   272  	validateOperation(t, c, contractBackend, func() {
   273  		number, hash := getRecent()
   274  		v, r, s := collectSig(0, checkpoint0.Hash(), 1, nil)
   275  		c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
   276  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   277  		return assert(0, emptyHash, big.NewInt(0))
   278  	}, "test un-multi-signature checkpoint registration")
   279  
   280  	// Test valid checkpoint registration
   281  	validateOperation(t, c, contractBackend, func() {
   282  		number, hash := getRecent()
   283  		v, r, s := collectSig(0, checkpoint0.Hash(), 2, nil)
   284  		c.SetCheckpoint(transactOpts, number, hash, checkpoint0.Hash(), 0, v, r, s)
   285  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   286  		if valid, recv := validateEvents(2, events); !valid {
   287  			return errors.New("receive incorrect number of events")
   288  		} else {
   289  			for i := 0; i < len(recv); i++ {
   290  				event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
   291  				if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
   292  					return errors.New("recover signer failed")
   293  				}
   294  			}
   295  		}
   296  		number, _ := getRecent()
   297  		return assert(0, checkpoint0.Hash(), number.Add(number, big.NewInt(1)))
   298  	}, "test valid checkpoint registration")
   299  
   300  	distance := 3*sectionSize.Uint64() + processConfirms.Uint64() - contractBackend.Blockchain().CurrentHeader().Number.Uint64()
   301  	insertEmptyBlocks(int(distance))
   302  
   303  	// Test uncontinuous checkpoint registration
   304  	validateOperation(t, c, contractBackend, func() {
   305  		number, hash := getRecent()
   306  		v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
   307  		c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
   308  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   309  		if valid, recv := validateEvents(2, events); !valid {
   310  			return errors.New("receive incorrect number of events")
   311  		} else {
   312  			for i := 0; i < len(recv); i++ {
   313  				event := recv[i].Interface().(*contract.CheckpointOracleNewCheckpointVote)
   314  				if !assertSignature(contractAddr, event.Index, event.CheckpointHash, event.R, event.S, event.V, accounts[i].addr) {
   315  					return errors.New("recover signer failed")
   316  				}
   317  			}
   318  		}
   319  		number, _ := getRecent()
   320  		return assert(2, checkpoint2.Hash(), number.Add(number, big.NewInt(1)))
   321  	}, "test uncontinuous checkpoint registration")
   322  
   323  	// Test old checkpoint registration
   324  	validateOperation(t, c, contractBackend, func() {
   325  		number, hash := getRecent()
   326  		v, r, s := collectSig(1, checkpoint1.Hash(), 2, nil)
   327  		c.SetCheckpoint(transactOpts, number, hash, checkpoint1.Hash(), 1, v, r, s)
   328  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   329  		number, _ := getRecent()
   330  		return assert(2, checkpoint2.Hash(), number)
   331  	}, "test uncontinuous checkpoint registration")
   332  
   333  	// Test stale checkpoint registration
   334  	validateOperation(t, c, contractBackend, func() {
   335  		number, hash := getRecent()
   336  		v, r, s := collectSig(2, checkpoint2.Hash(), 2, nil)
   337  		c.SetCheckpoint(transactOpts, number, hash, checkpoint2.Hash(), 2, v, r, s)
   338  	}, func(events <-chan *contract.CheckpointOracleNewCheckpointVote) error {
   339  		number, _ := getRecent()
   340  		return assert(2, checkpoint2.Hash(), number.Sub(number, big.NewInt(1)))
   341  	}, "test stale checkpoint registration")
   342  }