mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
Add v2 yourturn (#38)
* Add v2 yourturn * add isEpochSwitchByRound into YourTurn Co-authored-by: Gerui Wang <wgr523@gmail.com>
This commit is contained in:
parent
f8d3f9f8c6
commit
38c3582841
3 changed files with 145 additions and 63 deletions
|
|
@ -319,41 +319,55 @@ func (x *XDPoS_v2) calcDifficulty(chain consensus.ChainReader, parent *types.Hea
|
|||
return big.NewInt(1)
|
||||
}
|
||||
|
||||
// Copy from v1
|
||||
// Check if it's my turm to mine a block. Note: The second return value `preIndex` is useless in V2 engine
|
||||
func (x *XDPoS_v2) YourTurn(chain consensus.ChainReader, parent *types.Header, signer common.Address) (int, int, int, bool, error) {
|
||||
snap, err := x.GetSnapshot(chain, parent)
|
||||
x.lock.RLock()
|
||||
defer x.lock.RUnlock()
|
||||
|
||||
round := x.currentRound
|
||||
isEpochSwitch, _, err := x.IsEpochSwitchAtRound(round, parent)
|
||||
if err != nil {
|
||||
log.Error("[YourTurn] Failed while getting snapshot", "parentHash", parent.Hash(), "err", err)
|
||||
log.Error("[YourTurn]", "Error", err)
|
||||
return 0, -1, -1, false, err
|
||||
}
|
||||
masternodes := x.GetMasternodes(chain, parent)
|
||||
if len(masternodes) == 0 {
|
||||
var masterNodes []common.Address
|
||||
if isEpochSwitch {
|
||||
if x.config.XDPoSV2Block.Cmp(parent.Number) == 0 {
|
||||
// TODO: read v1 master nodes
|
||||
} else {
|
||||
// TODO: calc master nodes by smart contract - penalty
|
||||
// TODO: related to snapshot
|
||||
}
|
||||
} else {
|
||||
// this block and parent belong to the same epoch
|
||||
masterNodes = x.GetMasternodes(chain, parent)
|
||||
}
|
||||
|
||||
if len(masterNodes) == 0 {
|
||||
log.Error("[YourTurn] Fail to find any master nodes from current block round epoch", "Hash", parent.Hash(), "CurrentRound", round, "Number", parent.Number)
|
||||
return 0, -1, -1, false, errors.New("Masternodes not found")
|
||||
}
|
||||
pre := common.Address{}
|
||||
// masternode[0] has chance to create block 1
|
||||
preIndex := -1
|
||||
if parent.Number.Uint64() != 0 {
|
||||
pre, err = whoIsCreator(snap, parent)
|
||||
if err != nil {
|
||||
return 0, 0, 0, false, err
|
||||
}
|
||||
preIndex = utils.Position(masternodes, pre)
|
||||
}
|
||||
curIndex := utils.Position(masternodes, signer)
|
||||
leaderIndex := uint64(round) % x.config.Epoch % uint64(len(masterNodes))
|
||||
|
||||
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)
|
||||
log.Debug("[YourTurn] masterNodes cycle info", "number of masternodes", len(masterNodes), "current", signer, "position", curIndex, "parentBlock", parent)
|
||||
}
|
||||
for i, s := range masternodes {
|
||||
log.Debug("Masternode:", "index", i, "address", s.String())
|
||||
for i, s := range masterNodes {
|
||||
log.Debug("[YourTurn] Masternode:", "index", i, "address", s.String(), "parentBlockNum", parent.Number)
|
||||
}
|
||||
if (preIndex+1)%len(masternodes) == curIndex {
|
||||
return len(masternodes), preIndex, curIndex, true, nil
|
||||
|
||||
if masterNodes[leaderIndex] == signer {
|
||||
return len(masterNodes), -1, curIndex, true, nil
|
||||
}
|
||||
return len(masternodes), preIndex, curIndex, false, nil
|
||||
log.Warn("[YourTurn] Not authorised signer", "signer", signer, "MN", masterNodes, "Hash", parent.Hash(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "signer", signer)
|
||||
return len(masterNodes), -1, curIndex, false, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *types.Header, address common.Address) bool {
|
||||
x.lock.RLock()
|
||||
defer x.lock.RUnlock()
|
||||
|
||||
var extraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(header.Extra, &extraField)
|
||||
if err != nil {
|
||||
|
|
@ -368,24 +382,16 @@ func (x *XDPoS_v2) IsAuthorisedAddress(chain consensus.ChainReader, header *type
|
|||
log.Error("[IsAuthorisedAddress] Fail to find any master nodes from current block round epoch", "Hash", header.Hash(), "Round", blockRound, "Number", header.Number)
|
||||
return false
|
||||
}
|
||||
leaderIndex := uint64(blockRound) % x.config.Epoch % uint64(len(masterNodes))
|
||||
if masterNodes[leaderIndex] == address {
|
||||
return true
|
||||
// leaderIndex := uint64(blockRound) % x.config.Epoch % uint64(len(masterNodes))
|
||||
for index, masterNodeAddress := range masterNodes {
|
||||
if masterNodeAddress == address {
|
||||
log.Debug("[IsAuthorisedAddress] Found matching master node address", "index", index, "Address", address, "MasterNodes", masterNodes)
|
||||
return true
|
||||
}
|
||||
}
|
||||
log.Warn("Not authorised address", "Address", address, "MN", masterNodes, "Hash", header.Hash(), "masterNodes[leaderIndex]", masterNodes[leaderIndex], "Address", address)
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy from v1
|
||||
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 := ecrecover(header, snap.sigcache)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return m, nil
|
||||
log.Warn("Not authorised address", "Address", address, "MN", masterNodes, "Hash", header.Hash())
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy from v1
|
||||
|
|
@ -1102,6 +1108,24 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
|
|||
return parentRound < epochStart, epochNum, nil
|
||||
}
|
||||
|
||||
// IsEpochSwitchAtRound() is used by miner to check whether it mines a block in the same epoch with parent
|
||||
func (x *XDPoS_v2) IsEpochSwitchAtRound(round utils.Round, parentHeader *types.Header) (bool, uint64, error) {
|
||||
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 parentHeader.Number.Cmp(x.config.XDPoSV2Block) == 0 {
|
||||
return true, epochNum, nil
|
||||
}
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
log.Error("[IsEpochSwitch] decode header error", "err", err, "header", parentHeader, "extra", common.Bytes2Hex(parentHeader.Extra))
|
||||
return false, 0, err
|
||||
}
|
||||
parentRound := decodedExtraField.Round
|
||||
epochStart := round - round%utils.Round(x.config.Epoch)
|
||||
return parentRound < epochStart, epochNum, nil
|
||||
}
|
||||
|
||||
// Given header and its hash, get epoch switch info from the epoch switch block of that epoch,
|
||||
// header is allow to be nil.
|
||||
func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types.Header, hash common.Hash) (*utils.EpochSwitchInfo, error) {
|
||||
|
|
|
|||
|
|
@ -87,20 +87,74 @@ func TestIsAuthorisedMNForConsensusV2(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// the first block will start from 1
|
||||
// As long as the address is in the master node list, they are all valid
|
||||
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)
|
||||
|
||||
isAuthorisedMN = adaptor.IsAuthorisedAddress(blockchain, currentBlock.Header(), common.HexToAddress("xdcbanana"))
|
||||
assert.False(t, isAuthorisedMN)
|
||||
}
|
||||
|
||||
func TestIsYourTurnConsensusV2(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 address is valid
|
||||
numberOfMN, _, curIndex, isYourTurn, err := adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, numberOfMN)
|
||||
assert.Equal(t, 0, curIndex)
|
||||
assert.True(t, isYourTurn)
|
||||
|
||||
// The second and third address are not valid
|
||||
numberOfMN, _, curIndex, isYourTurn, err = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, numberOfMN)
|
||||
assert.Equal(t, 1, curIndex)
|
||||
assert.False(t, isYourTurn)
|
||||
numberOfMN, _, curIndex, isYourTurn, err = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c"))
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 3, numberOfMN)
|
||||
assert.Equal(t, 2, curIndex)
|
||||
assert.False(t, isYourTurn)
|
||||
|
||||
// We continue to grow the chain which will increase the round number
|
||||
blockNum = 12
|
||||
blockHeader = createBlock(params.TestXDPoSMockChainConfigWithV2Engine, currentBlock, blockNum, int64(blockNum-10), blockCoinBase, signer, signFn)
|
||||
currentBlock, err = insertBlock(blockchain, blockHeader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
adaptor.EngineV2.SetNewRoundFaker(1, false)
|
||||
_, _, curIndex, isYourTurn, _ = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc0278C350152e15fa6FFC712a5A73D704Ce73E2E1"))
|
||||
assert.Equal(t, 0, curIndex)
|
||||
assert.False(t, isYourTurn)
|
||||
|
||||
_, _, curIndex, isYourTurn, _ = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc03d9e17Ae3fF2c6712E44e25B09Ac5ee91f6c9ff"))
|
||||
assert.Equal(t, 1, curIndex)
|
||||
assert.True(t, isYourTurn)
|
||||
|
||||
_, _, curIndex, isYourTurn, _ = adaptor.YourTurn(blockchain, currentBlock.Header(), common.HexToAddress("xdc065551F0dcAC6f00CAe11192D462db709bE3758c"))
|
||||
assert.Equal(t, 2, curIndex)
|
||||
assert.False(t, isYourTurn)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -967,23 +967,27 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch
|
|||
func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context, epochNum rpc.EpochNumber) (rpc.BlockNumber, rpc.EpochNumber) {
|
||||
var checkpointNumber uint64
|
||||
|
||||
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 {
|
||||
checkpointNumber = currentCheckpointNumber
|
||||
epochNum = rpc.EpochNumber(epochNumber)
|
||||
} else if epochNum < 2 {
|
||||
checkpointNumber = 0
|
||||
} else {
|
||||
blockNumberBeforeCurrentEpochSwitch := currentCheckpointNumber - 1
|
||||
checkpointNumber, _, err = s.b.GetEngine().(*XDPoS.XDPoS).GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(blockNumberBeforeCurrentEpochSwitch)))
|
||||
if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok {
|
||||
currentCheckpointNumber, epochNumber, err := engine.GetCurrentEpochSwitchBlock(s.chainReader, s.b.CurrentBlock().Number())
|
||||
if err != nil {
|
||||
log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get last epoch switch block information", "Number", blockNumberBeforeCurrentEpochSwitch, "Error", err)
|
||||
log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get current epoch switch block information", "Block", s.b.CurrentBlock(), "Error", err)
|
||||
}
|
||||
if epochNum == rpc.LatestEpochNumber {
|
||||
checkpointNumber = currentCheckpointNumber
|
||||
epochNum = rpc.EpochNumber(epochNumber)
|
||||
} else if epochNum < 2 {
|
||||
checkpointNumber = 0
|
||||
} else {
|
||||
blockNumberBeforeCurrentEpochSwitch := currentCheckpointNumber - 1
|
||||
checkpointNumber, _, err = engine.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
|
||||
} else {
|
||||
panic("[GetPreviousCheckpointFromEpoch] Error while trying to get XDPoS consensus engine")
|
||||
}
|
||||
return rpc.BlockNumber(checkpointNumber), epochNum
|
||||
}
|
||||
|
||||
// getCandidatesFromSmartContract returns all candidates with their capacities at the current time
|
||||
|
|
|
|||
Loading…
Reference in a new issue