mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
update forensics proof data structure to accomedate vote type (#104)
* update forensics proof data structure to accomedate vote type * refactor log * change blocknum type to uint64 * fix test Co-authored-by: Liam Lai <liam.icheng.lai@gmail.com>
This commit is contained in:
parent
35b964fc16
commit
3ebaea1945
4 changed files with 78 additions and 55 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package engine_v2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
|
@ -46,7 +47,7 @@ func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC types.Quo
|
|||
// highestCommitQCs is an array, assign the parentBlockQc and its child as well as its grandchild QC into this array for forensics purposes.
|
||||
if len(headers) != NUM_OF_FORENSICS_QC-1 {
|
||||
log.Error("[SetCommittedQcs] Received input length not equal to 2", len(headers))
|
||||
return fmt.Errorf("Received headers length not equal to 2 ")
|
||||
return fmt.Errorf("received headers length not equal to 2 ")
|
||||
}
|
||||
|
||||
var committedQCs []types.QuorumCert
|
||||
|
|
@ -55,13 +56,13 @@ func (f *Forensics) SetCommittedQCs(headers []types.Header, incomingQC types.Quo
|
|||
// Decode the qc1 and qc2
|
||||
err := utils.DecodeBytesExtraFields(h.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
log.Error("[SetCommittedQCs] Fail to decode extra when committing QC to forensics", "Error", err, "Index", i)
|
||||
log.Error("[SetCommittedQCs] Fail to decode extra when committing QC to forensics", "err", err, "index", i)
|
||||
return err
|
||||
}
|
||||
if i != 0 {
|
||||
if decodedExtraField.QuorumCert.ProposedBlockInfo.Hash != headers[i-1].Hash() {
|
||||
log.Error("[SetCommittedQCs] Headers shall be on the same chain and in the right order", "ParentHash", h.ParentHash.Hex(), "headers[i-1].Hash()", headers[i-1].Hash().Hex())
|
||||
return fmt.Errorf("Headers shall be on the same chain and in the right order")
|
||||
log.Error("[SetCommittedQCs] Headers shall be on the same chain and in the right order", "parentHash", h.ParentHash.Hex(), "headers[i-1].Hash()", headers[i-1].Hash().Hex())
|
||||
return fmt.Errorf("headers shall be on the same chain and in the right order")
|
||||
} else if i == len(headers)-1 { // The last header shall be pointed by the incoming QC
|
||||
if incomingQC.ProposedBlockInfo.Hash != h.Hash() {
|
||||
log.Error("[SetCommittedQCs] incomingQc is not pointing at the last header received", "hash", h.Hash().Hex(), "incomingQC.ProposedBlockInfo.Hash", incomingQC.ProposedBlockInfo.Hash.Hex())
|
||||
|
|
@ -114,7 +115,7 @@ func (f *Forensics) ProcessForensics(chain consensus.ChainReader, engine *XDPoS_
|
|||
// Not found, need a more complex approach to find the two QC
|
||||
ancestorQC, lowerRoundQCs, _, err := f.findAncestorQcThroughRound(chain, highestCommittedQCs, incomingQuorunCerts)
|
||||
if err != nil {
|
||||
log.Error("[ProcessForensics] Error while trying to find ancestor QC through round number", "Error", err)
|
||||
log.Error("[ProcessForensics] Error while trying to find ancestor QC through round number", "err", err)
|
||||
}
|
||||
f.SendForensicProof(chain, engine, ancestorQC, lowerRoundQCs[NUM_OF_FORENSICS_QC-1])
|
||||
}
|
||||
|
|
@ -136,7 +137,7 @@ func (f *Forensics) SendForensicProof(chain consensus.ChainReader, engine *XDPoS
|
|||
// Find common ancestor block
|
||||
ancestorHash, ancestorToLowerRoundPath, ancestorToHigherRoundPath, err := f.FindAncestorBlockHash(chain, lowerRoundQC.ProposedBlockInfo, higherRoundQC.ProposedBlockInfo)
|
||||
if err != nil {
|
||||
log.Error("[SendForensicProof] Error while trying to find ancestor block hash", err)
|
||||
log.Error("[SendForensicProof] Error while trying to find ancestor block hash", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -156,9 +157,9 @@ func (f *Forensics) SendForensicProof(chain consensus.ChainReader, engine *XDPoS
|
|||
accrossEpoches = true
|
||||
}
|
||||
|
||||
forensicsProof := &types.ForensicProof{
|
||||
DivergingHash: ancestorHash,
|
||||
AcrossEpochs: accrossEpoches,
|
||||
content, err := json.Marshal(&types.ForensicsContent{
|
||||
DivergingBlockHash: ancestorHash.Hex(),
|
||||
AcrossEpoch: accrossEpoches,
|
||||
SmallerRoundInfo: &types.ForensicsInfo{
|
||||
HashPath: ancestorToLowerRoundPath,
|
||||
QuorumCert: lowerRoundQC,
|
||||
|
|
@ -169,8 +170,18 @@ func (f *Forensics) SendForensicProof(chain consensus.ChainReader, engine *XDPoS
|
|||
QuorumCert: higherRoundQC,
|
||||
SignerAddresses: f.getQcSignerAddresses(higherRoundQC),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Error("[SendForensicProof] fail to json stringify forensics content", "err", err)
|
||||
return err
|
||||
}
|
||||
log.Info("Forensics proof report generated, sending to the stats server", forensicsProof)
|
||||
|
||||
forensicsProof := &types.ForensicProof{
|
||||
ForensicsType: "QC",
|
||||
Content: string(content),
|
||||
}
|
||||
log.Info("Forensics proof report generated, sending to the stats server", "forensicsProof", forensicsProof)
|
||||
go f.forensicsFeed.Send(types.ForensicsEvent{ForensicsProof: forensicsProof})
|
||||
return nil
|
||||
}
|
||||
|
|
@ -187,7 +198,7 @@ func (f *Forensics) findAncestorQCs(chain consensus.ChainReader, currentQc types
|
|||
parentHeader := chain.GetHeaderByHash(parentHash)
|
||||
if parentHeader == nil {
|
||||
log.Error("[findAncestorQCs] Forensics findAncestorQCs unable to find its parent block header", "BlockNum", parentHeader.Number.Int64(), "ParentHash", parentHash.Hex())
|
||||
return nil, fmt.Errorf("Unable to find parent block header in forensics")
|
||||
return nil, fmt.Errorf("unable to find parent block header in forensics")
|
||||
}
|
||||
var decodedExtraField types.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField)
|
||||
|
|
@ -221,7 +232,7 @@ func (f *Forensics) checkQCsOnTheSameChain(chain consensus.ChainReader, highestC
|
|||
var decodedExtraField types.ExtraFields_v2
|
||||
err := utils.DecodeBytesExtraFields(parentHeader.Extra, &decodedExtraField)
|
||||
if err != nil {
|
||||
log.Error("[checkQCsOnTheSameChain] Fail to decode extra when checking the two QCs set on the same chain", "Error", err)
|
||||
log.Error("[checkQCsOnTheSameChain] Fail to decode extra when checking the two QCs set on the same chain", "err", err)
|
||||
return false, err
|
||||
}
|
||||
proposedBlockInfo = decodedExtraField.QuorumCert.ProposedBlockInfo
|
||||
|
|
@ -322,7 +333,7 @@ func (f *Forensics) FindAncestorBlockHash(chain consensus.ChainReader, firstBloc
|
|||
for i := 0; i < int(blockNumberDifference); i++ {
|
||||
ph := chain.GetHeaderByHash(higherBlockNumberHash)
|
||||
if ph == nil {
|
||||
return common.Hash{}, ancestorToLowerBlockNumHashPath, ancestorToHigherBlockNumHashPath, fmt.Errorf("Unable to find parent block of hash %v", higherBlockNumberHash)
|
||||
return common.Hash{}, ancestorToLowerBlockNumHashPath, ancestorToHigherBlockNumHashPath, fmt.Errorf("unable to find parent block of hash %v", higherBlockNumberHash)
|
||||
}
|
||||
higherBlockNumberHash = ph.ParentHash
|
||||
ancestorToHigherBlockNumHashPath = append(ancestorToHigherBlockNumHashPath, ph.ParentHash.Hex())
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package engine_v2_tests
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -92,7 +93,7 @@ func TestSetCommittedQCsInOrder(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(903), *blockchain.GetHeaderByNumber(902)), *decodedExtraField.QuorumCert)
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "Headers shall be on the same chain and in the right order", err.Error())
|
||||
assert.Equal(t, "headers shall be on the same chain and in the right order", err.Error())
|
||||
|
||||
err = forensics.SetCommittedQCs(append(headers, *blockchain.GetHeaderByNumber(903), *blockchain.GetHeaderByNumber(904)), *decodedExtraField.QuorumCert)
|
||||
assert.Nil(t, err)
|
||||
|
|
@ -169,15 +170,18 @@ func TestForensicsMonitoringNotOnSameChainButHaveSameRoundQC(t *testing.T) {
|
|||
select {
|
||||
case forensics := <-forensicsEventCh:
|
||||
assert.NotNil(t, forensics.ForensicsProof)
|
||||
assert.False(t, forensics.ForensicsProof.AcrossEpochs)
|
||||
assert.Equal(t, types.Round(13), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(913), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 9, len(forensics.ForensicsProof.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(forensics.ForensicsProof.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(13), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(912), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 8, len(forensics.ForensicsProof.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(forensics.ForensicsProof.LargerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, "QC", forensics.ForensicsProof.ForensicsType)
|
||||
content := &types.ForensicsContent{}
|
||||
json.Unmarshal([]byte(forensics.ForensicsProof.Content), &content)
|
||||
assert.False(t, content.AcrossEpoch)
|
||||
assert.Equal(t, types.Round(13), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(913), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 9, len(content.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(13), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(912), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 8, len(content.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(content.LargerRoundInfo.SignerAddresses))
|
||||
return
|
||||
case <-time.After(5 * time.Second):
|
||||
t.FailNow()
|
||||
|
|
@ -225,15 +229,19 @@ func TestForensicsMonitoringNotOnSameChainDoNotHaveSameRoundQC(t *testing.T) {
|
|||
select {
|
||||
case forensics := <-forensicsEventCh:
|
||||
assert.NotNil(t, forensics.ForensicsProof)
|
||||
assert.False(t, forensics.ForensicsProof.AcrossEpochs)
|
||||
assert.Equal(t, types.Round(14), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(914), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 10, len(forensics.ForensicsProof.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(forensics.ForensicsProof.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(16), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(906), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 2, len(forensics.ForensicsProof.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 2, len(forensics.ForensicsProof.LargerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, "QC", forensics.ForensicsProof.ForensicsType)
|
||||
content := &types.ForensicsContent{}
|
||||
json.Unmarshal([]byte(forensics.ForensicsProof.Content), &content)
|
||||
|
||||
assert.False(t, content.AcrossEpoch)
|
||||
assert.Equal(t, types.Round(14), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(914), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(16), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(906), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 2, len(content.LargerRoundInfo.SignerAddresses))
|
||||
return
|
||||
case <-time.After(5 * time.Second):
|
||||
t.FailNow()
|
||||
|
|
@ -282,15 +290,19 @@ func TestForensicsAcrossEpoch(t *testing.T) {
|
|||
select {
|
||||
case forensics := <-forensicsEventCh:
|
||||
assert.NotNil(t, forensics.ForensicsProof)
|
||||
assert.True(t, forensics.ForensicsProof.AcrossEpochs)
|
||||
assert.Equal(t, types.Round(900), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(1800), forensics.ForensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 10, len(forensics.ForensicsProof.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(forensics.ForensicsProof.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(902), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(1792), forensics.ForensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 2, len(forensics.ForensicsProof.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 2, len(forensics.ForensicsProof.LargerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, "QC", forensics.ForensicsProof.ForensicsType)
|
||||
content := &types.ForensicsContent{}
|
||||
json.Unmarshal([]byte(forensics.ForensicsProof.Content), &content)
|
||||
|
||||
assert.True(t, content.AcrossEpoch)
|
||||
assert.Equal(t, types.Round(900), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(1800), content.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 10, len(content.SmallerRoundInfo.HashPath))
|
||||
assert.Equal(t, 4, len(content.SmallerRoundInfo.SignerAddresses))
|
||||
assert.Equal(t, types.Round(902), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Round)
|
||||
assert.Equal(t, uint64(1792), content.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Number.Uint64())
|
||||
assert.Equal(t, 2, len(content.LargerRoundInfo.HashPath))
|
||||
assert.Equal(t, 2, len(content.LargerRoundInfo.SignerAddresses))
|
||||
return
|
||||
case <-time.After(5 * time.Second):
|
||||
t.FailNow()
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
package types
|
||||
|
||||
import "github.com/XinFinOrg/XDPoSChain/common"
|
||||
|
||||
type ForensicsInfo struct {
|
||||
HashPath []string // HashesTillSmallerRoundQc or HashesTillLargerRoundQc
|
||||
QuorumCert QuorumCert
|
||||
SignerAddresses []string
|
||||
HashPath []string `json:"hashPath"`
|
||||
QuorumCert QuorumCert `json:"quorumCert"`
|
||||
SignerAddresses []string `json:"signerAddresses"`
|
||||
}
|
||||
|
||||
type ForensicsContent struct {
|
||||
DivergingBlockNumber uint64 `json:"divergingBlockNumber"`
|
||||
DivergingBlockHash string `json:"divergingBlockHash"`
|
||||
AcrossEpoch bool `json:"acrossEpoch"`
|
||||
SmallerRoundInfo *ForensicsInfo `json:"smallerRoundInfo"`
|
||||
LargerRoundInfo *ForensicsInfo `json:"largerRoundInfo"`
|
||||
}
|
||||
|
||||
type ForensicProof struct {
|
||||
SmallerRoundInfo *ForensicsInfo
|
||||
LargerRoundInfo *ForensicsInfo
|
||||
DivergingHash common.Hash
|
||||
AcrossEpochs bool
|
||||
ForensicsType string `json:"forensicsType"` // QC or VOTE
|
||||
Content string `json:"content"` // Json string of the forensics data
|
||||
}
|
||||
|
||||
type ForensicsEvent struct {
|
||||
|
|
|
|||
|
|
@ -547,11 +547,7 @@ func (s *Service) reportBlock(conn *websocket.Conn, block *types.Block) error {
|
|||
|
||||
// reportForensics forward the forensics repors it to the stats server.
|
||||
func (s *Service) reportForensics(conn *websocket.Conn, forensicsProof *types.ForensicProof) error {
|
||||
log.Info(
|
||||
"Sending Forensics report to ethstats",
|
||||
"SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Hash", forensicsProof.SmallerRoundInfo.QuorumCert.ProposedBlockInfo.Hash,
|
||||
"LargerRoundInfo.QuorumCert.ProposedBlockInfo.Hash", forensicsProof.LargerRoundInfo.QuorumCert.ProposedBlockInfo.Hash,
|
||||
)
|
||||
log.Info("Sending Forensics report to ethstats", "ForensicsType", forensicsProof.ForensicsType)
|
||||
|
||||
stats := map[string]interface{}{
|
||||
"id": s.node,
|
||||
|
|
|
|||
Loading…
Reference in a new issue