2017-10-31 15:24:11 -07:00
|
|
|
// 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"
|
|
|
|
"github.com/ethereum/go-ethereum/consensus/istanbul/validator"
|
|
|
|
"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 Engine
|
|
|
|
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, target common.Address) error {
|
|
|
|
testLogger.Info("enqueuing a message...", "address", self.Address())
|
|
|
|
self.sentMsgs = append(self.sentMsgs, message)
|
|
|
|
self.sys.queuedMessage <- istanbul.MessageEvent{
|
|
|
|
Payload: message,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *testSystemBackend) Broadcast(valSet istanbul.ValidatorSet, message []byte) error {
|
|
|
|
testLogger.Info("enqueuing a message...", "address", self.Address())
|
|
|
|
self.sentMsgs = append(self.sentMsgs, message)
|
|
|
|
self.sys.queuedMessage <- istanbul.MessageEvent{
|
|
|
|
Payload: message,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *testSystemBackend) Gossip(valSet istanbul.ValidatorSet, message []byte) error {
|
|
|
|
testLogger.Warn("not sign any data")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *testSystemBackend) Commit(proposal istanbul.Proposal, seals [][]byte) error {
|
|
|
|
testLogger.Info("commit message", "address", self.Address())
|
|
|
|
self.committedMsgs = append(self.committedMsgs, testCommittedMsgs{
|
|
|
|
commitProposal: proposal,
|
|
|
|
committedSeals: seals,
|
|
|
|
})
|
|
|
|
|
|
|
|
// fake new head events
|
2018-01-30 18:27:08 -08:00
|
|
|
go self.events.Post(istanbul.FinalCommittedEvent{})
|
2017-10-31 15:24:11 -07:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *testSystemBackend) Verify(proposal istanbul.Proposal) (time.Duration, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (self *testSystemBackend) Sign(data []byte) ([]byte, error) {
|
|
|
|
testLogger.Warn("not sign any data")
|
|
|
|
return data, 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.Address{}, 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,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-01-30 18:27:08 -08:00
|
|
|
func (self *testSystemBackend) HasBadProposal(hash common.Hash) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-10-31 15:24:11 -07:00
|
|
|
func (self *testSystemBackend) LastProposal() (istanbul.Proposal, common.Address) {
|
2018-01-30 18:27:08 -08:00
|
|
|
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
|
2017-10-31 15:24:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// ==============================================
|
|
|
|
//
|
|
|
|
// 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.RoundRobin)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.RoundRobin)
|
|
|
|
backend := sys.NewBackend(i)
|
|
|
|
backend.peers = vset
|
|
|
|
backend.address = vset.GetByIndex(i).Address()
|
|
|
|
|
|
|
|
core := New(backend, config).(*core)
|
|
|
|
core.state = StateAcceptRequest
|
|
|
|
core.current = newRoundState(&istanbul.View{
|
|
|
|
Round: big.NewInt(0),
|
|
|
|
Sequence: big.NewInt(1),
|
2018-01-30 18:27:08 -08:00
|
|
|
}, vset, common.Hash{}, nil, nil, func(hash common.Hash) bool {
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
core.valSet = vset
|
2017-10-31 15:24:11 -07:00
|
|
|
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 {
|
2018-01-30 18:27:08 -08:00
|
|
|
b.engine.Start() // start Istanbul core
|
2017-10-31 15:24:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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, _ := ethdb.NewMemDatabase()
|
|
|
|
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)
|
|
|
|
}
|