ceremonyclient/consensus/consensus_signature.go
Cassandra Heart c797d482f9
v2.1.0.5 (#457)
* wip: conversion of hotstuff from flow into Q-oriented model

* bulk of tests

* remaining non-integration tests

* add integration test, adjust log interface, small tweaks

* further adjustments, restore full pacemaker shape

* add component lifecycle management+supervisor

* further refinements

* resolve timeout hanging

* mostly finalized state for consensus

* bulk of engine swap out

* lifecycle-ify most types

* wiring nearly complete, missing needed hooks for proposals

* plugged in, vetting message validation paths

* global consensus, plugged in and verified

* app shard now wired in too

* do not decode empty keys.yml (#456)

* remove obsolete engine.maxFrames config parameter (#454)

* default to Info log level unless debug is enabled (#453)

* respect config's  "logging" section params, remove obsolete single-file logging (#452)

* Trivial code cleanup aiming to reduce Go compiler warnings (#451)

* simplify range traversal

* simplify channel read for single select case

* delete rand.Seed() deprecated in Go 1.20 and no-op as of Go 1.24

* simplify range traversal

* simplify channel read for single select case

* remove redundant type from array

* simplify range traversal

* simplify channel read for single select case

* RC slate

* finalize 2.1.0.5

* Update comments in StrictMonotonicCounter

Fix comment formatting and clarify description.

---------

Co-authored-by: Black Swan <3999712+blacks1ne@users.noreply.github.com>
2025-11-11 05:00:17 -06:00

162 lines
7.3 KiB
Go

package consensus
import (
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
)
// WeightedSignatureAggregator aggregates signatures of the same signature
// scheme and the same message from different signers. The public keys and
// message are agreed upon upfront. It is also recommended to only aggregate
// signatures generated with keys representing equivalent security-bit level.
// Furthermore, a weight [unsigned int64] is assigned to each signer ID. The
// WeightedSignatureAggregator internally tracks the total weight of all
// collected signatures. Implementations must be concurrency safe.
type WeightedSignatureAggregator interface {
// Verify verifies the signature under the stored public keys and message.
// Expected errors during normal operations:
// - model.InvalidSignerError if signerID is invalid (not a consensus
// participant)
// - model.ErrInvalidSignature if signerID is valid but signature is
// cryptographically invalid
Verify(signerID models.Identity, sig []byte) error
// TrustedAdd adds a signature to the internal set of signatures and adds the
// signer's weight to the total collected weight, iff the signature is _not_ a
// duplicate. The total weight of all collected signatures (excluding
// duplicates) is returned regardless of any returned error.
// Expected errors during normal operations:
// - model.InvalidSignerError if signerID is invalid (not a consensus
// participant)
// - model.DuplicatedSignerError if the signer has been already added
TrustedAdd(signerID models.Identity, sig []byte) (
totalWeight uint64,
exception error,
)
// TotalWeight returns the total weight presented by the collected signatures.
TotalWeight() uint64
// Aggregate aggregates the signatures and returns the aggregated consensus.
// The function performs a final verification and errors if the aggregated
// signature is invalid. This is required for the function safety since
// `TrustedAdd` allows adding invalid signatures.
// The function errors with:
// - model.InsufficientSignaturesError if no signatures have been added yet
// - model.InvalidSignatureIncludedError if:
// -- some signature(s), included via TrustedAdd, fail to deserialize
// (regardless of the aggregated public key)
// -- or all signatures deserialize correctly but some signature(s),
// included via TrustedAdd, are invalid (while aggregated public key is
// valid)
// - model.InvalidAggregatedKeyError if all signatures deserialize correctly
// but the signer's proving public keys sum up to an invalid key (BLS
// identity public key). Any aggregated signature would fail the
// cryptographic verification under the identity public key and therefore
// such signature is considered invalid. Such scenario can only happen if
// proving public keys of signers were forged to add up to the identity
// public key. Under the assumption that all proving key PoPs are valid,
// this error case can only happen if all signers are malicious and
// colluding. If there is at least one honest signer, there is a
// negligible probability that the aggregated key is identity.
//
// The function is thread-safe.
Aggregate() ([]models.WeightedIdentity, models.AggregatedSignature, error)
}
// TimeoutSignatureAggregator aggregates timeout signatures for one particular
// rank. When instantiating a TimeoutSignatureAggregator, the following
// information is supplied:
// - The rank for which the aggregator collects timeouts.
// - For each replicas that is authorized to send a timeout at this particular
// rank: the node ID, public proving keys, and weight
//
// Timeouts for other ranks or from non-authorized replicas are rejected.
// In their TimeoutStates, replicas include a signature over the pair (rank,
// newestQCRank), where `rank` is the rank number the timeout is for and
// `newestQCRank` is the rank of the newest QC known to the replica.
// TimeoutSignatureAggregator collects these signatures, internally tracks the
// total weight of all collected signatures. Note that in general the signed
// messages are different, which makes the aggregation a comparatively expensive
// operation. Upon calling `Aggregate`, the TimeoutSignatureAggregator
// aggregates all valid signatures collected up to this point. The aggregate
// signature is guaranteed to be correct, as only valid signatures are accepted
// as inputs.
// TimeoutSignatureAggregator internally tracks the total weight of all
// collected signatures. Implementations must be concurrency safe.
type TimeoutSignatureAggregator interface {
// VerifyAndAdd verifies the signature under the stored public keys and adds
// the signature and the corresponding highest QC to the internal set.
// Internal set and collected weight is modified iff signature _is_ valid.
// The total weight of all collected signatures (excluding duplicates) is
// returned regardless of any returned error.
// Expected errors during normal operations:
// - model.InvalidSignerError if signerID is invalid (not a consensus
// participant)
// - model.DuplicatedSignerError if the signer has been already added
// - model.ErrInvalidSignature if signerID is valid but signature is
// cryptographically invalid
VerifyAndAdd(
signerID models.Identity,
sig []byte,
newestQCRank uint64,
) (totalWeight uint64, exception error)
// TotalWeight returns the total weight presented by the collected signatures.
TotalWeight() uint64
// Rank returns the rank that this instance is aggregating signatures for.
Rank() uint64
// Aggregate aggregates the signatures and returns with additional data.
// Aggregated signature will be returned as SigData of timeout certificate.
// Caller can be sure that resulting signature is valid.
// Expected errors during normal operations:
// - model.InsufficientSignaturesError if no signatures have been added yet
Aggregate() (
signersInfo []TimeoutSignerInfo,
aggregatedSig models.AggregatedSignature,
exception error,
)
}
// TimeoutSignerInfo is a helper structure that stores the QC ranks that each
// signer contributed to a TC. Used as result of
// TimeoutSignatureAggregator.Aggregate()
type TimeoutSignerInfo struct {
NewestQCRank uint64
Signer models.Identity
}
// StateSignatureData is an intermediate struct for Packer to pack the
// aggregated signature data into raw bytes or unpack from raw bytes.
type StateSignatureData struct {
Signers []models.WeightedIdentity
Signature []byte
}
// Packer packs aggregated signature data into raw bytes to be used in state
// header.
type Packer interface {
// Pack serializes the provided StateSignatureData into a precursor format of
// a QC. rank is the rank of the state that the aggregated signature is for.
// sig is the aggregated signature data.
// Expected error returns during normal operations:
// * none; all errors are symptoms of inconsistent input data or corrupted
// internal state.
Pack(rank uint64, sig *StateSignatureData) (
signerIndices []byte,
sigData []byte,
err error,
)
// Unpack de-serializes the provided signature data.
// sig is the aggregated signature data
// It returns:
// - (sigData, nil) if successfully unpacked the signature data
// - (nil, model.InvalidFormatError) if failed to unpack the signature data
Unpack(signerIdentities []models.WeightedIdentity, sigData []byte) (
*StateSignatureData,
error,
)
}