mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
generate and verify timeout message
This commit is contained in:
parent
a1b77f3ca8
commit
4addb69561
10 changed files with 198 additions and 57 deletions
|
|
@ -20,7 +20,9 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -29,7 +31,9 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain"
|
"github.com/XinFinOrg/XDPoSChain"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||||
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
"github.com/XinFinOrg/XDPoSChain/accounts/abi/bind"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts/keystore"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/math"
|
"github.com/XinFinOrg/XDPoSChain/common/math"
|
||||||
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
|
@ -69,6 +73,28 @@ type SimulatedBackend struct {
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SimulateWalletAddressAndSignFn() (common.Address, func(account accounts.Account, hash []byte) ([]byte, error), error) {
|
||||||
|
veryLightScryptN := 2
|
||||||
|
veryLightScryptP := 1
|
||||||
|
dir, _ := ioutil.TempDir("", "eth-SimulateWalletAddressAndSignFn-test")
|
||||||
|
|
||||||
|
new := func(kd string) *keystore.KeyStore {
|
||||||
|
return keystore.NewKeyStore(kd, veryLightScryptN, veryLightScryptP)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
ks := new(dir)
|
||||||
|
pass := "" // not used but required by API
|
||||||
|
a1, err := ks.NewAccount(pass)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, nil, fmt.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
if err := ks.Unlock(a1, ""); err != nil {
|
||||||
|
return a1.Address, nil, fmt.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
return a1.Address, ks.SignHash, nil
|
||||||
|
}
|
||||||
|
|
||||||
// XDC simulated backend for testing purpose.
|
// XDC simulated backend for testing purpose.
|
||||||
func NewXDCSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64, chainConfig *params.ChainConfig) *SimulatedBackend {
|
func NewXDCSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64, chainConfig *params.ChainConfig) *SimulatedBackend {
|
||||||
// database := ethdb.NewMemDatabase()
|
// database := ethdb.NewMemDatabase()
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ func (x *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent
|
||||||
func (x *XDPoS) Authorize(signer common.Address, signFn clique.SignerFn) {
|
func (x *XDPoS) Authorize(signer common.Address, signFn clique.SignerFn) {
|
||||||
// Authorize each consensus individually
|
// Authorize each consensus individually
|
||||||
x.EngineV1.Authorize(signer, signFn)
|
x.EngineV1.Authorize(signer, signFn)
|
||||||
|
x.EngineV2.Authorize(signer, signFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XDPoS) GetPeriod() uint64 {
|
func (x *XDPoS) GetPeriod() uint64 {
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,36 @@
|
||||||
package engine_v2
|
package engine_v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/accounts"
|
||||||
"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/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/clique"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
"github.com/XinFinOrg/XDPoSChain/ethdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/log"
|
"github.com/XinFinOrg/XDPoSChain/log"
|
||||||
"github.com/XinFinOrg/XDPoSChain/params"
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
type XDPoS_v2 struct {
|
type XDPoS_v2 struct {
|
||||||
config *params.XDPoSConfig // Consensus engine configuration parameters
|
config *params.XDPoSConfig // Consensus engine configuration parameters
|
||||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||||
|
|
||||||
|
signer common.Address // Ethereum address of the signing key
|
||||||
|
signFn clique.SignerFn // Signer function to authorize hashes with
|
||||||
|
lock sync.RWMutex // Protects the signer fields
|
||||||
|
|
||||||
BroadcastCh chan interface{}
|
BroadcastCh chan interface{}
|
||||||
BFTQueue chan interface{}
|
BFTQueue chan interface{}
|
||||||
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
|
timeoutWorker *countdown.CountdownTimer // Timer to generate broadcast timeout msg if threashold reached
|
||||||
|
|
||||||
|
currentRound utils.Round
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
||||||
|
|
@ -30,6 +42,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
||||||
config: config,
|
config: config,
|
||||||
db: db,
|
db: db,
|
||||||
timeoutWorker: timer,
|
timeoutWorker: timer,
|
||||||
|
BroadcastCh: make(chan interface{}),
|
||||||
|
BFTQueue: make(chan interface{}),
|
||||||
}
|
}
|
||||||
// Add callback to the timer
|
// Add callback to the timer
|
||||||
timer.OnTimeoutFn = engine.onCountdownTimeout
|
timer.OnTimeoutFn = engine.onCountdownTimeout
|
||||||
|
|
@ -37,6 +51,10 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 {
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Testing tools
|
||||||
|
*/
|
||||||
|
// Test only. Never to be used for mainnet implementation
|
||||||
func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 {
|
func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 {
|
||||||
var fakeEngine *XDPoS_v2
|
var fakeEngine *XDPoS_v2
|
||||||
// Set any missing consensus parameters to their defaults
|
// Set any missing consensus parameters to their defaults
|
||||||
|
|
@ -50,25 +68,45 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 {
|
||||||
config: conf,
|
config: conf,
|
||||||
db: db,
|
db: db,
|
||||||
timeoutWorker: timer,
|
timeoutWorker: timer,
|
||||||
|
BroadcastCh: make(chan interface{}),
|
||||||
|
BFTQueue: make(chan interface{}),
|
||||||
}
|
}
|
||||||
|
// Add callback to the timer
|
||||||
|
timer.OnTimeoutFn = fakeEngine.onCountdownTimeout
|
||||||
return fakeEngine
|
return fakeEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) Author(header *types.Header) (common.Address, error) {
|
// Test only.
|
||||||
|
func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round) {
|
||||||
|
// Reset a bunch of things
|
||||||
|
x.timeoutWorker.Reset()
|
||||||
|
x.currentRound = newRound
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorize injects a private key into the consensus engine to mint new blocks with.
|
||||||
|
func (x *XDPoS_v2) Authorize(signer common.Address, signFn clique.SignerFn) {
|
||||||
|
x.lock.Lock()
|
||||||
|
defer x.lock.Unlock()
|
||||||
|
|
||||||
|
x.signer = signer
|
||||||
|
x.signFn = signFn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) Author(header *types.Header) (common.Address, error) {
|
||||||
return common.Address{}, nil
|
return common.Address{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
|
func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push mesages(i.e vote, sync info & timeout) into BFTQueue. This funciton shall be called by BFT protocal manager
|
// Push mesages(i.e vote, sync info & timeout) into BFTQueue. This funciton shall be called by BFT protocal manager
|
||||||
func (consensus *XDPoS_v2) Enqueue() error {
|
func (x *XDPoS_v2) Enqueue() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function for the v2 consensus.
|
// Main function for the v2 consensus.
|
||||||
func (consensus *XDPoS_v2) Dispatcher() error {
|
func (x *XDPoS_v2) Dispatcher() error {
|
||||||
// 1. Pull message from the BFTQueue and call the relevant handler by message type, such as vote, timeout or syncInfo
|
// 1. Pull message from the BFTQueue and call the relevant handler by message type, such as vote, timeout or syncInfo
|
||||||
// 2. Only 1 message processing at the time
|
// 2. Only 1 message processing at the time
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -78,7 +116,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(syncInfo utils.SyncInfo) error {
|
func (x *XDPoS_v2) VerifySyncInfoMessage(syncInfo utils.SyncInfo) error {
|
||||||
/*
|
/*
|
||||||
1. Verify items including:
|
1. Verify items including:
|
||||||
- verifyQC
|
- verifyQC
|
||||||
|
|
@ -88,7 +126,7 @@ func (consensus *XDPoS_v2) VerifySyncInfoMessage(syncInfo utils.SyncInfo) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) SyncInfoHandler(header *types.Header) error {
|
func (x *XDPoS_v2) SyncInfoHandler(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
1. processQC
|
1. processQC
|
||||||
2. processTC
|
2. processTC
|
||||||
|
|
@ -99,7 +137,7 @@ func (consensus *XDPoS_v2) SyncInfoHandler(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
Vote workflow
|
Vote workflow
|
||||||
*/
|
*/
|
||||||
func (consensus *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error {
|
func (x *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
|
||||||
|
|
@ -111,7 +149,7 @@ func (consensus *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) VoteHandler() {
|
func (x *XDPoS_v2) VoteHandler() {
|
||||||
/*
|
/*
|
||||||
1. checkRoundNumber
|
1. checkRoundNumber
|
||||||
3. Collect vote (TODO)
|
3. Collect vote (TODO)
|
||||||
|
|
@ -124,18 +162,32 @@ 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(utils.Timeout) error {
|
/*
|
||||||
/*
|
1. Check signature:
|
||||||
1. Check signature:
|
- Use ecRecover to get the public key
|
||||||
- Use ecRecover to get the public key
|
- Use the above public key to find out the xdc address
|
||||||
- Use the above public key to find out the xdc address
|
- Use the above xdc address to check against the master node(For the running epoch)
|
||||||
- Use the above xdc address to check against the master node(For the running epoch)
|
2. Broadcast(Not part of consensus)
|
||||||
2. Broadcast(Not part of consensus)
|
*/
|
||||||
*/
|
func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg utils.Timeout) (bool, error) {
|
||||||
return nil
|
// Recover the public key and the Ethereum address
|
||||||
|
pubkey, err := crypto.Ecrecover(utils.TimeoutSigHash(timeoutMsg.Round).Bytes(), timeoutMsg.Signature)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("Error while verifying time out message: %v", err)
|
||||||
|
}
|
||||||
|
var signerAddress common.Address
|
||||||
|
copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
masternodes := x.getCurrentRoundMasterNodes()
|
||||||
|
for _, mn := range masternodes {
|
||||||
|
if mn == signerAddress {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, fmt.Errorf("Masternodes does not contain signer address. Master node list %v, Signer address: %v", masternodes, signerAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) TimeoutHandler() {
|
func (x *XDPoS_v2) TimeoutHandler() {
|
||||||
/*
|
/*
|
||||||
1. checkRoundNumber()
|
1. checkRoundNumber()
|
||||||
2. Collect timeout (TODO)
|
2. Collect timeout (TODO)
|
||||||
|
|
@ -148,7 +200,7 @@ func (consensus *XDPoS_v2) TimeoutHandler() {
|
||||||
/*
|
/*
|
||||||
Process Block workflow
|
Process Block workflow
|
||||||
*/
|
*/
|
||||||
func (consensus *XDPoS_v2) ProcessBlockHandler() {
|
func (x *XDPoS_v2) ProcessBlockHandler() {
|
||||||
/*
|
/*
|
||||||
1. processQC()
|
1. processQC()
|
||||||
2. verifyVotingRule()
|
2. verifyVotingRule()
|
||||||
|
|
@ -162,16 +214,16 @@ func (consensus *XDPoS_v2) ProcessBlockHandler() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Genrate blockInfo which contains Hash, round and blockNumber and send to queue
|
// Genrate blockInfo which contains Hash, round and blockNumber and send to queue
|
||||||
func (consensus *XDPoS_v2) generateBlockInfo() error {
|
func (x *XDPoS_v2) generateBlockInfo() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(blockInfo utils.BlockInfo) error {
|
func (x *XDPoS_v2) VerifyBlockInfo(blockInfo utils.BlockInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) verifyQC(header *types.Header) error {
|
func (x *XDPoS_v2) verifyQC(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
1. Verify signer signatures: (List of signatures)
|
1. Verify signer signatures: (List of signatures)
|
||||||
- Use ecRecover to get the public key
|
- Use ecRecover to get the public key
|
||||||
|
|
@ -182,7 +234,7 @@ func (consensus *XDPoS_v2) verifyQC(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) verifyTC(header *types.Header) error {
|
func (x *XDPoS_v2) verifyTC(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
1. Verify signer signature: (List of signatures)
|
1. Verify signer signature: (List of signatures)
|
||||||
- Use ecRecover to get the public key
|
- Use ecRecover to get the public key
|
||||||
|
|
@ -193,7 +245,7 @@ func (consensus *XDPoS_v2) verifyTC(header *types.Header) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update local QC variables including highestQC & lockQC, as well as update commit blockInfo before call
|
// Update local QC variables including highestQC & lockQC, as well as update commit blockInfo before call
|
||||||
func (consensus *XDPoS_v2) processQC(header *types.Header) error {
|
func (x *XDPoS_v2) processQC(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
1. Update HighestQC and LockQC
|
1. Update HighestQC and LockQC
|
||||||
2. Update commit block info (TODO)
|
2. Update commit block info (TODO)
|
||||||
|
|
@ -202,7 +254,7 @@ func (consensus *XDPoS_v2) processQC(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) processTC(header *types.Header) error {
|
func (x *XDPoS_v2) processTC(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
1. Update highestTC
|
1. Update highestTC
|
||||||
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
||||||
|
|
@ -210,7 +262,7 @@ func (consensus *XDPoS_v2) processTC(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (consensus *XDPoS_v2) setNewRound() error {
|
func (x *XDPoS_v2) setNewRound() error {
|
||||||
/*
|
/*
|
||||||
1. Set currentRound = QC round + 1 (or TC round +1)
|
1. Set currentRound = QC round + 1 (or TC round +1)
|
||||||
2. Reset timer
|
2. Reset timer
|
||||||
|
|
@ -220,12 +272,12 @@ func (consensus *XDPoS_v2) setNewRound() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify round number against node's local round number(Should be equal)
|
// Verify round number against node's local round number(Should be equal)
|
||||||
func (consensus *XDPoS_v2) checkRoundNumber(header *types.Header) error {
|
func (x *XDPoS_v2) checkRoundNumber(header *types.Header) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hot stuff rule to decide whether this node is eligible to vote for the received block
|
// Hot stuff rule to decide whether this node is eligible to vote for the received block
|
||||||
func (consensus *XDPoS_v2) verifyVotingRule(header *types.Header) error {
|
func (x *XDPoS_v2) verifyVotingRule(header *types.Header) error {
|
||||||
/*
|
/*
|
||||||
Make sure this node has not voted for this round. We can have a variable highestVotedRound, and check currentRound > highestVotedRound.
|
Make sure this node has not voted for this round. We can have a variable highestVotedRound, and check currentRound > highestVotedRound.
|
||||||
HotStuff Voting rule:
|
HotStuff Voting rule:
|
||||||
|
|
@ -237,7 +289,7 @@ func (consensus *XDPoS_v2) verifyVotingRule(header *types.Header) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once Hot stuff voting rule has verified, this node can then send vote
|
// Once Hot stuff voting rule has verified, this node can then send vote
|
||||||
func (consensus *XDPoS_v2) sendVote(header *types.Header) error {
|
func (x *XDPoS_v2) sendVote(header *types.Header) error {
|
||||||
// First step: Generate the signature by using node's private key(The signature is the blockInfo signature)
|
// First step: Generate the signature by using node's private key(The signature is the blockInfo signature)
|
||||||
// Second step: Construct the vote struct with the above signature & blockinfo struct
|
// Second step: Construct the vote struct with the above signature & blockinfo struct
|
||||||
// Third step: Send the vote to broadcast channel
|
// Third step: Send the vote to broadcast channel
|
||||||
|
|
@ -245,17 +297,31 @@ func (consensus *XDPoS_v2) sendVote(header *types.Header) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and send timeout into BFT channel.
|
// Generate and send timeout into BFT channel.
|
||||||
func (consensus *XDPoS_v2) sendTimeout() error {
|
/*
|
||||||
/*
|
1. timeout.round = currentRound
|
||||||
1. timeout.round = currentRound
|
2. Sign the signature
|
||||||
2. Sign the signature
|
3. send to broadcast channel
|
||||||
3. send to broadcast channel
|
*/
|
||||||
*/
|
func (x *XDPoS_v2) sendTimeout() error {
|
||||||
|
// Don't hold the signer fields for the entire sealing procedure
|
||||||
|
x.lock.RLock()
|
||||||
|
signer, signFn := x.signer, x.signFn
|
||||||
|
x.lock.RUnlock()
|
||||||
|
|
||||||
|
signedHash, err := signFn(accounts.Account{Address: signer}, utils.TimeoutSigHash(x.currentRound).Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error while signing for timeout message")
|
||||||
|
}
|
||||||
|
timeoutMsg := &utils.Timeout{
|
||||||
|
Round: x.currentRound,
|
||||||
|
Signature: signedHash,
|
||||||
|
}
|
||||||
|
x.broadcastToBftChannel(timeoutMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate and send syncInfo into Broadcast channel. The SyncInfo includes local highest QC & TC
|
// Generate and send syncInfo into Broadcast channel. The SyncInfo includes local highest QC & TC
|
||||||
func (consensus *XDPoS_v2) sendSyncInfo() error {
|
func (x *XDPoS_v2) sendSyncInfo() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,10 +329,19 @@ func (consensus *XDPoS_v2) sendSyncInfo() error {
|
||||||
Function that will be called by timer when countdown reaches its threshold.
|
Function that will be called by timer when countdown reaches its threshold.
|
||||||
In the engine v2, we would need to broadcast timeout messages to other peers
|
In the engine v2, we would need to broadcast timeout messages to other peers
|
||||||
*/
|
*/
|
||||||
func (consensus *XDPoS_v2) onCountdownTimeout(time time.Time) error {
|
func (x *XDPoS_v2) onCountdownTimeout(time time.Time) error {
|
||||||
err := consensus.sendTimeout()
|
err := x.sendTimeout()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error while sending out timeout message at time: ", time)
|
log.Error("Error while sending out timeout message at time: ", time)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) {
|
||||||
|
x.BroadcastCh <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) getCurrentRoundMasterNodes() []common.Address {
|
||||||
|
return []common.Address{}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,10 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error {
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
hw := sha3.NewKeccak256()
|
hw := sha3.NewKeccak256()
|
||||||
rlp.Encode(hw, x)
|
err := rlp.Encode(hw, x)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("rlpHash failed", err)
|
||||||
|
}
|
||||||
hw.Sum(h[:0])
|
hw.Sum(h[:0])
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
@ -195,10 +198,10 @@ func (m *SyncInfo) Hash() common.Hash {
|
||||||
return rlpHash(m)
|
return rlpHash(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func VoteSigHash(m *BlockInfo) common.Hash {
|
func VoteSigHash(m BlockInfo) common.Hash {
|
||||||
return rlpHash(m)
|
return rlpHash(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TimeoutSigHash(m *Round) common.Hash {
|
func TimeoutSigHash(m Round) common.Hash {
|
||||||
return rlpHash(m)
|
return rlpHash(m)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,11 +134,11 @@ func TestHashAndSigHash(t *testing.T) {
|
||||||
if syncInfo1.Hash() == syncInfo2.Hash() {
|
if syncInfo1.Hash() == syncInfo2.Hash() {
|
||||||
t.Fatalf("Hash of two sync info shouldn't equal")
|
t.Fatalf("Hash of two sync info shouldn't equal")
|
||||||
}
|
}
|
||||||
if VoteSigHash(&blockInfo1) == VoteSigHash(&blockInfo2) {
|
if VoteSigHash(blockInfo1) == VoteSigHash(blockInfo2) {
|
||||||
t.Fatalf("SigHash of two block info shouldn't equal")
|
t.Fatalf("SigHash of two block info shouldn't equal")
|
||||||
}
|
}
|
||||||
round2 := Round(999)
|
round2 := Round(999)
|
||||||
if TimeoutSigHash(&round) == TimeoutSigHash(&round2) {
|
if TimeoutSigHash(round) == TimeoutSigHash(round2) {
|
||||||
t.Fatalf("SigHash of two round shouldn't equal")
|
t.Fatalf("SigHash of two round shouldn't equal")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,9 @@ var (
|
||||||
XDPoSV2Config = &V2{
|
XDPoSV2Config = &V2{
|
||||||
TimeoutWorkerDuration: 50000,
|
TimeoutWorkerDuration: 50000,
|
||||||
}
|
}
|
||||||
|
TestXDPoSV2Config = &V2{
|
||||||
|
TimeoutWorkerDuration: 5000,
|
||||||
|
}
|
||||||
|
|
||||||
// XDPoSChain mainnet config
|
// XDPoSChain mainnet config
|
||||||
XDCMainnetChainConfig = &ChainConfig{
|
XDCMainnetChainConfig = &ChainConfig{
|
||||||
|
|
@ -125,9 +128,9 @@ var (
|
||||||
|
|
||||||
TestXDPoSChanConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 2, Epoch: 900, Reward: 250, RewardCheckpoint: 900, Gap: 890, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), V2: *XDPoSV2Config}}
|
TestXDPoSChanConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &XDPoSConfig{Period: 2, Epoch: 900, Reward: 250, RewardCheckpoint: 900, Gap: 890, FoudationWalletAddr: common.HexToAddress("0x0000000000000000000000000000000000000068"), V2: *XDPoSV2Config}}
|
||||||
// XDPoS config in use for v1 engine only
|
// XDPoS config in use for v1 engine only
|
||||||
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: *XDPoSV2Config}}
|
TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: *TestXDPoSV2Config}}
|
||||||
// XDPoS config with v2 engine after block 10
|
// XDPoS config with v2 engine after block 10
|
||||||
TestXDPoSMockChainConfigWithV2Engine = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(10), V2: *XDPoSV2Config}}
|
TestXDPoSMockChainConfigWithV2Engine = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(10), V2: *TestXDPoSV2Config}}
|
||||||
|
|
||||||
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil}
|
||||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) {
|
||||||
blockchain, _, currentBlock := PrepareXDCTestBlockChain(t, 10, params.TestXDPoSMockChainConfigWithV2Engine)
|
blockchain, _, currentBlock, _ := PrepareXDCTestBlockChain(t, 10, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
|
||||||
|
|
||||||
addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header())
|
addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header())
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
// Should NOT update signerList if not on the gap block
|
// Should NOT update signerList if not on the gap block
|
||||||
func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
||||||
blockchain, backend, parentBlock := PrepareXDCTestBlockChain(t, 400, params.TestXDPoSMockChainConfig)
|
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, 400, params.TestXDPoSMockChainConfig)
|
||||||
parentSigners, err := GetSnapshotSigner(blockchain, parentBlock.Header())
|
parentSigners, err := GetSnapshotSigner(blockchain, parentBlock.Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -50,7 +50,7 @@ func TestNotUpdateSignerListIfNotOnGapBlock(t *testing.T) {
|
||||||
|
|
||||||
// Should call updateM1 at the gap block, and have the same snapshot values as the parent block if no SM transaction is involved
|
// Should call updateM1 at the gap block, and have the same snapshot values as the parent block if no SM transaction is involved
|
||||||
func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
||||||
blockchain, _, parentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||||
// Insert block 450
|
// Insert block 450
|
||||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450)
|
||||||
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
|
||||||
|
|
@ -78,7 +78,7 @@ func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) {
|
||||||
//Should call updateM1 at gap block, and update the snapshot if there are SM transactions involved
|
//Should call updateM1 at gap block, and update the snapshot if there are SM transactions involved
|
||||||
func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||||
|
|
||||||
blockchain, backend, parentBlock := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-2, params.TestXDPoSMockChainConfig)
|
||||||
// Insert first Block 449
|
// Insert first Block 449
|
||||||
t.Logf("Inserting block with propose at 449...")
|
t.Logf("Inserting block with propose at 449...")
|
||||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000449"
|
blockCoinbaseA := "0xaaa0000000000000000000000000000000000449"
|
||||||
|
|
@ -136,7 +136,7 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
|
||||||
//Should call updateM1 before gap block, and update the snapshot if there are SM transactions involved
|
//Should call updateM1 before gap block, and update the snapshot if there are SM transactions involved
|
||||||
func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
||||||
|
|
||||||
blockchain, backend, currentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||||
// Insert first Block 450 A
|
// Insert first Block 450 A
|
||||||
t.Logf("Inserting block with propose at 450 A...")
|
t.Logf("Inserting block with propose at 450 A...")
|
||||||
blockCoinbaseA := "0xaaa0000000000000000000000000000000000450"
|
blockCoinbaseA := "0xaaa0000000000000000000000000000000000450"
|
||||||
|
|
@ -165,7 +165,7 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
|
||||||
// Should call updateM1 and update snapshot when a forked block(at gap block number) is inserted back into main chain (Edge case)
|
// Should call updateM1 and update snapshot when a forked block(at gap block number) is inserted back into main chain (Edge case)
|
||||||
func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||||
|
|
||||||
blockchain, backend, currentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
blockchain, backend, currentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||||
// Check initial signer, by default, acc3 is in the signerList
|
// Check initial signer, by default, acc3 is in the signerList
|
||||||
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -288,7 +288,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
|
||||||
|
|
||||||
func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testing.T) {
|
func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testing.T) {
|
||||||
|
|
||||||
blockchain, backend, parentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||||
|
|
||||||
state, err := blockchain.State()
|
state, err := blockchain.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -422,7 +422,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
|
||||||
blockchain, backend, parentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
blockchain, backend, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig)
|
||||||
// Check initial signer, by default, acc3 is in the signerList
|
// Check initial signer, by default, acc3 is in the signerList
|
||||||
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -223,13 +223,20 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi
|
||||||
return ms
|
return ms
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block) {
|
func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address) {
|
||||||
// Preparation
|
// Preparation
|
||||||
var err error
|
var err error
|
||||||
backend := getCommonBackend(t, chainConfig)
|
backend := getCommonBackend(t, chainConfig)
|
||||||
blockchain := backend.GetBlockChain()
|
blockchain := backend.GetBlockChain()
|
||||||
blockchain.Client = backend
|
blockchain.Client = backend
|
||||||
|
|
||||||
|
// Authorise
|
||||||
|
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err))
|
||||||
|
}
|
||||||
|
blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn)
|
||||||
|
|
||||||
currentBlock := blockchain.Genesis()
|
currentBlock := blockchain.Genesis()
|
||||||
|
|
||||||
// Insert initial blocks
|
// Insert initial blocks
|
||||||
|
|
@ -248,7 +255,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockchain, backend, currentBlock
|
return blockchain, backend, currentBlock, signer
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert Block without transcation attached
|
// insert Block without transcation attached
|
||||||
|
|
|
||||||
26
tests/consensus/v2_test.go
Normal file
26
tests/consensus/v2_test.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package consensus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
|
||||||
|
blockchain, _, _, _ := PrepareXDCTestBlockChain(t, 11, params.TestXDPoSMockChainConfigWithV2Engine)
|
||||||
|
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||||
|
|
||||||
|
engineV2.SetNewRoundFaker(utils.Round(1))
|
||||||
|
|
||||||
|
timeoutMsg := <-engineV2.BroadcastCh
|
||||||
|
assert.NotNil(t, timeoutMsg)
|
||||||
|
|
||||||
|
valid, err := engineV2.VerifyTimeoutMessage(*timeoutMsg.(*utils.Timeout))
|
||||||
|
// We can only test valid = false for now as the implementation for getCurrentRoundMasterNodes is not complete
|
||||||
|
assert.False(t, valid)
|
||||||
|
// This shows we are able to decode the timeout message, which is what this test is all about
|
||||||
|
assert.Regexp(t, "^Masternodes does not contain signer addres.*", err.Error())
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue