github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/testbackend_test.go (about)

     1  // Copyright 2017 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 core
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"fmt"
    22  	"math"
    23  	"math/big"
    24  	"testing"
    25  	"time"
    26  
    27  	bls "github.com/celo-org/bls-zexe/go"
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/consensus"
    30  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    31  	"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
    32  	"github.com/ethereum/go-ethereum/core/types"
    33  	"github.com/ethereum/go-ethereum/crypto"
    34  	blscrypto "github.com/ethereum/go-ethereum/crypto/bls"
    35  	"github.com/ethereum/go-ethereum/ethdb"
    36  	"github.com/ethereum/go-ethereum/event"
    37  	elog "github.com/ethereum/go-ethereum/log"
    38  	"github.com/ethereum/go-ethereum/p2p/enode"
    39  )
    40  
    41  var testLogger = elog.New()
    42  
    43  type testSystemBackend struct {
    44  	id  uint64
    45  	sys *testSystem
    46  
    47  	engine Engine
    48  	peers  istanbul.ValidatorSet
    49  	events *event.TypeMux
    50  
    51  	committedMsgs []testCommittedMsgs
    52  	sentMsgs      [][]byte // store the message when Send is called by core
    53  
    54  	key     ecdsa.PrivateKey
    55  	blsKey  []byte
    56  	address common.Address
    57  	db      ethdb.Database
    58  
    59  	// Function pointer to a verify function, so that the test core_test.go/TestVerifyProposal
    60  	// can inject in different proposal verification statuses.
    61  	verifyImpl func(proposal istanbul.Proposal) (time.Duration, error)
    62  }
    63  
    64  type testCommittedMsgs struct {
    65  	commitProposal                  istanbul.Proposal
    66  	aggregatedSeal                  types.IstanbulAggregatedSeal
    67  	aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal
    68  }
    69  
    70  // ==============================================
    71  //
    72  // define the functions that needs to be provided for Istanbul.
    73  
    74  func (self *testSystemBackend) Authorize(address common.Address, _ istanbul.SignerFn, _ istanbul.BLSSignerFn, _ istanbul.MessageSignerFn) {
    75  	self.address = address
    76  	self.engine.SetAddress(address)
    77  }
    78  
    79  func (self *testSystemBackend) Address() common.Address {
    80  	return self.address
    81  }
    82  
    83  // Peers returns all connected peers
    84  func (self *testSystemBackend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet {
    85  	return self.peers
    86  }
    87  
    88  func (self *testSystemBackend) NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) {
    89  	//This doesn't really return the next block validators
    90  	return self.peers, nil
    91  }
    92  
    93  func (self *testSystemBackend) EventMux() *event.TypeMux {
    94  	return self.events
    95  }
    96  
    97  func (self *testSystemBackend) Send(message []byte, target common.Address) error {
    98  	testLogger.Info("enqueuing a message...", "address", self.Address())
    99  	self.sentMsgs = append(self.sentMsgs, message)
   100  	self.sys.queuedMessage <- istanbul.MessageEvent{
   101  		Payload: message,
   102  	}
   103  	return nil
   104  }
   105  
   106  func (self *testSystemBackend) BroadcastConsensusMsg(validators []common.Address, message []byte) error {
   107  	testLogger.Info("enqueuing a message...", "address", self.Address())
   108  	self.sentMsgs = append(self.sentMsgs, message)
   109  	self.sys.queuedMessage <- istanbul.MessageEvent{
   110  		Payload: message,
   111  	}
   112  	return nil
   113  }
   114  
   115  func (self *testSystemBackend) Gossip(validators []common.Address, message []byte, msgCode uint64, ignoreCache bool) error {
   116  	return nil
   117  }
   118  
   119  func (self *testSystemBackend) SignBlockHeader(data []byte) (blscrypto.SerializedSignature, error) {
   120  	privateKey, _ := bls.DeserializePrivateKey(self.blsKey)
   121  	defer privateKey.Destroy()
   122  
   123  	signature, _ := privateKey.SignMessage(data, []byte{}, false)
   124  	defer signature.Destroy()
   125  	signatureBytes, _ := signature.Serialize()
   126  
   127  	return blscrypto.SerializedSignatureFromBytes(signatureBytes)
   128  }
   129  
   130  func (self *testSystemBackend) SignBLSWithCompositeHash(data []byte) (blscrypto.SerializedSignature, error) {
   131  	privateKey, _ := bls.DeserializePrivateKey(self.blsKey)
   132  	defer privateKey.Destroy()
   133  
   134  	signature, _ := privateKey.SignMessage(data, []byte{}, true)
   135  	defer signature.Destroy()
   136  	signatureBytes, _ := signature.Serialize()
   137  
   138  	return blscrypto.SerializedSignatureFromBytes(signatureBytes)
   139  }
   140  
   141  func (self *testSystemBackend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal) error {
   142  	testLogger.Info("commit message", "address", self.Address())
   143  	self.committedMsgs = append(self.committedMsgs, testCommittedMsgs{
   144  		commitProposal:                  proposal,
   145  		aggregatedSeal:                  aggregatedSeal,
   146  		aggregatedEpochValidatorSetSeal: aggregatedEpochValidatorSetSeal,
   147  	})
   148  
   149  	// fake new head events
   150  	go self.events.Post(istanbul.FinalCommittedEvent{})
   151  	return nil
   152  }
   153  
   154  func (self *testSystemBackend) Verify(proposal istanbul.Proposal) (time.Duration, error) {
   155  	if self.verifyImpl == nil {
   156  		return self.verifyWithSuccess(proposal)
   157  	} else {
   158  		return self.verifyImpl(proposal)
   159  	}
   160  }
   161  
   162  func (self *testSystemBackend) verifyWithSuccess(proposal istanbul.Proposal) (time.Duration, error) {
   163  	return 0, nil
   164  }
   165  
   166  func (self *testSystemBackend) verifyWithFailure(proposal istanbul.Proposal) (time.Duration, error) {
   167  	return 0, InvalidProposalError
   168  }
   169  
   170  func (self *testSystemBackend) verifyWithFutureProposal(proposal istanbul.Proposal) (time.Duration, error) {
   171  	return 5, consensus.ErrFutureBlock
   172  }
   173  
   174  func (self *testSystemBackend) Sign(data []byte) ([]byte, error) {
   175  	hashData := crypto.Keccak256(data)
   176  	return crypto.Sign(hashData, &self.key)
   177  }
   178  
   179  func (self *testSystemBackend) CheckSignature([]byte, common.Address, []byte) error {
   180  	return nil
   181  }
   182  
   183  func (self *testSystemBackend) CheckValidatorSignature(data []byte, sig []byte) (common.Address, error) {
   184  	return istanbul.CheckValidatorSignature(self.peers, data, sig)
   185  }
   186  
   187  func (self *testSystemBackend) Hash(b interface{}) common.Hash {
   188  	return common.BytesToHash([]byte("Test"))
   189  }
   190  
   191  func (self *testSystemBackend) NewRequest(request istanbul.Proposal) {
   192  	go self.events.Post(istanbul.RequestEvent{
   193  		Proposal: request,
   194  	})
   195  }
   196  
   197  func (self *testSystemBackend) GetCurrentHeadBlock() istanbul.Proposal {
   198  	l := len(self.committedMsgs)
   199  	if l > 0 {
   200  		testLogger.Info("have proposal for block", "num", l)
   201  		return self.committedMsgs[l-1].commitProposal
   202  	}
   203  	return makeBlock(0)
   204  }
   205  
   206  func (self *testSystemBackend) GetCurrentHeadBlockAndAuthor() (istanbul.Proposal, common.Address) {
   207  	l := len(self.committedMsgs)
   208  	if l > 0 {
   209  		testLogger.Info("have proposal for block", "num", l)
   210  		return self.committedMsgs[l-1].commitProposal, common.Address{}
   211  	}
   212  	return makeBlock(0), common.Address{}
   213  }
   214  
   215  func (self *testSystemBackend) LastSubject() (istanbul.Subject, error) {
   216  	lastProposal := self.GetCurrentHeadBlock()
   217  	lastView := &istanbul.View{Sequence: lastProposal.Number(), Round: big.NewInt(1)}
   218  	return istanbul.Subject{View: lastView, Digest: lastProposal.Hash()}, nil
   219  }
   220  
   221  // Only block height 5 will return true
   222  func (self *testSystemBackend) HasBlock(hash common.Hash, number *big.Int) bool {
   223  	return number.Cmp(big.NewInt(5)) == 0
   224  }
   225  
   226  func (self *testSystemBackend) AuthorForBlock(number uint64) common.Address {
   227  	return common.Address{}
   228  }
   229  
   230  func (self *testSystemBackend) ParentBlockValidators(proposal istanbul.Proposal) istanbul.ValidatorSet {
   231  	return self.peers
   232  }
   233  
   234  func (self *testSystemBackend) finalizeAndReturnMessage(msg *istanbul.Message) (istanbul.Message, error) {
   235  	message := new(istanbul.Message)
   236  	data, err := self.engine.(*core).finalizeMessage(msg)
   237  	if err != nil {
   238  		return *message, err
   239  	}
   240  	err = message.FromPayload(data, self.engine.(*core).validateFn)
   241  	return *message, err
   242  }
   243  
   244  func (self *testSystemBackend) getPrepareMessage(view istanbul.View, digest common.Hash) (istanbul.Message, error) {
   245  	prepare := &istanbul.Subject{
   246  		View:   &view,
   247  		Digest: digest,
   248  	}
   249  
   250  	payload, err := Encode(prepare)
   251  	if err != nil {
   252  		return istanbul.Message{}, err
   253  	}
   254  
   255  	msg := &istanbul.Message{
   256  		Code: istanbul.MsgPrepare,
   257  		Msg:  payload,
   258  	}
   259  
   260  	return self.finalizeAndReturnMessage(msg)
   261  }
   262  
   263  func (self *testSystemBackend) getCommitMessage(view istanbul.View, proposal istanbul.Proposal) (istanbul.Message, error) {
   264  	subject := &istanbul.Subject{
   265  		View:   &view,
   266  		Digest: proposal.Hash(),
   267  	}
   268  
   269  	committedSeal, err := self.engine.(*core).generateCommittedSeal(subject)
   270  	if err != nil {
   271  		return istanbul.Message{}, err
   272  	}
   273  
   274  	committedSubject := &istanbul.CommittedSubject{
   275  		Subject:       subject,
   276  		CommittedSeal: committedSeal[:],
   277  	}
   278  
   279  	payload, err := Encode(committedSubject)
   280  	if err != nil {
   281  		return istanbul.Message{}, err
   282  	}
   283  
   284  	msg := &istanbul.Message{
   285  		Code: istanbul.MsgCommit,
   286  		Msg:  payload,
   287  	}
   288  
   289  	// // We swap in the provided proposal so that the message is finalized for the provided proposal
   290  	// // and not for the current preprepare.
   291  	// cachePreprepare := self.engine.(*core).current.Preprepare()
   292  	// fmt.Println("5")
   293  	// self.engine.(*core).current.TransitionToPreprepared(&istanbul.Preprepare{
   294  	// 	View:     &view,
   295  	// 	Proposal: proposal,
   296  	// })
   297  	message, err := self.finalizeAndReturnMessage(msg)
   298  	// self.engine.(*core).current.TransitionToPreprepared(cachePreprepare)
   299  	return message, err
   300  }
   301  
   302  func (self *testSystemBackend) getRoundChangeMessage(view istanbul.View, preparedCert istanbul.PreparedCertificate) (istanbul.Message, error) {
   303  	rc := &istanbul.RoundChange{
   304  		View:                &view,
   305  		PreparedCertificate: preparedCert,
   306  	}
   307  
   308  	payload, err := Encode(rc)
   309  	if err != nil {
   310  		return istanbul.Message{}, err
   311  	}
   312  
   313  	msg := &istanbul.Message{
   314  		Code: istanbul.MsgRoundChange,
   315  		Msg:  payload,
   316  	}
   317  
   318  	return self.finalizeAndReturnMessage(msg)
   319  }
   320  
   321  func (self *testSystemBackend) Enode() *enode.Node {
   322  	return nil
   323  }
   324  
   325  func (self *testSystemBackend) RefreshValPeers(valSet istanbul.ValidatorSet) {}
   326  
   327  func (self *testSystemBackend) setVerifyImpl(verifyImpl func(proposal istanbul.Proposal) (time.Duration, error)) {
   328  	self.verifyImpl = verifyImpl
   329  }
   330  
   331  // ==============================================
   332  //
   333  // define the struct that need to be provided for integration tests.
   334  
   335  type testSystem struct {
   336  	backends       []*testSystemBackend
   337  	f              uint64
   338  	n              uint64
   339  	validatorsKeys [][]byte
   340  
   341  	queuedMessage chan istanbul.MessageEvent
   342  	quit          chan struct{}
   343  }
   344  
   345  func newTestSystem(n uint64, f uint64, keys [][]byte) *testSystem {
   346  	testLogger.SetHandler(elog.StdoutHandler)
   347  	return &testSystem{
   348  		backends:       make([]*testSystemBackend, n),
   349  		validatorsKeys: keys,
   350  		f:              f,
   351  		n:              n,
   352  
   353  		queuedMessage: make(chan istanbul.MessageEvent),
   354  		quit:          make(chan struct{}),
   355  	}
   356  }
   357  
   358  func generateValidators(n int) ([]istanbul.ValidatorData, [][]byte, []*ecdsa.PrivateKey) {
   359  	vals := make([]istanbul.ValidatorData, 0)
   360  	blsKeys := make([][]byte, 0)
   361  	keys := make([]*ecdsa.PrivateKey, 0)
   362  	for i := 0; i < n; i++ {
   363  		privateKey, _ := crypto.GenerateKey()
   364  		blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey)
   365  		blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey)
   366  		vals = append(vals, istanbul.ValidatorData{
   367  			crypto.PubkeyToAddress(privateKey.PublicKey),
   368  			blsPublicKey,
   369  		})
   370  		keys = append(keys, privateKey)
   371  		blsKeys = append(blsKeys, blsPrivateKey)
   372  	}
   373  	return vals, blsKeys, keys
   374  }
   375  
   376  func newTestValidatorSet(n int) istanbul.ValidatorSet {
   377  	validators, _, _ := generateValidators(n)
   378  	return validator.NewSet(validators)
   379  }
   380  
   381  // FIXME: int64 is needed for N and F
   382  func NewTestSystemWithBackend(n, f uint64) *testSystem {
   383  	testLogger.SetHandler(elog.StdoutHandler)
   384  
   385  	validators, blsKeys, keys := generateValidators(int(n))
   386  	sys := newTestSystem(n, f, blsKeys)
   387  	config := *istanbul.DefaultConfig
   388  	config.ProposerPolicy = istanbul.RoundRobin
   389  	config.RoundStateDBPath = ""
   390  	config.RequestTimeout = 300
   391  	config.TimeoutBackoffFactor = 100
   392  	config.MinResendRoundChangeTimeout = 1000
   393  	config.MaxResendRoundChangeTimeout = 10000
   394  
   395  	for i := uint64(0); i < n; i++ {
   396  		vset := validator.NewSet(validators)
   397  		backend := sys.NewBackend(i)
   398  		backend.peers = vset
   399  		backend.address = vset.GetByIndex(i).Address()
   400  		backend.key = *keys[i]
   401  		backend.blsKey = blsKeys[i]
   402  
   403  		core := New(backend, &config).(*core)
   404  		core.logger = testLogger
   405  		core.validateFn = backend.CheckValidatorSignature
   406  
   407  		backend.engine = core
   408  	}
   409  
   410  	return sys
   411  }
   412  
   413  // listen will consume messages from queue and deliver a message to core
   414  func (t *testSystem) listen() {
   415  	for {
   416  		select {
   417  		case <-t.quit:
   418  			return
   419  		case queuedMessage := <-t.queuedMessage:
   420  			testLogger.Info("consuming a queue message...")
   421  			for _, backend := range t.backends {
   422  				go backend.EventMux().Post(queuedMessage)
   423  			}
   424  		}
   425  	}
   426  }
   427  
   428  // Run will start system components based on given flag, and returns a closer
   429  // function that caller can control lifecycle
   430  //
   431  // Given a true for core if you want to initialize core engine.
   432  func (t *testSystem) Run(core bool) func() {
   433  	for _, b := range t.backends {
   434  		if core {
   435  			err := b.engine.Start() // start Istanbul core
   436  			if err != nil {
   437  				fmt.Printf("Error Starting istanbul engine: %s", err)
   438  				panic("Error Starting istanbul engine")
   439  			}
   440  		}
   441  	}
   442  
   443  	go t.listen()
   444  	closer := func() { t.stop(core) }
   445  	return closer
   446  }
   447  
   448  func (t *testSystem) stop(core bool) {
   449  	close(t.quit)
   450  
   451  	for _, b := range t.backends {
   452  		if core {
   453  			err := b.engine.Stop()
   454  			if err != nil {
   455  				fmt.Printf("Error Stopping istanbul engine: %s", err)
   456  				panic("Error Stopping istanbul engine")
   457  			}
   458  		}
   459  	}
   460  }
   461  
   462  func (t *testSystem) NewBackend(id uint64) *testSystemBackend {
   463  	// assume always success
   464  	ethDB := ethdb.NewMemDatabase()
   465  	backend := &testSystemBackend{
   466  		id:     id,
   467  		sys:    t,
   468  		events: new(event.TypeMux),
   469  		db:     ethDB,
   470  	}
   471  
   472  	t.backends[id] = backend
   473  	return backend
   474  }
   475  
   476  func (t *testSystem) F() uint64 {
   477  	return t.f
   478  }
   479  
   480  func (t *testSystem) MinQuorumSize() uint64 {
   481  	return uint64(math.Ceil(float64(2*t.n) / 3))
   482  }
   483  
   484  func (sys *testSystem) getPreparedCertificate(t *testing.T, views []istanbul.View, proposal istanbul.Proposal) istanbul.PreparedCertificate {
   485  	preparedCertificate := istanbul.PreparedCertificate{
   486  		Proposal:                proposal,
   487  		PrepareOrCommitMessages: []istanbul.Message{},
   488  	}
   489  	for i, backend := range sys.backends {
   490  		if uint64(i) == sys.MinQuorumSize() {
   491  			break
   492  		}
   493  		var err error
   494  		var msg istanbul.Message
   495  		if i%2 == 0 {
   496  			msg, err = backend.getPrepareMessage(views[i%len(views)], proposal.Hash())
   497  		} else {
   498  			msg, err = backend.getCommitMessage(views[i%len(views)], proposal)
   499  		}
   500  		if err != nil {
   501  			t.Errorf("Failed to create message %v: %v", i, err)
   502  		}
   503  		preparedCertificate.PrepareOrCommitMessages = append(preparedCertificate.PrepareOrCommitMessages, msg)
   504  	}
   505  	return preparedCertificate
   506  }
   507  
   508  func (sys *testSystem) getRoundChangeCertificate(t *testing.T, views []istanbul.View, preparedCertificate istanbul.PreparedCertificate) istanbul.RoundChangeCertificate {
   509  	var roundChangeCertificate istanbul.RoundChangeCertificate
   510  	for i, backend := range sys.backends {
   511  		if uint64(i) == sys.MinQuorumSize() {
   512  			break
   513  		}
   514  		msg, err := backend.getRoundChangeMessage(views[i%len(views)], preparedCertificate)
   515  		if err != nil {
   516  			t.Errorf("Failed to create ROUND CHANGE message: %v", err)
   517  		}
   518  		roundChangeCertificate.RoundChangeMessages = append(roundChangeCertificate.RoundChangeMessages, msg)
   519  	}
   520  	return roundChangeCertificate
   521  }
   522  
   523  // ==============================================
   524  //
   525  // helper functions.
   526  
   527  func getPublicKeyAddress(privateKey *ecdsa.PrivateKey) common.Address {
   528  	return crypto.PubkeyToAddress(privateKey.PublicKey)
   529  }