Documentation
¶
Index ¶
- Constants
- Variables
- func DigestToCid(digest []byte) []byte
- func IsStrongQuorum(part uint16, whole uint16) bool
- func MakeCid(data []byte) []byte
- func VerifyTicket(nn NetworkName, beacon []byte, instance uint64, round uint64, source PubKey, ...) bool
- type ActorID
- type CID
- type Chain
- type ChainKey
- type Clock
- type ConvergeTicket
- type ConvergeValue
- type DecisionReceiver
- type ECChain
- func (c ECChain) Base() *TipSet
- func (c ECChain) BaseChain() ECChain
- func (c ECChain) Eq(other ECChain) bool
- func (c ECChain) Extend(tips ...TipSetKey) ECChain
- func (c ECChain) HasBase(t *TipSet) bool
- func (c ECChain) HasPrefix(other ECChain) bool
- func (c ECChain) HasSuffix() bool
- func (c ECChain) HasTipset(t *TipSet) bool
- func (c ECChain) Head() *TipSet
- func (c ECChain) IsZero() bool
- func (c ECChain) Key() ChainKey
- func (c ECChain) Prefix(to int) ECChain
- func (c ECChain) SameBase(other ECChain) bool
- func (c ECChain) String() string
- func (c ECChain) Suffix() []TipSet
- func (c ECChain) Validate() error
- type GMessage
- type Host
- type Justification
- type MessageBuilder
- type MessageReceiver
- type MessageValidator
- type Network
- type NetworkName
- type Option
- func WithDelta(d time.Duration) Option
- func WithDeltaBackOffExponent(e float64) Option
- func WithMaxCachedInstances(v int) Option
- func WithMaxCachedMessagesPerInstance(v int) Option
- func WithMaxLookaheadRounds(r uint64) Option
- func WithRebroadcastBackoff(exponent float64, base, max time.Duration) Option
- func WithTracer(t Tracer) Option
- type PanicError
- type Participant
- func (p *Participant) CurrentInstance() uint64
- func (p *Participant) CurrentRound() uint64
- func (p *Participant) Describe() string
- func (p *Participant) ReceiveAlarm() (err error)
- func (p *Participant) ReceiveMessage(vmsg ValidatedMessage) (err error)
- func (p *Participant) StartInstanceAt(instance uint64, when time.Time) (err error)
- func (p *Participant) ValidateMessage(msg *GMessage) (valid ValidatedMessage, err error)
- type Payload
- type Phase
- type PowerEntries
- func (p PowerEntries) Equal(o PowerEntries) bool
- func (p PowerEntries) Len() int
- func (p PowerEntries) Less(i, j int) bool
- func (t *PowerEntries) MarshalCBOR(w io.Writer) error
- func (p PowerEntries) Scaled() (scaled []uint16, total uint16, err error)
- func (p PowerEntries) Swap(i, j int)
- func (t *PowerEntries) UnmarshalCBOR(r io.Reader) (err error)
- type PowerEntry
- type PowerTable
- func (p *PowerTable) Add(entries ...PowerEntry) error
- func (p *PowerTable) Copy() *PowerTable
- func (p *PowerTable) Get(id ActorID) (uint16, PubKey)
- func (p *PowerTable) Has(id ActorID) bool
- func (p *PowerTable) Len() int
- func (p *PowerTable) Less(i, j int) bool
- func (p *PowerTable) Swap(i, j int)
- func (p *PowerTable) Validate() error
- type PubKey
- type QuorumResult
- type Receiver
- type SignatureBuilder
- type Signatures
- type Signer
- type SignerWithMarshaler
- type SigningMarshaler
- type StoragePower
- type SupplementalData
- type Ticket
- type TipSet
- type TipSetKey
- type Tracer
- type ValidatedMessage
- type ValidationError
- type Verifier
Constants ¶
const ( // CidMaxLen specifies the maximum length of a CID. CidMaxLen = 38 // ChainMaxLen specifies the maximum length of a chain value. ChainMaxLen = 100 // TipsetKeyMaxLen specifies the maximum length of a tipset. The max size is // chosen such that it allows ample space for an impossibly-unlikely number of // blocks in a tipset, while maintaining a practical limit to prevent abuse. TipsetKeyMaxLen = 20 * CidMaxLen )
const DomainSeparationTag = "GPBFT"
const DomainSeparationTagVRF = "VRF"
Variables ¶
var ( // ErrValidationTooOld signals that a message is invalid because belongs to prior // instances of gpbft. ErrValidationTooOld = newValidationError("message is for prior instance") // ErrValidationNoCommittee signals that a message is invalid because there is no // committee for the instance to which it belongs. // // See: Chain.GetCommitteeForInstance. ErrValidationNoCommittee = newValidationError("no committee for instance") // ErrValidationInvalid signals that a message violates the validity rules of // gpbft protocol. ErrValidationInvalid = newValidationError("message invalid") // ErrValidationWrongBase signals that a message is invalid due to having an // unexpected base ECChain. // // See: ECChain, TipSet, ECChain.Base ErrValidationWrongBase = newValidationError("unexpected base chain") //ErrValidationWrongSupplement signals that a message is invalid due to unexpected supplemental data. // // See SupplementalData. ErrValidationWrongSupplement = newValidationError("unexpected supplemental data") // ErrReceivedWrongInstance signals that a message is received with mismatching instance ID. ErrReceivedWrongInstance = errors.New("received message for wrong instance") // ErrReceivedAfterTermination signals that a message is received after the gpbft instance is terminated. ErrReceivedAfterTermination = errors.New("received message after terminating") // ErrReceivedInternalError signals that an error has occurred during message processing. ErrReceivedInternalError = errors.New("error processing message") )
var ErrNoPower = errors.New("no power")
ErrNoPower is returned by the MessageBuilder if the specified participant has no power.
Functions ¶
func DigestToCid ¶
DigestToCid turns a digest into CBOR + blake2b-256 CID
func IsStrongQuorum ¶
Check whether a portion of storage power is a strong quorum of the total
func VerifyTicket ¶
Types ¶
type Chain ¶
type Chain interface {
// Returns the supplemental data and the chain to propose for a new GPBFT instance.
// The chain must be a suffix of the chain finalised by the immediately prior instance.
// The supplemental data must be derived entirely from prior instances and all participants
// must propose the same supplemental data.
//
// Returns an error if the chain for the instance is not available.
GetProposalForInstance(instance uint64) (data *SupplementalData, chain ECChain, err error)
// Returns the power table and beacon value to be used for a GPBFT instance.
// These values should be derived from a chain previously received as final by the host,
// or known to be final via some other channel (e.g. when bootstrapping the protocol).
// The offset (how many instances to look back) is determined by the host.
// Returns an error if the committee for the instance is not available.
GetCommitteeForInstance(instance uint64) (power *PowerTable, beacon []byte, err error)
}
type Clock ¶
type Clock interface {
// Returns the current network time.
Time() time.Time
// SetAlarm sets an alarm to fire after the given timestamp. At most one alarm
// can be set at a time. Setting an alarm replaces any previous alarm that has
// not yet fired. The timestamp may be in the past, in which case the alarm will
// fire as soon as possible (but not synchronously).
SetAlarm(at time.Time)
}
type ConvergeTicket ¶
type ConvergeValue ¶
type ConvergeValue struct {
Chain ECChain
Justification *Justification
}
type DecisionReceiver ¶
type DecisionReceiver interface {
// Receives a finality decision from the instance, with signatures from a strong quorum
// of participants justifying it.
// The decision payload always has round = 0 and step = DECIDE.
// The notification must return the timestamp at which the next instance should begin,
// based on the decision received (which may be in the past).
// E.g. this might be: finalised tipset timestamp + epoch duration + stabilisation delay.
ReceiveDecision(decision *Justification) (time.Time, error)
}
type ECChain ¶
type ECChain []TipSet
A chain of tipsets comprising a base (the last finalised tipset from which the chain extends). and (possibly empty) suffix. Tipsets are assumed to be built contiguously on each other, though epochs may be missing due to null rounds. The zero value is not a valid chain, and represents a "bottom" value when used in a Granite message.
func (ECChain) BaseChain ¶
Returns a new chain with the same base and no suffix. Invalid for a zero value.
func (ECChain) HasBase ¶
Check whether a chain has a specific base tipset. Always false for a zero value.
func (ECChain) HasPrefix ¶
Checks whether a chain has some prefix (including the base). Always false for a zero value.
func (ECChain) Head ¶
Returns the last tipset in the chain. This could be the base tipset if there is no suffix. This will panic on a zero value.
func (ECChain) Key ¶
Returns an identifier for the chain suitable for use as a map key. This must completely determine the sequence of tipsets in the chain.
func (ECChain) Prefix ¶
Returns a chain with suffix (after the base) truncated to a maximum length. Prefix(0) returns the base chain. Invalid for a zero value.
func (ECChain) SameBase ¶
Checks whether two chains have the same base. Always false for a zero value.
func (ECChain) Suffix ¶
Returns the suffix of the chain after the base. An empty slice for a zero value.
func (ECChain) Validate ¶
Validates a chain value, returning an error if it finds any issues. A chain is valid if it meets the following criteria: 1) All contained tipsets are non-empty. 2) All epochs are >= 0 and increasing. 3) The chain is not longer than ChainMaxLen. An entirely zero-valued chain itself is deemed valid. See ECChain.IsZero.
type GMessage ¶
type GMessage struct {
// ID of the sender/signer of this message (a miner actor ID).
Sender ActorID
// Vote is the payload that is signed by the signature
Vote Payload
// Signature by the sender's public key over Instance || Round || Step || Value.
Signature []byte `cborgen:"maxlen=96"`
// VRF ticket for CONVERGE messages (otherwise empty byte array).
Ticket Ticket `cborgen:"maxlen=96"`
// Justification for this message (some messages must be justified by a strong quorum of messages from some previous step).
Justification *Justification
}
A message in the Granite protocol. The same message structure is used for all rounds and phases. Note that the message is self-attesting so no separate envelope or signature is needed. - The signature field fixes the included sender ID via the implied public key; - The signature payload includes all fields a sender can freely choose; - The ticket field is a signature of the same public key, so also self-attesting.
type Host ¶
type Host interface {
Chain
Network
Clock
Signatures
DecisionReceiver
}
Participant interface to the host system resources.
type Justification ¶
type Justification struct {
// Vote is the payload that is signed by the signature
Vote Payload
// Indexes in the base power table of the signers (bitset)
Signers bitfield.BitField
// BLS aggregate signature of signers
Signature []byte `cborgen:"maxlen=96"`
}
func (*Justification) MarshalCBOR ¶
func (t *Justification) MarshalCBOR(w io.Writer) error
func (*Justification) UnmarshalCBOR ¶
func (t *Justification) UnmarshalCBOR(r io.Reader) (err error)
type MessageBuilder ¶
type MessageBuilder struct {
NetworkName NetworkName
PowerTable powerTableAccessor
Payload Payload
BeaconForTicket []byte
Justification *Justification
SigningMarshaler SigningMarshaler
}
func (*MessageBuilder) Build ¶
Build uses the builder and a signer interface to build GMessage It is a shortcut for when separated flow is not required
func (*MessageBuilder) PrepareSigningInputs ¶
func (mb *MessageBuilder) PrepareSigningInputs(id ActorID) (*SignatureBuilder, error)
type MessageReceiver ¶
type MessageReceiver interface {
// Receives a validated Granite message from some other participant.
// Returns an error, wrapping (use errors.Is()/Unwrap()):
// - ErrValidationTooOld if the message is for a prior instance
// - ErrValidationWrongBase if the message has an invalid base chain
// - ErrReceivedAfterTermination if the message is received after the instance has terminated (a programming error)
// - both ErrReceivedInternalError and a cause if there was an internal error processing the message
ReceiveMessage(msg ValidatedMessage) error
// ReceiveAlarm signals the trigger of the alarm set by Clock.SetAlarm.
ReceiveAlarm() error
}
Receives a Granite protocol message. Calls to methods on this interface are expected to be serialized. The methods are not safe for concurrent use, and may panic if called concurrently.
type MessageValidator ¶
type MessageValidator interface {
// Validates a Granite message.
// An invalid message can never become valid, so may be dropped.
// Returns an error, wrapping (use errors.Is()/Unwrap()):
// - ErrValidationTooOld if the message is for a prior instance;
// - both ErrValidationNoCommittee and an error describing the reason;
// if there is no committee available with with to validate the message;
// - both ErrValidationInvalid and a cause if the message is invalid,
// Returns a validated message if the message is valid.
//
// Implementations must be safe for concurrent use.
ValidateMessage(msg *GMessage) (valid ValidatedMessage, err error)
}
type Network ¶
type Network interface {
// Returns the network's name (for signature separation)
NetworkName() NetworkName
// Requests that the message is signed and broadcasted, it should also be delivered locally
RequestBroadcast(mb *MessageBuilder) error
}
Endpoint to which participants can send messages.
type NetworkName ¶
type NetworkName string
NetworkName provides separation between different networks it is implicitly included in all signatures and VRFs
type Option ¶
type Option func(*options) error
Option represents a configurable parameter.
func WithDelta ¶
WithDelta sets the expected bound on message propagation latency. Defaults to 3 seconds if unspecified. Delta must be larger than zero.
The default of 3 seconds for the value of Dela is based previous observations of the upper bound on the GossipSub network-wide propagation time in Filecoin network.
func WithDeltaBackOffExponent ¶
WithDeltaBackOffExponent sets the delta back-off exponent for each round. Defaults to 1.3 if unspecified. It must be larger than zero.
func WithMaxCachedInstances ¶ added in v0.1.0
WithMaxCachedInstances sets the maximum number of instances for which validated messages are cached. Defaults to 10 if unset.
func WithMaxCachedMessagesPerInstance ¶ added in v0.1.0
WithMaxCachedMessagesPerInstance sets the maximum number of validated messages that are cached per instance. Defaults to 25K if unset.
func WithMaxLookaheadRounds ¶
WithMaxLookaheadRounds sets the maximum number of rounds ahead of the current round for which messages without justification are buffered. Setting a max value of larger than zero would aid gPBFT to potentially reach consensus in fewer rounds during periods of asynchronous broadcast as well as re-broadcast. Defaults to zero if unset.
func WithRebroadcastBackoff ¶
WithRebroadcastBackoff sets the duration after the gPBFT timeout has elapsed, at which all messages in the current round are rebroadcast if the round cannot be terminated, i.e. a strong quorum of senders has not yet been achieved.
The backoff duration grows exponentially up to the configured max. Defaults to exponent of 1.3 with 3s base backoff growing to a maximum of 30s.
func WithTracer ¶
WithTracer sets the Tracer for this gPBFT instance, which receives diagnostic logs about the state mutation. Defaults to no tracer if unspecified.
type PanicError ¶
type PanicError struct {
Err any
// contains filtered or unexported fields
}
func (*PanicError) Error ¶
func (e *PanicError) Error() string
type Participant ¶
type Participant struct {
// contains filtered or unexported fields
}
An F3 participant runs repeated instances of Granite to finalise longer chains.
func NewParticipant ¶
func NewParticipant(host Host, o ...Option) (*Participant, error)
func (*Participant) CurrentInstance ¶
func (p *Participant) CurrentInstance() uint64
func (*Participant) CurrentRound ¶
func (p *Participant) CurrentRound() uint64
func (*Participant) Describe ¶
func (p *Participant) Describe() string
func (*Participant) ReceiveAlarm ¶
func (p *Participant) ReceiveAlarm() (err error)
func (*Participant) ReceiveMessage ¶
func (p *Participant) ReceiveMessage(vmsg ValidatedMessage) (err error)
Receives a validated Granite message from some other participant.
func (*Participant) StartInstanceAt ¶ added in v0.0.3
func (p *Participant) StartInstanceAt(instance uint64, when time.Time) (err error)
func (*Participant) ValidateMessage ¶
func (p *Participant) ValidateMessage(msg *GMessage) (valid ValidatedMessage, err error)
ValidateMessage checks if the given message is valid. If invalid, an error is returned. ErrValidationInvalid indicates that the message will never be valid invalid and may be safely dropped.
type Payload ¶
type Payload struct {
// GossiPBFT instance (epoch) number.
Instance uint64
// GossiPBFT round number.
Round uint64
// GossiPBFT step name.
Step Phase
// The common data.
SupplementalData SupplementalData
// The value agreed-upon in a single instance.
Value ECChain
}
Fields of the message that make up the signature payload.
func (*Payload) MarshalForSigning ¶
func (p *Payload) MarshalForSigning(nn NetworkName) []byte
type PowerEntries ¶
type PowerEntries []PowerEntry
func (PowerEntries) Equal ¶ added in v0.0.4
func (p PowerEntries) Equal(o PowerEntries) bool
func (PowerEntries) Len ¶
func (p PowerEntries) Len() int
Len returns the number of entries in this PowerTable.
func (PowerEntries) Less ¶
func (p PowerEntries) Less(i, j int) bool
Less determines if the entry at index i should be sorted before the entry at index j. Entries are sorted descending order of their power, where entries with equal power are sorted by ascending order of their ID. This ordering is guaranteed to be stable, since a valid PowerTable cannot contain entries with duplicate IDs; see Validate.
func (*PowerEntries) MarshalCBOR ¶
func (t *PowerEntries) MarshalCBOR(w io.Writer) error
func (PowerEntries) Scaled ¶
func (p PowerEntries) Scaled() (scaled []uint16, total uint16, err error)
func (PowerEntries) Swap ¶
func (p PowerEntries) Swap(i, j int)
Swap swaps the entry at index i with the entry at index j. This function must not be called directly since it is used as part of sort.Interface.
func (*PowerEntries) UnmarshalCBOR ¶
func (t *PowerEntries) UnmarshalCBOR(r io.Reader) (err error)
type PowerEntry ¶
type PowerEntry struct {
ID ActorID
Power StoragePower
PubKey PubKey
}
PowerEntry represents a single entry in the PowerTable, including ActorID and its StoragePower and PubKey.
func (*PowerEntry) Equal ¶ added in v0.0.4
func (p *PowerEntry) Equal(o *PowerEntry) bool
func (*PowerEntry) MarshalCBOR ¶
func (t *PowerEntry) MarshalCBOR(w io.Writer) error
func (*PowerEntry) UnmarshalCBOR ¶
func (t *PowerEntry) UnmarshalCBOR(r io.Reader) (err error)
type PowerTable ¶
type PowerTable struct {
Entries PowerEntries // Slice to maintain the order. Meant to be maintained in order in order by (Power descending, ID ascending)
ScaledPower []uint16
Lookup map[ActorID]int // Maps ActorID to the index of the associated entry in Entries
Total StoragePower
ScaledTotal uint16
}
PowerTable maps ActorID to a unique index in the range [0, len(powerTable.Entries)). Entries is the reverse mapping to a PowerEntry.
func NewPowerTable ¶
func NewPowerTable() *PowerTable
NewPowerTable creates a new PowerTable from a slice of PowerEntry . It is more efficient than Add, as it only needs to sort the entries once.
func (*PowerTable) Add ¶
func (p *PowerTable) Add(entries ...PowerEntry) error
Add inserts one or more entries to this PowerTable.
Each inserted entry must meet the following criteria: * It must not already be present int the PowerTable. * It must have StoragePower larger than zero. * It must have a non-zero length public key.
func (*PowerTable) Copy ¶
func (p *PowerTable) Copy() *PowerTable
Copy creates a deep copy of this PowerTable.
func (*PowerTable) Get ¶
func (p *PowerTable) Get(id ActorID) (uint16, PubKey)
Get retrieves the scaled power, unscaled StoragePower and PubKey for the given id, if present in the table. Otherwise, returns 0/nil.
func (*PowerTable) Has ¶
func (p *PowerTable) Has(id ActorID) bool
Has check whether this PowerTable contains an entry for the given id.
func (*PowerTable) Len ¶
func (p *PowerTable) Len() int
Len returns the number of entries in this PowerTable.
func (*PowerTable) Less ¶
func (p *PowerTable) Less(i, j int) bool
Less determines if the entry at index i should be sorted before the entry at index j. Entries are sorted descending order of their power, where entries with equal power are sorted by ascending order of their ID. This ordering is guaranteed to be stable, since a valid PowerTable cannot contain entries with duplicate IDs; see Validate.
func (*PowerTable) Swap ¶
func (p *PowerTable) Swap(i, j int)
Swap swaps the entry at index i with the entry at index j. This function must not be called directly since it is used as part of sort.Interface.
func (*PowerTable) Validate ¶
func (p *PowerTable) Validate() error
Validate checks the validity of this PowerTable. Such table must meet the following criteria: * Its entries must be in order as defined by Less. * It must not contain any entries with duplicate ID. * All entries must have power larger than zero * All entries must have non-zero public key. * All entries must match their scaled powers. * PowerTable.Total must correspond to the total aggregated power of entries. * PowerTable.ScaledTotal must correspond to the total aggregated scaled power. * PowerTable.Lookup must contain the expected mapping of entry actor ID to index.
type QuorumResult ¶
type QuorumResult struct {
// Signers is an array of indexes into the powertable, sorted in increasing order
Signers []int
PubKeys []PubKey
Signatures [][]byte
}
func (QuorumResult) SignersBitfield ¶
func (q QuorumResult) SignersBitfield() bitfield.BitField
type Receiver ¶
type Receiver interface {
// Begins executing the protocol from some instance.
// The node will subsequently request the canonical chain to propose from the host.
// If the participant is already executing some instance, it will be abandoned.
StartInstanceAt(uint64, time.Time) error
MessageValidator
MessageReceiver
}
Interface from host to a network participant. Calls to methods on this interface are expected to be serialized. The methods are not safe for concurrent use, and may panic if called concurrently.
type SignatureBuilder ¶
type SignatureBuilder struct {
NetworkName NetworkName
ParticipantID ActorID
Payload Payload
Justification *Justification
PubKey PubKey
PayloadToSign []byte
VRFToSign []byte
}
SignatureBuilder's fields are exposed to facilitate JSON encoding
type Signatures ¶
type Signatures interface {
SigningMarshaler
Verifier
}
type SignerWithMarshaler ¶
type SignerWithMarshaler interface {
Signer
SigningMarshaler
}
type SigningMarshaler ¶
type SigningMarshaler interface {
// MarshalPayloadForSigning marshals the given payload into the bytes that should be signed.
// This should usually call `Payload.MarshalForSigning(NetworkName)` except when testing as
// that method is slow (computes a merkle tree that's necessary for testing).
// Implementations must be safe for concurrent use.
MarshalPayloadForSigning(NetworkName, *Payload) []byte
}
type StoragePower ¶
func NewStoragePower ¶
func NewStoragePower(value int64) StoragePower
Creates a new StoragePower struct with a specific value and returns the result
type SupplementalData ¶
type SupplementalData struct {
// Merkle-tree of instance-specific commitments. Currently empty but this will eventually
// include things like snark-friendly power-table commitments.
Commitments [32]byte
// The DagCBOR-blake2b256 CID of the power table used to validate the next instance, taking
// lookback into account.
PowerTable CID `cborgen:"maxlen=38"` // []PowerEntry
}
func (*SupplementalData) Eq ¶
func (d *SupplementalData) Eq(other *SupplementalData) bool
func (*SupplementalData) MarshalCBOR ¶
func (t *SupplementalData) MarshalCBOR(w io.Writer) error
func (*SupplementalData) UnmarshalCBOR ¶
func (t *SupplementalData) UnmarshalCBOR(r io.Reader) (err error)
type TipSet ¶
type TipSet struct {
// The EC epoch (strictly increasing).
Epoch int64
// The tipset's key (canonically ordered concatenated block-header CIDs).
Key TipSetKey `cborgen:"maxlen=760"` // 20 * 38B
// Blake2b256-32 CID of the CBOR-encoded power table.
PowerTable CID `cborgen:"maxlen=38"` // []PowerEntry
// Keccak256 root hash of the commitments merkle tree.
Commitments [32]byte
}
TipSet represents a single EC tipset.
func (*TipSet) MarshalForSigning ¶
type TipSetKey ¶
type TipSetKey = []byte
TipSetKey is the canonically ordered concatenation of the block CIDs in a tipset.
type Tracer ¶
Tracer collects trace logs that capture logical state changes. The primary purpose of Tracer is to aid debugging and simulation.
type ValidatedMessage ¶
type ValidatedMessage interface {
// Returns the validated message.
Message() *GMessage
}
Opaque type tagging a validated message.
type ValidationError ¶
type ValidationError struct {
// contains filtered or unexported fields
}
ValidationError signals that an error has occurred while validating a GMessage.
func (ValidationError) Error ¶
func (e ValidationError) Error() string
type Verifier ¶
type Verifier interface {
// Verifies a signature for the given public key.
// Implementations must be safe for concurrent use.
Verify(pubKey PubKey, msg, sig []byte) error
// Aggregates signatures from a participants.
Aggregate(pubKeys []PubKey, sigs [][]byte) ([]byte, error)
// VerifyAggregate verifies an aggregate signature.
// Implementations must be safe for concurrent use.
VerifyAggregate(payload, aggSig []byte, signers []PubKey) error
}