Skip to content

Commit

Permalink
wip: update signature of ValidateFinalizedProof
Browse files Browse the repository at this point in the history
The sum of the signatures is meaningless if not everyone voted for the
same block. So instead, split the signature bits out by voted hash --
which allows the caller to sum up the total vote power -- and for
convenience, indicate here whether there were any duplicate signature
bits. It is the caller's responsibility to handle double signs.
  • Loading branch information
mark-rushakoff committed Jan 15, 2025
1 parent 044efcd commit 3b2ae0c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 27 deletions.
20 changes: 14 additions & 6 deletions gcrypto/commonmessagesignatureproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,21 @@ type CommonMessageSignatureProofScheme interface {
// Implementations are expected to panic if those assumptions do not hold.
Finalize(primary CommonMessageSignatureProof, rest []CommonMessageSignatureProof) FinalizedCommonMessageSignatureProof

// ValidateFinalized reports whether all signatures in the proof are valid.
// The provided bit set will be written such that is has
// a set bit corresponding to each key represented in the signatures.
// ValidateFinalized returns a map whose keys are the block hashes that have signatures,
// and whose values are the bit sets representing the validators who signed for that hash.
//
// If the result is false, the bit set may be in an indeterminate state,
// and so the bit set's contents should be disregarded.
ValidateFinalizedProof(proof FinalizedCommonMessageSignatureProof, bits *bitset.BitSet) bool
// The FinalizedCommonMessageSignatureProof includes signing content,
// and the output is intended to be keyed by block hash,
// so the hashesBySignContent is the glue to get the output in the desired form.
//
// If there are any invalid signatures or other errors,
// allSignaturesUnique will be false and the map will be nil.
// If all the signatures were valid, the allSignaturesUnique return value
// will be true if every signature is unique,
// or false if any validator has double signed.
ValidateFinalizedProof(proof FinalizedCommonMessageSignatureProof, hashesBySignContent map[string]string) (
signBitsByHash map[string]*bitset.BitSet, allSignaturesUnique bool,
)
}

// KeyIDChecker reports whether a sparse signature's key ID
Expand Down
36 changes: 15 additions & 21 deletions gcrypto/simplecommonmessagesignatureproof.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ func (SimpleCommonMessageSignatureProofScheme) Finalize(
}

func (SimpleCommonMessageSignatureProofScheme) ValidateFinalizedProof(
proof FinalizedCommonMessageSignatureProof, bits *bitset.BitSet,
) bool {
proof FinalizedCommonMessageSignatureProof,
hashesBySignContent map[string]string,
) (map[string]*bitset.BitSet, bool) {
// The main proof is unconditionally required.
tempProof, err := NewSimpleCommonMessageSignatureProof(proof.MainMessage, proof.Keys, proof.PubKeyHash)
if err != nil {
Expand All @@ -63,50 +64,43 @@ func (SimpleCommonMessageSignatureProofScheme) ValidateFinalizedProof(
//
// Unfortunately we are swallowing the error here.
// We could possibly change the scheme to accept a logger.
return false
return nil, false
}

if res := tempProof.MergeSparse(SparseSignatureProof{
PubKeyHash: proof.PubKeyHash,
Signatures: proof.MainSignatures,
}); !res.AllValidSignatures {
return false
return nil, false
}

// For the main proof, we can just write out the bits directly.
// (This destroys any existing data in bits, which is what we want.)
tempProof.SignatureBitSet(bits)
out := make(map[string]*bitset.BitSet, len(proof.Rest)+1)

// Avoid some work if there is nothing else to do.
if len(proof.Rest) == 0 {
return true
}
var bs bitset.BitSet
tempProof.SignatureBitSet(&bs)
out[hashesBySignContent[string(proof.MainMessage)]] = &bs

// Otherwise we are going to need hold bit sets for the remaining proofs.
var tempBits bitset.BitSet
for msg, sigs := range proof.Rest {
tempProof, err := NewSimpleCommonMessageSignatureProof([]byte(msg), proof.Keys, proof.PubKeyHash)
if err != nil {
// Same caveats as the main case.
return false
return nil, false
}

if res := tempProof.MergeSparse(SparseSignatureProof{
PubKeyHash: proof.PubKeyHash,
Signatures: sigs,
}); !res.AllValidSignatures {
return false
return nil, false
}

// Successful merge, so capture the signature bits first.
tempProof.SignatureBitSet(&tempBits)

// Then union the new bits with whatever else we have so far.
bits.InPlaceUnion(&tempBits)
var bs bitset.BitSet
tempProof.SignatureBitSet(&bs)
out[hashesBySignContent[msg]] = &bs
}

// All the bits from rest were merged in, so we're done.
return true
return out, true
}

// SimpleCommonMessageSignatureProof is the simplest signature proof,
Expand Down

0 comments on commit 3b2ae0c

Please sign in to comment.