mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
Implement BFT Message receiver (#13)
* fix or skip tests due to PR-136 changes * add bft receiver functions * add bft receiver functions * rename tc to TimeoutCert * implement more functions * New struct in consensus/XDPoS/utils/types.go, util functions, and test. (#14) * define vote, timeout, sync info, qc, tc, extra fields in types.go, add test in types_test.go * add json tag in types.go, refine encoder decoder of extra fields * refactor types.go utils.go * re-write types, comments * add Hash SigHash for types, and tests * define Round type * remove unnecessary logs * add temp functions * add v2 engine functions placeholder * typo fix on the consensus v2 function placeholders * add countdown timer * make initilised private to countdown * push verify function * add test on receiving vote * revert type change * add async on broadcast function * add quit initial * fix test Co-authored-by: Jianrong <wjrjerome@gmail.com> Co-authored-by: wgr523 <wgr523@gmail.com>
This commit is contained in:
parent
521b703207
commit
a1b77f3ca8
11 changed files with 526 additions and 24 deletions
|
|
@ -53,8 +53,8 @@ type XDPoS struct {
|
||||||
GetLendingService func() utils.LendingService
|
GetLendingService func() utils.LendingService
|
||||||
|
|
||||||
// The exact consensus engine with different versions
|
// The exact consensus engine with different versions
|
||||||
EngineV1 engine_v1.XDPoS_v1
|
EngineV1 *engine_v1.XDPoS_v1
|
||||||
EngineV2 engine_v2.XDPoS_v2
|
EngineV2 *engine_v2.XDPoS_v2
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
|
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
|
||||||
|
|
@ -74,8 +74,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
|
||||||
db: db,
|
db: db,
|
||||||
|
|
||||||
signingTxsCache: signingTxsCache,
|
signingTxsCache: signingTxsCache,
|
||||||
EngineV1: *engine_v1.New(&conf, db),
|
EngineV1: engine_v1.New(&conf, db),
|
||||||
EngineV2: *engine_v2.New(&conf, db),
|
EngineV2: engine_v2.New(&conf, db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,12 +93,14 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS {
|
||||||
signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit)
|
signingTxsCache, _ := lru.New(utils.BlockSignersCacheLimit)
|
||||||
|
|
||||||
fakeEngine = &XDPoS{
|
fakeEngine = &XDPoS{
|
||||||
config: conf,
|
config: conf,
|
||||||
db: db,
|
db: db,
|
||||||
|
GetXDCXService: func() utils.TradingService { return nil },
|
||||||
|
GetLendingService: func() utils.LendingService { return nil },
|
||||||
|
|
||||||
signingTxsCache: signingTxsCache,
|
signingTxsCache: signingTxsCache,
|
||||||
EngineV1: *engine_v1.NewFaker(db, conf),
|
EngineV1: engine_v1.NewFaker(db, conf),
|
||||||
EngineV2: *engine_v2.NewFaker(db, conf),
|
EngineV2: engine_v2.NewFaker(db, conf),
|
||||||
}
|
}
|
||||||
return fakeEngine
|
return fakeEngine
|
||||||
}
|
}
|
||||||
|
|
@ -350,3 +352,20 @@ func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*t
|
||||||
func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
|
func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) {
|
||||||
return x.signingTxsCache.Get(hash)
|
return x.signingTxsCache.Get(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//V2
|
||||||
|
func (x *XDPoS) VerifyVote(utils.Vote) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS) VerifyTimeout(utils.Timeout) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS) VerifySyncInfo(utils.SyncInfo) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS) VerifyBlockInfo(utils.BlockInfo) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/countdown"
|
"github.com/XinFinOrg/XDPoSChain/common/countdown"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
|
@ -77,7 +78,7 @@ func (consensus *XDPoS_v2) Dispatcher() error {
|
||||||
SyncInfo workflow
|
SyncInfo workflow
|
||||||
*/
|
*/
|
||||||
// Verify syncInfo and trigger trigger process QC or TC if successful
|
// Verify syncInfo and trigger trigger process QC or TC if successful
|
||||||
func (consensus *XDPoS_v2) VerifySyncInfoMessage(header *types.Header) error {
|
func (consensus *XDPoS_v2) VerifySyncInfoMessage(syncInfo utils.SyncInfo) error {
|
||||||
/*
|
/*
|
||||||
1. Verify items including:
|
1. Verify items including:
|
||||||
- verifyQC
|
- verifyQC
|
||||||
|
|
@ -98,7 +99,7 @@ func (consensus *XDPoS_v2) SyncInfoHandler(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
Vote workflow
|
Vote workflow
|
||||||
*/
|
*/
|
||||||
func (consensus *XDPoS_v2) VerifyVoteMessage() error {
|
func (consensus *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error {
|
||||||
/*
|
/*
|
||||||
1. Check signature:
|
1. Check signature:
|
||||||
- Use ecRecover to get the public key
|
- Use ecRecover to get the public key
|
||||||
|
|
@ -123,7 +124,7 @@ func (consensus *XDPoS_v2) VoteHandler() {
|
||||||
Timeout workflow
|
Timeout workflow
|
||||||
*/
|
*/
|
||||||
// Verify timeout message type from peers in bft.go
|
// Verify timeout message type from peers in bft.go
|
||||||
func (consensus *XDPoS_v2) VerifyTimeoutMessage() error {
|
func (consensus *XDPoS_v2) VerifyTimeoutMessage(utils.Timeout) error {
|
||||||
/*
|
/*
|
||||||
1. Check signature:
|
1. Check signature:
|
||||||
- Use ecRecover to get the public key
|
- Use ecRecover to get the public key
|
||||||
|
|
@ -166,7 +167,7 @@ func (consensus *XDPoS_v2) generateBlockInfo() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// To be used by different message verification. Verify local DB block info against the received block information(i.e hash, blockNum, round)
|
// To be used by different message verification. Verify local DB block info against the received block information(i.e hash, blockNum, round)
|
||||||
func (consensus *XDPoS_v2) verifyBlockInfo(header *types.Header) error {
|
func (consensus *XDPoS_v2) VerifyBlockInfo(blockInfo utils.BlockInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,13 @@ type PublicApiSnapshot struct {
|
||||||
// Round number type in XDPoS 2.0
|
// Round number type in XDPoS 2.0
|
||||||
type Round uint64
|
type Round uint64
|
||||||
|
|
||||||
|
// Block Info struct in XDPoS 2.0, used for vote message, etc.
|
||||||
|
type BlockInfo struct {
|
||||||
|
Hash common.Hash
|
||||||
|
Round Round
|
||||||
|
Number *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
// Vote message in XDPoS 2.0
|
// Vote message in XDPoS 2.0
|
||||||
type Vote struct {
|
type Vote struct {
|
||||||
ProposedBlockInfo BlockInfo
|
ProposedBlockInfo BlockInfo
|
||||||
|
|
@ -79,13 +86,6 @@ type SyncInfo struct {
|
||||||
HighestTimeoutCert TimeoutCert
|
HighestTimeoutCert TimeoutCert
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block Info struct in XDPoS 2.0, used for vote message, etc.
|
|
||||||
type BlockInfo struct {
|
|
||||||
Hash common.Hash
|
|
||||||
Round Round
|
|
||||||
Number *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quorum Certificate struct in XDPoS 2.0
|
// Quorum Certificate struct in XDPoS 2.0
|
||||||
type QuorumCert struct {
|
type QuorumCert struct {
|
||||||
ProposedBlockInfo BlockInfo
|
ProposedBlockInfo BlockInfo
|
||||||
|
|
|
||||||
156
eth/bfter/bft.go
Normal file
156
eth/bfter/bft.go
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
package bfter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
messageLimit = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
//Define Verify Group functions
|
||||||
|
type VerifySyncInfoFn func(utils.SyncInfo) error
|
||||||
|
type VerifyVoteFn func(utils.Vote) error
|
||||||
|
type VerifyTimeoutFn func(utils.Timeout) error
|
||||||
|
|
||||||
|
//Define Boradcast Group functions
|
||||||
|
type broadcastVoteFn func(utils.Vote)
|
||||||
|
type broadcastTimeoutFn func(utils.Timeout)
|
||||||
|
type broadcastSyncInfoFn func(utils.SyncInfo)
|
||||||
|
|
||||||
|
type Bfter struct {
|
||||||
|
broadcastCh chan interface{}
|
||||||
|
quit chan struct{}
|
||||||
|
consensus ConsensusFns
|
||||||
|
broadcast BroadcastFns
|
||||||
|
|
||||||
|
// Message Cache
|
||||||
|
knownVotes *lru.ARCCache
|
||||||
|
knownSyncInfos *lru.ARCCache
|
||||||
|
knownTimeouts *lru.ARCCache
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConsensusFns struct {
|
||||||
|
verifySyncInfo VerifySyncInfoFn
|
||||||
|
verifyVote VerifyVoteFn
|
||||||
|
verifyTimeout VerifyTimeoutFn
|
||||||
|
}
|
||||||
|
|
||||||
|
type BroadcastFns struct {
|
||||||
|
Vote broadcastVoteFn
|
||||||
|
Timeout broadcastTimeoutFn
|
||||||
|
SyncInfo broadcastSyncInfoFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(broadcasts BroadcastFns) *Bfter {
|
||||||
|
knownVotes, _ := lru.NewARC(messageLimit)
|
||||||
|
knownSyncInfos, _ := lru.NewARC(messageLimit)
|
||||||
|
knownTimeouts, _ := lru.NewARC(messageLimit)
|
||||||
|
return &Bfter{
|
||||||
|
quit: make(chan struct{}),
|
||||||
|
broadcastCh: make(chan interface{}),
|
||||||
|
broadcast: broadcasts,
|
||||||
|
knownVotes: knownVotes,
|
||||||
|
knownSyncInfos: knownSyncInfos,
|
||||||
|
knownTimeouts: knownTimeouts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bfter) SetConsensusFuns(engine consensus.Engine) {
|
||||||
|
e := engine.(*XDPoS.XDPoS)
|
||||||
|
b.broadcastCh = e.EngineV2.BroadcastCh
|
||||||
|
b.consensus = ConsensusFns{
|
||||||
|
verifySyncInfo: e.VerifySyncInfo,
|
||||||
|
verifyVote: e.VerifyVote,
|
||||||
|
verifyTimeout: e.VerifyTimeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: rename
|
||||||
|
func (b *Bfter) Vote(vote utils.Vote) {
|
||||||
|
log.Trace("Receive Vote", "vote", vote)
|
||||||
|
|
||||||
|
if b.knownVotes.Contains(vote.Hash()) {
|
||||||
|
log.Trace("Discarded vote, known vote", "Signature", vote.Signature, "hash", vote.Hash())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.consensus.verifyVote(vote)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Verify BFT Vote", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.knownVotes.Add(vote.Hash(), true)
|
||||||
|
b.broadcastCh <- vote
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bfter) Timeout(timeout utils.Timeout) {
|
||||||
|
log.Trace("Receive Timeout", "timeout", timeout)
|
||||||
|
|
||||||
|
if b.knownVotes.Contains(timeout.Hash()) {
|
||||||
|
log.Trace("Discarded Timeout, known Timeout", "Signature", timeout.Signature, "hash", timeout.Hash(), "round", timeout.Round)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.consensus.verifyTimeout(timeout)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Verify BFT Timeout", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.knownTimeouts.Add(timeout.Hash(), true)
|
||||||
|
b.broadcastCh <- timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bfter) SyncInfo(syncInfo utils.SyncInfo) {
|
||||||
|
log.Trace("Receive SyncInfo", "syncInfo", syncInfo)
|
||||||
|
|
||||||
|
if b.knownVotes.Contains(syncInfo.Hash()) {
|
||||||
|
log.Trace("Discarded SyncInfo, known SyncInfo", "hash", syncInfo.Hash())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := b.consensus.verifySyncInfo(syncInfo)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Verify BFT SyncInfo", "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b.knownSyncInfos.Add(syncInfo.Hash(), true)
|
||||||
|
b.broadcastCh <- syncInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start Bft receiver
|
||||||
|
func (b *Bfter) Start() {
|
||||||
|
go b.loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bfter) Stop() {
|
||||||
|
close(b.quit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bfter) loop() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-b.quit:
|
||||||
|
return
|
||||||
|
case obj := <-b.broadcastCh:
|
||||||
|
switch v := obj.(type) {
|
||||||
|
case utils.Vote:
|
||||||
|
go b.broadcast.Vote(v)
|
||||||
|
case utils.Timeout:
|
||||||
|
go b.broadcast.Timeout(v)
|
||||||
|
case utils.SyncInfo:
|
||||||
|
go b.broadcast.SyncInfo(v)
|
||||||
|
default:
|
||||||
|
log.Error("Unknown message type received, value: %v", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
eth/bfter/bft_test.go
Normal file
118
eth/bfter/bft_test.go
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
package bfter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/engines/engine_v2"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// make different votes based on Signatures
|
||||||
|
func makeVotes(n int) []utils.Vote {
|
||||||
|
var votes []utils.Vote
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
votes = append(votes, utils.Vote{Signature: []byte{byte(i)}})
|
||||||
|
}
|
||||||
|
return votes
|
||||||
|
}
|
||||||
|
|
||||||
|
// bfterTester is a test simulator for mocking out bfter worker.
|
||||||
|
type bfterTester struct {
|
||||||
|
bfter *Bfter
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTester creates a new bft fetcher test mocker.
|
||||||
|
func newTester() *bfterTester {
|
||||||
|
testConsensus := &XDPoS.XDPoS{EngineV2: &engine_v2.XDPoS_v2{}}
|
||||||
|
broadcasts := BroadcastFns{}
|
||||||
|
|
||||||
|
tester := &bfterTester{}
|
||||||
|
tester.bfter = New(broadcasts)
|
||||||
|
tester.bfter.SetConsensusFuns(testConsensus)
|
||||||
|
tester.bfter.broadcastCh = make(chan interface{})
|
||||||
|
tester.bfter.Start()
|
||||||
|
|
||||||
|
return tester
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that a bfter accepts vote and process verfiy and broadcast
|
||||||
|
func TestSequentialVotes(t *testing.T) {
|
||||||
|
tester := newTester()
|
||||||
|
verifyCounter := uint32(0)
|
||||||
|
broadcastCounter := uint32(0)
|
||||||
|
targetVotes := 10
|
||||||
|
|
||||||
|
tester.bfter.consensus.verifyVote = func(vote utils.Vote) error {
|
||||||
|
atomic.AddUint32(&verifyCounter, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tester.bfter.broadcast.Vote = func(utils.Vote) {
|
||||||
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
votes := makeVotes(targetVotes)
|
||||||
|
for _, vote := range votes {
|
||||||
|
tester.bfter.Vote(vote)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if int(verifyCounter) != targetVotes || int(broadcastCounter) != targetVotes {
|
||||||
|
t.Fatalf("count mismatch: have %v on verify and have %v on broadcast, want %v", verifyCounter, broadcastCounter, targetVotes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that vote already being retrieved will not be duplicated.
|
||||||
|
func TestDuplicateVotes(t *testing.T) {
|
||||||
|
tester := newTester()
|
||||||
|
verifyCounter := uint32(0)
|
||||||
|
broadcastCounter := uint32(0)
|
||||||
|
targetVotes := 1
|
||||||
|
|
||||||
|
tester.bfter.consensus.verifyVote = func(vote utils.Vote) error {
|
||||||
|
atomic.AddUint32(&verifyCounter, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tester.bfter.broadcast.Vote = func(utils.Vote) {
|
||||||
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
vote := utils.Vote{}
|
||||||
|
|
||||||
|
// send twice
|
||||||
|
tester.bfter.Vote(vote)
|
||||||
|
tester.bfter.Vote(vote)
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if int(verifyCounter) != targetVotes || int(broadcastCounter) != targetVotes {
|
||||||
|
t.Fatalf("count mismatch: have %v on verify and have %v on broadcast, want %v", verifyCounter, broadcastCounter, targetVotes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that avoid boardcast if there is bad vote
|
||||||
|
func TestNotBoardcastInvalidVote(t *testing.T) {
|
||||||
|
tester := newTester()
|
||||||
|
broadcastCounter := uint32(0)
|
||||||
|
targetVotes := 0
|
||||||
|
|
||||||
|
tester.bfter.consensus.verifyVote = func(vote utils.Vote) error {
|
||||||
|
return fmt.Errorf("This is invalid vote")
|
||||||
|
}
|
||||||
|
tester.bfter.broadcast.Vote = func(utils.Vote) {
|
||||||
|
atomic.AddUint32(&broadcastCounter, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
vote := utils.Vote{}
|
||||||
|
tester.bfter.Vote(vote)
|
||||||
|
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if int(broadcastCounter) != targetVotes {
|
||||||
|
t.Fatalf("count mismatch: have %v on broadcast, want %v", broadcastCounter, targetVotes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: SyncInfo and Timeout Test, should be same as Vote.
|
||||||
|
// Once all test on vote covered, then duplicate to others
|
||||||
|
|
@ -29,9 +29,11 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus"
|
"github.com/XinFinOrg/XDPoSChain/consensus"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
"github.com/XinFinOrg/XDPoSChain/consensus/misc"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/eth/bfter"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
"github.com/XinFinOrg/XDPoSChain/eth/downloader"
|
||||||
"github.com/XinFinOrg/XDPoSChain/eth/fetcher"
|
"github.com/XinFinOrg/XDPoSChain/eth/fetcher"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
|
|
@ -80,6 +82,7 @@ type ProtocolManager struct {
|
||||||
downloader *downloader.Downloader
|
downloader *downloader.Downloader
|
||||||
fetcher *fetcher.Fetcher
|
fetcher *fetcher.Fetcher
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
bfter *bfter.Bfter
|
||||||
|
|
||||||
SubProtocols []p2p.Protocol
|
SubProtocols []p2p.Protocol
|
||||||
|
|
||||||
|
|
@ -218,6 +221,16 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
||||||
return manager.blockchain.PrepareBlock(block)
|
return manager.blockchain.PrepareBlock(block)
|
||||||
}
|
}
|
||||||
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer)
|
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, prepare, manager.removePeer)
|
||||||
|
//Define bft function
|
||||||
|
broadcasts := bfter.BroadcastFns{
|
||||||
|
Vote: manager.BroadcastVote,
|
||||||
|
Timeout: manager.BroadcastTimeout,
|
||||||
|
SyncInfo: manager.BroadcastSyncInfo,
|
||||||
|
}
|
||||||
|
manager.bfter = bfter.New(broadcasts)
|
||||||
|
if blockchain.Config().XDPoS != nil {
|
||||||
|
manager.bfter.SetConsensusFuns(engine)
|
||||||
|
}
|
||||||
|
|
||||||
return manager, nil
|
return manager, nil
|
||||||
}
|
}
|
||||||
|
|
@ -253,7 +266,6 @@ func (pm *ProtocolManager) Start(maxPeers int) {
|
||||||
// broadcast transactions
|
// broadcast transactions
|
||||||
pm.txCh = make(chan core.TxPreEvent, txChanSize)
|
pm.txCh = make(chan core.TxPreEvent, txChanSize)
|
||||||
pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh)
|
pm.txSub = pm.txpool.SubscribeTxPreEvent(pm.txCh)
|
||||||
|
|
||||||
pm.orderTxCh = make(chan core.OrderTxPreEvent, txChanSize)
|
pm.orderTxCh = make(chan core.OrderTxPreEvent, txChanSize)
|
||||||
if pm.orderpool != nil {
|
if pm.orderpool != nil {
|
||||||
pm.orderTxSub = pm.orderpool.SubscribeTxPreEvent(pm.orderTxCh)
|
pm.orderTxSub = pm.orderpool.SubscribeTxPreEvent(pm.orderTxCh)
|
||||||
|
|
@ -808,6 +820,31 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
if pm.lendingpool != nil {
|
if pm.lendingpool != nil {
|
||||||
pm.lendingpool.AddRemotes(txs)
|
pm.lendingpool.AddRemotes(txs)
|
||||||
}
|
}
|
||||||
|
case msg.Code == VoteMsg:
|
||||||
|
var vote utils.Vote
|
||||||
|
if err := msg.Decode(&vote); err != nil {
|
||||||
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
// Mark the peer as owning the vote and process it
|
||||||
|
p.MarkVote(vote)
|
||||||
|
pm.bfter.Vote(vote)
|
||||||
|
case msg.Code == TimeoutMsg:
|
||||||
|
var timeout utils.Timeout
|
||||||
|
if err := msg.Decode(&timeout); err != nil {
|
||||||
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the peer as owning the timeout and process it
|
||||||
|
p.MarkTimeout(timeout)
|
||||||
|
pm.bfter.Timeout(timeout)
|
||||||
|
case msg.Code == SyncInfoMsg:
|
||||||
|
var syncInfo utils.SyncInfo
|
||||||
|
if err := msg.Decode(&syncInfo); err != nil {
|
||||||
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
// Mark the peer as owning the syncInfo and process it
|
||||||
|
p.MarkSyncInfo(syncInfo)
|
||||||
|
pm.bfter.SyncInfo(syncInfo)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||||
|
|
@ -859,6 +896,42 @@ func (pm *ProtocolManager) BroadcastTx(hash common.Hash, tx *types.Transaction)
|
||||||
log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers))
|
log.Trace("Broadcast transaction", "hash", hash, "recipients", len(peers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BroadcastVote will propagate a Vote to all peers which are not known to
|
||||||
|
// already have the given vote.
|
||||||
|
func (pm *ProtocolManager) BroadcastVote(vote utils.Vote) {
|
||||||
|
//hash := Vote.Hash()
|
||||||
|
hash := common.Hash{}
|
||||||
|
peers := pm.peers.PeersWithoutVote(hash)
|
||||||
|
for _, peer := range peers {
|
||||||
|
peer.SendVote(vote)
|
||||||
|
}
|
||||||
|
log.Trace("Propagated Vote", "hash", hash, "recipients", len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BroadcastTimeout will propagate a Timeout to all peers which are not known to
|
||||||
|
// already have the given timeout.
|
||||||
|
func (pm *ProtocolManager) BroadcastTimeout(timeout utils.Timeout) {
|
||||||
|
//hash := timeout.Hash()
|
||||||
|
hash := common.Hash{}
|
||||||
|
peers := pm.peers.PeersWithoutTimeout(hash)
|
||||||
|
for _, peer := range peers {
|
||||||
|
peer.SendTimeout(timeout)
|
||||||
|
}
|
||||||
|
log.Trace("Propagated Timeout", "hash", hash, "recipients", len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BroadcastSyncInfo will propagate a SyncInfo to all peers which are not known to
|
||||||
|
// already have the given SyncInfo.
|
||||||
|
func (pm *ProtocolManager) BroadcastSyncInfo(syncInfo utils.SyncInfo) {
|
||||||
|
//hash := syncInfo.Hash()
|
||||||
|
hash := common.Hash{}
|
||||||
|
peers := pm.peers.PeersWithoutSyncInfo(hash)
|
||||||
|
for _, peer := range peers {
|
||||||
|
peer.SendSyncInfo(syncInfo)
|
||||||
|
}
|
||||||
|
log.Trace("Propagated SyncInfo", "hash", hash, "recipients", len(peers))
|
||||||
|
}
|
||||||
|
|
||||||
// OrderBroadcastTx will propagate a transaction to all peers which are not known to
|
// OrderBroadcastTx will propagate a transaction to all peers which are not known to
|
||||||
// already have the given transaction.
|
// already have the given transaction.
|
||||||
func (pm *ProtocolManager) OrderBroadcastTx(hash common.Hash, tx *types.OrderTransaction) {
|
func (pm *ProtocolManager) OrderBroadcastTx(hash common.Hash, tx *types.OrderTransaction) {
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,14 @@
|
||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,13 @@ package eth
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
|
|
|
||||||
130
eth/peer.go
130
eth/peer.go
|
|
@ -41,6 +41,9 @@ const (
|
||||||
maxKnownOrderTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
maxKnownOrderTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
maxKnownLendingTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
maxKnownLendingTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
|
maxKnownBlocks = 1024 // Maximum block hashes to keep in the known list (prevent DOS)
|
||||||
|
maxKnownVote = 1024 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
|
maxKnownTimeout = 1024 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
|
maxKnownSyncInfo = 1024 // Maximum transactions hashes to keep in the known list (prevent DOS)
|
||||||
handshakeTimeout = 5 * time.Second
|
handshakeTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -66,10 +69,15 @@ type peer struct {
|
||||||
td *big.Int
|
td *big.Int
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
|
||||||
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
|
knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
|
||||||
knownBlocks mapset.Set // Set of block hashes known to be known by this peer
|
knownBlocks mapset.Set // Set of block hashes known to be known by this peer
|
||||||
|
|
||||||
knownOrderTxs mapset.Set // Set of order transaction hashes known to be known by this peer
|
knownOrderTxs mapset.Set // Set of order transaction hashes known to be known by this peer
|
||||||
knownLendingTxs mapset.Set // Set of lending transaction hashes known to be known by this peer
|
knownLendingTxs mapset.Set // Set of lending transaction hashes known to be known by this peer
|
||||||
|
|
||||||
|
knownVote mapset.Set // Set of BFT Vote known to be known by this peer
|
||||||
|
knownTimeout mapset.Set // Set of BFT timeout known to be known by this peer
|
||||||
|
knownSyncInfo mapset.Set // Set of BFT Sync Info known to be known by this peer`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||||
|
|
@ -157,6 +165,36 @@ func (p *peer) MarkLendingTransaction(hash common.Hash) {
|
||||||
p.knownLendingTxs.Add(hash)
|
p.knownLendingTxs.Add(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarkVote marks a vote as known for the peer, ensuring that it
|
||||||
|
// will never be propagated to this particular peer.
|
||||||
|
func (p *peer) MarkVote(hash interface{}) {
|
||||||
|
// If we reached the memory allowance, drop a previously known transaction hash
|
||||||
|
for p.knownVote.Cardinality() >= maxKnownVote {
|
||||||
|
p.knownVote.Pop()
|
||||||
|
}
|
||||||
|
p.knownVote.Add(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkTimeout marks a timeout as known for the peer, ensuring that it
|
||||||
|
// will never be propagated to this particular peer.
|
||||||
|
func (p *peer) MarkTimeout(hash interface{}) {
|
||||||
|
// If we reached the memory allowance, drop a previously known transaction hash
|
||||||
|
for p.knownTimeout.Cardinality() >= maxKnownTimeout {
|
||||||
|
p.knownTimeout.Pop()
|
||||||
|
}
|
||||||
|
p.knownTimeout.Add(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkSyncInfo marks a syncInfo as known for the peer, ensuring that it
|
||||||
|
// will never be propagated to this particular peer.
|
||||||
|
func (p *peer) MarkSyncInfo(hash interface{}) {
|
||||||
|
// If we reached the memory allowance, drop a previously known transaction hash
|
||||||
|
for p.knownSyncInfo.Cardinality() >= maxKnownSyncInfo {
|
||||||
|
p.knownSyncInfo.Pop()
|
||||||
|
}
|
||||||
|
p.knownSyncInfo.Add(hash)
|
||||||
|
}
|
||||||
|
|
||||||
// SendTransactions sends transactions to the peer and includes the hashes
|
// SendTransactions sends transactions to the peer and includes the hashes
|
||||||
// in its transaction hash set for future reference.
|
// in its transaction hash set for future reference.
|
||||||
func (p *peer) SendTransactions(txs types.Transactions) error {
|
func (p *peer) SendTransactions(txs types.Transactions) error {
|
||||||
|
|
@ -256,6 +294,49 @@ func (p *peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *peer) SendVote(vote interface{}) error {
|
||||||
|
p.knownVote.Add(vote)
|
||||||
|
if p.pairRw != nil {
|
||||||
|
return p2p.Send(p.pairRw, VoteMsg, vote)
|
||||||
|
} else {
|
||||||
|
return p2p.Send(p.rw, VoteMsg, vote)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (p *peer) AsyncSendVote() {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p *peer) SendTimeout(timeout interface{}) error {
|
||||||
|
p.knownTimeout.Add(timeout)
|
||||||
|
if p.pairRw != nil {
|
||||||
|
return p2p.Send(p.pairRw, TimeoutMsg, timeout)
|
||||||
|
} else {
|
||||||
|
return p2p.Send(p.rw, TimeoutMsg, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (p *peer) AsyncSendTimeout() {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (p *peer) SendSyncInfo(syncInfo interface{}) error {
|
||||||
|
p.knownSyncInfo.Add(syncInfo)
|
||||||
|
if p.pairRw != nil {
|
||||||
|
return p2p.Send(p.pairRw, SyncInfoMsg, syncInfo)
|
||||||
|
} else {
|
||||||
|
return p2p.Send(p.rw, SyncInfoMsg, syncInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (p *peer) AsyncSendSyncInfo() {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// RequestOneHeader is a wrapper around the header query functions to fetch a
|
// RequestOneHeader is a wrapper around the header query functions to fetch a
|
||||||
// single header. It is used solely by the fetcher.
|
// single header. It is used solely by the fetcher.
|
||||||
func (p *peer) RequestOneHeader(hash common.Hash) error {
|
func (p *peer) RequestOneHeader(hash common.Hash) error {
|
||||||
|
|
@ -486,6 +567,51 @@ func (ps *peerSet) PeersWithoutTx(hash common.Hash) []*peer {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeersWithoutVote retrieves a list of peers that do not have a given block in
|
||||||
|
// their set of known hashes.
|
||||||
|
func (ps *peerSet) PeersWithoutVote(hash common.Hash) []*peer {
|
||||||
|
ps.lock.RLock()
|
||||||
|
defer ps.lock.RUnlock()
|
||||||
|
|
||||||
|
list := make([]*peer, 0, len(ps.peers))
|
||||||
|
for _, p := range ps.peers {
|
||||||
|
if !p.knownVote.Contains(hash) {
|
||||||
|
list = append(list, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeersWithoutTimeout retrieves a list of peers that do not have a given block in
|
||||||
|
// their set of known hashes.
|
||||||
|
func (ps *peerSet) PeersWithoutTimeout(hash common.Hash) []*peer {
|
||||||
|
ps.lock.RLock()
|
||||||
|
defer ps.lock.RUnlock()
|
||||||
|
|
||||||
|
list := make([]*peer, 0, len(ps.peers))
|
||||||
|
for _, p := range ps.peers {
|
||||||
|
if !p.knownTimeout.Contains(hash) {
|
||||||
|
list = append(list, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeersWithoutSyncInfo retrieves a list of peers that do not have a given block in
|
||||||
|
// their set of known hashes.
|
||||||
|
func (ps *peerSet) PeersWithoutSyncInfo(hash common.Hash) []*peer {
|
||||||
|
ps.lock.RLock()
|
||||||
|
defer ps.lock.RUnlock()
|
||||||
|
|
||||||
|
list := make([]*peer, 0, len(ps.peers))
|
||||||
|
for _, p := range ps.peers {
|
||||||
|
if !p.knownSyncInfo.Contains(hash) {
|
||||||
|
list = append(list, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
// PeersWithoutTx retrieves a list of peers that do not have a given transaction
|
// PeersWithoutTx retrieves a list of peers that do not have a given transaction
|
||||||
// in their set of known hashes.
|
// in their set of known hashes.
|
||||||
func (ps *peerSet) OrderPeersWithoutTx(hash common.Hash) []*peer {
|
func (ps *peerSet) OrderPeersWithoutTx(hash common.Hash) []*peer {
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,11 @@ const (
|
||||||
NodeDataMsg = 0x0e
|
NodeDataMsg = 0x0e
|
||||||
GetReceiptsMsg = 0x0f
|
GetReceiptsMsg = 0x0f
|
||||||
ReceiptsMsg = 0x10
|
ReceiptsMsg = 0x10
|
||||||
|
|
||||||
|
// Protocol messages belonging to eth/100
|
||||||
|
VoteMsg = 0xe0
|
||||||
|
TimeoutMsg = 0xe1
|
||||||
|
SyncInfoMsg = 0xe2
|
||||||
)
|
)
|
||||||
|
|
||||||
type errCode int
|
type errCode int
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,9 @@ func (pm *ProtocolManager) txsyncLoop() {
|
||||||
func (pm *ProtocolManager) syncer() {
|
func (pm *ProtocolManager) syncer() {
|
||||||
// Start and ensure cleanup of sync mechanisms
|
// Start and ensure cleanup of sync mechanisms
|
||||||
pm.fetcher.Start()
|
pm.fetcher.Start()
|
||||||
|
pm.bfter.Start()
|
||||||
defer pm.fetcher.Stop()
|
defer pm.fetcher.Stop()
|
||||||
|
defer pm.bfter.Stop()
|
||||||
defer pm.downloader.Terminate()
|
defer pm.downloader.Terminate()
|
||||||
|
|
||||||
// Wait for different events to fire synchronisation operations
|
// Wait for different events to fire synchronisation operations
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue