-
Notifications
You must be signed in to change notification settings - Fork 69
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
chore(halo/voter): improve errors and logs #2437
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 |
---|---|---|
|
@@ -3,9 +3,6 @@ package keeper | |
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"log/slog" | ||
"strconv" | ||
|
||
"github.com/omni-network/omni/halo/attest/types" | ||
rtypes "github.com/omni-network/omni/halo/registry/types" | ||
|
@@ -713,29 +710,7 @@ func (k *Keeper) ExtendVote(ctx sdk.Context, _ *abci.RequestExtendVote) (*abci.R | |
votesExtended.WithLabelValues(k.namer(chainVer)).Observe(float64(count)) | ||
} | ||
|
||
// Make nice logs | ||
const limit = 5 | ||
offsets := make(map[xchain.ChainVersion][]string) | ||
for _, vote := range filtered { | ||
offset := offsets[vote.AttestHeader.XChainVersion()] | ||
if len(offset) < limit { | ||
offset = append(offset, strconv.FormatUint(vote.AttestHeader.AttestOffset, 10)) | ||
} else if len(offset) == limit { | ||
offset = append(offset, "...") | ||
} else { | ||
continue | ||
} | ||
offsets[vote.AttestHeader.XChainVersion()] = offset | ||
} | ||
attrs := []any{slog.Int("votes", len(offsets))} | ||
for chainVer, offset := range offsets { | ||
attrs = append(attrs, slog.String( | ||
fmt.Sprintf("%d-%d", chainVer.ID, chainVer.ConfLevel), | ||
fmt.Sprint(offset), | ||
)) | ||
} | ||
|
||
log.Info(ctx, "Voted for rollup blocks", attrs...) | ||
log.Info(ctx, "Voter voting", types.VoteLogs(filtered)...) | ||
|
||
return &abci.ResponseExtendVote{ | ||
VoteExtension: bz, | ||
|
@@ -780,20 +755,31 @@ func (k *Keeper) VerifyVoteExtension(ctx sdk.Context, req *abci.RequestVerifyVot | |
return respReject, nil | ||
} | ||
|
||
duplicate := make(map[xchain.AttestHeader]bool) | ||
duplicate := make(map[common.Hash]bool) // Detect identical duplicate votes (same AttestationRoot) | ||
doubleSign := make(map[xchain.AttestHeader]bool) // Detect double sign votes (same AttestHeader) | ||
for _, vote := range votes.Votes { | ||
if err := vote.Verify(); err != nil { | ||
log.Warn(ctx, "Rejecting invalid vote", err) | ||
return respReject, nil | ||
} | ||
|
||
if duplicate[vote.AttestHeader.ToXChain()] { | ||
attRoot, err := vote.AttestationRoot() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "att root [BUG]") // Should error in Verify | ||
} | ||
if duplicate[attRoot] { | ||
log.Warn(ctx, "Rejecting duplicate identical vote", nil) | ||
return respReject, nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're returning with an error on duplicate votes and double signing, is it somehow possible to cause a dos by re-submitting valid votes of honest validators or by a perpetual double-signing by a malicious validator? Potentially the votes deduplicated somewhere in the stack before the voter can get them, but I wonder why just logging a warning and skipping is not enough? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is during |
||
} | ||
duplicate[attRoot] = true | ||
|
||
if doubleSign[vote.AttestHeader.ToXChain()] { | ||
doubleSignCounter.WithLabelValues(ethAddr.Hex()).Inc() | ||
log.Warn(ctx, "Rejecting duplicate slashable vote", err) | ||
|
||
return respReject, nil | ||
} | ||
duplicate[vote.AttestHeader.ToXChain()] = true | ||
doubleSign[vote.AttestHeader.ToXChain()] = true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: the name is potentially misleading as presence in the map does not imply a double-signing yet. It is used to detected the double signing... Same with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mmm, I don't find it so confusing, just read the code...? |
||
|
||
// Ensure the votes are from the requesting validator itself. | ||
if !bytes.Equal(vote.Signature.ValidatorAddress, ethAddr[:]) { | ||
|
@@ -880,6 +866,7 @@ func (k *Keeper) windowCompare(ctx context.Context, chainVer xchain.ChainVersion | |
// - Ensure all aggregation is valid; no duplicate aggregate votes. | ||
// - Ensure the vote extension limit is not exceeded per validator. | ||
// - Ensure all votes are from validators in the provided set. | ||
// - Ensure all votes are unique per validator. | ||
// - Ensure the vote block header is in the vote window. | ||
func (k *Keeper) verifyAggVotes( | ||
ctx context.Context, | ||
|
@@ -911,12 +898,18 @@ func (k *Keeper) verifyAggVotes( | |
duplicate[attRoot] = true | ||
|
||
// Ensure all votes are from unique validators in the set | ||
duplicateSig := make(map[common.Address]bool) // Enforce vote extension limit. | ||
for _, sig := range agg.Signatures { | ||
addr, err := sig.ValidatorEthAddress() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if duplicateSig[addr] { | ||
return errors.New("duplicate validator vote", append(errAttrs, "validator", addr)...) | ||
} | ||
duplicateSig[addr] = true | ||
|
||
if !valset.Contains(addr) { | ||
return errors.New("vote from unknown validator", append(errAttrs, "validator", addr)...) | ||
} | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package types | ||
|
||
import ( | ||
"fmt" | ||
"log/slog" | ||
"strconv" | ||
|
||
"github.com/omni-network/omni/lib/xchain" | ||
) | ||
|
||
const logLimit = 5 | ||
|
||
// VoteLogs returns the logs as opinionated human-readable logging attributes. | ||
func VoteLogs(votes []*Vote) []any { | ||
var headers []*AttestHeader | ||
for _, vote := range votes { | ||
headers = append(headers, vote.GetAttestHeader()) | ||
} | ||
|
||
return AttLogs(headers) | ||
} | ||
|
||
// AttLogs returns the headers as opinionated human-readable logging attributes. | ||
func AttLogs(headers []*AttestHeader) []any { | ||
offsets := make(map[xchain.ChainVersion][]string) | ||
for _, header := range headers { | ||
offset := offsets[header.XChainVersion()] | ||
if len(offset) < logLimit { | ||
offset = append(offset, strconv.FormatUint(header.AttestOffset, 10)) | ||
} else if len(offset) == logLimit { | ||
offset = append(offset, "...") | ||
} else { | ||
continue | ||
} | ||
offsets[header.XChainVersion()] = offset | ||
} | ||
|
||
attrs := []any{slog.Int("count", len(offsets))} | ||
for chainVer, offsets := range offsets { | ||
attrs = append(attrs, slog.String( | ||
fmt.Sprintf("%d-%d", chainVer.ID, chainVer.ConfLevel), | ||
fmt.Sprint(offsets), | ||
)) | ||
} | ||
|
||
return attrs | ||
} |
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.
when the same sig is included, it isn't a double sign