From f8d3f9f8c6741b550b738c95614a552c7056104c Mon Sep 17 00:00:00 2001 From: Jerome Date: Fri, 14 Jan 2022 21:38:38 +1100 Subject: [PATCH] Xin 113 is epoch switch (#37) * add isEpochSwitch function and refactor utils * fix broken first v2 epoch switch block * use adaptor epoch switch function to determine v1 v2 epoch swtich block * add test for the GetMasternodesByNumber and GetCurrentEpochSwitchBlock function * add v2 test for isAuthroisedAddress * Use GetCurrentEpochSwitchBlock in findNearestSignedBlock api --- consensus/XDPoS/XDPoS.go | 55 ++++-- consensus/XDPoS/engines/engine_v1/engine.go | 94 +++++----- consensus/XDPoS/engines/engine_v1/snapshot.go | 2 +- consensus/XDPoS/engines/engine_v1/utils.go | 112 ++++++++++++ .../XDPoS/engines/engine_v1/utils_test.go | 49 ++++++ consensus/XDPoS/engines/engine_v2/engine.go | 56 +++++- consensus/XDPoS/engines/engine_v2/utils.go | 59 +++++++ consensus/XDPoS/utils/utils.go | 162 ------------------ consensus/XDPoS/utils/utils_test.go | 44 +---- consensus/tests/adaptor_test.go | 75 +++++++- consensus/tests/authorised_masternode_test.go | 44 ++++- consensus/tests/test_helper.go | 14 +- contracts/utils.go | 3 +- core/block_validator_test.go | 3 +- core/blockchain.go | 35 +++- eth/api_backend.go | 12 +- eth/backend.go | 8 +- internal/ethapi/api.go | 54 +++--- internal/ethapi/backend.go | 4 +- les/backend.go | 2 +- miner/worker.go | 51 ++++-- 21 files changed, 589 insertions(+), 349 deletions(-) create mode 100644 consensus/XDPoS/engines/engine_v1/utils.go create mode 100644 consensus/XDPoS/engines/engine_v1/utils_test.go create mode 100644 consensus/XDPoS/engines/engine_v2/utils.go diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index e41b1135f3..ecfa762834 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -35,8 +35,13 @@ import ( lru "github.com/hashicorp/golang-lru" ) -func SigHash(header *types.Header) (hash common.Hash) { - return utils.SigHash(header) +func (x *XDPoS) SigHash(header *types.Header) (hash common.Hash) { + switch x.config.BlockConsensusVersion(header.Number) { + case params.ConsensusEngineVersion2: + return x.EngineV2.SignHash(header) + default: // Default "v1" + return x.EngineV1.SigHash(header) + } } // XDPoS is the delegated-proof-of-stake consensus engine proposed to support the @@ -240,12 +245,12 @@ func (x *XDPoS) GetPeriod() uint64 { return x.config.Period } -func (x *XDPoS) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool { +func (x *XDPoS) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool { switch x.config.BlockConsensusVersion(header.Number) { case params.ConsensusEngineVersion2: - return x.EngineV2.IsAuthorisedAddress(header, chain, address) + return x.EngineV2.IsAuthorisedAddress(chain, header, address) default: // Default "v1" - return x.EngineV1.IsAuthorisedAddress(header, chain, address) + return x.EngineV1.IsAuthorisedAddress(chain, header, address) } } @@ -258,6 +263,20 @@ func (x *XDPoS) GetMasternodes(chain consensus.ChainReader, header *types.Header } } +func (x *XDPoS) GetMasternodesByNumber(chain consensus.ChainReader, blockNumber uint64) []common.Address { + blockHeader := chain.GetHeaderByNumber(blockNumber) + if blockHeader == nil { + log.Error("[GetMasternodesByNumber] Unable to find block", "Num", blockNumber) + return []common.Address{} + } + switch x.config.BlockConsensusVersion(big.NewInt(int64(blockNumber))) { + case params.ConsensusEngineVersion2: + return x.EngineV2.GetMasternodes(chain, blockHeader) + default: // Default "v1" + return x.EngineV1.GetMasternodes(chain, blockHeader) + } +} + func (x *XDPoS) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) { switch x.config.BlockConsensusVersion(parent.Number) { case params.ConsensusEngineVersion2: @@ -302,30 +321,34 @@ func (x *XDPoS) RecoverValidator(header *types.Header) (common.Address, error) { } // Get master nodes over extra data of previous checkpoint block. -func (x *XDPoS) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Header, n, e uint64) []common.Address { - switch x.config.BlockConsensusVersion(preCheckpointHeader.Number) { +func (x *XDPoS) GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address { + switch x.config.BlockConsensusVersion(checkpointHeader.Number) { case params.ConsensusEngineVersion2: - return x.EngineV2.GetMasternodesFromEpochSwitchHeader(preCheckpointHeader) + return x.EngineV2.GetMasternodesFromEpochSwitchHeader(checkpointHeader) default: // Default "v1" - return x.EngineV1.GetMasternodesFromCheckpointHeader(preCheckpointHeader, n, e) + return x.EngineV1.GetMasternodesFromCheckpointHeader(checkpointHeader) } } // Check is epoch switch (checkpoint) block -func (x *XDPoS) IsEpochSwitch(header *types.Header) bool { +func (x *XDPoS) IsEpochSwitch(header *types.Header) (bool, uint64, error) { switch x.config.BlockConsensusVersion(header.Number) { case params.ConsensusEngineVersion2: - b, _, err := x.EngineV2.IsEpochSwitch(header) - if err != nil { - log.Error("[IsEpochSwitch] Adaptor v2 IsEpochSwitch has error", "err", err) - return false - } - return b + return x.EngineV2.IsEpochSwitch(header) default: // Default "v1" return x.EngineV1.IsEpochSwitch(header) } } +func (x *XDPoS) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, blockNumber *big.Int) (uint64, uint64, error) { + switch x.config.BlockConsensusVersion(blockNumber) { + case params.ConsensusEngineVersion2: + return x.EngineV2.GetCurrentEpochSwitchBlock(chain, blockNumber) + default: // Default "v1" + return x.EngineV1.GetCurrentEpochSwitchBlock(blockNumber) + } +} + // Same DB across all consensus engines func (x *XDPoS) GetDb() ethdb.Database { return x.db diff --git a/consensus/XDPoS/engines/engine_v1/engine.go b/consensus/XDPoS/engines/engine_v1/engine.go index c2351de31b..6aac555f86 100644 --- a/consensus/XDPoS/engines/engine_v1/engine.go +++ b/consensus/XDPoS/engines/engine_v1/engine.go @@ -53,6 +53,23 @@ type XDPoS_v1 struct { HookGetSignersFromContract func(blockHash common.Hash) ([]common.Address, error) } +/* V1 Block +SignerFn is a signer callback function to request a hash to be signed by a +backing account. +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_v1) SigHash(header *types.Header) (hash common.Hash) { + return sigHash(header) +} + // 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 { @@ -81,7 +98,7 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v1 { // Author implements consensus.Engine, returning the Ethereum address recovered // from the signature in the header's extra-data section. func (x *XDPoS_v1) Author(header *types.Header) (common.Address, error) { - return utils.Ecrecover(header, x.signatures) + return ecrecover(header, x.signatures) } // VerifyHeader checks whether a header conforms to the consensus rules. @@ -296,7 +313,7 @@ func (x *XDPoS_v1) checkSignersOnCheckpoint(chain consensus.ChainReader, header return nil } -func (x *XDPoS_v1) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool { +func (x *XDPoS_v1) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool { snap, err := x.GetSnapshot(chain, header) if err != nil { log.Error("[IsAuthorisedAddress] Can't get snapshot with at ", "number", header.Number, "hash", header.Hash().Hex(), "err", err) @@ -330,36 +347,37 @@ func (x *XDPoS_v1) StoreSnapshot(snap *SnapshotV1) error { return snap.store(x.db) } -func position(list []common.Address, x common.Address) int { - for i, item := range list { - if item == x { - return i - } - } - return -1 -} - func (x *XDPoS_v1) GetMasternodes(chain consensus.ChainReader, header *types.Header) []common.Address { n := header.Number.Uint64() e := x.config.Epoch switch { case n%e == 0: - return x.GetMasternodesFromCheckpointHeader(header, n, e) + return x.GetMasternodesFromCheckpointHeader(header) case n%e != 0: h := chain.GetHeaderByNumber(n - (n % e)) - return x.GetMasternodesFromCheckpointHeader(h, n, e) + if h == nil { + log.Warn("[GetMasternodes v1] epoch switch block header nil", "BlockNum", n) + } + return x.GetMasternodesFromCheckpointHeader(h) default: return []common.Address{} } } +func (x *XDPoS_v1) GetCurrentEpochSwitchBlock(blockNumber *big.Int) (uint64, uint64, error) { + currentBlockNum := blockNumber.Uint64() + currentCheckpointNumber := currentBlockNum - currentBlockNum%x.config.Epoch + epochNumber := currentBlockNum / x.config.Epoch + return currentCheckpointNumber, epochNumber, nil +} + func (x *XDPoS_v1) GetPeriod() uint64 { return x.config.Period } -func whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) { +func (x *XDPoS_v1) whoIsCreator(snap *SnapshotV1, header *types.Header) (common.Address, error) { if header.Number.Uint64() == 0 { return common.Address{}, errors.New("Don't take block 0") } - m, err := utils.Ecrecover(header, snap.sigcache) + m, err := ecrecover(header, snap.sigcache) if err != nil { return common.Address{}, err } @@ -390,13 +408,13 @@ func (x *XDPoS_v1) YourTurn(chain consensus.ChainReader, parent *types.Header, s // masternode[0] has chance to create block 1 preIndex := -1 if parent.Number.Uint64() != 0 { - pre, err = whoIsCreator(snap, parent) + pre, err = x.whoIsCreator(snap, parent) if err != nil { return 0, 0, 0, false, err } - preIndex = position(masternodes, pre) + preIndex = utils.Position(masternodes, pre) } - curIndex := position(masternodes, signer) + curIndex := utils.Position(masternodes, signer) if signer == x.signer { log.Debug("Masternodes cycle info", "number of masternodes", len(masternodes), "previous", pre, "position", preIndex, "current", signer, "position", curIndex) } @@ -524,7 +542,7 @@ func (x *XDPoS_v1) verifySeal(chain consensus.ChainReader, header *types.Header, } // Resolve the authorization key and check against signers - creator, err := utils.Ecrecover(header, x.signatures) + creator, err := ecrecover(header, x.signatures) if err != nil { return err } @@ -618,7 +636,7 @@ func (x *XDPoS_v1) GetValidator(creator common.Address, chain consensus.ChainRea return common.Address{}, fmt.Errorf("couldn't find checkpoint header") } } - m, err := utils.GetM1M2FromCheckpointHeader(cpHeader, header, chain.Config()) + m, err := getM1M2FromCheckpointHeader(cpHeader, header, chain.Config()) if err != nil { return common.Address{}, err } @@ -851,7 +869,7 @@ func (x *XDPoS_v1) Seal(chain consensus.ChainReader, block *types.Block, stop <- default: } // Sign all the things! - sighash, err := signFn(accounts.Account{Address: signer}, utils.SigHash(header).Bytes()) + sighash, err := signFn(accounts.Account{Address: signer}, x.SigHash(header).Bytes()) if err != nil { return nil, err } @@ -886,7 +904,7 @@ func (x *XDPoS_v1) calcDifficulty(chain consensus.ChainReader, parent *types.Hea } func (x *XDPoS_v1) RecoverSigner(header *types.Header) (common.Address, error) { - return utils.Ecrecover(header, x.signatures) + return ecrecover(header, x.signatures) } func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error) { @@ -901,7 +919,7 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error return common.Address{}, consensus.ErrFailValidatorSignature } // Recover the public key and the Ethereum address - pubkey, err := crypto.Ecrecover(utils.SigHash(header).Bytes(), header.Validator) + pubkey, err := crypto.Ecrecover(x.SigHash(header).Bytes(), header.Validator) if err != nil { return common.Address{}, err } @@ -912,18 +930,13 @@ func (x *XDPoS_v1) RecoverValidator(header *types.Header) (common.Address, error return signer, nil } -// Get master nodes over extra data of previous checkpoint block. -func (x *XDPoS_v1) GetMasternodesFromCheckpointHeader(preCheckpointHeader *types.Header, n, e uint64) []common.Address { - if preCheckpointHeader == nil { - log.Info("Previous checkpoint's header is empty", "block number", n, "epoch", e) +// Get master nodes over extra data of checkpoint block. +func (x *XDPoS_v1) GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address { + if checkpointHeader == nil { + log.Warn("Checkpoint's header is empty", "Header", checkpointHeader) return []common.Address{} } - masternodes := make([]common.Address, (len(preCheckpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength) - for i := 0; i < len(masternodes); i++ { - copy(masternodes[i][:], preCheckpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:]) - } - - return masternodes + return decodeMasternodesFromHeaderExtra(checkpointHeader) } func (x *XDPoS_v1) GetDb() ethdb.Database { @@ -945,15 +958,6 @@ func removePenaltiesFromBlock(chain consensus.ChainReader, masternodes []common. return masternodes } -// Get masternodes address from checkpoint Header. -func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address { - masternodes := make([]common.Address, (len(checkpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength) - for i := 0; i < len(masternodes); i++ { - copy(masternodes[i][:], checkpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:]) - } - return masternodes -} - func (x *XDPoS_v1) getSignersFromContract(chain consensus.ChainReader, checkpointHeader *types.Header) ([]common.Address, error) { startGapBlockHeader := checkpointHeader number := checkpointHeader.Number.Uint64() @@ -990,6 +994,8 @@ func NewFaker(db ethdb.Database, config *params.XDPoSConfig) *XDPoS_v1 { } // Epoch Switch is also known as checkpoint in v1 -func (x *XDPoS_v1) IsEpochSwitch(header *types.Header) bool { - return (header.Number.Uint64() % x.config.Epoch) == 0 +func (x *XDPoS_v1) IsEpochSwitch(header *types.Header) (bool, uint64, error) { + epochNumber := header.Number.Uint64() / x.config.Epoch + blockNumInEpoch := header.Number.Uint64() % x.config.Epoch + return blockNumInEpoch == 0, epochNumber, nil } diff --git a/consensus/XDPoS/engines/engine_v1/snapshot.go b/consensus/XDPoS/engines/engine_v1/snapshot.go index 3aa7ad030d..3ad8b22189 100644 --- a/consensus/XDPoS/engines/engine_v1/snapshot.go +++ b/consensus/XDPoS/engines/engine_v1/snapshot.go @@ -187,7 +187,7 @@ func (s *SnapshotV1) apply(headers []*types.Header) (*SnapshotV1, error) { delete(snap.Recents, number-limit) } // Resolve the authorization key and check against signers - signer, err := utils.Ecrecover(header, s.sigcache) + signer, err := ecrecover(header, s.sigcache) if err != nil { return nil, err } diff --git a/consensus/XDPoS/engines/engine_v1/utils.go b/consensus/XDPoS/engines/engine_v1/utils.go new file mode 100644 index 0000000000..56f1716a83 --- /dev/null +++ b/consensus/XDPoS/engines/engine_v1/utils.go @@ -0,0 +1,112 @@ +package engine_v1 + +import ( + "errors" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/params" + "github.com/XinFinOrg/XDPoSChain/rlp" + lru "github.com/hashicorp/golang-lru" +) + +// Get masternodes address from checkpoint Header. +func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.Address { + masternodes := make([]common.Address, (len(checkpointHeader.Extra)-utils.ExtraVanity-utils.ExtraSeal)/common.AddressLength) + for i := 0; i < len(masternodes); i++ { + copy(masternodes[i][:], checkpointHeader.Extra[utils.ExtraVanity+i*common.AddressLength:]) + } + return masternodes +} + +// Get m2 list from checkpoint block. +func getM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) { + if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 { + return nil, errors.New("This block is not checkpoint block epoc.") + } + // Get signers from this block. + masternodes := decodeMasternodesFromHeaderExtra(checkpointHeader) + validators := utils.ExtractValidatorsFromBytes(checkpointHeader.Validators) + m1m2, _, err := getM1M2(masternodes, validators, currentHeader, config) + if err != nil { + return map[common.Address]common.Address{}, err + } + return m1m2, nil +} + +func getM1M2(masternodes []common.Address, validators []int64, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, uint64, error) { + m1m2 := map[common.Address]common.Address{} + maxMNs := len(masternodes) + moveM2 := uint64(0) + if len(validators) < maxMNs { + return nil, moveM2, errors.New("len(m2) is less than len(m1)") + } + if maxMNs > 0 { + isForked := config.IsTIPRandomize(currentHeader.Number) + if isForked { + moveM2 = ((currentHeader.Number.Uint64() % config.XDPoS.Epoch) / uint64(maxMNs)) % uint64(maxMNs) + } + for i, m1 := range masternodes { + m2Index := uint64(validators[i] % int64(maxMNs)) + m2Index = (m2Index + moveM2) % uint64(maxMNs) + m1m2[m1] = masternodes[m2Index] + } + } + return m1m2, moveM2, nil +} + +func sigHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewKeccak256() + + err := rlp.Encode(hasher, []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short + header.MixDigest, + header.Nonce, + }) + if err != nil { + log.Debug("Fail to encode", err) + } + hasher.Sum(hash[:0]) + return hash +} + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { + // If the signature's already cached, return that + hash := header.Hash() + if address, known := sigcache.Get(hash); known { + return address.(common.Address), nil + } + // Retrieve the signature from the header extra-data + if len(header.Extra) < utils.ExtraSeal { + return common.Address{}, utils.ErrMissingSignature + } + signature := header.Extra[len(header.Extra)-utils.ExtraSeal:] + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + sigcache.Add(hash, signer) + return signer, nil +} diff --git a/consensus/XDPoS/engines/engine_v1/utils_test.go b/consensus/XDPoS/engines/engine_v1/utils_test.go new file mode 100644 index 0000000000..8a3ffe67dc --- /dev/null +++ b/consensus/XDPoS/engines/engine_v1/utils_test.go @@ -0,0 +1,49 @@ +package engine_v1 + +import ( + "fmt" + "math/big" + "testing" + + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/params" +) + +func TestGetM1M2FromCheckpointHeader(t *testing.T) { + masternodes := []common.Address{ + common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + common.StringToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc"), + } + validators := []int64{ + 2, + 1, + 0, + } + epoch := uint64(900) + config := ¶ms.ChainConfig{ + XDPoS: ¶ms.XDPoSConfig{ + Epoch: uint64(epoch), + }, + } + testMoveM2 := []uint64{0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2} + //try from block 3410001 to 3410018 + for i := uint64(3464001); i <= 3464018; i++ { + currentNumber := int64(i) + currentHeader := &types.Header{ + Number: big.NewInt(currentNumber), + } + m1m2, moveM2, err := getM1M2(masternodes, validators, currentHeader, config) + if err != nil { + t.Error("can't get m1m2", "err", err) + } + fmt.Printf("block: %v, moveM2: %v\n", currentHeader.Number.Int64(), moveM2) + for _, k := range masternodes { + fmt.Printf("m1: %v - m2: %v\n", k.Str(), m1m2[k].Str()) + } + if moveM2 != testMoveM2[i-3464001] { + t.Error("wrong moveM2", "currentNumber", currentNumber, "want", testMoveM2[i-3464001], "have", moveM2) + } + } +} diff --git a/consensus/XDPoS/engines/engine_v2/engine.go b/consensus/XDPoS/engines/engine_v2/engine.go index fc91eb75e6..d368c17cd2 100644 --- a/consensus/XDPoS/engines/engine_v2/engine.go +++ b/consensus/XDPoS/engines/engine_v2/engine.go @@ -98,6 +98,23 @@ func New(config *params.XDPoSConfig, db ethdb.Database) *XDPoS_v2 { return engine } +/* V2 Block +SignerFn is a signer callback function to request a hash to be signed by a +backing account. +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) +} + // 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 { @@ -146,7 +163,12 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er log.Debug("CalcDifficulty ", "number", header.Number, "difficulty", header.Difficulty) // TODO: previous round should sit on previous Epoch and x.currentRound should >= Epoch number - if number%x.config.Epoch == 0 { + isEpochSwitchBlock, _, err := x.IsEpochSwitch(header) + if err != nil { + log.Error("[Prepare] Error while trying to determine if header is an epoch switch during Prepare", "header", header, "Error", err) + return err + } + if isEpochSwitchBlock { snap, err := x.snapshot(chain, number-1, header.ParentHash, nil) if err != nil { return err @@ -222,7 +244,7 @@ func (x *XDPoS_v2) Authorize(signer common.Address, signFn clique.SignerFn) { } func (x *XDPoS_v2) Author(header *types.Header) (common.Address, error) { - return utils.EcrecoverV2(header, x.signatures) + return ecrecover(header, x.signatures) } // Seal implements consensus.Engine, attempting to create a sealed block using @@ -237,7 +259,11 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- } // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) // checkpoint blocks have no tx - if x.config.Period == 0 && len(block.Transactions()) == 0 && number%x.config.Epoch != 0 { + isEpochSwitch, _, err := x.IsEpochSwitch(header) + if err != nil { + log.Error("[Seal] Error while checking whether header is a epoch switch during sealing", "Header", header) + } + if x.config.Period == 0 && len(block.Transactions()) == 0 && !isEpochSwitch { return nil, utils.ErrWaitTransactions } // Don't hold the signer fields for the entire sealing procedure @@ -271,7 +297,7 @@ func (x *XDPoS_v2) Seal(chain consensus.ChainReader, block *types.Block, stop <- } // Sign all the things! - signature, err := signFn(accounts.Account{Address: signer}, utils.SigHashV2(header).Bytes()) + signature, err := signFn(accounts.Account{Address: signer}, sigHash(header).Bytes()) if err != nil { return nil, err } @@ -327,7 +353,7 @@ func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, s return len(masternodes), preIndex, curIndex, false, nil } -func (x *XDPoS_v2) IsAuthorisedAddress(header *types.Header, chain consensus.ChainReader, address common.Address) bool { +func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool { var extraField utils.ExtraFields_v2 err := utils.DecodeBytesExtraFields(header.Extra, &extraField) if err != nil { @@ -355,7 +381,7 @@ func whoIsCreator(snap *SnapshotV2, header *types.Header) (common.Address, error if header.Number.Uint64() == 0 { return common.Address{}, errors.New("Don't take block 0") } - m, err := utils.EcrecoverV2(header, snap.sigcache) + m, err := ecrecover(header, snap.sigcache) if err != nil { return common.Address{}, err } @@ -1066,13 +1092,14 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { parentRound := decodedExtraField.QuorumCert.ProposedBlockInfo.Round round := decodedExtraField.Round epochStart := round - round%utils.Round(x.config.Epoch) + epochNum := x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch // if parent is last v1 block and this is first v2 block, this is treated as epoch switch if decodedExtraField.QuorumCert.ProposedBlockInfo.Number.Cmp(x.config.XDPoSV2Block) == 0 { log.Info("[IsEpochSwitch] true, parent equals XDPoSV2Block", "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) - return true, x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch, nil + return true, epochNum, nil } log.Info("[IsEpochSwitch]", "parent round", parentRound, "round", round, "number", header.Number.Uint64(), "hash", header.Hash()) - return parentRound < epochStart, x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(round)/x.config.Epoch, nil + return parentRound < epochStart, epochNum, nil } // Given header and its hash, get epoch switch info from the epoch switch block of that epoch, @@ -1133,3 +1160,16 @@ func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Hea } return epochSwitchInfo.Masternodes } + +func (x *XDPoS_v2) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, blockNum *big.Int) (uint64, uint64, error) { + header := chain.GetHeaderByNumber(blockNum.Uint64()) + epochSwitchInfo, err := x.getEpochSwitchInfo(chain, header, header.Hash()) + if err != nil { + log.Error("[GetCurrentEpochSwitchBlock] Fail to get epoch switch info", "Num", header.Number, "Hash", header.Hash()) + return 0, 0, err + } + + currentCheckpointNumber := epochSwitchInfo.EpochSwitchBlockInfo.Number.Uint64() + epochNum := x.config.XDPoSV2Block.Uint64()/x.config.Epoch + uint64(epochSwitchInfo.EpochSwitchBlockInfo.Round)/x.config.Epoch + return currentCheckpointNumber, epochNum, nil +} diff --git a/consensus/XDPoS/engines/engine_v2/utils.go b/consensus/XDPoS/engines/engine_v2/utils.go new file mode 100644 index 0000000000..7414dce8cd --- /dev/null +++ b/consensus/XDPoS/engines/engine_v2/utils.go @@ -0,0 +1,59 @@ +package engine_v2 + +import ( + "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/XinFinOrg/XDPoSChain/crypto/sha3" + "github.com/XinFinOrg/XDPoSChain/log" + "github.com/XinFinOrg/XDPoSChain/rlp" + lru "github.com/hashicorp/golang-lru" +) + +func sigHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewKeccak256() + + err := rlp.Encode(hasher, []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.Extra, + header.MixDigest, + header.Nonce, + header.Validators, + header.Penalties, + }) + if err != nil { + log.Debug("Fail to encode", err) + } + hasher.Sum(hash[:0]) + return hash +} + +func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { + // If the signature's already cached, return that + hash := header.Hash() + if address, known := sigcache.Get(hash); known { + return address.(common.Address), nil + } + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), header.Validator) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + sigcache.Add(hash, signer) + return signer, nil +} diff --git a/consensus/XDPoS/utils/utils.go b/consensus/XDPoS/utils/utils.go index 504c72a474..4c3d5a90a1 100644 --- a/consensus/XDPoS/utils/utils.go +++ b/consensus/XDPoS/utils/utils.go @@ -2,20 +2,14 @@ package utils import ( "bytes" - "errors" "fmt" "reflect" "sort" "strconv" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/crypto" - "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/XinFinOrg/XDPoSChain/log" - "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" - lru "github.com/hashicorp/golang-lru" ) func Position(list []common.Address, x common.Address) int { @@ -55,51 +49,6 @@ func ExtractValidatorsFromBytes(byteValidators []byte) []int64 { return validators } -// Get masternodes address from checkpoint Header. -func GetMasternodesFromCheckpointHeader(checkpointHeader *types.Header) []common.Address { - masternodes := make([]common.Address, (len(checkpointHeader.Extra)-ExtraVanity-ExtraSeal)/common.AddressLength) - for i := 0; i < len(masternodes); i++ { - copy(masternodes[i][:], checkpointHeader.Extra[ExtraVanity+i*common.AddressLength:]) - } - return masternodes -} - -// Get m2 list from checkpoint block. -func GetM1M2FromCheckpointHeader(checkpointHeader *types.Header, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, error) { - if checkpointHeader.Number.Uint64()%common.EpocBlockRandomize != 0 { - return nil, errors.New("This block is not checkpoint block epoc.") - } - // Get signers from this block. - masternodes := GetMasternodesFromCheckpointHeader(checkpointHeader) - validators := ExtractValidatorsFromBytes(checkpointHeader.Validators) - m1m2, _, err := GetM1M2(masternodes, validators, currentHeader, config) - if err != nil { - return map[common.Address]common.Address{}, err - } - return m1m2, nil -} - -func GetM1M2(masternodes []common.Address, validators []int64, currentHeader *types.Header, config *params.ChainConfig) (map[common.Address]common.Address, uint64, error) { - m1m2 := map[common.Address]common.Address{} - maxMNs := len(masternodes) - moveM2 := uint64(0) - if len(validators) < maxMNs { - return nil, moveM2, errors.New("len(m2) is less than len(m1)") - } - if maxMNs > 0 { - isForked := config.IsTIPRandomize(currentHeader.Number) - if isForked { - moveM2 = ((currentHeader.Number.Uint64() % config.XDPoS.Epoch) / uint64(maxMNs)) % uint64(maxMNs) - } - for i, m1 := range masternodes { - m2Index := uint64(validators[i] % int64(maxMNs)) - m2Index = (m2Index + moveM2) % uint64(maxMNs) - m1m2[m1] = masternodes[m2Index] - } - } - return m1m2, moveM2, nil -} - // compare 2 signers lists // return true if they are same elements, otherwise return false func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool { @@ -115,73 +64,6 @@ func CompareSignersLists(list1 []common.Address, list2 []common.Address) bool { return reflect.DeepEqual(list1, list2) } -// SignerFn is a signer callback function to request a hash to be signed by a -// backing account. -//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 SigHash(header *types.Header) (hash common.Hash) { - hasher := sha3.NewKeccak256() - - err := rlp.Encode(hasher, []interface{}{ - header.ParentHash, - header.UncleHash, - header.Coinbase, - header.Root, - header.TxHash, - header.ReceiptHash, - header.Bloom, - header.Difficulty, - header.Number, - header.GasLimit, - header.GasUsed, - header.Time, - header.Extra[:len(header.Extra)-65], // Yes, this will panic if extra is too short - header.MixDigest, - header.Nonce, - }) - if err != nil { - log.Debug("Fail to encode", err) - } - hasher.Sum(hash[:0]) - return hash -} - -func SigHashV2(header *types.Header) (hash common.Hash) { - hasher := sha3.NewKeccak256() - - err := rlp.Encode(hasher, []interface{}{ - header.ParentHash, - header.UncleHash, - header.Coinbase, - header.Root, - header.TxHash, - header.ReceiptHash, - header.Bloom, - header.Difficulty, - header.Number, - header.GasLimit, - header.GasUsed, - header.Time, - header.Extra, - header.MixDigest, - header.Nonce, - header.Validators, - header.Penalties, - }) - if err != nil { - log.Debug("Fail to encode", err) - } - hasher.Sum(hash[:0]) - return hash -} - // Decode extra fields for consensus version >= 2 (XDPoS 2.0 and future versions) func DecodeBytesExtraFields(b []byte, val interface{}) error { if len(b) == 0 { @@ -196,47 +78,3 @@ func DecodeBytesExtraFields(b []byte, val interface{}) error { return fmt.Errorf("consensus version %d is not defined", b[0]) } } - -// ecrecover extracts the Ethereum account address from a signed header. -func Ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { - // If the signature's already cached, return that - hash := header.Hash() - if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil - } - // Retrieve the signature from the header extra-data - if len(header.Extra) < ExtraSeal { - return common.Address{}, ErrMissingSignature - } - signature := header.Extra[len(header.Extra)-ExtraSeal:] - - // Recover the public key and the Ethereum address - pubkey, err := crypto.Ecrecover(SigHash(header).Bytes(), signature) - if err != nil { - return common.Address{}, err - } - var signer common.Address - copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) - - sigcache.Add(hash, signer) - return signer, nil -} - -func EcrecoverV2(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) { - // If the signature's already cached, return that - hash := header.Hash() - if address, known := sigcache.Get(hash); known { - return address.(common.Address), nil - } - - // Recover the public key and the Ethereum address - pubkey, err := crypto.Ecrecover(SigHashV2(header).Bytes(), header.Validator) - if err != nil { - return common.Address{}, err - } - var signer common.Address - copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) - - sigcache.Add(hash, signer) - return signer, nil -} diff --git a/consensus/XDPoS/utils/utils_test.go b/consensus/XDPoS/utils/utils_test.go index 6624f410af..995869ab1e 100644 --- a/consensus/XDPoS/utils/utils_test.go +++ b/consensus/XDPoS/utils/utils_test.go @@ -1,53 +1,11 @@ package utils import ( - "fmt" - "math/big" "testing" "github.com/XinFinOrg/XDPoSChain/common" - "github.com/XinFinOrg/XDPoSChain/core/types" - "github.com/XinFinOrg/XDPoSChain/params" ) -func TestGetM1M2FromCheckpointHeader(t *testing.T) { - masternodes := []common.Address{ - common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - common.StringToAddress("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc"), - } - validators := []int64{ - 2, - 1, - 0, - } - epoch := uint64(900) - config := ¶ms.ChainConfig{ - XDPoS: ¶ms.XDPoSConfig{ - Epoch: uint64(epoch), - }, - } - testMoveM2 := []uint64{0, 0, 0, 1, 1, 1, 2, 2, 2, 0, 0, 0, 1, 1, 1, 2, 2, 2} - //try from block 3410001 to 3410018 - for i := uint64(3464001); i <= 3464018; i++ { - currentNumber := int64(i) - currentHeader := &types.Header{ - Number: big.NewInt(currentNumber), - } - m1m2, moveM2, err := GetM1M2(masternodes, validators, currentHeader, config) - if err != nil { - t.Error("can't get m1m2", "err", err) - } - fmt.Printf("block: %v, moveM2: %v\n", currentHeader.Number.Int64(), moveM2) - for _, k := range masternodes { - fmt.Printf("m1: %v - m2: %v\n", k.Str(), m1m2[k].Str()) - } - if moveM2 != testMoveM2[i-3464001] { - t.Error("wrong moveM2", "currentNumber", currentNumber, "want", testMoveM2[i-3464001], "have", moveM2) - } - } -} - func TestCompareSignersLists(t *testing.T) { list1 := []common.Address{ common.StringToAddress("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), @@ -81,4 +39,4 @@ func TestCompareSignersLists(t *testing.T) { if CompareSignersLists([]common.Address{common.StringToAddress("aaaaaaaaaaaaaaaa")}, []common.Address{common.StringToAddress("cccccccccccccccccccccccccccccccccccccccc")}) { t.Error("Failed with list has only one signer") } -} \ No newline at end of file +} diff --git a/consensus/tests/adaptor_test.go b/consensus/tests/adaptor_test.go index 63891f8340..a13b0fef6d 100644 --- a/consensus/tests/adaptor_test.go +++ b/consensus/tests/adaptor_test.go @@ -40,7 +40,7 @@ func TestAdaptorShouldGetAuthorForDifferentConsensusVersion(t *testing.T) { ParentHash: currentBlock.Hash(), Coinbase: common.HexToAddress(blockCoinBase), } - err := generateSignature(backend, header) + err := generateSignature(backend, adaptor, header) if err != nil { t.Fatal(err) } @@ -66,11 +66,11 @@ func TestAdaptorGetMasternodesFromCheckpointHeader(t *testing.T) { adaptor := blockchain.Engine().(*XDPoS.XDPoS) headerV1 := currentBlock.Header() headerV1.Extra = common.Hex2Bytes("d7830100018358444388676f312e31352e38856c696e757800000000000000000278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758ccef312ee5eea8d7bad5374c6a652150515d744508b61c1a4deb4e4e7bf057e4e3824c11fd2569bcb77a52905cda63b5a58507910bed335e4c9d87ae0ecdfafd400") - masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1, 0, 0) + masternodesV1 := adaptor.GetMasternodesFromCheckpointHeader(headerV1) headerV2 := currentBlock.Header() headerV2.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(1)) headerV2.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c") - masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2, 0, 0) + masternodesV2 := adaptor.GetMasternodesFromCheckpointHeader(headerV2) assert.True(t, reflect.DeepEqual(masternodesV1, masternodesV2), "GetMasternodesFromCheckpointHeader in adaptor for v1 v2 not equal", "v1", masternodesV1, "v2", masternodesV2) } func TestAdaptorIsEpochSwitch(t *testing.T) { @@ -79,9 +79,15 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { header := currentBlock.Header() // v1 header.Number.SetUint64(0) - assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header) + + isEpochSwitchBlock, epochNum, err := adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) + assert.Equal(t, uint64(0), epochNum) header.Number.SetUint64(1) - assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header) + isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) // v2 parentBlockInfo := &utils.BlockInfo{ Hash: header.ParentHash, @@ -100,7 +106,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { assert.Nil(t, err) header.Extra = extraBytes header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(1)) - assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header) + isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) parentBlockInfo = &utils.BlockInfo{ Hash: header.ParentHash, Round: utils.Round(1), @@ -118,7 +126,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { assert.Nil(t, err) header.Extra = extraBytes header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(2)) - assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header) + isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) parentBlockInfo = &utils.BlockInfo{ Hash: header.ParentHash, Round: utils.Round(blockchain.Config().XDPoS.Epoch) - 1, @@ -136,7 +146,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { assert.Nil(t, err) header.Extra = extraBytes header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(101)) - assert.True(t, adaptor.IsEpochSwitch(header), "header should be epoch switch", header) + isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.True(t, isEpochSwitchBlock, "header should be epoch switch", header) parentBlockInfo = &utils.BlockInfo{ Hash: header.ParentHash, Round: utils.Round(blockchain.Config().XDPoS.Epoch) + 1, @@ -154,7 +166,9 @@ func TestAdaptorIsEpochSwitch(t *testing.T) { assert.Nil(t, err) header.Extra = extraBytes header.Number.Add(blockchain.Config().XDPoS.XDPoSV2Block, big.NewInt(101)) - assert.False(t, adaptor.IsEpochSwitch(header), "header should not be epoch switch", header) + isEpochSwitchBlock, _, err = adaptor.IsEpochSwitch(header) + assert.Nil(t, err) + assert.False(t, isEpochSwitchBlock, "header should not be epoch switch", header) } func TestAdaptorGetMasternodesV2(t *testing.T) { @@ -173,6 +187,8 @@ func TestAdaptorGetMasternodesV2(t *testing.T) { } masternodes1 := adaptor.GetMasternodes(blockchain, currentBlock.Header()) assert.Equal(t, 3, len(masternodes1)) + masternodes1ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64()) + assert.True(t, reflect.DeepEqual(masternodes1, masternodes1ByNumber), "at block number", blockNum) for blockNum = 12; blockNum < 15; blockNum++ { blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn) currentBlock, err = insertBlock(blockchain, blockHeader) @@ -181,5 +197,46 @@ func TestAdaptorGetMasternodesV2(t *testing.T) { } masternodes2 := adaptor.GetMasternodes(blockchain, currentBlock.Header()) assert.True(t, reflect.DeepEqual(masternodes1, masternodes2), "at block number", blockNum) + masternodes2ByNumber := adaptor.GetMasternodesByNumber(blockchain, currentBlock.NumberU64()) + assert.True(t, reflect.DeepEqual(masternodes2, masternodes2ByNumber), "at block number", blockNum) + } +} + +func TestGetCurrentEpochSwitchBlock(t *testing.T) { + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + + // V1 + currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, big.NewInt(9)) + assert.Nil(t, err) + assert.Equal(t, uint64(0), currentCheckpointNumber) + assert.Equal(t, uint64(0), epochNum) + + // V2 + blockNum := 11 + blockCoinBase := "0x111000000000000000000000000000000123" + blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 1, blockCoinBase, signer, signFn) + // it contains 3 master nodes + blockHeader.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c") + // block 11 is the first v2 block, and is treated as epoch switch block + currentBlock, err = insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + currentCheckpointNumber, epochNum, err = adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number()) + assert.Nil(t, err) + assert.Equal(t, uint64(11), currentCheckpointNumber) + assert.Equal(t, uint64(0), epochNum) + + for blockNum = 12; blockNum < 15; blockNum++ { + blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn) + currentBlock, err = insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + currentCheckpointNumber, epochNum, err := adaptor.GetCurrentEpochSwitchBlock(blockchain, currentBlock.Number()) + assert.Nil(t, err) + assert.Equal(t, uint64(11), currentCheckpointNumber) + assert.Equal(t, uint64(0), epochNum) } } diff --git a/consensus/tests/authorised_masternode_test.go b/consensus/tests/authorised_masternode_test.go index 1c899c6f33..d509400394 100644 --- a/consensus/tests/authorised_masternode_test.go +++ b/consensus/tests/authorised_masternode_test.go @@ -42,10 +42,10 @@ func TestIsAuthorisedMNForConsensusV1(t *testing.T) { // Acc3 is the default account that is on the signerList engine := blockchain.Engine().(*XDPoS.XDPoS) - isAuthorisedMN := engine.IsAuthorisedAddress(block449.Header(), blockchain, acc3Addr) + isAuthorisedMN := engine.IsAuthorisedAddress(blockchain, block449.Header(), acc3Addr) assert.True(t, isAuthorisedMN) - isAuthorisedMN = engine.IsAuthorisedAddress(block449.Header(), blockchain, acc1Addr) + isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block449.Header(), acc1Addr) assert.False(t, isAuthorisedMN) // Now, let's mine another block to trigger the GAP block signerList update @@ -62,9 +62,45 @@ func TestIsAuthorisedMNForConsensusV1(t *testing.T) { t.Fatal(err) } - isAuthorisedMN = engine.IsAuthorisedAddress(block450.Header(), blockchain, acc3Addr) + isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc3Addr) assert.False(t, isAuthorisedMN) - isAuthorisedMN = engine.IsAuthorisedAddress(block450.Header(), blockchain, acc1Addr) + isAuthorisedMN = engine.IsAuthorisedAddress(blockchain, block450.Header(), acc1Addr) + assert.True(t, isAuthorisedMN) +} + +func TestIsAuthorisedMNForConsensusV2(t *testing.T) { + // we skip test for v1 since it's hard to make a real genesis block + blockchain, _, currentBlock, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 10, params.TestXDPoSMockChainConfigWithV2Engine, 0) + adaptor := blockchain.Engine().(*XDPoS.XDPoS) + blockNum := 11 + blockCoinBase := "0x111000000000000000000000000000000123" + blockHeader := createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, 1, blockCoinBase, signer, signFn) + // it contains 3 master nodes + // xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1 + // xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff + // xdc065551F0dcAC6f00CAe11192D462db709bE3758c + blockHeader.Validators = common.Hex2Bytes("0278c350152e15fa6ffc712a5a73d704ce73e2e103d9e17ae3ff2c6712e44e25b09ac5ee91f6c9ff065551f0dcac6f00cae11192d462db709be3758c") + // block 11 is the first v2 block, and is treated as epoch switch block + currentBlock, err := insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + + // the first block will start from 1 + isAuthorisedMN := adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff")) + assert.True(t, isAuthorisedMN) + // The third address hence not valid + isAuthorisedMN = adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c")) + assert.False(t, isAuthorisedMN) + + for blockNum = 12; blockNum < 16; blockNum++ { + blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn) + currentBlock, err = insertBlock(blockchain, blockHeader) + if err != nil { + t.Fatal(err) + } + } + isAuthorisedMN = adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c")) assert.True(t, isAuthorisedMN) } diff --git a/consensus/tests/test_helper.go b/consensus/tests/test_helper.go index 746c603ef5..51fb51bae1 100644 --- a/consensus/tests/test_helper.go +++ b/consensus/tests/test_helper.go @@ -262,6 +262,10 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params if err != nil { t.Fatal(err) } + go func() { + checkpointChanMsg := <-core.CheckpointCh + log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg) + }() return blockchain, backend, currentBlock, signer } @@ -320,6 +324,10 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon if err != nil { t.Fatal(err) } + go func() { + checkpointChanMsg := <-core.CheckpointCh + log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg) + }() return blockchain, backend, currentBlock, signer, signFn, currentForkBlock } @@ -385,13 +393,13 @@ func createBlock(chainConfig *params.ChainConfig, startingBlock *types.Block, bl return header } -func generateSignature(backend *backends.SimulatedBackend, header *types.Header) error { +func generateSignature(backend *backends.SimulatedBackend, adaptor *XDPoS.XDPoS, header *types.Header) error { signer, signFn, err := backends.SimulateWalletAddressAndSignFn() if err != nil { panic(fmt.Errorf("Error while creating simulated wallet for generating singer address and signer fn: %v", err)) } - signature, err := signFn(accounts.Account{Address: signer}, utils.SigHashV2(header).Bytes()) + signature, err := signFn(accounts.Account{Address: signer}, adaptor.SigHash(header).Bytes()) if err != nil { return err } @@ -488,7 +496,7 @@ func createXDPoSTestBlock(bc *BlockChain, customHeader *types.Header, txs []*typ Time: big.NewInt(customHeader.Number.Int64() * 10), Extra: customHeader.Extra, Validator: customHeader.Validator, - Validators: customHeader.Validators, + Validators: customHeader.Validators, } var block *types.Block if len(txs) == 0 { diff --git a/contracts/utils.go b/contracts/utils.go index 66ec359163..d521049884 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -352,8 +352,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header } } header = chain.GetHeader(header.ParentHash, prevCheckpoint) - //TODO: i think this should be c.GetMasternodesFrom... - masternodes := utils.GetMasternodesFromCheckpointHeader(header) + masternodes := c.GetMasternodesFromCheckpointHeader(header) for i := startBlockNumber; i <= endBlockNumber; i++ { if i%common.MergeSignRange == 0 || !chain.Config().IsTIP2019(big.NewInt(int64(i))) { diff --git a/core/block_validator_test.go b/core/block_validator_test.go index d1f7086ba0..14c43c8d0e 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -17,11 +17,12 @@ package core import ( - "github.com/XinFinOrg/XDPoSChain/core/rawdb" "runtime" "testing" "time" + "github.com/XinFinOrg/XDPoSChain/core/rawdb" + "github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/vm" diff --git a/core/blockchain.go b/core/blockchain.go index b90b0fe372..2882ce25f4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1590,8 +1590,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty bc.reportBlock(block, nil, err) return i, events, coalescedLogs, err } - if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 { - if err := tradingService.UpdateMediumPriceBeforeEpoch(block.NumberU64()/bc.chainConfig.XDPoS.Epoch, tradingState, statedb); err != nil { + isEpochSwithBlock, epochNumber, err := engine.IsEpochSwitch(block.Header()) + if err != nil { + log.Error("[insertChain] Error while checking if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number()) + bc.reportBlock(block, nil, err) + } + if isEpochSwithBlock { + if err := tradingService.UpdateMediumPriceBeforeEpoch(epochNumber, tradingState, statedb); err != nil { return i, events, coalescedLogs, err } } else { @@ -1708,9 +1713,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty stats.report(chain, i, dirty) if bc.chainConfig.XDPoS != nil { // epoch block - if (chain[i].NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 { + isEpochSwithBlock, _, err := engine.IsEpochSwitch(chain[i].Header()) + if err != nil { + log.Error("[insertChain] Error while checking and notifying channel CheckpointCh if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number()) + bc.reportBlock(block, nil, err) + } + if isEpochSwithBlock { CheckpointCh <- 1 - } } } @@ -1855,8 +1864,15 @@ func (bc *BlockChain) getResultBlock(block *types.Block, verifiedM2 bool) (*Resu bc.reportBlock(block, nil, err) return nil, err } - if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 { - if err := tradingService.UpdateMediumPriceBeforeEpoch(block.NumberU64()/bc.chainConfig.XDPoS.Epoch, tradingState, statedb); err != nil { + + isEpochSwithBlock, epochNumber, err := engine.IsEpochSwitch(block.Header()) + if err != nil { + log.Error("[getResultBlock] Error while checking block is epoch switch block", "Hash", block.Hash(), "Number", block.Number()) + bc.reportBlock(block, nil, err) + } + + if isEpochSwithBlock { + if err := tradingService.UpdateMediumPriceBeforeEpoch(epochNumber, tradingState, statedb); err != nil { return nil, err } } else { @@ -2029,7 +2045,12 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L stats.report(types.Blocks{block}, 0, dirty) if bc.chainConfig.XDPoS != nil { // epoch block - if (block.NumberU64() % bc.chainConfig.XDPoS.Epoch) == 0 { + isEpochSwithBlock, _, err := bc.Engine().(*XDPoS.XDPoS).IsEpochSwitch(block.Header()) + if err != nil { + log.Error("[insertBlock] Error while checking if the incoming block is epoch switch block", "Hash", block.Hash(), "Number", block.Number()) + bc.reportBlock(block, nil, err) + } + if isEpochSwithBlock { CheckpointCh <- 1 } diff --git a/eth/api_backend.go b/eth/api_backend.go index 9285afbac5..7255c826bc 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -303,7 +303,17 @@ func (b *EthApiBackend) GetVotersRewards(masternodeAddr common.Address) map[comm number := block.Number().Uint64() engine := b.GetEngine().(*XDPoS.XDPoS) foundationWalletAddr := chain.Config().XDPoS.FoudationWalletAddr - lastCheckpointNumber := number - (number % b.ChainConfig().XDPoS.Epoch) - b.ChainConfig().XDPoS.Epoch // calculate for 2 epochs ago + + // calculate for 2 epochs ago + currentCheckpointNumber, _, err := engine.GetCurrentEpochSwitchBlock(chain, block.Number()) + if err != nil { + log.Error("[GetVotersRewards] Fail to get GetCurrentEpochSwitchBlock for current checkpoint block", "block", block) + } + lastCheckpointNumber, _, err := engine.GetCurrentEpochSwitchBlock(chain, big.NewInt(int64(currentCheckpointNumber-1))) + if err != nil { + log.Error("[GetVotersRewards] Fail to get GetCurrentEpochSwitchBlock for last checkpoint block", "block", block) + } + lastCheckpointBlock := chain.GetBlockByNumber(lastCheckpointNumber) rCheckpoint := chain.Config().XDPoS.RewardCheckpoint diff --git a/eth/backend.go b/eth/backend.go index e0fbf2ffb8..a2451606cb 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -267,7 +267,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS return block, false, err } header := block.Header() - sighash, err := wallet.SignHash(accounts.Account{Address: eb}, XDPoS.SigHash(header).Bytes()) + sighash, err := wallet.SignHash(accounts.Account{Address: eb}, c.SigHash(header).Bytes()) if err != nil || sighash == nil { log.Error("Can't get signature hash of m2", "sighash", sighash, "err", err) return block, false, err @@ -296,7 +296,7 @@ func New(ctx *node.ServiceContext, config *Config, XDCXServ *XDCx.XDCX, lendingS // not genesis block header = parentHeader } - return c.IsAuthorisedAddress(header, eth.blockchain, address) + return c.IsAuthorisedAddress(eth.blockchain, header, address) } } @@ -364,7 +364,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, config *ethash.Config, chai // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *Ethereum) APIs() []rpc.API { - apis := ethapi.GetAPIs(s.ApiBackend) + apis := ethapi.GetAPIs(s.ApiBackend, s.BlockChain()) // Append any APIs exposed explicitly by the consensus engine apis = append(apis, s.engine.APIs(s.BlockChain())...) @@ -464,7 +464,7 @@ func (s *Ethereum) ValidateMasternode() (bool, error) { //check if miner's wallet is in set of validators c := s.engine.(*XDPoS.XDPoS) - authorized := c.IsAuthorisedAddress(s.blockchain.CurrentHeader(), s.blockchain, eb) + authorized := c.IsAuthorisedAddress(s.blockchain, s.blockchain.CurrentHeader(), eb) if !authorized { //This miner doesn't belong to set of validators return false, nil diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 0f64231d3c..31a78f7e40 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -27,6 +27,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" + "github.com/XinFinOrg/XDPoSChain/consensus" "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" @@ -497,12 +498,16 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args Sen // PublicBlockChainAPI provides an API to access the Ethereum blockchain. // It offers only methods that operate on public data that is freely available to anyone. type PublicBlockChainAPI struct { - b Backend + b Backend + chainReader consensus.ChainReader } // NewPublicBlockChainAPI creates a new Ethereum blockchain API. -func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { - return &PublicBlockChainAPI{b} +func NewPublicBlockChainAPI(b Backend, chainReader consensus.ChainReader) *PublicBlockChainAPI { + return &PublicBlockChainAPI{ + b, + chainReader, + } } // BlockNumber returns the block number of the chain head. @@ -694,11 +699,7 @@ func (s *PublicBlockChainAPI) GetMasternodes(ctx context.Context, b *types.Block } if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { // Get block epoc latest. - lastCheckpointNumber := prevBlockNumber - (prevBlockNumber % s.b.ChainConfig().XDPoS.Epoch) - prevCheckpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(lastCheckpointNumber)) - if prevCheckpointBlock != nil { - masternodes = engine.GetMasternodesFromCheckpointHeader(prevCheckpointBlock.Header(), curBlockNumber, s.b.ChainConfig().XDPoS.Epoch) - } + return engine.GetMasternodesByNumber(s.chainReader, prevBlockNumber), nil } else { log.Error("Undefined XDPoS consensus engine") } @@ -791,7 +792,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd // Second, Find candidates that have masternode status if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { - masternodes = engine.GetMasternodesFromCheckpointHeader(header, block.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch) + masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes), "blockNum", header.Number.Uint64()) result[fieldSuccess] = false @@ -902,7 +903,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch // Second, Find candidates that have masternode status if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { - masternodes = engine.GetMasternodesFromCheckpointHeader(header, block.Number().Uint64(), s.b.ChainConfig().XDPoS.Epoch) + masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { log.Error("Failed to get masternodes", "err", err, "len(masternodes)", len(masternodes), "blockNum", header.Number.Uint64()) result[fieldSuccess] = false @@ -965,21 +966,22 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch // GetPreviousCheckpointFromEpoch returns header of the previous checkpoint func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context, epochNum rpc.EpochNumber) (rpc.BlockNumber, rpc.EpochNumber) { var checkpointNumber uint64 - epoch := s.b.ChainConfig().XDPoS.Epoch + currentCheckpointNumber, epochNumber, err := s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, s.b.CurrentBlock().Number()) + if err != nil { + log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get current epoch switch block information", "Block", s.b.CurrentBlock(), "Error", err) + } if epochNum == rpc.LatestEpochNumber { - blockNumer := s.b.CurrentBlock().Number().Uint64() - diff := blockNumer % epoch - // checkpoint number - checkpointNumber = blockNumer - diff - epochNum = rpc.EpochNumber(checkpointNumber / epoch) - if diff > 0 { - epochNum += 1 - } + checkpointNumber = currentCheckpointNumber + epochNum = rpc.EpochNumber(epochNumber) } else if epochNum < 2 { checkpointNumber = 0 } else { - checkpointNumber = epoch * (uint64(epochNum) - 1) + blockNumberBeforeCurrentEpochSwitch := currentCheckpointNumber - 1 + checkpointNumber, _, err = s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(blockNumberBeforeCurrentEpochSwitch))) + if err != nil { + log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get last epoch switch block information", "Number", blockNumberBeforeCurrentEpochSwitch, "Error", err) + } } return rpc.BlockNumber(checkpointNumber), epochNum } @@ -1301,7 +1303,11 @@ func (s *PublicBlockChainAPI) findNearestSignedBlock(ctx context.Context, b *typ } // Get block epoc latest - checkpointNumber := signedBlockNumber - (signedBlockNumber % s.b.ChainConfig().XDPoS.Epoch) + checkpointNumber, _, err := s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(signedBlockNumber))) + if err != nil { + log.Error("[findNearestSignedBlock] Error while trying to get current Epoch switch block", "Number", signedBlockNumber) + } + checkpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(checkpointNumber)) if checkpointBlock != nil { @@ -1385,13 +1391,9 @@ func (s *PublicBlockChainAPI) getSigners(ctx context.Context, block *types.Block var err error var filterSigners []common.Address var signers []common.Address - blockNumber := block.Number().Uint64() - // Get block epoc latest. - checkpointNumber := blockNumber - (blockNumber % s.b.ChainConfig().XDPoS.Epoch) - checkpointBlock, _ := s.b.BlockByNumber(ctx, rpc.BlockNumber(checkpointNumber)) + masternodes := engine.GetMasternodes(s.chainReader, block.Header()) - masternodes := engine.GetMasternodesFromCheckpointHeader(checkpointBlock.Header(), blockNumber, s.b.ChainConfig().XDPoS.Epoch) signers, err = GetSignersFromBlocks(s.b, block.NumberU64(), block.Hash(), masternodes) if err != nil { log.Error("Fail to get signers from block signer SC.", "error", err) diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 7f2d297baf..34b41b71c3 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -97,7 +97,7 @@ type Backend interface { GetOrderNonce(address common.Hash) (uint64, error) } -func GetAPIs(apiBackend Backend) []rpc.API { +func GetAPIs(apiBackend Backend, chainReader consensus.ChainReader) []rpc.API { nonceLock := new(AddrLocker) return []rpc.API{ { @@ -108,7 +108,7 @@ func GetAPIs(apiBackend Backend) []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: NewPublicBlockChainAPI(apiBackend), + Service: NewPublicBlockChainAPI(apiBackend, chainReader), Public: true, }, { Namespace: "eth", diff --git a/les/backend.go b/les/backend.go index 3eced66bcd..b172e8fc00 100644 --- a/les/backend.go +++ b/les/backend.go @@ -176,7 +176,7 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. func (s *LightEthereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{ + return append(ethapi.GetAPIs(s.ApiBackend, nil), []rpc.API{ { Namespace: "eth", Version: "1.0", diff --git a/miner/worker.go b/miner/worker.go index b77b93db29..16b1bf5d77 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -381,7 +381,11 @@ func (self *worker) wait() { } if work.config.XDPoS != nil { // epoch block - if (block.NumberU64() % work.config.XDPoS.Epoch) == 0 { + isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(block.Header()) + if err != nil { + log.Error("[wait] fail to check if block is epoch switch block when worker waiting", "BlockNum", block.Number(), "Hash", block.Hash()) + } + if isEpochSwitchBlock { core.CheckpointCh <- 1 } } @@ -402,7 +406,7 @@ func (self *worker) wait() { log.Error("[wait] Unable to handle new proposed block", "err", err, "number", block.Number(), "hash", block.Hash()) } - authorized := c.IsAuthorisedAddress(block.Header(), self.chain, self.coinbase) + authorized := c.IsAuthorisedAddress(self.chain, block.Header(), self.coinbase) if !authorized { valid := false masternodes := c.GetMasternodes(self.chain, block.Header()) @@ -635,13 +639,19 @@ func (self *worker) commitNewWork() { lendingFinalizedTradeTransaction *types.Transaction ) feeCapacity := state.GetTRC21FeeCapacityFromStateWithCache(parent.Root(), work.state) - if self.config.XDPoS != nil && header.Number.Uint64()%self.config.XDPoS.Epoch != 0 { - pending, err := self.eth.TxPool().Pending() + if self.config.XDPoS != nil { + isEpochSwitchBlock, _, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) if err != nil { - log.Error("Failed to fetch pending transactions", "err", err) - return + log.Error("[commitNewWork] fail to check if block is epoch switch block when fetching pending transactions", "BlockNum", header.Number, "Hash", header.Hash()) + } + if !isEpochSwitchBlock { + pending, err := self.eth.TxPool().Pending() + if err != nil { + log.Error("Failed to fetch pending transactions", "err", err) + return + } + txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity) } - txs, specialTxs = types.NewTransactionsByPriceAndNonce(self.current.signer, pending, signers, feeCapacity) } if atomic.LoadInt32(&self.mining) == 1 { wallet, err := self.eth.AccountManager().Find(accounts.Account{Address: self.coinbase}) @@ -653,16 +663,20 @@ func (self *worker) commitNewWork() { XDCX := self.eth.GetXDCX() XDCXLending := self.eth.GetXDCXLending() if XDCX != nil && header.Number.Uint64() > self.config.XDPoS.Epoch { - if header.Number.Uint64()%self.config.XDPoS.Epoch == 0 { - err := XDCX.UpdateMediumPriceBeforeEpoch(header.Number.Uint64()/self.config.XDPoS.Epoch, work.tradingState, work.state) + isEpochSwitchBlock, epochNumber, err := self.engine.(*XDPoS.XDPoS).IsEpochSwitch(header) + if err != nil { + log.Error("[commitNewWork] fail to check if block is epoch switch block when performing XDCX and XDCXLending operations", "BlockNum", header.Number, "Hash", header.Hash()) + } + + if isEpochSwitchBlock { + err := XDCX.UpdateMediumPriceBeforeEpoch(epochNumber, work.tradingState, work.state) if err != nil { log.Error("Fail when update medium price last epoch", "error", err) return } - } - // won't grasp tx at checkpoint - //https://github.com/XinFinOrg/XDPoSChain-v1/pull/416 - if header.Number.Uint64()%self.config.XDPoS.Epoch != 0 { + } else { + // won't grasp tx at checkpoint + //https://github.com/XinFinOrg/XDPoSChain-v1/pull/416 log.Debug("Start processing order pending") tradingOrderPending, _ := self.eth.OrderPool().Pending() log.Debug("Start processing order pending", "len", len(tradingOrderPending)) @@ -680,6 +694,7 @@ func (self *worker) commitNewWork() { } } } + if len(tradingTxMatches) > 0 { txMatchBatch := &tradingstate.TxMatchBatch{ Data: tradingTxMatches, @@ -1062,10 +1077,16 @@ func (env *Work) commitTransactions(mux *event.TypeMux, balanceFee map[common.Ad } go func(logs []*types.Log, tcount int) { if len(logs) > 0 { - mux.Post(core.PendingLogsEvent{Logs: logs}) + err := mux.Post(core.PendingLogsEvent{Logs: logs}) + if err != nil { + log.Warn("[commitTransactions] Error when sending PendingLogsEvent", "LogLength", len(logs)) + } } if tcount > 0 { - mux.Post(core.PendingStateEvent{}) + err := mux.Post(core.PendingStateEvent{}) + if err != nil { + log.Warn("[commitTransactions] Error when sending PendingStateEvent", "tcount", tcount) + } } }(cpy, env.tcount) }