mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
add calcMasternodes, HookPenalty for v2, tests (#52)
This commit is contained in:
parent
5a3acd173d
commit
9b47146120
8 changed files with 403 additions and 27 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
102
consensus/tests/penalty_test.go
Normal file
102
consensus/tests/penalty_test.go
Normal 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))
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
138
eth/hooks/engine_v2_hooks.go
Normal file
138
eth/hooks/engine_v2_hooks.go
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue