Extend masternode candidate (#261)

* V2 truncate MaxMasternodes from candidates after penalty,
V1 same as before

TestUpdateMultipleMasterNodes: test V2, in snapshot we have all candidates, but at epoch switch, we pick MaxMasternodes

* code looks better
This commit is contained in:
wgr523 2023-05-16 21:43:56 +08:00 committed by GitHub
parent 9191730c63
commit cd74061ac2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 299 additions and 112 deletions

View file

@ -1248,7 +1248,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
}
var engine consensus.Engine
if config.XDPoS != nil {
engine = XDPoS.New(config.XDPoS, chainDb)
engine = XDPoS.New(config, chainDb)
} else {
engine = ethash.NewFaker()
if !ctx.GlobalBool(FakePoWFlag.Name) {

View file

@ -78,8 +78,9 @@ func (x *XDPoS) SubscribeForensicsEvent(ch chan<- types.ForensicsEvent) event.Su
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
// signers set to the ones provided by the user.
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS {
log.Info("[New] initialise consensus engines")
config := chainConfig.XDPoS
// Set any missing consensus parameters to their defaults
if config.Epoch == 0 {
config.Epoch = utils.EpochLength
@ -108,8 +109,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS {
WaitPeriodCh: waitPeriodCh,
signingTxsCache: signingTxsCache,
EngineV1: engine_v1.New(config, db),
EngineV2: engine_v2.New(config, db, waitPeriodCh),
EngineV1: engine_v1.New(chainConfig, db),
EngineV2: engine_v2.New(chainConfig, db, waitPeriodCh),
}
}
@ -138,8 +139,8 @@ func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS {
GetLendingService: func() utils.LendingService { return nil },
signingTxsCache: signingTxsCache,
EngineV1: engine_v1.NewFaker(db, conf),
EngineV2: engine_v2.New(conf, db, waitPeriodCh),
EngineV1: engine_v1.NewFaker(db, chainConfig),
EngineV2: engine_v2.New(chainConfig, db, waitPeriodCh),
}
return fakeEngine
}

View file

@ -10,7 +10,7 @@ import (
func TestAdaptorShouldShareDbWithV1Engine(t *testing.T) {
database := rawdb.NewMemoryDatabase()
config := params.TestXDPoSMockChainConfig.XDPoS
config := params.TestXDPoSMockChainConfig
engine := New(config, database)
assert := assert.New(t)

View file

@ -38,6 +38,8 @@ const (
// XDPoS is the delegated-proof-of-stake consensus engine proposed to support the
// Ethereum testnet following the Ropsten attacks.
type XDPoS_v1 struct {
chainConfig *params.ChainConfig // Chain & network configuration
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
@ -79,7 +81,8 @@ func (x *XDPoS_v1) SigHash(header *types.Header) (hash common.Hash) {
// New creates a XDPoS delegated-proof-of-stake consensus engine with the initial
// signers set to the ones provided by the user.
func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
func New(chainConfig *params.ChainConfig, db ethdb.Database) *XDPoS_v1 {
config := chainConfig.XDPoS
// Set any missing consensus parameters to their defaults
conf := *config
if conf.Epoch == 0 {
@ -91,6 +94,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 {
validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
return &XDPoS_v1{
chainConfig: chainConfig,
config: &conf,
db: db,
@ -778,7 +783,20 @@ func (x *XDPoS_v1) Prepare(chain consensus.ChainReader, header *types.Header) er
return nil
}
// Update masternodes into snapshot. In V1, truncating ms[:MaxMasternodes] is done in this function.
func (x *XDPoS_v1) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error {
var maxMasternodes int
// check if block number is increase ms checkpoint
if x.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (x.config.V2.SwitchBlock != nil && header.Number.Cmp(x.config.V2.SwitchBlock) == 1) {
// using new masterndoes
maxMasternodes = common.MaxMasternodesV2
} else {
// using old masterndoes
maxMasternodes = common.MaxMasternodes
}
if len(ms) > maxMasternodes {
ms = ms[:maxMasternodes]
}
number := header.Number.Uint64()
log.Trace("take snapshot", "number", number, "hash", header.Hash())
// get snapshot
@ -1007,10 +1025,10 @@ func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpoin
return signers, nil
}
func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v1 {
func NewFaker(db ethdb.Database, chainConfig *params.ChainConfig) *XDPoS_v1 {
var fakeEngine *XDPoS_v1
// Set any missing consensus parameters to their defaults
conf := config
conf := chainConfig.XDPoS
// Allocate the snapshot caches and create the engine
recents, _ := lru.NewARC(utils.InmemorySnapshots)
@ -1018,6 +1036,8 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v1 {
validatorSignatures, _ := lru.NewARC(utils.InmemorySnapshots)
verifiedHeaders, _ := lru.NewARC(utils.InmemorySnapshots)
fakeEngine = &XDPoS_v1{
chainConfig: chainConfig,
config: conf,
db: db,
recents: recents,

View file

@ -24,6 +24,8 @@ import (
)
type XDPoS_v2 struct {
chainConfig *params.ChainConfig // Chain & network configuration
config *params.XDPoSConfig // Consensus engine configuration parameters
db ethdb.Database // Database to store and retrieve snapshot checkpoints
isInitilised bool // status of v2 variables
@ -64,7 +66,8 @@ type XDPoS_v2 struct {
votePoolCollectionTime time.Time
}
func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 {
func New(chainConfig *params.ChainConfig, db ethdb.Database, waitPeriodCh chan int) *XDPoS_v2 {
config := chainConfig.XDPoS
// Setup timeoutTimer
duration := time.Duration(config.V2.CurrentConfig.TimeoutPeriod) * time.Second
timeoutTimer := countdown.NewCountDown(duration)
@ -77,6 +80,8 @@ func New(config *params.XDPoSConfig, db ethdb.Database, waitPeriodCh chan int) *
timeoutPool := utils.NewPool()
votePool := utils.NewPool()
engine := &XDPoS_v2{
chainConfig: chainConfig,
config: config,
db: db,
isInitilised: false,
@ -988,7 +993,11 @@ func (x *XDPoS_v2) GetPenalties(chain consensus.ChainReader, header *types.Heade
return epochSwitchInfo.Penalties
}
// Calculate masternodes for a block number and parent hash. In V2, truncating candidates[:MaxMasternodes] is done in this function.
func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) {
// using new max masterndoes
maxMasternodes := common.MaxMasternodesV2
snap, err := x.getSnapshot(chain, blockNum.Uint64(), false)
if err != nil {
log.Error("[calcMasternodes] Adaptor v2 getSnapshot has error", "err", err)
@ -998,11 +1007,17 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In
if blockNum.Uint64() == x.config.V2.SwitchBlock.Uint64()+1 {
log.Info("[calcMasternodes] examing first v2 block")
if len(candidates) > maxMasternodes {
candidates = candidates[:maxMasternodes]
}
return candidates, []common.Address{}, nil
}
if x.HookPenalty == nil {
log.Info("[calcMasternodes] no hook penalty defined")
if len(candidates) > maxMasternodes {
candidates = candidates[:maxMasternodes]
}
return candidates, []common.Address{}, nil
}
@ -1012,6 +1027,18 @@ func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.In
return nil, nil, err
}
masternodes := common.RemoveItemFromArray(candidates, penalties)
if len(masternodes) > maxMasternodes {
masternodes = masternodes[:maxMasternodes]
}
if len(masternodes) < x.config.V2.CurrentConfig.CertThreshold {
log.Warn("[calcMasternodes] Current epoch masternodes less than threshold", "number", blockNum, "masternodes", len(masternodes), "threshold", x.config.V2.CurrentConfig.CertThreshold)
for i, a := range masternodes {
log.Warn("final masternode", "i", i, "addr", a)
}
for i, a := range penalties {
log.Warn("penalty", "i", i, "addr", a)
}
}
return masternodes, penalties, nil
}

View file

@ -154,6 +154,7 @@ func TestUpdateSignerListIfVotedBeforeGap(t *testing.T) {
if err != nil {
t.Fatalf("Failed while trying to get signers")
}
assert.Equal(t, common.MaxMasternodes, len(signers))
// Now, we voted acc 1 to be in the signerList, which will kick out acc3 because it has less funds
if signers[acc3Addr.Hex()] == true {
debugMessage(backend, signers, t)
@ -195,6 +196,7 @@ func TestCallUpdateM1WithSmartContractTranscation(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc1Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should sit in the signer list")
@ -244,6 +246,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc1Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should sit in the signer list")
@ -279,6 +282,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
// Should not run the `updateM1` for forked chain, hence account3 still exit
if signers[acc3Addr.Hex()] != true {
debugMessage(backend, signers, t)
@ -312,6 +316,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 2 should sit in the signer list")
@ -325,6 +330,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 2 should sit in the signer list")
@ -338,6 +344,7 @@ func TestCallUpdateM1WhenForkedBlockBackToMainChain(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("acc2Addr should sit in the signer list")
@ -404,6 +411,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc1Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should sit in the signer list")
@ -445,6 +453,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
// Should not run the `updateM1` for forked chain, hence account3 still exit
if signers[acc3Addr.Hex()] != true {
debugMessage(backend, signers, t)
@ -473,6 +482,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 2 should sit in the signer list")
@ -482,6 +492,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 2 should sit in the signer list")
@ -491,6 +502,7 @@ func TestStatesShouldBeUpdatedWhenForkedBlockBecameMainChainAtGapBlock(t *testin
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc2Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("acc2Addr should sit in the signer list")
@ -564,6 +576,7 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
if signers[acc1Addr.Hex()] == true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should NOT sit in the signer list")
@ -623,6 +636,7 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
if err != nil {
t.Fatal(err)
}
assert.Equal(t, common.MaxMasternodes, len(signers))
// Should run the `updateM1` for forked chain, but it should not be affected by the voted block 451A which is not on the mainchain anymore
if signers[acc3Addr.Hex()] != true {
@ -630,63 +644,3 @@ func TestVoteShouldNotBeAffectedByFork(t *testing.T) {
t.Fatalf("account 3 should sit in the signer list as previos block result")
}
}
/*
V2 Consensus
*/
/*
// Pending for creating cross version blocks
func TestV2UpdateSignerListIfVotedBeforeGap(t *testing.T) {
config := params.TestXDPoSMockChainConfig
blockchain, backend, parentBlock, signer, signFn := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)+GAP-2, config)
// Insert first Block 1349
t.Logf("Inserting block with propose at 1349...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000001349"
tx, err := voteTX(37117, 0, acc1Addr.String())
if err != nil {
t.Fatal(err)
}
//Get from block validator error message
merkleRoot := "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
header := &types.Header{
Root: common.HexToHash(merkleRoot),
Number: big.NewInt(int64(1349)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(blockCoinbaseA),
}
block1349, err := insertBlockTxs(blockchain, header, []*types.Transaction{tx}, signer, signFn, blockchain.Config())
if err != nil {
t.Fatal(err)
}
parentBlock = block1349
// Now, let's mine another block to trigger the GAP block signerList update
block1350CoinbaseAddress := "0xaaa0000000000000000000000000000000001350"
merkleRoot = "46234e9cd7e85a267f7f0435b15256a794a2f6d65cc98cdbd21dcd10a01d9772"
header = &types.Header{
Root: common.HexToHash(merkleRoot),
Number: big.NewInt(int64(1350)),
ParentHash: parentBlock.Hash(),
Coinbase: common.HexToAddress(block1350CoinbaseAddress),
}
block1350, err := insertBlock(blockchain, header)
if err != nil {
t.Fatal(err)
}
signers, err := GetSnapshotSigner(blockchain, block1350.Header())
if err != nil {
t.Fatalf("Failed while trying to get signers")
}
// Now, we voted acc 1 to be in the signerList, which will kick out acc3 because it has less funds
if signers[acc3Addr.Hex()] == true {
debugMessage(backend, signers, t)
t.Fatalf("account 3 should NOT sit in the signer list")
}
if signers[acc1Addr.Hex()] != true {
debugMessage(backend, signers, t)
t.Fatalf("account 1 should sit in the signer list")
}
}
*/

View file

@ -180,7 +180,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, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil, "")
// block 901 is the first v2 block, and is treated as epoch switch block
err := blockchain.InsertBlock(currentBlock)
@ -190,7 +190,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, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header())
@ -213,7 +213,7 @@ func TestGetCurrentEpochSwitchBlock(t *testing.T) {
// V2
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number())
@ -222,7 +222,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, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
@ -248,13 +248,13 @@ func TestGetParentBlock(t *testing.T) {
// V2
blockNum := 901
blockCoinBase := "0x111000000000000000000000000000000123"
block901 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block900, blockNum, 1, blockCoinBase, signer, signFn, nil, nil)
block901 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block900, blockNum, 1, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block901)
assert.Nil(t, err)
// let's inject another one, but the highestedQC has not been updated, so it shall still point to 900
blockNum = 902
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block901, blockNum, 1, blockCoinBase, signer, signFn, nil, nil)
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block901, blockNum, 1, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block902)
assert.Nil(t, err)
block = adaptor.FindParentBlockToAssign(blockchain, block902)

View file

@ -17,7 +17,7 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 902
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil, "")
err := blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
// As long as the address is in the master node list, they are all valid
@ -38,7 +38,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, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 1, blockCoinBase, signer, signFn, nil, nil, "")
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock)
@ -64,7 +64,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, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
time.Sleep(time.Duration(minePeriod) * time.Second)
@ -89,7 +89,7 @@ func TestIsYourTurnConsensusV2CrossConfig(t *testing.T) {
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
blockNum := 910 // 910 is new config switch block
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 10, blockCoinBase, signer, signFn, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 10, blockCoinBase, signer, signFn, nil, nil, "")
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock)

View file

@ -30,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/XinFinOrg/XDPoSChain/rlp"
"github.com/stretchr/testify/assert"
)
type masterNodes map[string]big.Int
@ -199,6 +200,90 @@ func getCommonBackend(t *testing.T, chainConfig *params.ChainConfig) *backends.S
return contractBackend2
}
func getMultiCandidatesBackend(t *testing.T, chainConfig *params.ChainConfig, n int) *backends.SimulatedBackend {
assert.GreaterOrEqual(t, n, 4)
// initial helper backend, give a very large gas limit
contractBackendForSC := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
}, 1000000000, chainConfig)
transactOpts := bind.NewKeyedTransactor(voterKey)
var candidates []common.Address
var caps []*big.Int
defalutCap := new(big.Int)
defalutCap.SetString("1000000000", 10)
for i := 1; i <= n-4; i++ {
addr := fmt.Sprintf("%04d", i)
candidates = append(candidates, common.StringToAddress(addr))
caps = append(caps, defalutCap)
}
acc1Cap, acc2Cap, acc3Cap, voterCap := new(big.Int), new(big.Int), new(big.Int), new(big.Int)
acc1Cap.SetString("10000001", 10)
acc2Cap.SetString("10000002", 10)
acc3Cap.SetString("10000003", 10)
voterCap.SetString("2000000000", 10) // give voter the highest cap to make it win the masternode selection
caps = append(caps, voterCap, acc1Cap, acc2Cap, acc3Cap)
candidates = append(candidates, voterAddr, acc1Addr, acc2Addr, acc3Addr)
// create validator smart contract
validatorSCAddr, _, _, err := contractValidator.DeployXDCValidator(
transactOpts,
contractBackendForSC,
candidates,
caps,
voterAddr, // first owner, not used
big.NewInt(50000),
big.NewInt(1),
big.NewInt(99),
big.NewInt(100),
big.NewInt(100),
)
if err != nil {
t.Fatalf("can't deploy root registry: %v", err)
}
contractBackendForSC.Commit() // Write into database(state)
// Prepare Code and Storage
d := time.Now().Add(3000 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer cancel()
code, _ := contractBackendForSC.CodeAt(ctx, validatorSCAddr, nil)
storage := make(map[common.Hash]common.Hash)
f := func(key, val common.Hash) bool {
decode := []byte{}
trim := bytes.TrimLeft(val.Bytes(), "\x00")
err := rlp.DecodeBytes(trim, &decode)
if err != nil {
t.Fatalf("Failed while decode byte")
}
storage[key] = common.BytesToHash(decode)
log.Info("DecodeBytes", "value", val.String(), "decode", storage[key].String())
return true
}
err = contractBackendForSC.ForEachStorageAt(ctx, validatorSCAddr, nil, f)
if err != nil {
t.Fatalf("Failed while trying to read all keys from SC")
}
// create test backend with smart contract in it
contractBackend2 := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
acc1Addr: {Balance: new(big.Int).SetUint64(10000000000)},
acc2Addr: {Balance: new(big.Int).SetUint64(10000000000)},
acc3Addr: {Balance: new(big.Int).SetUint64(10000000000)},
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
common.HexToAddress(common.MasternodeVotingSMC): {Balance: new(big.Int).SetUint64(1), Code: code, Storage: storage}, // Binding the MasternodeVotingSMC with newly created 'code' for SC execution
}, 10000000, chainConfig)
return contractBackend2
}
func signingTxWithKey(header *types.Header, nonce uint64, privateKey *ecdsa.PrivateKey) (*types.Transaction, error) {
tx := contracts.CreateTxSign(header.Number, header.Hash(), nonce, common.HexToAddress(common.BlockSigners))
s := types.NewEIP155Signer(big.NewInt(chainID))
@ -316,7 +401,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
blockCoinBase = signer.Hex()
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil)
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block)
if err != nil {
@ -338,7 +423,7 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
forkedBlockRoundNumber = roundNumber + int64(*forkedBlockOptions.numOfForkedBlocks)
}
forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil, forkedBlockOptions.signersKey)
forkedBlock := CreateBlock(blockchain, chainConfig, currentForkBlock, i, forkedBlockRoundNumber, forkedBlockCoinBase, signer, signFn, nil, forkedBlockOptions.signersKey, "")
err = blockchain.InsertBlock(forkedBlock)
if err != nil {
@ -403,7 +488,7 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in
}
roundNumber := int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()
// use signer itself as penalty
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil)
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, signer[:], nil, "")
err = blockchain.InsertBlock(block)
if err != nil {
@ -421,9 +506,62 @@ func PrepareXDCTestBlockChainWithPenaltyForV2Engine(t *testing.T, numOfBlocks in
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, signersKey []*ecdsa.PrivateKey) *types.Block {
// V2 concensus engine, compared to PrepareXDCTestBlockChainForV2Engine: (1) no forking (2) 128 masternode candidates
func PrepareXDCTestBlockChainWith128Candidates(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 := getMultiCandidatesBackend(t, chainConfig, 128)
blockchain := backend.GetBlockChain()
blockchain.Client = backend
engine := blockchain.Engine().(*XDPoS.XDPoS)
// Authorise
engine.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()
block := CreateBlock(blockchain, chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn, nil, nil, "b345a8560bd51926803dd17677c9f0751193914a851a4ec13063d6bf50220b53")
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, signersKey []*ecdsa.PrivateKey, merkleRoot string) *types.Block {
currentBlock := startingBlock
merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
if len(merkleRoot) == 0 {
merkleRoot = "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930"
}
var header *types.Header
if big.NewInt(int64(blockNumber)).Cmp(chainConfig.XDPoS.V2.SwitchBlock) == 1 { // Build engine v2 compatible extra data field

View file

@ -59,7 +59,7 @@ func TestInitialOtherV2Block(t *testing.T) {
blockCoinBase := "0x111000000000000000000000000000000123"
for blockNum := 901; blockNum <= 910; blockNum++ {
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, int64(blockNum-900), blockCoinBase, signer, signFn, nil, nil, "")
err := blockchain.InsertBlock(currentBlock)
assert.Nil(t, err)
}

View file

@ -222,3 +222,63 @@ func TestPrepareHappyPath(t *testing.T) {
assert.Equal(t, types.Round(4), decodedExtraField.Round)
assert.Equal(t, types.Round(0), decodedExtraField.QuorumCert.ProposedBlockInfo.Round)
}
// test if we have 128 candidates, then snapshot will store all of them, and when preparing (and verifying) candidates is truncated to MaxMasternodes
func TestUpdateMultipleMasterNodes(t *testing.T) {
config := params.TestXDPoSMockChainConfig
blockchain, _, currentBlock, signer, signFn := PrepareXDCTestBlockChainWith128Candidates(t, int(config.XDPoS.Epoch+config.XDPoS.Gap)-1, config)
adaptor := blockchain.Engine().(*XDPoS.XDPoS)
x := adaptor.EngineV2
// Insert block 1350
t.Logf("Inserting block with propose at 1350...")
blockCoinbaseA := "0xaaa0000000000000000000000000000000001350"
//Get from block validator error message
merkleRoot := "b345a8560bd51926803dd17677c9f0751193914a851a4ec13063d6bf50220b53"
parentBlock := CreateBlock(blockchain, config, currentBlock, 1350, 450, blockCoinbaseA, signer, signFn, nil, nil, merkleRoot)
err := blockchain.InsertBlock(parentBlock)
assert.Nil(t, err)
// 1350 is a gap block, need to update the snapshot
err = blockchain.UpdateM1()
assert.Nil(t, err)
// but we wait until 1800 to test the snapshot
t.Logf("Inserting block from 1351 to 1800...")
for i := 1351; i <= 1800; i++ {
blockCoinbase := fmt.Sprintf("0xaaa000000000000000000000000000000000%4d", i)
block := CreateBlock(blockchain, config, parentBlock, i, int64(i-900), blockCoinbase, signer, signFn, nil, nil, merkleRoot)
err = blockchain.InsertBlock(block)
assert.Nil(t, err)
if i < 1800 {
parentBlock = block
}
if i == 1800 {
snap, err := x.GetSnapshot(blockchain, block.Header())
assert.Nil(t, err)
assert.Equal(t, 1350, int(snap.Number))
assert.Equal(t, 128, len(snap.NextEpochMasterNodes)) // 128 is all masternode candidates, not limited by MaxMasternodes
}
}
tstamp := time.Now().Unix()
header1800 := &types.Header{
ParentHash: parentBlock.Hash(),
Number: big.NewInt(int64(1800)),
GasLimit: params.TargetGasLimit,
Time: big.NewInt(tstamp),
Coinbase: voterAddr,
}
adaptor.EngineV2.SetNewRoundFaker(blockchain, types.Round(900), false)
blockInfo := &types.BlockInfo{Hash: parentBlock.Hash(), Round: types.Round(900 - 1), Number: parentBlock.Number()}
signature := []byte{1, 2, 3, 4, 5, 6, 7, 8}
signatures := []types.Signature{signature}
quorumCert := &types.QuorumCert{ProposedBlockInfo: blockInfo, Signatures: signatures, GapNumber: 1350}
adaptor.EngineV2.ProcessQCFaker(blockchain, quorumCert)
adaptor.EngineV2.AuthorizeFaker(voterAddr)
err = adaptor.Prepare(blockchain, header1800)
assert.Nil(t, err)
assert.Equal(t, common.MaxMasternodesV2, len(header1800.Validators)/common.AddressLength) // although 128 masternode candidates, we can only pick MaxMasternodes
assert.Equal(t, 0, len(header1800.Penalties)/common.AddressLength)
}

View file

@ -45,7 +45,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, nil, nil)
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block902)
assert.Nil(t, err)
err = engineV2.ProposedBlockHandler(blockchain, block902.Header())
@ -63,7 +63,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, nil, nil)
block903 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block902, blockNum, 3, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block903)
assert.Nil(t, err)
err = engineV2.ProposedBlockHandler(blockchain, block903.Header())
@ -82,7 +82,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, nil, nil)
block904 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block903, blockNum, 4, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block904)
assert.Nil(t, err)
err = engineV2.ProposedBlockHandler(blockchain, block904.Header())
@ -134,7 +134,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, nil, nil)
block906 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 7, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block906)
assert.Nil(t, err)
err = engineV2.ProposedBlockHandler(blockchain, block906.Header())
@ -156,7 +156,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, nil, nil)
block907 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, block906, blockNum, 8, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block907)
assert.Nil(t, err)
err = engineV2.ProposedBlockHandler(blockchain, block907.Header())

