mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-20 13:44:31 +00:00
implement verify functions (#43)
* implement verify functions * Use go routine to call verifyMsgSignature * verify timeout msg shall use round from the msg * add comment for verifyQC
This commit is contained in:
parent
646042a651
commit
e063f67f34
4 changed files with 153 additions and 48 deletions
|
|
@ -483,14 +483,14 @@ func (x *XDPoS_v2) GetTimeoutPoolSize(timeout *utils.Timeout) int {
|
|||
SyncInfo workflow
|
||||
*/
|
||||
// Verify syncInfo and trigger process QC or TC if successful
|
||||
func (x *XDPoS_v2) VerifySyncInfoMessage(syncInfo *utils.SyncInfo) error {
|
||||
func (x *XDPoS_v2) VerifySyncInfoMessage(chain consensus.ChainReader, syncInfo *utils.SyncInfo) error {
|
||||
/*
|
||||
1. Verify items including:
|
||||
- verifyQC
|
||||
- verifyTC
|
||||
2. Broadcast(Not part of consensus)
|
||||
*/
|
||||
err := x.verifyQC(syncInfo.HighestQuorumCert)
|
||||
err := x.verifyQC(chain, syncInfo.HighestQuorumCert)
|
||||
if err != nil {
|
||||
log.Warn("SyncInfo message verification failed due to QC", err)
|
||||
return err
|
||||
|
|
@ -520,16 +520,21 @@ func (x *XDPoS_v2) SyncInfoHandler(chain consensus.ChainReader, syncInfo *utils.
|
|||
/*
|
||||
Vote workflow
|
||||
*/
|
||||
func (x *XDPoS_v2) VerifyVoteMessage(vote *utils.Vote) (bool, error) {
|
||||
func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *utils.Vote) (bool, error) {
|
||||
/*
|
||||
1. Check signature:
|
||||
1. Get masterNode list belong to this epoch by hash
|
||||
2. Check signature:
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node list(For the running epoch)
|
||||
2. Verify blockInfo
|
||||
3. Broadcast(Not part of consensus)
|
||||
- Use the above xdc address to check against the master node list from step 1(For the running epoch)
|
||||
3. Verify blockInfo
|
||||
4. Broadcast(Not part of consensus)
|
||||
*/
|
||||
return x.verifyMsgSignature(utils.VoteSigHash(vote.ProposedBlockInfo), vote.Signature)
|
||||
epochInfo, err := x.getEpochSwitchInfo(chain, nil, vote.ProposedBlockInfo.Hash)
|
||||
if err != nil {
|
||||
log.Error("[VerifyVoteMessage] Error when getting epoch switch Info to verify vote message", "Error", err)
|
||||
}
|
||||
return x.verifyMsgSignature(utils.VoteSigHash(vote.ProposedBlockInfo), vote.Signature, epochInfo.Masternodes)
|
||||
}
|
||||
|
||||
// Consensus entry point for processing vote message to produce QC
|
||||
|
|
@ -591,14 +596,17 @@ func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, poole
|
|||
*/
|
||||
// Verify timeout message type from peers in bft.go
|
||||
/*
|
||||
1. Check signature:
|
||||
1. Get master node list by timeout msg round
|
||||
2. Check signature:
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node(For the running epoch)
|
||||
2. Broadcast(Not part of consensus)
|
||||
- Use the above xdc address to check against the master node list from step 1(For the running epoch)
|
||||
3. Broadcast(Not part of consensus)
|
||||
*/
|
||||
func (x *XDPoS_v2) VerifyTimeoutMessage(timeoutMsg *utils.Timeout) (bool, error) {
|
||||
return x.verifyMsgSignature(utils.TimeoutSigHash(&timeoutMsg.Round), timeoutMsg.Signature)
|
||||
func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg *utils.Timeout) (bool, error) {
|
||||
|
||||
masternodes := x.GetMasternodesAtRound(chain, timeoutMsg.Round, chain.CurrentHeader())
|
||||
return x.verifyMsgSignature(utils.TimeoutSigHash(&timeoutMsg.Round), timeoutMsg.Signature, masternodes)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -689,7 +697,7 @@ func (x *XDPoS_v2) ProposedBlockHandler(blockChainReader consensus.ChainReader,
|
|||
quorumCert := decodedExtraField.QuorumCert
|
||||
round := decodedExtraField.Round
|
||||
|
||||
err = x.verifyQC(quorumCert)
|
||||
err = x.verifyQC(blockChainReader, quorumCert)
|
||||
if err != nil {
|
||||
log.Error("[ProposedBlockHandler] Fail to verify QC", "Extra round", round, "QC proposed BlockInfo Hash", quorumCert.ProposedBlockInfo.Hash)
|
||||
return err
|
||||
|
|
@ -728,23 +736,57 @@ func (x *XDPoS_v2) VerifyBlockInfo(blockInfo *utils.BlockInfo) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyQC(quorumCert *utils.QuorumCert) error {
|
||||
func (x *XDPoS_v2) verifyQC(blockChainReader consensus.ChainReader, quorumCert *utils.QuorumCert) error {
|
||||
/*
|
||||
1. Verify signer signatures: (List of signatures)
|
||||
1. Check if num of QC signatures is >= x.config.v2.CertThreshold
|
||||
2. Get epoch master node list by hash
|
||||
3. Verify signer signatures: (List of signatures)
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node list(For the received QC epoch)
|
||||
2. Verify blockInfo
|
||||
- Use the above xdc address to check against the master node list from step 1(For the received QC epoch)
|
||||
4. Verify blockInfo
|
||||
*/
|
||||
return nil
|
||||
epochInfo, err := x.getEpochSwitchInfo(blockChainReader, nil, quorumCert.ProposedBlockInfo.Hash)
|
||||
if err != nil {
|
||||
log.Error("[verifyQC] Error when getting epoch switch Info to verify QC", "Error", err)
|
||||
return fmt.Errorf("Fail to verify QC due to failure in getting epoch switch info")
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(quorumCert.Signatures))
|
||||
var haveError error
|
||||
|
||||
for _, signature := range quorumCert.Signatures {
|
||||
go func(sig utils.Signature) {
|
||||
defer wg.Done()
|
||||
verified, err := x.verifyMsgSignature(utils.VoteSigHash(quorumCert.ProposedBlockInfo), sig, epochInfo.Masternodes)
|
||||
if err != nil {
|
||||
log.Error("[verifyQC] Error while verfying QC message signatures", "Error", err)
|
||||
haveError = fmt.Errorf("Error while verfying QC message signatures")
|
||||
return
|
||||
}
|
||||
if !verified {
|
||||
log.Warn("[verifyQC] Signature not verified doing QC verification", "QC", quorumCert)
|
||||
haveError = fmt.Errorf("Fail to verify QC due to signature mis-match")
|
||||
return
|
||||
}
|
||||
}(signature)
|
||||
}
|
||||
wg.Wait()
|
||||
if haveError != nil {
|
||||
return haveError
|
||||
}
|
||||
|
||||
return x.VerifyBlockInfo(quorumCert.ProposedBlockInfo)
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyTC(timeoutCert *utils.TimeoutCert) error {
|
||||
/*
|
||||
1. Verify signer signature: (List of signatures)
|
||||
1. Get epoch master node list by round/number
|
||||
2. Verify signer signature: (List of signatures)
|
||||
- Use ecRecover to get the public key
|
||||
- Use the above public key to find out the xdc address
|
||||
- Use the above xdc address to check against the master node list(For the received TC epoch)
|
||||
- Use the above xdc address to check against the master node list from step 1(For the received TC epoch)
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
|
@ -917,7 +959,7 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (utils.Signature, erro
|
|||
return signedHash, nil
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature utils.Signature) (bool, error) {
|
||||
func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signature utils.Signature, masternodes []common.Address) (bool, error) {
|
||||
// Recover the public key and the Ethereum address
|
||||
pubkey, err := crypto.Ecrecover(signedHashToBeVerified.Bytes(), signature)
|
||||
if err != nil {
|
||||
|
|
@ -925,7 +967,6 @@ func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signat
|
|||
}
|
||||
var signerAddress common.Address
|
||||
copy(signerAddress[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
masternodes := x.getCurrentRoundMasterNodes()
|
||||
for _, mn := range masternodes {
|
||||
if mn == signerAddress {
|
||||
return true, nil
|
||||
|
|
@ -957,7 +998,7 @@ func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) {
|
|||
}()
|
||||
}
|
||||
|
||||
func (x *XDPoS_v2) getCurrentRoundMasterNodes() []common.Address {
|
||||
func (x *XDPoS_v2) GetMasternodesAtRound(chain consensus.ChainReader, round utils.Round, currentHeader *types.Header) []common.Address {
|
||||
return []common.Address{}
|
||||
}
|
||||
|
||||
|
|
@ -1076,6 +1117,12 @@ func (x *XDPoS_v2) GetMasternodesFromEpochSwitchHeader(epochSwitchHeader *types.
|
|||
}
|
||||
|
||||
func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) {
|
||||
// Return true directly if we are examing the last v1 block. This could happen if the calling function is examing parent block
|
||||
if header.Number.Cmp(x.config.XDPoSV2Block) == 0 {
|
||||
log.Info("[IsEpochSwitch] examing last v1 block 👯♂️")
|
||||
return true, header.Number.Uint64() / x.config.Epoch, nil
|
||||
}
|
||||
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(header.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
|
|
@ -1133,22 +1180,38 @@ func (x *XDPoS_v2) getEpochSwitchInfo(chain consensus.ChainReader, header *types
|
|||
}
|
||||
if isEpochSwitch {
|
||||
log.Debug("[getEpochSwitchInfo] header is epoch switch", "hash", hash.Hex(), "number", h.Number.Uint64())
|
||||
masternodes := x.GetMasternodesFromEpochSwitchHeader(h)
|
||||
// create the epoch switch info and cache it
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err = utils.DecodeBytesExtraFields(h.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epochSwitchInfo := &utils.EpochSwitchInfo{
|
||||
Masternodes: masternodes,
|
||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||
Hash: hash,
|
||||
Number: h.Number,
|
||||
Round: decodedExtraField.Round,
|
||||
},
|
||||
EpochSwitchParentBlockInfo: decodedExtraField.QuorumCert.ProposedBlockInfo,
|
||||
var epochSwitchInfo *utils.EpochSwitchInfo
|
||||
// Special case, in case of last v1 block, we manually build the epoch switch info
|
||||
if h.Number.Cmp(x.config.XDPoSV2Block) == 0 {
|
||||
masternodes := decodeMasternodesFromHeaderExtra(h)
|
||||
epochSwitchInfo = &utils.EpochSwitchInfo{
|
||||
Masternodes: masternodes,
|
||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||
Hash: hash,
|
||||
Number: h.Number,
|
||||
Round: utils.Round(0),
|
||||
},
|
||||
EpochSwitchParentBlockInfo: nil,
|
||||
}
|
||||
} else { // v2 normal flow
|
||||
masternodes := x.GetMasternodesFromEpochSwitchHeader(h)
|
||||
// create the epoch switch info and cache it
|
||||
var decodedExtraField utils.ExtraFields_v2
|
||||
err = utils.DecodeBytesExtraFields(h.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
epochSwitchInfo = &utils.EpochSwitchInfo{
|
||||
Masternodes: masternodes,
|
||||
EpochSwitchBlockInfo: &utils.BlockInfo{
|
||||
Hash: hash,
|
||||
Number: h.Number,
|
||||
Round: decodedExtraField.Round,
|
||||
},
|
||||
EpochSwitchParentBlockInfo: decodedExtraField.QuorumCert.ProposedBlockInfo,
|
||||
}
|
||||
}
|
||||
|
||||
x.epochSwitches.Add(hash, epochSwitchInfo)
|
||||
return epochSwitchInfo, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package engine_v2
|
|||
|
||||
import (
|
||||
"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"
|
||||
|
|
@ -56,4 +57,14 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
|
|||
|
||||
sigcache.Add(hash, signer)
|
||||
return signer, nil
|
||||
|
||||
}
|
||||
|
||||
// Get masternodes address from checkpoint Header. Only used for v1 last block
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func TestCountdownTimeoutToSendTimeoutMessage(t *testing.T) {
|
|||
assert.Equal(t, poolSize, 1)
|
||||
assert.NotNil(t, timeoutMsg)
|
||||
|
||||
valid, err := engineV2.VerifyTimeoutMessage(timeoutMsg.(*utils.Timeout))
|
||||
valid, err := engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg.(*utils.Timeout))
|
||||
// We can only test valid = false for now as the implementation for getCurrentRoundMasterNodes is not complete
|
||||
assert.False(t, valid)
|
||||
// This shows we are able to decode the timeout message, which is what this test is all about
|
||||
|
|
|
|||
|
|
@ -241,6 +241,11 @@ func PrepareXDCTestBlockChain(t *testing.T, numOfBlocks int, chainConfig *params
|
|||
|
||||
currentBlock := blockchain.Genesis()
|
||||
|
||||
go func() {
|
||||
checkpointChanMsg := <-core.CheckpointCh
|
||||
log.Info("[V1] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
|
||||
}()
|
||||
|
||||
// Insert initial blocks
|
||||
for i := 1; i <= numOfBlocks; i++ {
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||
|
|
@ -262,10 +267,6 @@ 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
|
||||
}
|
||||
|
|
@ -288,11 +289,45 @@ func PrepareXDCTestBlockChainForV2Engine(t *testing.T, numOfBlocks int, chainCon
|
|||
|
||||
var currentForkBlock *types.Block
|
||||
|
||||
go func() {
|
||||
checkpointChanMsg := <-core.CheckpointCh
|
||||
log.Info("[V2] Got a message from core CheckpointChan!", "msg", checkpointChanMsg)
|
||||
}()
|
||||
|
||||
var masternodesFromV1LastEpoch []common.Address
|
||||
|
||||
// Insert initial blocks
|
||||
for i := 1; i <= numOfBlocks; i++ {
|
||||
blockCoinBase := fmt.Sprintf("0x111000000000000000000000000000000%03d", i)
|
||||
roundNumber := int64(i) - chainConfig.XDPoS.XDPoSV2Block.Int64()
|
||||
header := createBlock(chainConfig, currentBlock, i, roundNumber, blockCoinBase, signer, signFn)
|
||||
// Inject the hardcoded master node list for the last v1 epoch block
|
||||
if int64(i) == chainConfig.XDPoS.XDPoSV2Block.Int64() {
|
||||
// reset extra
|
||||
header.Extra = []byte{}
|
||||
if len(header.Extra) < utils.ExtraVanity {
|
||||
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, utils.ExtraVanity-len(header.Extra))...)
|
||||
}
|
||||
header.Extra = header.Extra[:utils.ExtraVanity]
|
||||
var masternodes []common.Address
|
||||
masternodes = append(masternodes, acc1Addr, acc2Addr, acc3Addr, signer)
|
||||
masternodesFromV1LastEpoch = masternodes
|
||||
for _, masternode := range masternodes {
|
||||
header.Extra = append(header.Extra, masternode[:]...)
|
||||
}
|
||||
header.Extra = append(header.Extra, make([]byte, utils.ExtraSeal)...)
|
||||
|
||||
// Sign all the things for v1 block use v1 sigHash function
|
||||
sighash, err := signFn(accounts.Account{Address: signer}, blockchain.Engine().(*XDPoS.XDPoS).SigHash(header).Bytes())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
copy(header.Extra[len(header.Extra)-utils.ExtraSeal:], sighash)
|
||||
} else if (int64(i) == (chainConfig.XDPoS.XDPoSV2Block.Int64() + 1)) && masternodesFromV1LastEpoch != nil { // This is the first v2 block, we need to copy the last v1 epoch master node list and inject into v2 validators
|
||||
for _, v := range masternodesFromV1LastEpoch {
|
||||
header.Validators = append(header.Validators, v[:]...)
|
||||
}
|
||||
}
|
||||
|
||||
block, err := insertBlock(blockchain, header)
|
||||
if err != nil {
|
||||
|
|
@ -324,10 +359,6 @@ 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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue