123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // Copyright 2017 The go-ethereum Authors
- // This file is part of the go-ethereum library.
- //
- // The go-ethereum library is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Lesser General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // The go-ethereum library is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Lesser General Public License for more details.
- //
- // You should have received a copy of the GNU Lesser General Public License
- // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
- package core
- import (
- "crypto/ecdsa"
- "math/big"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/consensus/istanbul"
- ibfttypes "github.com/ethereum/go-ethereum/consensus/istanbul/ibft/types"
- "github.com/ethereum/go-ethereum/consensus/istanbul/validator"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- elog "github.com/ethereum/go-ethereum/log"
- )
- var testLogger = elog.New()
- type testSystemBackend struct {
- id uint64
- sys *testSystem
- engine *core
- peers istanbul.ValidatorSet
- events *event.TypeMux
- committedMsgs []testCommittedMsgs
- sentMsgs [][]byte // store the message when Send is called by core
- address common.Address
- db ethdb.Database
- }
- type testCommittedMsgs struct {
- commitProposal istanbul.Proposal
- committedSeals [][]byte
- }
- // ==============================================
- //
- // define the functions that needs to be provided for Istanbul.
- func (self *testSystemBackend) Address() common.Address {
- return self.address
- }
- // Peers returns all connected peers
- func (self *testSystemBackend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet {
- return self.peers
- }
- func (self *testSystemBackend) EventMux() *event.TypeMux {
- return self.events
- }
- func (self *testSystemBackend) Send(message []byte, code uint64, target common.Address) error {
- testLogger.Info("enqueuing a message...", "address", self.Address())
- self.sentMsgs = append(self.sentMsgs, message)
- self.sys.queuedMessage <- istanbul.MessageEvent{
- Code: code,
- Payload: message,
- }
- return nil
- }
- func (self *testSystemBackend) Broadcast(valSet istanbul.ValidatorSet, code uint64, message []byte) error {
- testLogger.Info("enqueuing a message...", "address", self.Address())
- self.sentMsgs = append(self.sentMsgs, message)
- self.sys.queuedMessage <- istanbul.MessageEvent{
- Code: code,
- Payload: message,
- }
- return nil
- }
- func (self *testSystemBackend) Gossip(valSet istanbul.ValidatorSet, code uint64, message []byte) error {
- testLogger.Warn("not sign any data")
- return nil
- }
- func (self *testSystemBackend) Commit(proposal istanbul.Proposal, seals [][]byte, round *big.Int) error {
- testLogger.Info("commit message", "address", self.Address())
- self.committedMsgs = append(self.committedMsgs, testCommittedMsgs{
- commitProposal: proposal,
- committedSeals: seals,
- })
- // fake new head events
- go self.events.Post(istanbul.FinalCommittedEvent{})
- return nil
- }
- func (self *testSystemBackend) Verify(proposal istanbul.Proposal) (time.Duration, error) {
- return 0, nil
- }
- func (self *testSystemBackend) Sign(data []byte) ([]byte, error) {
- testLogger.Info("returning current backend address so that CheckValidatorSignature returns the same value")
- return self.address.Bytes(), nil
- }
- func (self *testSystemBackend) SignWithoutHashing(data []byte) ([]byte, error) {
- testLogger.Info("returning current backend address so that CheckValidatorSignature returns the same value")
- return self.address.Bytes(), nil
- }
- func (self *testSystemBackend) CheckSignature([]byte, common.Address, []byte) error {
- return nil
- }
- func (self *testSystemBackend) CheckValidatorSignature(data []byte, sig []byte) (common.Address, error) {
- return common.BytesToAddress(sig), nil
- }
- func (self *testSystemBackend) Hash(b interface{}) common.Hash {
- return common.StringToHash("Test")
- }
- func (self *testSystemBackend) NewRequest(request istanbul.Proposal) {
- go self.events.Post(istanbul.RequestEvent{
- Proposal: request,
- })
- }
- func (self *testSystemBackend) HasBadProposal(hash common.Hash) bool {
- return false
- }
- func (self *testSystemBackend) LastProposal() (istanbul.Proposal, common.Address) {
- l := len(self.committedMsgs)
- if l > 0 {
- return self.committedMsgs[l-1].commitProposal, common.Address{}
- }
- return makeBlock(0), common.Address{}
- }
- // Only block height 5 will return true
- func (self *testSystemBackend) HasPropsal(hash common.Hash, number *big.Int) bool {
- return number.Cmp(big.NewInt(5)) == 0
- }
- func (self *testSystemBackend) GetProposer(number uint64) common.Address {
- return common.Address{}
- }
- func (self *testSystemBackend) ParentValidators(proposal istanbul.Proposal) istanbul.ValidatorSet {
- return self.peers
- }
- func (sb *testSystemBackend) Close() error {
- return nil
- }
- func (sb *testSystemBackend) IsQBFTConsensusAt(*big.Int) bool {
- return false
- }
- func (sb *testSystemBackend) StartQBFTConsensus() error {
- return nil
- }
- // ==============================================
- //
- // define the struct that need to be provided for integration tests.
- type testSystem struct {
- backends []*testSystemBackend
- queuedMessage chan istanbul.MessageEvent
- quit chan struct{}
- }
- func newTestSystem(n uint64) *testSystem {
- testLogger.SetHandler(elog.StdoutHandler)
- return &testSystem{
- backends: make([]*testSystemBackend, n),
- queuedMessage: make(chan istanbul.MessageEvent),
- quit: make(chan struct{}),
- }
- }
- func generateValidators(n int) []common.Address {
- vals := make([]common.Address, 0)
- for i := 0; i < n; i++ {
- privateKey, _ := crypto.GenerateKey()
- vals = append(vals, crypto.PubkeyToAddress(privateKey.PublicKey))
- }
- return vals
- }
- func newTestValidatorSet(n int) istanbul.ValidatorSet {
- return validator.NewSet(generateValidators(n), istanbul.NewRoundRobinProposerPolicy())
- }
- // FIXME: int64 is needed for N and F
- func NewTestSystemWithBackend(n, f uint64) *testSystem {
- testLogger.SetHandler(elog.StdoutHandler)
- addrs := generateValidators(int(n))
- sys := newTestSystem(n)
- config := istanbul.DefaultConfig
- for i := uint64(0); i < n; i++ {
- vset := validator.NewSet(addrs, istanbul.NewRoundRobinProposerPolicy())
- backend := sys.NewBackend(i)
- backend.peers = vset
- backend.address = vset.GetByIndex(i).Address()
- core := New(backend, config)
- core.state = ibfttypes.StateAcceptRequest
- core.current = newRoundState(&istanbul.View{
- Round: big.NewInt(0),
- Sequence: big.NewInt(1),
- }, vset, common.Hash{}, nil, nil, func(hash common.Hash) bool {
- return false
- })
- core.valSet = vset
- core.logger = testLogger
- core.validateFn = backend.CheckValidatorSignature
- backend.engine = core
- }
- return sys
- }
- // listen will consume messages from queue and deliver a message to core
- func (t *testSystem) listen() {
- for {
- select {
- case <-t.quit:
- return
- case queuedMessage := <-t.queuedMessage:
- testLogger.Info("consuming a queue message...")
- for _, backend := range t.backends {
- go backend.EventMux().Post(queuedMessage)
- }
- }
- }
- }
- // Run will start system components based on given flag, and returns a closer
- // function that caller can control lifecycle
- //
- // Given a true for core if you want to initialize core engine.
- func (t *testSystem) Run(core bool) func() {
- for _, b := range t.backends {
- if core {
- b.engine.Start() // start Istanbul core
- }
- }
- go t.listen()
- closer := func() { t.stop(core) }
- return closer
- }
- func (t *testSystem) stop(core bool) {
- close(t.quit)
- for _, b := range t.backends {
- if core {
- b.engine.Stop()
- }
- }
- }
- func (t *testSystem) NewBackend(id uint64) *testSystemBackend {
- // assume always success
- ethDB := rawdb.NewMemoryDatabase()
- backend := &testSystemBackend{
- id: id,
- sys: t,
- events: new(event.TypeMux),
- db: ethDB,
- }
- t.backends[id] = backend
- return backend
- }
- // ==============================================
- //
- // helper functions.
- func getPublicKeyAddress(privateKey *ecdsa.PrivateKey) common.Address {
- return crypto.PubkeyToAddress(privateKey.PublicKey)
- }
|