add calcMasternodes, HookPenalty for v2, tests (#52)

This commit is contained in:
wgr523 2022-02-13 22:15:23 +08:00 committed by GitHub
parent 5a3acd173d
commit 9b47146120
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 403 additions and 27 deletions

View file

@ -53,7 +53,8 @@ type XDPoS_v2 struct {
highestTimeoutCert *utils.TimeoutCert
highestCommitBlock *utils.BlockInfo
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
HookReward func(chain consensus.ChainReader, state *state.StateDB, parentState *state.StateDB, header *types.Header) (error, map[string]interface{})
HookPenalty func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error)
}
func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 {
@ -204,15 +205,16 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
return err
}
if isEpochSwitchBlock {
snap, err := x.getSnapshot(chain, number)
masterNodes, penalties, err := x.calcMasternodes(chain, header.Number, header.ParentHash)
if err != nil {
return err
}
masternodes := snap.NextEpochMasterNodes
//TODO: remove penalty nodes and add comeback nodes, or change this logic into yourturn function
for _, v := range masternodes {
for _, v := range masterNodes {
header.Validators = append(header.Validators, v[:]...)
}
for _, v := range penalties {
header.Penalties = append(header.Penalties, v[:]...)
}
}
// Mix digest is reserved for now, set to empty
@ -366,16 +368,14 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s
log.Error("[YourTurn] Cannot find snapshot at gap num of last V1", "err", err, "number", x.config.V2.SwitchBlock.Uint64())
return false, err
}
// the initial snapshot of v1->v2 switch containes penalites node
// the initial master nodes of v1->v2 switch contains penalties node
masterNodes = snap.NextEpochMasterNodes
} else {
snap, err := x.getSnapshot(chain, parent.Number.Uint64()+1)
masterNodes, _, err = x.calcMasternodes(chain, big.NewInt(0).Add(parent.Number, big.NewInt(1)), parent.Hash())
if err != nil {
log.Error("[YourTurn] Cannot find snapshot at gap block", "err", err, "number", x.config.V2.SwitchBlock.Uint64())
log.Error("[YourTurn] Cannot calcMasternodes at gap num ", "err", err, "parent number", parent.Number)
return false, err
}
masterNodes = snap.NextEpochMasterNodes
// TODO: calculate master nodes with penalty and comback
}
} else {
// this block and parent belong to the same epoch
@ -1189,6 +1189,13 @@ func (x *XDPoS_v2) SetNewRoundFaker(newRound utils.Round, resetTimer bool) {
x.currentRound = newRound
}
// for test only
func (x *XDPoS_v2) ProcessQC(chain consensus.ChainReader, qc *utils.QuorumCert) error {
x.lock.Lock()
defer x.lock.Unlock()
return x.processQC(chain, qc)
}
// Utils for test to check currentRound value
func (x *XDPoS_v2) GetCurrentRound() utils.Round {
x.lock.RLock()
@ -1348,3 +1355,50 @@ func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, block
epochNum := x.config.V2.SwitchBlock.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch
return currentCheckpointNumber, epochNum, nil
}
func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) {
snap, err := x.getSnapshot(chain, blockNum.Uint64())
if err != nil {
log.Error("[calcMasternodes] Adaptor v2 getSnapshot has error", "err", err)
return nil, nil, err
}
candidates := snap.NextEpochMasterNodes
if x.HookPenalty != nil {
penalties, err := x.HookPenalty(chain, blockNum, parentHash, candidates)
if err != nil {
log.Error("[calcMasternodes] Adaptor v2 HookPenalty has error", "err", err)
return nil, nil, err
}
masternodes := common.RemoveItemFromArray(candidates, penalties)
return masternodes, penalties, nil
}
return candidates, []common.Address{}, nil
}
// Given hash, get master node from the epoch switch block of the epoch
func (x *XDPoS_v2) GetMasternodesByHash(chain consensus.ChainReader, hash common.Hash) []common.Address {
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
if err != nil {
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
return []common.Address{}
}
return epochSwitchInfo.Masternodes
}
// Given hash, get master node from the epoch switch block of the previous `limit` epoch
func (x *XDPoS_v2) GetPreviousPenaltyByHash(chain consensus.ChainReader, hash common.Hash, limit int) []common.Address {
epochSwitchInfo, err := x.getEpochSwitchInfo(chain, nil, hash)
if err != nil {
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
return []common.Address{}
}
for i := 0; i < limit; i++ {
epochSwitchInfo, err = x.getEpochSwitchInfo(chain, nil, epochSwitchInfo.EpochSwitchParentBlockInfo.Hash)
if err != nil {
log.Error("[GetMasternodes] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err)
return []common.Address{}
}
}
header := chain.GetHeaderByHash(epochSwitchInfo.EpochSwitchBlockInfo.Hash)
return common.ExtractAddressFromBytes(header.Penalties)
}

View file

@ -178,7 +178,7 @@ func TestAdaptorGetMasternodesV2(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
// block 901 is the first v2 block, and is treated as epoch switch block
blockchain.InsertBlock(currentBlock)
@ -187,7 +187,7 @@ func TestAdaptorGetMasternodesV2(t *testing.T) {
masternodes1ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64())
assert.True(t, reflect.DeepEqual(masternodes1, masternodes1ByNumber), "at block number", blockNum)
for blockNum = 902; blockNum < 915; blockNum++ {
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
assert.True(t, reflect.DeepEqual(masternodes1, masternodes2), "at block number", blockNum)
@ -209,7 +209,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) {
// V2
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
assert.Nil(t, err)
@ -217,7 +217,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) {
assert.Equal(t, uint64(1), epochNum)
for blockNum = 902; blockNum < 915; blockNum++ {
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())

View file

@ -78,7 +78,7 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
// As long as the address is in the master node list, they are all valid
@ -99,7 +99,7 @@ func TestIsYourTurnConsensusV2(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
// Less then Mine Period
@ -123,7 +123,7 @@ func TestIsYourTurnConsensusV2(t *testing.T) {
// We continue to grow the chain which will increase the round number
blockNum = 902
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(currentBlock)
time.Sleep(time.Duration(minePeriod) * time.Second)

View file

@ -0,0 +1,102 @@
package tests
import (
"math/big"
"reflect"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/eth/hooks"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)
func TestHookPenaltyV2Mining(t *testing.T) {
config := params.TestXDPoSMockChainConfig
blockchain, _, _, _, _, _ := PrepareXDCTestBlockChainForV2Engine(t, int(config.XDPoS.Epoch)*7, config, 0)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
hooks.AttachConsensusV2Hooks(adaptor, blockchain, config)
assert.NotNil(t, adaptor.EngineV2.HookPenalty)
var extraField utils.ExtraFields_v2
// 901 is the first v2 block
header901 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 1)
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
assert.Nil(t, err)
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
assert.Equal(t, 4, len(masternodes))
header6300 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 7)
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
assert.Nil(t, err)
// miner (coinbase) is not in penalty. all others are in penalty
assert.Equal(t, 3, len(penalty))
assert.True(t, reflect.DeepEqual([]common.Address{header901.Coinbase}, common.RemoveItemFromArray(masternodes, penalty)))
// set adaptor round/qc to that of 6299
err = utils.DecodeBytesExtraFields(header6300.Extra, &extraField)
assert.Nil(t, err)
err = adaptor.EngineV2.ProcessQC(blockchain, extraField.QuorumCert)
assert.Nil(t, err)
headerMining := &types.Header{
ParentHash: header6300.ParentHash,
Number: header6300.Number,
GasLimit: params.TargetGasLimit,
Time: header6300.Time,
}
err = adaptor.Prepare(blockchain, headerMining)
assert.Nil(t, err)
assert.Equal(t, 3, len(headerMining.Penalties)/common.AddressLength)
// 20 candidates (set by PrepareXDCTestBlockChainForV2Engine) - 3 penalty = 17
assert.Equal(t, 17, len(headerMining.Validators)/common.AddressLength)
}
func TestHookPenaltyV2Comeback(t *testing.T) {
config := params.TestXDPoSMockChainConfig
blockchain, _, _, signer, signFn := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, int(config.XDPoS.Epoch)*7, config)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
hooks.AttachConsensusV2Hooks(adaptor, blockchain, config)
assert.NotNil(t, adaptor.EngineV2.HookPenalty)
var extraField utils.ExtraFields_v2
// 901 is the first v2 block
header901 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 1)
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
assert.Nil(t, err)
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
assert.Equal(t, 4, len(masternodes))
header6300 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch * 7)
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
assert.Nil(t, err)
// miner (coinbase) is in comeback. so all addresses are in penalty
assert.Equal(t, 4, len(penalty))
header6285 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch*7 - common.MergeSignRange)
// forcely insert signing tx into cache, to cancel comeback. since no comeback, penalty is 3
tx, err := signingTx(header6285, 0, signer, signFn)
assert.Nil(t, err)
adaptor.CacheSigningTxs(header6285.Hash(), []*types.Transaction{tx})
penalty, err = adaptor.EngineV2.HookPenalty(blockchain, big.NewInt(int64(config.XDPoS.Epoch*7)), header6300.ParentHash, masternodes)
assert.Nil(t, err)
assert.Equal(t, 3, len(penalty))
}
func TestHookPenaltyV2Jump(t *testing.T) {
config := params.TestXDPoSMockChainConfig
end := int(config.XDPoS.Epoch)*7 - common.MergeSignRange
blockchain, _, _, _, _ := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, int(config.XDPoS.Epoch)*7, config)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
hooks.AttachConsensusV2Hooks(adaptor, blockchain, config)
assert.NotNil(t, adaptor.EngineV2.HookPenalty)
var extraField utils.ExtraFields_v2
// 901 is the first v2 block
header901 := blockchain.GetHeaderByNumber(config.XDPoS.Epoch + 1)
err := utils.DecodeBytesExtraFields(header901.Extra, &extraField)
assert.Nil(t, err)
masternodes := adaptor.GetMasternodesFromCheckpointHeader(header901)
assert.Equal(t, 4, len(masternodes))
header6285 := blockchain.GetHeaderByNumber(uint64(end))
adaptor.EngineV2.SetNewRoundFaker(utils.Round(config.XDPoS.Epoch*7), false)
// round 6285-6300 miss blocks, penalty should work as usual
penalty, err := adaptor.EngineV2.HookPenalty(blockchain, header6285.Number, header6285.ParentHash, masternodes)
assert.Nil(t, err)
assert.Equal(t, 4, len(penalty))
}

