-
Notifications
You must be signed in to change notification settings - Fork 69
consensus: use signer pubkey to check for unique signatures and optimize performance, close XFN-03 #1625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev-upgrade
Are you sure you want to change the base?
consensus: use signer pubkey to check for unique signatures and optimize performance, close XFN-03 #1625
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,8 @@ | ||||||||||
| package engine_v2 | ||||||||||
|
|
||||||||||
| import ( | ||||||||||
| "context" | ||||||||||
| "errors" | ||||||||||
| "fmt" | ||||||||||
| "runtime" | ||||||||||
| "strconv" | ||||||||||
| "strings" | ||||||||||
| "time" | ||||||||||
|
|
@@ -14,7 +12,6 @@ import ( | |||||||||
| "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils" | ||||||||||
| "github.com/XinFinOrg/XDPoSChain/core/types" | ||||||||||
| "github.com/XinFinOrg/XDPoSChain/log" | ||||||||||
| "golang.org/x/sync/errgroup" | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg *types.Timeout) (bool, error) { | ||||||||||
|
|
@@ -168,60 +165,29 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time | |||||||||
| return errors.New("empty master node lists from snapshot") | ||||||||||
| } | ||||||||||
|
|
||||||||||
| signatures, duplicates := UniqueSignatures(timeoutCert.Signatures) | ||||||||||
| if len(duplicates) != 0 { | ||||||||||
| for _, d := range duplicates { | ||||||||||
| log.Warn("[verifyTC] duplicated signature in QC", "duplicate", common.Bytes2Hex(d)) | ||||||||||
| } | ||||||||||
| } | ||||||||||
| signedTimeoutObj := types.TimeoutSigHash(&types.TimeoutForSign{ | ||||||||||
| Round: timeoutCert.Round, | ||||||||||
| GapNumber: timeoutCert.GapNumber, | ||||||||||
| }) | ||||||||||
|
|
||||||||||
| // TODO: snap.NextEpochCandidates should be replaced with epochInfo.Masternodes in another PR | ||||||||||
| numValidSignatures, err := x.countValidSignatures(signedTimeoutObj, timeoutCert.Signatures, snap.NextEpochCandidates) | ||||||||||
| if err != nil { | ||||||||||
| log.Error("[verifyTC] Error while verfying TC message signatures", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "Error", err) | ||||||||||
|
||||||||||
| log.Error("[verifyTC] Error while verfying TC message signatures", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "Error", err) | |
| log.Error("[verifyTC] Error while verifying TC message signatures", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "Error", err) |
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signature validation at line 174 uses snap.NextEpochCandidates to verify signatures, but the threshold check at line 185 uses epochInfo.MasternodesLen. This inconsistency could lead to incorrect validation if these two lists have different lengths. The TODO comment on line 173 acknowledges this issue but it's concerning that the threshold check happens against a different set of masternodes than what was used for signature verification.
| if float64(numValidSignatures) < float64(epochInfo.MasternodesLen)*certThreshold { | |
| log.Warn("[verifyTC] Invalid TC Signature is less or empty", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "certThreshold", float64(epochInfo.MasternodesLen)*certThreshold) | |
| if float64(numValidSignatures) < float64(len(snap.NextEpochCandidates))*certThreshold { | |
| log.Warn("[verifyTC] Invalid TC Signature is less or empty", "tcRound", timeoutCert.Round, "tcGapNumber", timeoutCert.GapNumber, "tcSignLen", len(timeoutCert.Signatures), "certThreshold", float64(len(snap.NextEpochCandidates))*certThreshold) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,9 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package engine_v2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "context" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "errors" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "math/big" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "runtime" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/XinFinOrg/XDPoSChain/accounts" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/XinFinOrg/XDPoSChain/common" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -14,6 +16,7 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/XinFinOrg/XDPoSChain/log" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/XinFinOrg/XDPoSChain/rlp" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "golang.org/x/crypto/sha3" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "golang.org/x/sync/errgroup" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func sigHash(header *types.Header) (hash common.Hash) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -76,22 +79,6 @@ func decodeMasternodesFromHeaderExtra(checkpointHeader *types.Header) []common.A | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return masternodes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func UniqueSignatures(signatureSlice []types.Signature) ([]types.Signature, []types.Signature) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys := make(map[string]bool) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list := []types.Signature{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duplicates := []types.Signature{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, signature := range signatureSlice { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hexOfSig := common.Bytes2Hex(signature) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if _, value := keys[hexOfSig]; !value { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys[hexOfSig] = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| list = append(list, signature) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duplicates = append(duplicates, signature) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return list, duplicates | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Don't hold the signFn for the whole signing operation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| x.signLock.RLock() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -108,6 +95,53 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return signedHash, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (x *XDPoS_v2) countValidSignatures(messageHash common.Hash, signatures []types.Signature, candidates []common.Address) (int, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signatureList := make([]types.Signature, len(signatures)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pubkeys := make([]common.Address, len(signatures)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
wanwiset25 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eg, ctx := errgroup.WithContext(context.Background()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eg.SetLimit(runtime.NumCPU()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i, signature := range signatures { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| eg.Go(func() error { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| select { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case <-ctx.Done(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ctx.Err() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verified, signerAddress, err := x.verifyMsgSignature(messageHash, signature, candidates) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Error("[verifySignatures] Error while verfying QC message signatures", "error", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.Error("[verifySignatures] Error while verfying QC message signatures", "error", err) | |
| log.Error("[verifySignatures] Error while verifying QC message signatures", "error", err) |
gzliudan marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Dec 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The loop variables i and signature are being captured by the goroutine closure, which creates a race condition. All goroutines will share the same variables, and by the time they execute, these variables will likely have been modified by subsequent loop iterations. This means multiple goroutines may access the same index or signature, leading to incorrect results or data races when writing to signatureList[i] and pubkeys[i].
The loop variables must be captured properly by passing them as parameters to the goroutine or by creating local copies within the loop body before starting the goroutine.
| eg.Go(func() error { | |
| select { | |
| case <-ctx.Done(): | |
| return ctx.Err() | |
| default: | |
| verified, signerAddress, err := x.verifyMsgSignature(messageHash, signature, candidates) | |
| if err != nil { | |
| log.Error("[verifySignatures] Error while verfying QC message signatures", "error", err) | |
| return err | |
| } | |
| if !verified { | |
| return fmt.Errorf("signature verification failed, signer is not part of masternode list. Signature: %v, SignedMessage: %v, SignerAddress: %v, Masternodes: %v", signature, messageHash.Hex(), signerAddress, candidates) | |
| } | |
| signatureList[i] = signature | |
| pubkeys[i] = signerAddress | |
| idx := i | |
| sig := signature | |
| eg.Go(func() error { | |
| select { | |
| case <-ctx.Done(): | |
| return ctx.Err() | |
| default: | |
| verified, signerAddress, err := x.verifyMsgSignature(messageHash, sig, candidates) | |
| if err != nil { | |
| log.Error("[verifySignatures] Error while verfying QC message signatures", "error", err) | |
| return err | |
| } | |
| if !verified { | |
| return fmt.Errorf("signature verification failed, signer is not part of masternode list. Signature: %v, SignedMessage: %v, SignerAddress: %v, Masternodes: %v", sig, messageHash.Hex(), signerAddress, candidates) | |
| } | |
| signatureList[idx] = sig | |
| pubkeys[idx] = signerAddress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling error: "verfying" should be "verifying".