View file

@ -109,7 +109,7 @@ func TestTimeoutPeriodAndThreadholdConfigChange(t *testing.T) {
// Create another block to trigger update parameters
blockNum := 1800
blockCoinBase := "0x111000000000000000000000000000000123"
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 900, blockCoinBase, signer, signFn, nil, nil)
currentBlock = CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 900, blockCoinBase, signer, signFn, nil, nil, "")
currentBlockHeader := currentBlock.Header()
currentBlockHeader.Time = big.NewInt(time.Now().Unix())
err := blockchain.InsertBlock(currentBlock)

View file

@ -26,7 +26,7 @@ func TestShouldVerifyBlockInfo(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, nil, nil)
block902 := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 2, blockCoinBase, signer, signFn, nil, nil, "")
err = blockchain.InsertBlock(block902)
assert.Nil(t, err)

View file

@ -421,12 +421,12 @@ func TestShouldVerifyHeadersEvenIfParentsNotYetWrittenIntoDB(t *testing.T) {
// Create block 911 but don't write into DB
blockNumber := 911
roundNumber := int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64()
block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil)
block911 := CreateBlock(blockchain, &config, block910, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil, "")
// Create block 912 and not write into DB as well
blockNumber = 912
roundNumber = int64(blockNumber) - config.XDPoS.V2.SwitchBlock.Int64()
block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil)
block912 := CreateBlock(blockchain, &config, block911, blockNumber, roundNumber, signer.Hex(), signer, signFn, nil, nil, "")
headersTobeVerified = append(headersTobeVerified, block910.Header(), block911.Header(), block912.Header())
// Randomly set full verify