View file

@ -43,7 +43,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Insert another Block, but it won't trigger commit
blockNum := 902
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn)
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(block902)
err = engineV2.ProposedBlockHandler(blockchain, block902.Header())
if err != nil {
@ -60,7 +60,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Insert one more Block, but still won't trigger commit
blockNum = 903
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn)
block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(block903)
err = engineV2.ProposedBlockHandler(blockchain, block903.Header())
if err != nil {
@ -78,7 +78,7 @@ func TestShouldSendVoteMsgAndCommitGrandGrandParentBlock(t *testing.T) {
// Insert one more Block, this time will trigger commit
blockNum = 904
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn)
block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(block904)
err = engineV2.ProposedBlockHandler(blockchain, block904.Header())
if err != nil {
@ -129,7 +129,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
// Injecting new block which have gaps in the round number (Round 7 instead of 6)
blockNum := 906
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn)
block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(block906)
err = engineV2.ProposedBlockHandler(blockchain, block906.Header())
if err != nil {
@ -150,7 +150,7 @@ func TestShouldNotCommitIfRoundsNotContinousFor3Rounds(t *testing.T) {
blockNum = 907
blockCoinBase = fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn)
block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(block907)
err = engineV2.ProposedBlockHandler(blockchain, block907.Header())
if err != nil {

View file

@ -21,6 +21,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/contracts"
contractValidator "github.com/XinFinOrg/XDPoSChain/contracts/validator/contract"
"github.com/XinFinOrg/XDPoSChain/core"
. "github.com/XinFinOrg/XDPoSChain/core"
@ -229,6 +230,21 @@ func voteTX(gasLimit uint64, nonce uint64, addr string) (*types.Transaction, err
return signedTX, nil
}
func signingTx(header *types.Header, nonce uint64, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) (*types.Transaction, error) {
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
s := types.NewEIP155Signer(big.NewInt(chainID))
h := s.Hash(tx)
sig, err := signFn(accounts.Account{Address: signer}, h[:])
if err != nil {
return nil, err
}
signedTx, err := tx.WithSignature(s, sig)
if err != nil {
return nil, err
}
return signedTx, nil
}
func UpdateSigner(bc *BlockChain) error {
err := bc.UpdateM1()
return err
@ -354,8 +370,12 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
// Insert initial blocks
for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
// for v2 blocks, fill in correct coinbase
if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() {
blockCoinBase = signer.Hex()
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn)
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil)
err = blockchain.InsertBlock(block)
if err != nil {
@ -370,7 +390,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
forkedBlockRoundNumber := roundNumber + int64(numOfForkedBlocks)
forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn)
forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil)
blockchain.InsertBlock(forkedBlock)
currentForkBlock = forkedBlock
@ -387,7 +407,58 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
return blockchain, backend, currentBlock, signer, signFn, currentForkBlock
}
func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error)) *types.Block {
// V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) add penalty
func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks int, chainConfig *params.ChainConfig) (*BlockChain, *backends.SimulatedBackend, *types.Block, common.Address, func(account accounts.Account, hash []byte) ([]byte, error)) {
// Preparation
var err error
signer, signFn, err := backends.SimulateWalletAddressAndSignFn()
if err != nil {
t.Fatal("Error while creating simulated wallet for generating singer address and signer fn: ", err)
}
backend := getCommonBackend(t, chainConfig)
blockchain := backend.GetBlockChain()
blockchain.Client = backend
// Authorise
blockchain.Engine().(*XDPoS.XDPoS).Authorize(signer, signFn)
currentBlock := blockchain.Genesis()
go func() {
for range core.CheckpointCh {
checkpointChanMsg := <-core.CheckpointCh
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
}
}()
// Insert initial blocks
for i := 1; i <= numOfBlocks; i++ {
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
// for v2 blocks, fill in correct coinbase
if int64(i) > chainConfig.XDPoS.V2.SwitchBlock.Int64() {
blockCoinBase = signer.Hex()
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
// use signer itself as penalty
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:])
err = blockchain.InsertBlock(block)
if err != nil {
t.Fatal(err)
}
currentBlock = block
}
// Update Signer as there is no previous signer assigned
err = UpdateSigner(blockchain)
if err != nil {
t.Fatal(err)
}
return blockchain, backend, currentBlock, signer, signFn
}
func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, startingBlock *types.Block, blockNumber int, roundNumber int64, blockCoinBase string, signer common.Address, signFn func(account accounts.Account, hash []byte) ([]byte, error), penalties []byte) *types.Block {
currentBlock := startingBlock
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
var header *types.Header
@ -442,8 +513,18 @@ func CreateBlock(blockchain *BlockChain, chainConfig *params.ChainConfig, starti
for _, v := range masternodesFromV1LastEpoch {
header.Validators = append(header.Validators, v[:]...)
}
} else if roundNumber%int64(chainConfig.XDPoS.Epoch) == 0 {
// epoch switch blocks, copy the master node list and inject into v2 validators
// Get last master node list from last v1 block
lastv1Block := blockchain.GetBlockByNumber(chainConfig.XDPoS.V2.SwitchBlock.Uint64())
masternodesFromV1LastEpoch := decodeMasternodesFromHeaderExtra(lastv1Block.Header())
for _, v := range masternodesFromV1LastEpoch {
header.Validators = append(header.Validators, v[:]...)
}
if penalties != nil {
header.Penalties = penalties
}
}
} else {
// V1 block
header = &types.Header{
@ -531,6 +612,7 @@ func createBlockFromHeader(bc *BlockChain, customHeader *types.Header, txs []*ty
Extra: customHeader.Extra,
Validator: customHeader.Validator,
Validators: customHeader.Validators,
Penalties: customHeader.Penalties,
}
var block *types.Block
if len(txs) == 0 {

View file

@ -311,7 +311,7 @@ func TestVoteMessageShallNotThrowErrorIfBlockNotYetExist(t *testing.T) {
// Create a new block but don't inject it into the chain yet
blockNum := 906
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", blockNum)
block := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 6, blockCoinBase, signer, signFn)
block := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 6, blockCoinBase, signer, signFn, nil)
blockInfo := &utils.BlockInfo{
Hash: block.Header().Hash(),

View file

@ -0,0 +1,138 @@
package hooks
import (
"math/big"
"time"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
)
func AttachConsensusV2Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConfig *params.ChainConfig) {
// Hook scans for bad masternodes and decide to penalty them
adaptor.EngineV2.HookPenalty = func(chain consensus.ChainReader, number *big.Int, parentHash common.Hash, candidates []common.Address) ([]common.Address, error) {
start := time.Now()
listBlockHash := make([]common.Hash, chain.Config().XDPoS.Epoch)
// get list block hash & stats total created block
statMiners := make(map[common.Address]int)
listBlockHash[0] = parentHash
parentNumber := number.Uint64() - 1
pHash := parentHash
for i := uint64(1); ; i++ {
parentHeader := chain.GetHeader(pHash, parentNumber)
b, _, err := adaptor.EngineV2.IsEpochSwitch(parentHeader)
if err != nil {
log.Error("[HookPenalty]", "err", err)
return []common.Address{}, err
}
if b {
break
}
miner := parentHeader.Coinbase // we can directly use coinbase, since it's verified (Verification is a TODO)
value, exist := statMiners[miner]
if exist {
value = value + 1
} else {
value = 1
}
statMiners[miner] = value
pHash = parentHeader.ParentHash
parentNumber--
listBlockHash[i] = pHash
}
// add list not miner to penalties
preMasternodes := adaptor.EngineV2.GetMasternodesByHash(chain, parentHash)
penalties := []common.Address{}
for miner, total := range statMiners {
if total < common.MinimunMinerBlockPerEpoch {
log.Debug("Find a node not enough requirement create block", "addr", miner.Hex(), "total", total)
penalties = append(penalties, miner)
}
}
for _, addr := range preMasternodes {
if _, exist := statMiners[addr]; !exist {
log.Debug("Find a node don't create block", "addr", addr.Hex())
penalties = append(penalties, addr)
}
}
// get list check penalties signing block & list master nodes wil comeback
// start to calc comeback at v2 block + limitPenaltyEpoch to avoid reading v1 blocks
comebackHeight := (common.LimitPenaltyEpoch+1)*chain.Config().XDPoS.Epoch + chain.Config().XDPoS.V2.SwitchBlock.Uint64()
penComebacks := []common.Address{}
if number.Uint64() > comebackHeight {
pens := adaptor.EngineV2.GetPreviousPenaltyByHash(chain, parentHash, common.LimitPenaltyEpoch)
for _, p := range pens {
for _, addr := range candidates {
if p == addr {
penComebacks = append(penComebacks, p)
break
}
}
}
}
// Loop for each block to check missing sign. with comeback nodes
mapBlockHash := map[common.Hash]bool{}
startRange := common.RangeReturnSigner - 1
// to prevent visiting outside index of listBlockHash
if startRange >= len(listBlockHash) {
startRange = len(listBlockHash) - 1
}
for i := startRange; i >= 0; i-- {
if len(penComebacks) > 0 {
blockNumber := number.Uint64() - uint64(i) - 1
bhash := listBlockHash[i]
if blockNumber%common.MergeSignRange == 0 {
mapBlockHash[bhash] = true
}
signData, ok := adaptor.GetCachedSigningTxs(bhash)
if !ok {
block := chain.GetBlock(bhash, blockNumber)
txs := block.Transactions()
signData = adaptor.CacheSigningTxs(bhash, txs)
}
txs := signData.([]*types.Transaction)
// Check signer signed?
for _, tx := range txs {
blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:])
from := *tx.From()
if mapBlockHash[blkHash] {
for j, addr := range penComebacks {
if from == addr {
// Remove it from dupSigners.
penComebacks = append(penComebacks[:j], penComebacks[j+1:]...)
break
}
}
}
}
} else {
break
}
}
log.Debug("Time Calculated HookPenaltyV2 ", "block", number, "pen comeback nodes", len(penComebacks), "not enough miner", len(penalties), "time", common.PrettyDuration(time.Since(start)))
for _, comeback := range penComebacks {
ok := true
for _, p := range penalties {
if p == comeback {
ok = false
break
}
}
if ok {
penalties = append(penalties, comeback)
}
}
return penalties, nil
}
}