From 4addb69561dc725e9353ca9c74937ac456fbccb2 Mon Sep 17 00:00:00 2001 From: Jianrong Date: Sun, 7 Nov 2021 10:30:14 +1100 Subject: [PATCH] generate and verify timeout message --- accounts/abi/bind/backends/simulated.go | 26 ++++ consensus/XDPoS/XDPoS.go | 1 + consensus/XDPoS/engines/engine_v2/engine.go | 155 +++++++++++++++----- consensus/XDPoS/utils/utils.go | 9 +- consensus/XDPoS/utils/utils_test.go | 4 +- params/config.go | 7 +- tests/consensus/adaptor_test.go | 2 +- tests/consensus/block_signer_test.go | 14 +- tests/consensus/test_helper.go | 11 +- tests/consensus/v2_test.go | 26 ++++ 10 files changed, 198 insertions(+), 57 deletions(-) create mode 100644 tests/consensus/v2_test.go diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index b4fb82e071..bd08e3cb1e 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -20,7 +20,9 @@ import ( "context" "errors" "fmt" + "io/ioutil" "math/big" + "os" "sync" "time" @@ -29,7 +31,9 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/rawdb" "github.com/XinFinOrg/XDPoSChain" + "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/accounts/abi/bind" + "github.com/XinFinOrg/XDPoSChain/accounts/keystore" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/math" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" @@ -69,6 +73,28 @@ type SimulatedBackend struct { 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. func NewXDCSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64, chainConfig *params.ChainConfig) *SimulatedBackend { // database := ethdb.NewMemDatabase() diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 066b52a9bb..efd5ef29c1 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -210,6 +210,7 @@ func (x *XDPoS) CalcDifficulty(chain consensus.ChainReader, time uint64, parent func (x *XDPoS) Authorize(signer common.Address, signFn clique.SignerFn) { // Authorize each consensus individually x.EngineV1.Authorize(signer, signFn) + x.EngineV2.Authorize(signer, signFn) } func (x *XDPoS) GetPeriod() uint64 { diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index 15d9ee48f2..49e9e5de65 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -1,24 +1,36 @@ package engine_v2 import ( + "fmt" + "sync" "time" + "github.com/XinFinOrg/XDPoSChain/accounts" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/common/countdown" "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/consensus/clique" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" ) type XDPoS_v2 struct { - config *params.XDPoSConfig // Consensus engine configuration parameters - db ethdb.Database // Database to store and retrieve snapshot checkpoints + config *params.XDPoSConfig // Consensus engine configuration parameters + 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{} BFTQueue chan interface{} 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 { @@ -30,6 +42,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { config: config, db: db, timeoutWorker: timer, + BroadcastCh: make(chan interface{}), + BFTQueue: make(chan interface{}), } // Add callback to the timer timer.OnTimeoutFn = engine.onCountdownTimeout @@ -37,6 +51,10 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { return engine } +/* + Testing tools +*/ +// Test only. Never to be used for mainnet implementation func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 { var fakeEngine *XDPoS_v2 // Set any missing consensus parameters to their defaults @@ -50,25 +68,45 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v2 { config: conf, db: db, timeoutWorker: timer, + BroadcastCh: make(chan interface{}), + BFTQueue: make(chan interface{}), } + // Add callback to the timer + timer.OnTimeoutFn = fakeEngine.onCountdownTimeout 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 } -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 } // 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 } // 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 // 2. Only 1 message processing at the time return nil @@ -78,7 +116,7 @@ func (consensus *XDPoS_v2) Dispatcher() error { SyncInfo workflow */ // 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: - verifyQC @@ -88,7 +126,7 @@ func (consensus *XDPoS_v2) VerifySyncInfoMessage(syncInfo utils.SyncInfo) error return nil } -func (consensus *XDPoS_v2) SyncInfoHandler(header *types.Header) error { +func (x *XDPoS_v2) SyncInfoHandler(header *types.Header) error { /* 1. processQC 2. processTC @@ -99,7 +137,7 @@ func (consensus *XDPoS_v2) SyncInfoHandler(header *types.Header) error { /* Vote workflow */ -func (consensus *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error { +func (x *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error { /* 1. Check signature: - Use ecRecover to get the public key @@ -111,7 +149,7 @@ func (consensus *XDPoS_v2) VerifyVoteMessage(vote utils.Vote) error { return nil } -func (consensus *XDPoS_v2) VoteHandler() { +func (x *XDPoS_v2) VoteHandler() { /* 1. checkRoundNumber 3. Collect vote (TODO) @@ -124,18 +162,32 @@ func (consensus *XDPoS_v2) VoteHandler() { Timeout workflow */ // Verify timeout message type from peers in bft.go -func (consensus *XDPoS_v2) VerifyTimeoutMessage(utils.Timeout) error { - /* - 1. Check signature: - - Use ecRecover to get the public key - - 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) - 2. Broadcast(Not part of consensus) - */ - return nil +/* + 1. Check signature: + - Use ecRecover to get the public key + - 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) + 2. Broadcast(Not part of consensus) +*/ +func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg utils.Timeout) (bool, error) { + // 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() 2. Collect timeout (TODO) @@ -148,7 +200,7 @@ func (consensus *XDPoS_v2) TimeoutHandler() { /* Process Block workflow */ -func (consensus *XDPoS_v2) ProcessBlockHandler() { +func (x *XDPoS_v2) ProcessBlockHandler() { /* 1. processQC() 2. verifyVotingRule() @@ -162,16 +214,16 @@ func (consensus *XDPoS_v2) ProcessBlockHandler() { */ // 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 } // 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 } -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) - Use ecRecover to get the public key @@ -182,7 +234,7 @@ func (consensus *XDPoS_v2) verifyQC(header *types.Header) error { 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) - 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 -func (consensus *XDPoS_v2) processQC(header *types.Header) error { +func (x *XDPoS_v2) processQC(header *types.Header) error { /* 1. Update HighestQC and LockQC 2. Update commit block info (TODO) @@ -202,7 +254,7 @@ func (consensus *XDPoS_v2) processQC(header *types.Header) error { return nil } -func (consensus *XDPoS_v2) processTC(header *types.Header) error { +func (x *XDPoS_v2) processTC(header *types.Header) error { /* 1. Update highestTC 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 } -func (consensus *XDPoS_v2) setNewRound() error { +func (x *XDPoS_v2) setNewRound() error { /* 1. Set currentRound = QC round + 1 (or TC round +1) 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) -func (consensus *XDPoS_v2) checkRoundNumber(header *types.Header) error { +func (x *XDPoS_v2) checkRoundNumber(header *types.Header) error { return nil } // 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. 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 -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) // Second step: Construct the vote struct with the above signature & blockinfo struct // 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. -func (consensus *XDPoS_v2) sendTimeout() error { - /* - 1. timeout.round = currentRound - 2. Sign the signature - 3. send to broadcast channel - */ +/* + 1. timeout.round = currentRound + 2. Sign the signature + 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 } // 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 } @@ -263,10 +329,19 @@ func (consensus *XDPoS_v2) sendSyncInfo() error { 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 */ -func (consensus *XDPoS_v2) onCountdownTimeout(time time.Time) error { - err := consensus.sendTimeout() +func (x *XDPoS_v2) onCountdownTimeout(time time.Time) error { + err := x.sendTimeout() if err != nil { log.Error("Error while sending out timeout message at time: ", time) + return err } return nil } + +func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) { + x.BroadcastCh <- msg +} + +func (x *XDPoS_v2) getCurrentRoundMasterNodes() []common.Address { + return []common.Address{} +} diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go index fa756d02ed..4a073d4f50 100644 --- a/consensus/XDPoS/utils/utils.go +++ b/consensus/XDPoS/utils/utils.go @@ -178,7 +178,10 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error { func rlpHash(x interface{}) (h common.Hash) { hw := sha3.NewKeccak256() - rlp.Encode(hw, x) + err := rlp.Encode(hw, x) + if err != nil { + log.Error("rlpHash failed", err) + } hw.Sum(h[:0]) return h } @@ -195,10 +198,10 @@ func (m *SyncInfo) Hash() common.Hash { return rlpHash(m) } -func VoteSigHash(m *BlockInfo) common.Hash { +func VoteSigHash(m BlockInfo) common.Hash { return rlpHash(m) } -func TimeoutSigHash(m *Round) common.Hash { +func TimeoutSigHash(m Round) common.Hash { return rlpHash(m) } diff --git a/consensus/XDPoS/utils/utils_test.go b/consensus/XDPoS/utils/utils_test.go index 6dfc70971c..242aaf4501 100644 --- a/consensus/XDPoS/utils/utils_test.go +++ b/consensus/XDPoS/utils/utils_test.go @@ -134,11 +134,11 @@ func TestHashAndSigHash(t *testing.T) { if syncInfo1.Hash() == syncInfo2.Hash() { 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") } round2 := Round(999) - if TimeoutSigHash(&round) == TimeoutSigHash(&round2) { + if TimeoutSigHash(round) == TimeoutSigHash(round2) { t.Fatalf("SigHash of two round shouldn't equal") } } diff --git a/params/config.go b/params/config.go index 92e08da675..1bf0c24e62 100644 --- a/params/config.go +++ b/params/config.go @@ -38,6 +38,9 @@ var ( XDPoSV2Config = &V2{ TimeoutWorkerDuration: 50000, } + TestXDPoSV2Config = &V2{ + TimeoutWorkerDuration: 5000, + } // XDPoSChain mainnet config 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}} // 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 - 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} TestRules = TestChainConfig.Rules(new(big.Int)) diff --git a/tests/consensus/adaptor_test.go b/tests/consensus/adaptor_test.go index 517574adb0..d85827b3ac 100644 --- a/tests/consensus/adaptor_test.go +++ b/tests/consensus/adaptor_test.go @@ -10,7 +10,7 @@ import ( ) func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { - blockchain, _, currentBlock := PrepareXDCTestBlockChain(t, 10, params.TestXDPoSMockChainConfigWithV2Engine) + blockchain, _, currentBlock, _ := PrepareXDCTestBlockChain(t, 10, params.TestXDPoSMockChainConfigWithV2Engine) adaptor := blockchain.Engine().(*XDPoS.XDPoS) addressFromAdaptor, errorAdaptor := adaptor.Author(currentBlock.Header()) diff --git a/tests/consensus/block_signer_test.go b/tests/consensus/block_signer_test.go index f15659fcca..ee6c70eba9 100644 --- a/tests/consensus/block_signer_test.go +++ b/tests/consensus/block_signer_test.go @@ -12,7 +12,7 @@ import ( // Should NOT update signerList if not on the gap block 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()) if err != nil { 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 func TestNotChangeSingerListIfNothingProposedOrVoted(t *testing.T) { - blockchain, _, parentBlock := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig) + blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, GAP-1, params.TestXDPoSMockChainConfig) // Insert block 450 blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", 450) 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 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 t.Logf("Inserting block with propose at 449...") 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 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 t.Logf("Inserting block with propose at 450 A...") 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) 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 signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header()) if err != nil { @@ -288,7 +288,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(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() if err != nil { @@ -422,7 +422,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin } 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 signers, err := GetSnapshotSigner(blockchain, blockchain.CurrentBlock().Header()) if err != nil { diff --git a/tests/consensus/test_helper.go b/tests/consensus/test_helper.go index 4038a09ba7..9e457bd41a 100644 --- a/tests/consensus/test_helper.go +++ b/tests/consensus/test_helper.go @@ -223,13 +223,20 @@ func GetCandidateFromCurrentSmartContract(backend bind.ContractBackend, t *testi 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 var err error backend := getCommonBackend(t, chainConfig) blockchain := backend.GetBlockChain() 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() // Insert initial blocks @@ -248,7 +255,7 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params t.Fatal(err) } - return blockchain, backend, currentBlock + return blockchain, backend, currentBlock, signer } // insert Block without transcation attached diff --git a/tests/consensus/v2_test.go b/tests/consensus/v2_test.go new file mode 100644 index 0000000000..5e99d8ce9e --- /dev/null +++ b/tests/consensus/v2_test.go @@ -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()) +}