View file

@ -339,7 +339,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, nil, nil)
block := CreateBlock(blockchain, params.TestXDPoSMockChainConfig, currentBlock, blockNum, 6, blockCoinBase, signer, signFn, nil, nil, "")
blockInfo := &types.BlockInfo{
Hash: block.Header().Hash(),

View file

@ -2550,20 +2550,7 @@ func (bc *BlockChain) UpdateM1() error {
log.Info("Updating new set of masternodes")
// get block header
header := bc.CurrentHeader()
var maxMasternodes int
// check if block number is increase ms checkpoint
if bc.chainConfig.IsTIPIncreaseMasternodes(header.Number) || (bc.chainConfig.XDPoS.V2.SwitchBlock != nil && header.Number.Cmp(bc.chainConfig.XDPoS.V2.SwitchBlock) == 1) {
// using new masterndoes
maxMasternodes = common.MaxMasternodesV2
} else {
// using old masterndoes
maxMasternodes = common.MaxMasternodes
}
if len(ms) > maxMasternodes {
err = engine.UpdateMasternodes(bc, bc.CurrentHeader(), ms[:maxMasternodes])
} else {
err = engine.UpdateMasternodes(bc, bc.CurrentHeader(), ms)
}
err = engine.UpdateMasternodes(bc, header, ms)
if err != nil {
return err
}

View file

@ -338,7 +338,7 @@ func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Data
func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chainConfig *params.ChainConfig, db ethdb.Database) consensus.Engine {
// If delegated-proof-of-stake is requested, set it up
if chainConfig.XDPoS != nil {
return XDPoS.New(chainConfig.XDPoS, db)
return XDPoS.New(chainConfig, db)
}
// Otherwise assume proof-of-work