diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index bc82dbf6a1..24883b2860 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -278,7 +278,10 @@ func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber } func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (bool, error) { - switch x.config.BlockConsensusVersion(parent.Number) { + if parent.Number.Cmp(x.config.XDPoSV2Block) == 0 { + x.initialV2(chain, parent) + } + switch x.config.BlockConsensusVersion(big.NewInt(parent.Number.Int64() + 1)) { case params.ConsensusEngineVersion2: return x.EngineV2.YourTurn(chain, parent, signer) default: // Default "v1" @@ -296,7 +299,7 @@ func (x *XDPoS) GetValidator(creator common.Address, chain consensus.ChainReader func (x *XDPoS) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error { switch x.config.BlockConsensusVersion(header.Number) { case params.ConsensusEngineVersion2: - return nil + return x.EngineV2.UpdateMasternodes(chain, header, ms) default: // Default "v1" return x.EngineV1.UpdateMasternodes(chain, header, ms) } @@ -361,7 +364,7 @@ func (x *XDPoS) GetSnapshot(chain consensus.ChainReader, header *types.Header) ( return &utils.PublicApiSnapshot{ Number: sp.Number, Hash: sp.Hash, - Signers: sp.MasterNodes, + Signers: sp.GetMappedMasterNodes(), }, err default: // Default "v1" sp, err := x.EngineV1.GetSnapshot(chain, header) @@ -439,6 +442,14 @@ func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) { } //V2 +func (x *XDPoS) initialV2(chain consensus.ChainReader, header *types.Header) error { + checkpointBlockNumber := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch + checkpointHeader := chain.GetHeaderByNumber(checkpointBlockNumber) + masternodes := x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader) + x.EngineV2.Initial(chain, header, masternodes) + return nil +} + func (x *XDPoS) VerifyVote(*utils.Vote) error { return nil } diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index b6764c5751..f307eb256b 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -29,7 +29,7 @@ type XDPoS_v2 struct { config *params.XDPoSConfig // Consensus engine configuration parameters db ethdb.Database // Database to store and retrieve snapshot checkpoints - recents *lru.ARCCache // Snapshots for recent block to speed up reorgs + snapshots *lru.ARCCache // Snapshots for gap block signatures *lru.ARCCache // Signatures of recent blocks to speed up mining epochSwitches *lru.ARCCache // infos of epoch: master nodes, epoch switch block info, parent of that info @@ -60,7 +60,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { timer := countdown.NewCountDown(duration) timeoutPool := utils.NewPool(config.V2.CertThreshold) - recents, _ := lru.NewARC(utils.InmemorySnapshots) + snapshots, _ := lru.NewARC(utils.InmemorySnapshots) signatures, _ := lru.NewARC(utils.InmemorySnapshots) epochSwitches, _ := lru.NewARC(int(utils.InmemoryEpochs)) @@ -70,7 +70,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { db: db, signatures: signatures, - recents: recents, + snapshots: snapshots, epochSwitches: epochSwitches, timeoutWorker: timer, BroadcastCh: make(chan interface{}), @@ -106,40 +106,55 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error) sigHash returns the hash which is used as input for the delegated-proof-of-stake signing. It is the hash of the entire header apart from the 65 byte signature contained at the end of the extra data. - -Note, the method requires the extra data to be at least 65 bytes, otherwise it -panics. This is done to avoid accidentally using both forms (signature present -or not), which could be abused to produce different hashes for the same header. */ func (x *XDPoS_v2) SignHash(header *types.Header) (hash common.Hash) { return sigHash(header) } +func (x *XDPoS_v2) Initial(chain consensus.ChainReader, header *types.Header, masternodes []common.Address) error { + log.Info("[Initial] initial v2 related parameters") + + if x.highestQuorumCert.ProposedBlockInfo.Round != 0 { //already initialized + log.Warn("[Initial] Already initialized") + return nil + } + + x.lock.Lock() + defer x.lock.Unlock() + // Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC + + log.Info("[Initial] highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash()) + // Generate new parent blockInfo and put it into QC + parentBlockInfo := &utils.BlockInfo{ + Hash: header.ParentHash, + Round: utils.Round(0), + Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)), + } + quorumCert := &utils.QuorumCert{ + ProposedBlockInfo: parentBlockInfo, + Signatures: nil, + } + x.currentRound = 1 + x.highestQuorumCert = quorumCert + + // Initial snapshot + lastGapNum := header.Number.Uint64() - header.Number.Uint64()%x.config.Epoch - x.config.Gap + lastGapHeader := chain.GetHeaderByNumber(lastGapNum) + + snap := newSnapshot(lastGapNum, lastGapHeader.Hash(), x.currentRound, x.highestQuorumCert, masternodes) + x.snapshots.Add(snap.Hash, snap) + storeSnapshot(snap, x.db) + return nil +} + // Prepare implements consensus.Engine, preparing all the consensus fields of the // header for running the transactions on top. func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) error { - // Verify mined block parent matches highest QC - x.lock.Lock() - // Check header if it is the first consensus v2 block, if so, assign initial values to current round and highestQC - if header.Number.Cmp(big.NewInt(0).Add(x.config.XDPoSV2Block, big.NewInt(1))) == 0 { - log.Info("[Prepare] Initilising highest QC for consensus v2 first block", "Block Num", header.Number.String(), "BlockHash", header.Hash()) - // Generate new parent blockInfo and put it into QC - parentBlockInfo := &utils.BlockInfo{ - Hash: header.ParentHash, - Round: utils.Round(0), - Number: big.NewInt(0).Sub(header.Number, big.NewInt(1)), - } - quorumCert := &utils.QuorumCert{ - ProposedBlockInfo: parentBlockInfo, - Signatures: nil, - } - x.currentRound = 1 - x.highestQuorumCert = quorumCert - } + x.lock.RLock() currentRound := x.currentRound highestQC := x.highestQuorumCert - x.lock.Unlock() + x.lock.RUnlock() if (highestQC == nil) || (header.ParentHash != highestQC.ProposedBlockInfo.Hash) { return consensus.ErrNotReadyToPropose @@ -169,11 +184,11 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er return err } if isEpochSwitchBlock { - snap, err := x.snapshot(chain, number-1, header.ParentHash, nil) + snap, err := x.getSnapshot(chain, number-1) if err != nil { return err } - masternodes := snap.GetMasterNodes() + masternodes := snap.NextEpochMasterNodes //TODO: remove penalty nodes and add comeback nodes for _, v := range masternodes { header.Validators = append(header.Validators, v[:]...) @@ -193,7 +208,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er // Ensure the timestamp has the correct delay // TODO: Proper deal with time - // TODO: if timestamp > current time, how to deal with future timestamp + // TODO: if timestamp > current time, how to deal with future timestamp header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(x.config.Period)) if header.Time.Int64() < time.Now().Unix() { header.Time = big.NewInt(time.Now().Unix()) @@ -273,23 +288,17 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- x.signLock.RUnlock() // Bail out if we're unauthorized to sign a block - snap, err := x.snapshot(chain, number-1, header.ParentHash, nil) - if err != nil { - return nil, err - } masternodes := x.GetMasternodes(chain, header) - if _, authorized := snap.MasterNodes[signer]; !authorized { - valid := false - for _, m := range masternodes { - if m == signer { - valid = true - break - } - } - if !valid { - return nil, utils.ErrUnauthorized + valid := false + for _, m := range masternodes { + if m == signer { + valid = true + break } } + if !valid { + return nil, utils.ErrUnauthorized + } select { case <-stop: @@ -398,8 +407,8 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type // Copy from v1 func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header) (*SnapshotV2, error) { number := header.Number.Uint64() - log.Trace("get snapshot", "number", number, "hash", header.Hash()) - snap, err := x.snapshot(chain, number, header.Hash(), nil) + log.Trace("get snapshot", "number", number) + snap, err := x.getSnapshot(chain, number) if err != nil { return nil, err } @@ -407,84 +416,53 @@ func (x *XDPoS_v2) GetSnapshot(chain consensus.ChainReader, header *types.Header } // snapshot retrieves the authorization snapshot at a given point in time. -func (x *XDPoS_v2) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*SnapshotV2, error) { - // Search for a SnapshotV2 in memory or on disk for checkpoints - var ( - headers []*types.Header - snap *SnapshotV2 - ) - for snap == nil { - // If an in-memory SnapshotV2 was found, use that - if s, ok := x.recents.Get(hash); ok { - snap = s.(*SnapshotV2) - break - } - // If an on-disk checkpoint snapshot can be found, use that - // checkpoint snapshot = checkpoint - gap - if (number+x.config.Gap)%x.config.Epoch == 0 { - if s, err := loadSnapshot(x.signatures, x.db, hash); err == nil { - log.Trace("Loaded snapshot form disk", "number", number, "hash", hash) - snap = s - break - } - } - // If we're at 0 block, make a snapshot - // TODO: We may need to store snapshot at the v1 -> v2 switch block - if number == 0 { - genesis := chain.GetHeaderByNumber(0) - if err := x.VerifyHeader(chain, genesis, true); err != nil { - return nil, err - } - signers := make([]common.Address, (len(genesis.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength) - for i := 0; i < len(signers); i++ { - copy(signers[i][:], genesis.Extra[utils.ExtraVanity+i*common.AddressLength:]) - } - snap = newSnapshot(x.signatures, 0, genesis.Hash(), x.currentRound, x.highestQuorumCert, signers) - if err := storeSnapshot(snap, x.db); err != nil { - return nil, err - } - log.Trace("Stored genesis voting snapshot to disk") - break - } - // No snapshot for this header, gather the header and move backward - var header *types.Header - if len(parents) > 0 { - // If we have explicit parents, pick from there (enforced) - header = parents[len(parents)-1] - if header.Hash() != hash || header.Number.Uint64() != number { - return nil, consensus.ErrUnknownAncestor - } - parents = parents[:len(parents)-1] - } else { - // No explicit parents (or no more left), reach out to the database - header = chain.GetHeader(hash, number) - if header == nil { - log.Error("[Seal] Failed due to no header found", "hash", hash, "number", number) - return nil, consensus.ErrUnknownAncestor - } - } - headers = append(headers, header) - number, hash = number-1, header.ParentHash +func (x *XDPoS_v2) getSnapshot(chain consensus.ChainReader, number uint64) (*SnapshotV2, error) { + // checkpoint snapshot = checkpoint - gap + gapBlockNum := number - number%x.config.Epoch - x.config.Gap + gapBlockHash := chain.GetHeaderByNumber(gapBlockNum).Hash() + log.Debug("get snapshot from gap block", "number", gapBlockNum, "hash", gapBlockHash.Hex()) + + // If an in-memory SnapshotV2 was found, use that + if s, ok := x.snapshots.Get(gapBlockHash); ok { + snap := s.(*SnapshotV2) + log.Trace("Loaded snapshot from memory", "number", gapBlockNum, "hash", gapBlockHash) + return snap, nil } - // Previous snapshot found, apply any pending headers on top of it - for i := 0; i < len(headers)/2; i++ { - headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] - } - snap, err := snap.apply(headers) + // If an on-disk checkpoint snapshot can be found, use that + snap, err := loadSnapshot(x.signatures, x.db, gapBlockHash) if err != nil { + log.Error("Cannot find snapshot from last gap block", "err", err, "number", gapBlockNum, "hash", gapBlockHash) return nil, err } - x.recents.Add(snap.Hash, snap) - // If we've generated a new checkpoint snapshot, save to disk - // TODO how to save correct snapshot - if uint64(snap.Round)%x.config.Epoch == x.config.Gap { - if err = storeSnapshot(snap, x.db); err != nil { - return nil, err - } - log.Trace("Stored snapshot to disk", "round number", snap.Round, "hash", snap.Hash) + log.Trace("Loaded snapshot from disk", "number", gapBlockNum, "hash", gapBlockHash) + x.snapshots.Add(snap.Hash, snap) + return snap, nil +} + +func (x *XDPoS_v2) UpdateMasternodes(chain consensus.ChainReader, header *types.Header, ms []utils.Masternode) error { + number := header.Number.Uint64() + log.Trace("take snapshot", "number", number, "hash", header.Hash()) + + masterNodes := []common.Address{} + for _, m := range ms { + masterNodes = append(masterNodes, m.Address) } - return snap, err + + x.lock.RLock() + snap := newSnapshot(number, header.Hash(), x.currentRound, x.highestQuorumCert, masterNodes) + x.lock.RUnlock() + + storeSnapshot(snap, x.db) + x.snapshots.Add(snap.Hash, snap) + + nm := []string{} + for _, n := range ms { + nm = append(nm, n.Address.String()) + } + log.Info("New set of masternodes has been updated to snapshot", "number", snap.Number, "hash", snap.Hash, "new masternodes", nm) + + return nil } func (x *XDPoS_v2) VerifyHeader(chain consensus.ChainReader, header *types.Header, fullVerify bool) error { diff --git a/consensus/XDPoS/engines/engine_v2/snapshot.go b/consensus/XDPoS/engines/engine_v2/snapshot.go index 8a70fdb1ab..acdd78062f 100644 --- a/consensus/XDPoS/engines/engine_v2/snapshot.go +++ b/consensus/XDPoS/engines/engine_v2/snapshot.go @@ -2,41 +2,32 @@ package engine_v2 import ( "encoding/json" - "sort" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" - "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" lru "github.com/hashicorp/golang-lru" ) // Snapshot is the state of the smart contract validator list +// The validator list is used on next epoch master nodes +// If we don't have the snapshot, then we have to trace back the gap block smart contract state which is very costly type SnapshotV2 struct { - sigcache *lru.ARCCache // Cache of recent block signatures to speed up ecrecover - Round utils.Round `json:"round"` // Round number Number uint64 `json:"number"` // Block number where the snapshot was created Hash common.Hash `json:"hash"` // Block hash where the snapshot was created // MasterNodes will get assigned on updateM1 - MasterNodes map[common.Address]struct{} `json:"masterNodes"` // Set of authorized master nodes at this moment + NextEpochMasterNodes []common.Address `json:"masterNodes"` // Set of authorized master nodes at this moment for next epoch } -// newSnapshot creates a new snapshot with the specified startup parameters. This -// method does not initialize the set of recent signers, so only ever use if for -// the genesis block. -func newSnapshot(sigcache *lru.ARCCache, number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 { +// create new snapshot for next epoch to use +func newSnapshot(number uint64, hash common.Hash, round utils.Round, qc *utils.QuorumCert, masternodes []common.Address) *SnapshotV2 { snap := &SnapshotV2{ - sigcache: sigcache, - Round: round, - Number: number, - Hash: hash, - - MasterNodes: make(map[common.Address]struct{}), - } - for _, signer := range masternodes { - snap.MasterNodes[signer] = struct{}{} + Round: round, + Number: number, + Hash: hash, + NextEpochMasterNodes: masternodes, } return snap } @@ -51,7 +42,6 @@ func loadSnapshot(sigcache *lru.ARCCache, db ethdb.Database, hash common.Hash) ( if err := json.Unmarshal(blob, snap); err != nil { return nil, err } - snap.sigcache = sigcache return snap, nil } @@ -65,69 +55,11 @@ func storeSnapshot(s *SnapshotV2, db ethdb.Database) error { return db.Put(append([]byte("XDPoS-"), s.Hash[:]...), blob) } -// copy creates a deep copy of the SnapshotV2, though not the individual votes. -func (s *SnapshotV2) copy() *SnapshotV2 { - cpy := &SnapshotV2{ - sigcache: s.sigcache, - Round: s.Round, - Number: s.Number, - Hash: s.Hash, - MasterNodes: make(map[common.Address]struct{}), +// retrieves master nodes list in map type +func (s *SnapshotV2) GetMappedMasterNodes() map[common.Address]struct{} { + ms := make(map[common.Address]struct{}) + for _, n := range s.NextEpochMasterNodes { + ms[n] = struct{}{} } - for signer := range s.MasterNodes { - cpy.MasterNodes[signer] = struct{}{} - } - - return cpy -} - -// apply creates a new authorization SnapshotV2 by applying the given headers to -// the original one. -// TODO: XIN-100 -func (s *SnapshotV2) apply(headers []*types.Header) (*SnapshotV2, error) { - return s, nil - - // Allow passing in no headers for cleaner code - // if len(headers) == 0 { - // return s, nil - // } - // // Sanity check that the headers can be applied - // for i := 0; i < len(headers)-1; i++ { - // if headers[i+1].Number.Uint64() != headers[i].Number.Uint64()+1 { - // return nil, utils.ErrInvalidHeaderOrder - // } - // } - // if headers[0].Number.Uint64() != s.Number+1 { - // return nil, utils.ErrInvalidChild - // } - // // Iterate through the headers and create a new SnapshotV2 - // snap := s.copy() - // lastHeader := headers[len(headers)-1] - - // snap.Number += uint64(len(headers)) - // snap.Hash = lastHeader.Hash() - - // extraV2 := new(utils.ExtraFields_v2) - // err := utils.DecodeBytesExtraFields(lastHeader.Extra, &extraV2) - // if err != nil { - // return nil, err - // } - // snap.Round = extraV2.Round - // return snap, nil -} - -// signers retrieves the list of authorized signers in ascending order, convert into strings then use native sort lib -func (s *SnapshotV2) GetMasterNodes() []common.Address { - nodes := make([]common.Address, 0, len(s.MasterNodes)) - nodeStrs := make([]string, 0, len(s.MasterNodes)) - - for node := range s.MasterNodes { - nodeStrs = append(nodeStrs, node.Str()) - } - sort.Strings(nodeStrs) - for _, str := range nodeStrs { - nodes = append(nodes, common.StringToAddress(str)) - } - - return nodes + return ms } diff --git a/consensus/XDPoS/engines/engine_v2/snapshot_test.go b/consensus/XDPoS/engines/engine_v2/snapshot_test.go index b29c29ff8f..3183698224 100644 --- a/consensus/XDPoS/engines/engine_v2/snapshot_test.go +++ b/consensus/XDPoS/engines/engine_v2/snapshot_test.go @@ -3,103 +3,28 @@ package engine_v2 import ( "fmt" "io/ioutil" - "math/big" "testing" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" "github.com/XinFinOrg/XDPoSChain/core/rawdb" - "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb/leveldb" - "github.com/stretchr/testify/assert" ) func TestGetMasterNodes(t *testing.T) { - masterNodes := []common.Address{ - {4}, {3}, {2}, {1}, - } - snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes) - sortedNodes := snap.GetMasterNodes() - for i := range masterNodes { - if masterNodes[i] != sortedNodes[3-i] { - t.Error("should get sorted master nodes list", i, sortedNodes[i]) + masterNodes := []common.Address{{0x4}, {0x3}, {0x2}, {0x1}} + snap := newSnapshot(1, common.Hash{}, utils.Round(1), nil, masterNodes) + + for _, address := range masterNodes { + if _, ok := snap.GetMappedMasterNodes()[address]; !ok { + t.Error("should get master node from map", address.Hex(), snap.GetMappedMasterNodes()) return } } } -func TestApplyNewSnapshot(t *testing.T) { - t.Skip("apply has been temporary commented out") - snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil) - extra := utils.ExtraFields_v2{ - Round: 10, - QuorumCert: &utils.QuorumCert{ - ProposedBlockInfo: &utils.BlockInfo{}, - }, - } - extraBytes, err := extra.EncodeToBytes() - assert.Nil(t, err) - - headers := []*types.Header{ - {Number: big.NewInt(2)}, - {Number: big.NewInt(3)}, - {Number: big.NewInt(4)}, - { - Number: big.NewInt(5), - Extra: extraBytes, - }, - } - newSnap, err := snap.apply(headers) - assert.Nil(t, err) - if newSnap.Number != 5 { - t.Error("newSnapshot number should have last header number") - } - if newSnap.Hash != headers[3].Hash() { - t.Error("newSnapshot hash should equal the last header given") - } - if newSnap.Round != 10 { - t.Error("newSnapshot round number should also have last header round number") - } -} - -func TestApplyWithWrongHeader(t *testing.T) { - t.Skip("apply has been temporary commented out") - snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil) - headers := []*types.Header{ - {Number: big.NewInt(3)}, - } - _, err := snap.apply(headers) - assert.Equal(t, err, utils.ErrInvalidChild) - - snap = newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, nil) - headers = []*types.Header{ - {Number: big.NewInt(2)}, - {Number: big.NewInt(4)}, - } - _, err = snap.apply(headers) - assert.Equal(t, err, utils.ErrInvalidHeaderOrder) -} - -// Should perform deep copy -func TestCopySnapshot(t *testing.T) { - masterNodes := []common.Address{ - {4}, {3}, {2}, {1}, - } - snap := newSnapshot(nil, 1, common.Hash{}, utils.Round(1), nil, masterNodes) - - newSnapshot := snap.copy() - if newSnapshot == snap { - t.Error("should return given different memory address") - } - - for node := range snap.MasterNodes { - if _, ok := newSnapshot.MasterNodes[node]; !ok { - t.Error("snapshot masternodes should copy to new object") - } - } -} func TestStoreLoadSnapshot(t *testing.T) { - snap := newSnapshot(nil, 1, common.Hash{0x1}, utils.Round(1), nil, nil) + snap := newSnapshot(1, common.Hash{0x1}, utils.Round(1), nil, nil) dir, err := ioutil.TempDir("", "snapshot-test") if err != nil { panic(fmt.Sprintf("can't create temporary directory: %v", err)) diff --git a/consensus/tests/block_signer_test.go b/consensus/tests/block_signer_test.go index 4fdf41fa38..44caf27fdd 100644 --- a/consensus/tests/block_signer_test.go +++ b/consensus/tests/block_signer_test.go @@ -602,3 +602,63 @@ 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.TestXDPoSMockChainConfigWithV2EngineEpochSwitch + blockchain, backend, parentBlock, _ := 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}) + 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") + } +} +*/ diff --git a/consensus/tests/initialize_v2_test.go b/consensus/tests/initialize_v2_test.go new file mode 100644 index 0000000000..773a297e5c --- /dev/null +++ b/consensus/tests/initialize_v2_test.go @@ -0,0 +1,48 @@ +package tests + +import ( + "math/big" + "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/params" + "github.com/stretchr/testify/assert" +) + +func TestYourTurnInitialV2(t *testing.T) { + config := params.TestXDPoSMockChainConfigWithV2EngineEpochSwitch + blockchain, _, parentBlock, _ := PrepareXDCTestBlockChain(t, int(config.XDPoS.Epoch)-1, config) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + + // Insert block 900 + t.Logf("Inserting block with propose at 900...") + blockCoinbaseA := "0xaaa0000000000000000000000000000000000900" + //Get from block validator error message + merkleRoot := "35999dded35e8db12de7e6c1471eb9670c162eec616ecebbaf4fddd4676fb930" + header := &types.Header{ + Root: common.HexToHash(merkleRoot), + Number: big.NewInt(int64(900)), + ParentHash: parentBlock.Hash(), + Coinbase: common.HexToAddress(blockCoinbaseA), + Extra: common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400"), + } + block900, err := insertBlock(blockchain, header) + if err != nil { + t.Fatal(err) + } + + // YourTurn is called before mine first v2 block + adaptor.YourTurn(blockchain, block900.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1")) + assert.Equal(t, adaptor.EngineV2.GetCurrentRound(), utils.Round(1)) + + snap, err := adaptor.EngineV2.GetSnapshot(blockchain, block900.Header()) + assert.Nil(t, err) + assert.NotNil(t, snap) + masterNodes := adaptor.EngineV1.GetMasternodesFromCheckpointHeader(block900.Header()) + for i := 0; i < len(masterNodes); i++ { + assert.Equal(t, masterNodes[i].Hex(), snap.NextEpochMasterNodes[i].Hex()) + } +} diff --git a/params/config.go b/params/config.go index fb0846da51..37c754d36d 100644 --- a/params/config.go +++ b/params/config.go @@ -133,6 +133,8 @@ var ( TestXDPoSMockChainConfig = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, V2: *TestXDPoSV2Config}} // XDPoS config with v2 engine after block 10 TestXDPoSMockChainConfigWithV2Engine = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(10), V2: *TestXDPoSV2Config}} + // XDPoS config with v2 engine after block 901 + TestXDPoSMockChainConfigWithV2EngineEpochSwitch = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, &XDPoSConfig{Epoch: 900, Gap: 450, SkipValidation: true, XDPoSV2Block: big.NewInt(900), V2: *TestXDPoSV2Config}} TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int))