metadata

package
v0.0.0-...-38d9c2b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: BSD-3-Clause Imports: 18 Imported by: 0

README

Reconfiguring a Simplex L1

In this document, we describe the reconfiguration technique of Simplex. We start by briefly recalling the Simplex consensus protocol, following by describing the challenges of reconfiguring a consensus system, after which we describe how epochs address these challenges. Specifically, we distinguish between ICM epochs and Simplex epochs and then explain how Simplex epochs are implemented. We then introduce the MSM (Metadata State-Machine) which is a consensus agnostic functionality that is used to manage metadata for consensus systems. Once we have explained how epochs and metadata are maintained, we describe the Simplex reconfiguration mechanism and how it is accomplished by using the MSM. We then explain how a new node that onboards the system can validate all blocks in the chain from genesis despite the fact that the validator set changed over time. Lastly, we outline how Simplex blocks are encoded.

1. The Simplex consensus protocol

The Simplex consensus protocol is a single leader / block proposer consensus protocol which assumes a semi-synchronous network. The protocol guarantees safety and liveness as long as less than a third of the nodes are faulty.

The protocol proceeds in rounds, where in each round a block is built by a different node, which is the leader / block proposer of that round. When receiving a block proposal from the leader of the round, a node broadcasts a vote for the block, and in order to progress to the next round, a node either collects a quorum of votes for that block (denoted as a notarization of the block), or it collects a quorum of votes that say that this round will be skipped in order to let the next leader propose a block. Upon collecting a quorum of votes for a block, a node broadcasts a finalization message to let the rest of the nodes know that it has seen a quorum of votes for the block. A block is considered as finalized (can be delivered to the application) once a quorum of finalization messages is collected for it, which convinces the node that no other node has skipped the round in which the block was proposed.

What differentiates Simplex from other consensus protocols is that it allows two or more nodes to progress to the next round due to different reasons. In particular, a node may collect a notarization for a block, while another node may collect a quorum of messages that say that the round is skipped. In such a case, the block that was notarized might be finalized later on, once a descendant block is finalized, but it might also be that the block will never be finalized, and a different block with the same height but in a different round will be finalized instead.

Challenges of reconfiguring a (Simplex) consensus system

Reconfiguring a consensus system is all about making sure that all nodes are consistent with how many nodes should vote on each block. While it may at first seem like a trivial problem, it isn't always so:

  • In some protocols, a block might be voted on while the predecessor block is still being voted on. If the predecessor block changes the amount of nodes that need to vote on a block, then this fact needs to be taken into account when counting the votes of the descendant block. However, it is often that the consensus instance needs to interact with its application in order to understand how the voting rules would change as a result of accepting the predecessor block which as mentioned, may not be accepted yet at the time of voting on its descendant. A technique known in the scientific literature to solve this problem involves "flushing the pipeline" by adding a series of "no-op" blocks after each block that reconfigures the consensus protocol. Flushing the pipeline of blocks makes it that the consensus protocol instances can delay the reconfiguration until the reconfiguration block has been accepted and processed by the application.
  • In Simplex, a block may be notarized but only finalized at a later round once a descendant block is finalized. This phenomenon is possible when in a round, a few nodes collect a notarization on the block, but a quorum or more of the nodes vote that no block will be finalized in that round, due to network conditions or the leader being faulty. As a result, if the block ends up being finalized it is only once a descendant block is finalized. This behavior makes reconfiguration challenging, since in a reconfiguration, the protocol needs to change its voting logic for descendant blocks based on a predecessor block which may not end up part of the chain.

2. Epoch management

ICM epochs vs Simplex epochs

The epoch management for ICM is defined as follows: Let $E$ be the current epoch with a start time of $T_{E}$ and end time of $T_{E} + D$. The first block whose build time is at least $T_{E} + D$ and is denoted BEend, is the last block of epoch $E$. The next block belongs to epoch $E + 1$ and its P-chain height is defined to be the P-chain height of BEend. The start time of epoch $E + 1$ is defined to be the build time of BEend.

The purpose of ICM epochs is to aggregate validator changes across all L1s into coarse grained units of reference.

Unlike ICM epochs, A Simplex epoch doesn't change unless the validator set of the L1 changes, as its role is to convey the validator set that is used to validate the quorum certificate of the block.

Further distinguishing themselves from ICM epochs, Simplex epoch numbers aren't successive, but are defined to be the sequence numbers of the blocks that seal them. More concretely, let $B_k$ be the last block of epoch $e$. Then, the next epoch is numbered $k$ and not $e+1$.

We denote such a block $B_k$ with sequence number $k$ which is part of epoch $e$ as the $sealing~block$ of epoch $e$. The validator set for epoch $k$ is derived from the P-chain height of the sealing block $B_k$.

In order to support ICM epochs, the Simplex protocol facilitates the encoding of both ICM epochs and Simplex epochs. Whenever a block is built, the block builder encodes the ICM epoch information (EpochStartTime, epoch_number, PChainEpochHeight) in the same manner as the proposerVM encodes it, and also encodes the Simplex epoch information, as well as some additional auxiliary information that may be used by other protocols, as explained below:

message SimplexEpochInfo {
   uint64 p_chain_reference_height = 1;
   uint64 epoch_number = 2;
   bytes prev_sealing_block_hash = 3; // The hash of the sealing block of the previous epoch
   uint64 next_p_chain_reference_height = 4;
   uint64 prev_vm_block_seq = 5; // The sequence of the previous VM block
   BlockValidationDescriptor block_validation_descriptor = 6; // Describes how to validate the blocks of the next epoch
   NextEpochApprovals next_epoch_approvals = 7; // The epoch change approvals of the next epoch by at least n-f nodes.
   sealing_block_seq = 8; // The sequence number of the sealing block of the current epoch, or 0 if a sealing block does not proceed this block.
}
  • The validator set of the epoch numbered epoch_number is derived from p_chain_reference_height.

  • The prev_sealing_block_hash is the hash of the sealing block of the previous epoch, and it is used to efficiently validate the sealing block of the previous epoch. If there is no previous epoch (i.e., the current epoch is the first ever epoch), then is equal to the hash of the first Simplex block.

  • The next_p_chain_reference_height is the P-chain height of the next epoch, otherwise it is set to 0.

  • The prev_vm_block_seq is the sequence number of the previous VM block, and it is used to efficiently find the last VM block upon startup.

  • The block_validation_descriptor describes how to validate the blocks of the next epoch. It is encoded only in the sealing block, and its presence identifies the sealing block of the epoch. It is used to verify the quorum certificates of blocks that are built in the next epoch.

  • The next_epoch_approvals is a canoto message that contains the approvals of the next epoch by at least n-f nodes. It is required in order to create the sealing block of the epoch and indicates that the nodes comprising the next epoch's validator set are ready to start the next epoch. It prevents cases where nodes in the current epoch creating a sealing block and moving to the next epoch while the nodes of the next epoch still replicating blocks and cannot yet participate in the consensus protocol.

  • The genesis block does not contain the Simplex epoch information, and it is implicitly set to be the zero values of the fields above.

Auxiliary information encoding

Apart from the ICM epoch information and the Simplex epoch information, the block also contains auxiliary information that may be used by other protocols that require their information to be collected via the process of block building by the protocol participants. A common way for such information collection is to have each participant supply its input as part of the auxiliary information in the block, when it is its turn to build a block.

The auxiliary information is encoded as a canoto message with the following schema:

message AuxiliaryInfo {
   bytes info = 1; // The auxiliary information. Can be empty if no auxiliary information is provided.
   uint64 prev_aux_info_seq = 2; // The sequence number of the previous block containing auxiliary information.
   uint32 application_id = 3; // The application ID of the auxiliary information (if applicable).
}

The prev_aux_info_seq field points to the previous block that contained auxiliary information, or 0 if the chain has no such block. As most blocks are not expected to contain auxiliary information, this provides a way for the application that uses the auxiliary information to efficiently find all blocks that contain auxiliary information without having to iterate over all blocks in the chain.

Simplex epoch update mechanism

Simplex updates its epoch information by block proposers building blocks with new updated epoch information, and the epoch information is then updated once the block is finalized. If a block contains incorrect epoch information, the block will be rejected by the nodes when they verify it.

Whenever the i'th node builds a block $B_{k+1}$ built on top of block $B_k$, it adopts the p_chain_reference_height of block $B_k$, since $B_{k+1}$ is in the same epoch. It then samples its P-chain height and if it detects that the validator set is different from the validator set that is derived from the P-chain height of p_chain_reference_height, it sets next_p_chain_reference_height to the sampled P-chain height.

When the block $B_{k+1}$ built by the i'th node is verified by nodes other than the block proposer, they ensure that:

  • The p_chain_reference_height hasn't changed from the previous block $B_k$.
  • next_p_chain_reference_height > p_chain_reference_height or next_p_chain_reference_height == 0.
  • If next_p_chain_reference_height > 0, the node has observed the P-chain height exists in the P-chain.
  • If next_p_chain_reference_height > 0, the validator set derived from the P-chain height corresponding to is different from the validator set derived by the P-chain height corresponding to p_chain_reference_height.

If block $B_{k+1}$ is considered to be the sealing block of its epoch, then the next block - block $B_{k+2}$ belongs to epoch k+1. When block $B_{k+2}$ is built, it will have its epoch_number set to $k+1$ and its p_chain_reference_height set to the next_p_chain_reference_height of $B_{k+1}$ and its next_p_chain_reference_height set to 0 (unless the block builder of $B_{k+2}$ has detected another validator change in the P-chain).

3. Consensus protocol metadata management using a Metadata State-Machine (MSM)

Motivation and introduction

Consensus systems may occasionally need to change the configuration or state of the consensus protocol uniformly and have the change appear as an atomic change. The simplest way of achieving a uniform and atomic state change is to encode the configuration or state change in a block and then have the block be finalized, and apply the change to the consensus protocol immediately after the block is finalized. Some consensus protocols (e.g. non-pipelined variants of PBFT) may facilitate such mechanisms, but others require a more sophisticated approach.

Consider for example, the Simplex consensus protocol, where a block B may be notarized in some round but not finalized in that round, but only in later rounds. In such a case, in order for that block to be finalized, an additional and unknown number of blocks need to be built on top of it, and one of them needs to be finalized. The number of blocks that need to be built on top of B is unknown because it is only known in retrospect, if enough nodes have sent finalization votes for B.

It is impossible to apply a configuration or state change to a consensus protocol if it is only known in retrospect when it should be applied.

To that end, we introduce the Metadata State-Machine (MSM) which lies between the consensus instance and the VM. The role of the MSM is to mask away the complexity of such state changes from the consensus protocol, let the consensus protocol be able to keep the existing application abstraction, and to prevent it from deviating from its usual standard operation. More specifically, the MSM detects when the state of the consensus protocol needs to change, and then it hijacks the block building and verification mechanisms of the VM, until the state change can be safely applied to the consensus protocol instance.

Similarly to the proposerVM, the MSM wraps the inner VM block with its own block wrapper and encodes the state transition data in the wrapped block.

However, it is important to distinguish between the MSM and a VM. The MSM is a very simple construction and in most cases it is simple enough to be implemented as a state transition function that receives as input the previous state along with auxiliary information, and outputs the new state. Unlike the VM, it has no transaction memory-pool, and it cannot perform any DB lookup, nor can it do any disk or network I/O.

When building a block $B_{i+1}$, the MSM has access to {$B_i$, $B_{i-1}$, ...} and to the corresponding finalization certificates, if applicable. The MSM intercepts all interaction between the Simplex consensus instance and the VM, namely the BlockBuilder, Storage, as well as the verification functions of blocks. Access to the finalization certificate of previous blocks is needed in order to know when the epoch has been sealed in order to transition to the next epoch. This is done by fetching for the finalization certificate of the sealing block of the current epoch.

Forcing chain progress

In some cases, there might not be any user activity, so blocks would not be naturally built by the underlying VM. However, the protocol may still need to extend the chain due to external reasons such as a change in the validator set, or auxiliary information that needs to be collected by a protocol independent to Simplex.

To that end, the MSM will build blocks that do not contain any transactions. Such blocks are called $Metablocks$, and they are built by the MSM without any interaction with the VM. A Metablock can carry auxiliary information that is needed by the Simplex consensus protocol, and can be a sealing block of an epoch. An epoch is not expected to contain a lot of metablocks, as the MSM will always prefer building a VM block over a metablock. The only way for a metablock to be built is for the MSM to determine that the current epoch should be sealed or the MSM has been given auxiliary information as input to be included in the next block, but there isn't any user activity that would trigger the VM to build a block.

Criteria for creation of the sealing block of an epoch

In order to create the sealing block, the following criteria need to be met:

  1. There is agreement upon which P-chain height will be used to select the validator set of the next epoch.

  2. At least n-f nodes belonging to the next epoch have approved the epoch change.

The first criterion is satisfied by nodes ensuring their P-chain possess the P-chain height corresponding to next_p_chain_reference_height. A block containing a specific next_p_chain_reference_height indicates that at least f+1 correct nodes have observed that P-chain height in the P-chain.

The second criterion is satisfied by the next_epoch_approvals field containing an aggregated BLS signature from at least n-f nodes belonging to the next epoch. Unlike the previous field, this field also needs to be updated by nodes in the next epoch that might not be in the current epoch. To that end, nodes in the next epoch create a NextEpochApprovals message with node_ids belonging only to themselves, and broadcast it to the nodes in the current epoch. Then, when a node in the current epoch builds a block, it sets the bits in the node_ids field corresponding to the union of the existing and new approving nodes, and aggregates the signatures of the nodes. The signature is over the info field of the AuxiliaryInfo and the next_p_chain_reference_height fields, to ensure that the nodes in the next epoch agree not only on the validator set, but also on any kind of application specific auxiliary information that might be needed by the next epoch. One example for such auxiliary information can be messages in a cryptographic protocol that are to be used in the next epoch.

However, it is up to the application that inspects the auxiliary information to decide when a NextEpochApprovals message should be sent or not. In fact, in order to avoid denial-of-service attacks, the application verification logic should be built in such a manner that refuses to mutate the auxiliary information once it is considered ready for the epoch being sealed. This ensures that the info of the AuxiliaryInfo and the next_p_chain_reference_height are constant once both of them are encoded in a block.

Epoch change using the MSM

When the MSM builds a block, it computes the Simplex epoch information and encodes it in the wrapped block. It then proceeds to ask the VM to build the block, and then it wraps the block built by the VM with its own simplex block.

We next describe how the MSM uses the simplex epoch information to perform the Simplex epoch change transition.

When the VM builds a block and the MSM determines that it is the sealing block of the current epoch, in order to progress to the next epoch, the MSM needs to make sure that the sealing block is finalized, otherwise the block might not end up being part of the blockchain but the node would regardless transition to the next epoch. To that end, the MSM builds artificial blocks that do not contain transactions, on top of the sealing block until it is finalized.

Such artificial blocks are termed "Telocks" and they are built by the MSM without any interaction with the VM. The name "Telock" is a portmanteau of "Telos" which is "end" in Greek, and "Block".

Hereafter, we will refer to an MSM block as a block made by the MSM and that wraps either a VM block or a Telock.

Telocks are treated by the Simplex consensus protocol as regular blocks, yet they only get stored in the WAL and are purged when the first block of the next epoch is accepted.

Telocks (as their name implies) can only exist at the end of an epoch, and never at the beginning or in the middle of an epoch.

Since Telocks are purged once an epoch changes, their block sequence numbers are also available for reuse in the next epoch. For example, if in epoch e the sealing block is $B_k$, there can be several telocks $B_{k+1}$, $B_{k+2}, ..., B_{k+l}$ belonging to epoch $e$. In the successive epoch $k$, the first block will have a sequence of $B_{k+1}$, the second block will have a sequence of $B_{k+2}$ and so on and so forth.

In order to know when the current epoch can be terminated and the next epoch can be instantiated, the MSM needs to determine if the sealing block has been finalized. A sealing block is identified by having the block_validation_descriptor defined and sealing_block_seq set to 0.

All blocks that are built after the sealing block (i.e., the telocks) will have their sealing_block_seq set to the sequence number of the sealing block. A telock is therefore identified by having a sealing_block_seq > 0. When moving to the next epoch, the node prunes its Telocks by dropping all blocks with sealing_block_seq > 0 in the last epoch.

Example of epoch change

Let's observe an example of a series of epoch information transitions made by the MSM.

Suppose the current epoch information is as follows (fields with zero values are omitted for succinctness):

{
  p_chain_reference_height: 100
  epoch_number: 20
}

Then, a block with sequence of 30 is built and a higher P-chain height (151) at which the validator set is different from the epoch's validator set is sampled. Therefore, the next_p_chain_reference_height is set to 151:

{
    p_chain_reference_height: 100
    epoch_number: 20
    next_p_chain_reference_height: 151
}

Later some other nodes build blocks and sample higher P-chain heights, however the next_p_chain_reference_height remains 151 since it is the first sampled P-chain height that indicates a different validator set.

At this point, the block builders start collecting approvals from the nodes belonging to the next epoch. Once enough approvals are gathered, the sealing block at sequence 40 can be built:

{
    p_chain_reference_height: 100
    epoch_number: 20
    next_p_chain_reference_height: 151
    block_validation_descriptor: [TGVhcm4gdG8g==, dmFsdWUgeW91cnNlbGY==, ... , XBwaW5lc3MuCg==]
    next_epoch_approvals: {
        node_ids: 10111101
        signature: "erpcyBqaGUg05z=="
    }
}

In case the sealing block is not finalized yet, the MSM will build descendent Telocks until it is finalized:

{
    p_chain_reference_height: 100
    epoch_number: 20
    sealing_block_seq: 40
}

All blocks in the next epoch, epoch 40 will have the following epoch information:

{
    p_chain_reference_height: 151
    epoch_number: 40
}

4. Onboarding new nodes across epochs

When onboarding a new node to the system, the node needs to replicate all blocks, and verify their finalization certificates before processing them. If the VM supports state sync, then a prefix of the blocks might be skipped if the node isn't a full node.

Nevertheless, a robust and complete technique that can verify finalization certificates across epochs is needed.

The challenge in verifying finalization certificates across epochs when onboarding a new node is that if the node is replicating the blocks of the P-chain in parallel, the process is asynchronous to the replication of the Simplex driven chain. When replicating a block belonging to the Simplex driven chain, the P-chain height of its epoch may still be replicating, or it might have already been replicated but cannot be easily reconstructed because there is no index between P-chain height to the validator set. In other words, given a present P-chain height h, it is impossible to know in constant time what was the validator set at P-chain height h' < h. Therefore, even if the P-chain is fully replicated prior to the blocks of the Simplex driven VM, it is impossible to efficiently verify the finalization certificate of previous epochs.

Determining how to validate the finalization certificates of an epoch

The block validation descriptor present in the sealing block of an epoch, describes how to validate the blocks of the next epoch. It contains the public keys of the validator set of the next epoch. For storage and bandwidth efficiency reasons, the block validation descriptor is only encoded in the sealing block.

Replicating a Simplex chain

When replicating a chain driven by Simplex, the onboarding node first replicates the P-chain and acquires the latest validator set of the chain. It then proceeds to fetch only the latest sealing block by querying a majority of nodes for the latest sealing block, and dropping candidate responses that aren't found in the responses of enough nodes.

Thanks to the fact that a sealing block $B_l$ will have the epoch number $k$ of its previous sealing block $B_k$ as well as the hash of $B_k$, it is possible to not only recursively find out the sealing blocks of all previous epochs, but also verify their authenticity.

After replicating the sealing blocks, the onboarding node has the public key(s) of the validator set for each epoch. It can thereafter parallelize the replication and finalization certificate verification of all blocks in the interval that resides in between the sealing blocks.

After replicating the blocks for the L1, the node can then proceed to process the blocks in-order using the VM.

The benefit of the aforementioned approach is that it allows onboarding nodes to verify blocks while replicating them, in contrast to replicate blocks in parallel and verify them only once they are all replicated, or replicate them sequentially backwards and verify them one by one.

4. Simplex block encoding

The blocks created by Simplex therefore have the following structure:

_________________________
____________________    |
|                  |  O |
|      Inner       |  u |
|      Block       |  t |
|                  |  e |
|__________________|  r |
|                       |
|                     B |
|                     l |
|  ICM epoch info     o |
|                     c |
| Simplex Epoch info  k |
|                       |
|   Auxiliary info      |
|_______________________|
message OuterBlock {
   bytes inner_block = 1; // The inner block built by the VM, opaque to Simplex.
   StateMachineMetadata metadata = 2; // The the metadata of the block.
}

where StateMachineMetadata is a protobuf message that contains the ICM epoch information, the Simplex epoch information, and auxiliary information:

message StateMachineMetadata {
  ICMEpochInfo icm_epoch_info = 1; // The ICM epoch information.
  SimplexEpochInfo simplex_epoch_info = 2; // The Simplex epoch information.
  bytes protocol_metadata = 3; // The Simplex protocol metadata, set by the Simplex consensus protocol.
  bytes blacklist = 4; // The blacklist of the Simplex protocol.
  AuxiliaryInfo auxiliary_info = 5; // The auxiliary information.
  uint64 p_chain_height = 6; // The P-chain height sampled when building the block.
  uint64 timestamp = 7; // The timestamp of the block, set by the block builder.
}

The digest of the simplex block is computed as follows:

Let $h_i$ be the hash of the inner block and $h_m$ be the hash of the metadata. The digest of the Simplex block is the hash of the following encoding: h_i || h_m where || denotes concatenation.

This way of hashing the block allows any holder of a finalization certificate for the block to authenticate the block while hiding the content of the inner block.

The ICM epoch info is encoded as a canoto message with the following schema:

message ICMEpochInfo {
   uint64 epoch_start_time = 1; // The start time
   uint64 epoch_number = 2; // The epoch number
   uint64 p_chain_epoch_height = 3; // The P-chain height of the epoch

The Simplex epoch information is a canoto encoded message with the following schema:

message NodeBLSMapping {
    bytes node_id = 1; // The nodeID
    bytes bls_key = 2; // The BLS key of the node
        uint64 weight = 3; // The weight of the node in the validator set, used for quorum calculations.
  }

message BlockValidationDescriptor {
  oneof BlockValidationType {
      AggregatedMembership aggregated_membership = 1;
      // Future types can be added here
  }
}

type AggregatedMembership {
  repeated NodeBLSMapping members = 1; // The BLS keys of the nodes in the next epoch
}

message NextEpochApprovals {
  bytes node_ids = 1; // The nodeIDs of nodes belonging to the next epoch that approve the epoch change.
                      // In practice, this is a bit-map that corresponds to the nodes in the block_validation_descriptor.
  bytes signature = 2; // An aggregated signature over the `info` of AuxiliaryInfo` and the `next_p_chain_reference_height`.
}

message SimplexEpochInfo {
   uint64 p_chain_reference_height = 1;
   uint64 epoch_number = 2;
   bytes prev_sealing_block_hash = 3; // The hash of the sealing block of the previous epoch
   uint64 next_p_chain_reference_height = 4;
   uint64 prev_vm_block_seq = 5; // The sequence of the previous VM block
   BlockValidationDescriptor block_validation_descriptor = 6; // Describes how to validate the blocks of the next epoch
   NextEpochApprovals next_epoch_approvals = 7; // The epoch change approvals of the next epoch by at least n-f nodes.
   uint64 sealing_block_seq = 8; // The sequence number of the sealing block of the current epoch.
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AggregatedMembership

type AggregatedMembership struct {
	Members []NodeBLSMapping `canoto:"repeated value,1"`
	// contains filtered or unexported fields
}

func (*AggregatedMembership) CachedCanotoSize

func (c *AggregatedMembership) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*AggregatedMembership) CalculateCanotoCache

func (c *AggregatedMembership) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*AggregatedMembership) CanotoSpec

func (*AggregatedMembership) CanotoSpec(types ...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*AggregatedMembership) Equals

func (c *AggregatedMembership) Equals(members []NodeBLSMapping) bool

func (*AggregatedMembership) MarshalCanoto

func (c *AggregatedMembership) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*AggregatedMembership) MarshalCanotoInto

func (c *AggregatedMembership) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*AggregatedMembership) UnmarshalCanoto

func (c *AggregatedMembership) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*AggregatedMembership) UnmarshalCanotoFrom

func (c *AggregatedMembership) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*AggregatedMembership) ValidCanoto

func (c *AggregatedMembership) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type ApprovalStore

type ApprovalStore struct {
	// contains filtered or unexported fields
}

func NewApprovalStore

func NewApprovalStore(signatureVerifier SignatureVerifier, validators NodeBLSMappings, logger common.Logger) *ApprovalStore

func (*ApprovalStore) Approvals

func (as *ApprovalStore) Approvals() ValidatorSetApprovals

func (*ApprovalStore) HandleApproval

func (as *ApprovalStore) HandleApproval(approval *common.ValidatorSetApproval, timestamp uint64)

func (*ApprovalStore) PutApprovals

func (as *ApprovalStore) PutApprovals(approvalStore *ApprovalStore)

PutApprovals copies all approvals from this store to the given approvalStore.

type AuxiliaryInfo

type AuxiliaryInfo struct {
	// Info is opaque bytes that can be used by applications to encode any information that describes
	// the current state for the application.
	Info []byte `canoto:"bytes,1"`
	// PrevAuxInfoSeq is a sequence number that applications can use to find previous AuxiliaryInfo in the chain.
	// It is zero if this is the first AuxiliaryInfo for this epoch.
	PrevAuxInfoSeq uint64 `canoto:"uint,2"`
	// VersionID is an identifier that identifies the application.
	// Can be used for backward-compatibility and upgrade purposes.
	VersionID common.VersionID `canoto:"uint,3"`
	// contains filtered or unexported fields
}

AuxiliaryInfo defines application-specific information for applications that might care about epoch change, such as threshold distributed public key generation.

func (*AuxiliaryInfo) CachedCanotoSize

func (c *AuxiliaryInfo) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*AuxiliaryInfo) CalculateCanotoCache

func (c *AuxiliaryInfo) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*AuxiliaryInfo) CanotoSpec

func (*AuxiliaryInfo) CanotoSpec(...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*AuxiliaryInfo) Clone

func (ai *AuxiliaryInfo) Clone() *AuxiliaryInfo

func (*AuxiliaryInfo) Equal

func (ai *AuxiliaryInfo) Equal(a *AuxiliaryInfo) bool

func (*AuxiliaryInfo) IsZero

func (ai *AuxiliaryInfo) IsZero() bool

func (*AuxiliaryInfo) MarshalCanoto

func (c *AuxiliaryInfo) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*AuxiliaryInfo) MarshalCanotoInto

func (c *AuxiliaryInfo) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*AuxiliaryInfo) UnmarshalCanoto

func (c *AuxiliaryInfo) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*AuxiliaryInfo) UnmarshalCanotoFrom

func (c *AuxiliaryInfo) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*AuxiliaryInfo) ValidCanoto

func (c *AuxiliaryInfo) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type AuxiliaryInfoGenVerifier

type AuxiliaryInfoGenVerifier interface {
	// IsLegalAppend checks whether the given auxiliary information byte slice [x]
	// can be appended to the history of auxiliary information for the given versionID, according to the app's rules.
	// Returns nil if the append is legal, or an error if the append is not legal or if any error occurs during the check.
	IsLegalAppend(versionID common.VersionID, nodes NodeBLSMappings, history [][]byte, x []byte) error

	// IsSufficient checks whether the given history of auxiliary information for the given versionID is sufficient
	// to start the epoch transition process.
	IsSufficient(versionID common.VersionID, nodes NodeBLSMappings, history [][]byte) (bool, error)

	// Generate generates an auxiliary information encoded as a byte slice based on the history of auxiliary information
	// for the given versionID in the current epoch so far.
	// If this is the first invocation in the epoch, DefaultVersionID() should be passed as the VersionID.
	// Otherwise, the versionID from previous blocks in the epoch should be used.
	// If the application deems the given history to be sufficient for the epoch change, it can return a nil byte slice,
	// in which case it will not be appended to the history.
	Generate(versionID common.VersionID, nodes NodeBLSMappings, history [][]byte) ([]byte, error)

	// DefaultVersionID returns the default VersionID that should be used for epochs that don't have any any auxiliary information yet.
	DefaultVersionID() common.VersionID
}

AuxiliaryInfoGenVerifier abstracts the application-specific logic for generating and verifying auxiliary information that is piggybacked on epoch transitions.

type BlockBuilder

type BlockBuilder interface {
	BuildBlock(ctx context.Context, pChainHeight uint64) (avalanchego.VMBlock, error)

	// WaitForPendingBlock returns when either the given context is cancelled,
	// or when the VM signals that a block should be built.
	WaitForPendingBlock(ctx context.Context)
}

BlockBuilder builds a new VM block with the given observed P-chain height.

type BlockRetriever

type BlockRetriever func(seq uint64, digest common.Digest) (StateMachineBlock, *common.Finalization, error)

BlockRetriever retrieves a block and its finalization status given the block's sequence number and expected digest. If the block cannot be found it returns ErrBlockNotFound. If an error occurs during retrieval, it returns a non-nil error.

type BlockType

type BlockType uint8
const (
	BlockTypeNormal        BlockType = iota + 1 // In-epoch block with no epoch transition in progress
	BlockTypeZero                               // The first ever Simplex block; establishes the first epoch
	BlockTypeTelock                             // Built after the sealing block to extend its epoch until the sealing block finalizes
	BlockTypeSealing                            // Seals its epoch; only Telocks may follow it
	BlockTypeTransitioning                      // An epoch transition is in progress (collecting aux info and approvals) but the epoch is not yet sealed
)

func (BlockType) String

func (bt BlockType) String() string

type BlockValidationDescriptor

type BlockValidationDescriptor struct {
	AggregatedMembership AggregatedMembership `canoto:"value,1"`
	// contains filtered or unexported fields
}

func (*BlockValidationDescriptor) CachedCanotoSize

func (c *BlockValidationDescriptor) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*BlockValidationDescriptor) CalculateCanotoCache

func (c *BlockValidationDescriptor) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*BlockValidationDescriptor) CanotoSpec

func (*BlockValidationDescriptor) CanotoSpec(types ...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*BlockValidationDescriptor) Clone

func (*BlockValidationDescriptor) Equals

func (*BlockValidationDescriptor) MarshalCanoto

func (c *BlockValidationDescriptor) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*BlockValidationDescriptor) MarshalCanotoInto

func (c *BlockValidationDescriptor) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*BlockValidationDescriptor) UnmarshalCanoto

func (c *BlockValidationDescriptor) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*BlockValidationDescriptor) UnmarshalCanotoFrom

func (c *BlockValidationDescriptor) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*BlockValidationDescriptor) ValidCanoto

func (c *BlockValidationDescriptor) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type Config

type Config struct {
	// LatestPersistedHeight is the height of the most recently persisted block.
	LatestPersistedHeight uint64
	// MaxBlockBuildingWaitTime is the maximum duration to wait for the VM to build a block
	// before producing a block without an inner block.
	MaxBlockBuildingWaitTime time.Duration
	// TimeSkewLimit is the maximum allowed time difference between a block's timestamp and the current time.
	TimeSkewLimit time.Duration
	// GetTime returns the current time.
	GetTime func() time.Time
	// GetPChainHeightForProposing returns the latest known P-chain height to be used when building a block.
	GetPChainHeightForProposing func() uint64
	// GetPChainHeightForVerifying returns the latest known P-chain height to be used when verifying a block.
	GetPChainHeightForVerifying func() uint64
	// BlockBuilder builds new VM blocks.
	BlockBuilder BlockBuilder
	// Logger is used for logging state machine operations.
	Logger common.Logger
	// GetValidatorSet retrieves the validator set at a given P-chain height.
	GetValidatorSet ValidatorSetRetriever
	// GetBlock retrieves a previously built or finalized block.
	GetBlock BlockRetriever
	// SignatureAggregatorCreator creates a new SignatureAggregator for aggregating validator signatures for epoch transitions.
	SignatureAggregatorCreator common.SignatureAggregatorCreator
	// KeyAggregator aggregates public keys from validators.
	KeyAggregator KeyAggregator
	// SignatureVerifier verifies signatures from validators.
	SignatureVerifier SignatureVerifier
	// PChainProgressListener listens for changes in the P-chain height to trigger block building or epoch transitions.
	PChainProgressListener PChainProgressListener
	// LastNonSimplexBlockPChainHeight is the P-chain height of the last block built by a non-Simplex proposer.
	// It is used to determine the validator set of the first ever Simplex epoch.
	LastNonSimplexBlockPChainHeight uint64
	// LastNonSimplexInnerBlock is the inner block of the last block built by a non-Simplex proposer.
	LastNonSimplexInnerBlock avalanchego.VMBlock
	// GenesisValidatorSet is the validator set used for the genesis block.
	GenesisValidatorSet NodeBLSMappings
	// MyNodeID
	MyNodeID common.NodeID
	// Signer
	Signer common.Signer
	// ComputeICMEpoch computes the ICM epoch information in order to know which P-chain height to encode.
	ComputeICMEpoch ICMEpochTransition
	// AuxiliaryInfoApp abstracts an application that piggybacks on epoch changes.
	AuxiliaryInfoApp AuxiliaryInfoGenVerifier
}

Config contains the dependencies and configuration parameters needed to initialize the StateMachine.

type ICMEpochInfo

type ICMEpochInfo struct {
	// EpochStartTime is the Unix timestamp when this ICM epoch started.
	EpochStartTime uint64 `canoto:"uint,1"`
	// EpochNumber is the sequential identifier of this ICM epoch.
	EpochNumber uint64 `canoto:"uint,2"`
	// PChainEpochHeight is the P-chain height associated with this ICM epoch.
	PChainEpochHeight uint64 `canoto:"uint,3"`
	// contains filtered or unexported fields
}

ICMEpochInfo is the ICM epoch information that is maintained by the StateMachine and used for the ICM protocol. The StateMachine maintains this information identically to how the proposerVM maintains it, and it does so by building the ICMEpochInput and then passing it into the StateMachine's ComputeICMEpoch function.

func (*ICMEpochInfo) CachedCanotoSize

func (c *ICMEpochInfo) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*ICMEpochInfo) CalculateCanotoCache

func (c *ICMEpochInfo) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*ICMEpochInfo) CanotoSpec

func (*ICMEpochInfo) CanotoSpec(...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*ICMEpochInfo) Clone

func (ei *ICMEpochInfo) Clone() ICMEpochInfo

Clone returns a copy of the ICMEpochInfo, skipping the canoto cache so it is safe to call while the original is being marshaled.

func (*ICMEpochInfo) Equal

func (ei *ICMEpochInfo) Equal(other *ICMEpochInfo) bool

func (*ICMEpochInfo) MarshalCanoto

func (c *ICMEpochInfo) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*ICMEpochInfo) MarshalCanotoInto

func (c *ICMEpochInfo) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*ICMEpochInfo) UnmarshalCanoto

func (c *ICMEpochInfo) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*ICMEpochInfo) UnmarshalCanotoFrom

func (c *ICMEpochInfo) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*ICMEpochInfo) ValidCanoto

func (c *ICMEpochInfo) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type ICMEpochInput

type ICMEpochInput struct {
	// ParentPChainHeight is the P-chain height recorded in the parent block.
	ParentPChainHeight uint64
	// ParentTimestamp is the timestamp of the parent block.
	ParentTimestamp time.Time
	// ChildTimestamp is the timestamp of the block being built.
	ChildTimestamp time.Time
	// ParentEpoch is the ICM epoch information from the parent block.
	ParentEpoch ICMEpochInfo
}

ICMEpochInput defines the input for computing the ICM Epoch information for the next block.

type ICMEpochTransition

type ICMEpochTransition func(ICMEpochInput) ICMEpochInfo

ICMEpochTransition computes the next ICM epoch given the current upgrade configuration and epoch input.

type KeyAggregator

type KeyAggregator interface {
	AggregateKeys(keys ...[]byte) ([]byte, error)
}

KeyAggregator combines multiple public keys into a single aggregated public key.

type NextEpochApprovals

type NextEpochApprovals struct {
	NodeIDs   []byte `canoto:"bytes,1"`
	Signature []byte `canoto:"bytes,2"`
	// contains filtered or unexported fields
}

func (*NextEpochApprovals) CachedCanotoSize

func (c *NextEpochApprovals) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*NextEpochApprovals) CalculateCanotoCache

func (c *NextEpochApprovals) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*NextEpochApprovals) CanotoSpec

func (*NextEpochApprovals) CanotoSpec(...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*NextEpochApprovals) Clone

func (nea *NextEpochApprovals) Clone() *NextEpochApprovals

func (*NextEpochApprovals) Equals

func (nea *NextEpochApprovals) Equals(other *NextEpochApprovals) bool

func (*NextEpochApprovals) MarshalCanoto

func (c *NextEpochApprovals) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*NextEpochApprovals) MarshalCanotoInto

func (c *NextEpochApprovals) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*NextEpochApprovals) UnmarshalCanoto

func (c *NextEpochApprovals) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*NextEpochApprovals) UnmarshalCanotoFrom

func (c *NextEpochApprovals) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*NextEpochApprovals) ValidCanoto

func (c *NextEpochApprovals) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type NodeBLSMapping

type NodeBLSMapping struct {
	NodeID avalanchego.NodeID `canoto:"fixed bytes,1"`
	BLSKey []byte             `canoto:"bytes,2"`
	Weight uint64             `canoto:"uint,3"`
	// contains filtered or unexported fields
}

func (*NodeBLSMapping) CachedCanotoSize

func (c *NodeBLSMapping) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*NodeBLSMapping) CalculateCanotoCache

func (c *NodeBLSMapping) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*NodeBLSMapping) CanotoSpec

func (*NodeBLSMapping) CanotoSpec(...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*NodeBLSMapping) Clone

func (nbm *NodeBLSMapping) Clone() NodeBLSMapping

func (*NodeBLSMapping) Equals

func (nbm *NodeBLSMapping) Equals(other *NodeBLSMapping) bool

func (*NodeBLSMapping) MarshalCanoto

func (c *NodeBLSMapping) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*NodeBLSMapping) MarshalCanotoInto

func (c *NodeBLSMapping) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*NodeBLSMapping) UnmarshalCanoto

func (c *NodeBLSMapping) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*NodeBLSMapping) UnmarshalCanotoFrom

func (c *NodeBLSMapping) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*NodeBLSMapping) ValidCanoto

func (c *NodeBLSMapping) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type NodeBLSMappings

type NodeBLSMappings []NodeBLSMapping

func (NodeBLSMappings) Clone

func (nbms NodeBLSMappings) Clone() NodeBLSMappings

func (NodeBLSMappings) Equal

func (nbms NodeBLSMappings) Equal(other NodeBLSMappings) bool

func (NodeBLSMappings) IndexByNodeID

func (nbms NodeBLSMappings) IndexByNodeID() map[avalanchego.NodeID]int

IndexByNodeID returns a mapping from NodeID to the validator's index in the set, which is the position used by approval bitmasks.

func (NodeBLSMappings) Nodes

func (nbms NodeBLSMappings) Nodes() common.Nodes

func (NodeBLSMappings) SelectSubset

func (nbms NodeBLSMappings) SelectSubset(bitmask avalanchego.Bitmask) []common.NodeID

type PChainProgressListener

type PChainProgressListener interface {
	// WaitForProgress should block until either the context is cancelled, or the P-chain height has increased from the provided pChainHeight.
	WaitForProgress(ctx context.Context, pChainHeight uint64) error
}

PChainProgressListener listens for changes in the P-chain height.

type RawBlock

type RawBlock struct {
	Metadata        StateMachineMetadata `canoto:"value,1"`
	InnerBlockBytes []byte               `canoto:"bytes,2"`
	// contains filtered or unexported fields
}

RawBlock is the serialized form of a StateMachineBlock.

func (*RawBlock) CachedCanotoSize

func (c *RawBlock) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*RawBlock) CalculateCanotoCache

func (c *RawBlock) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*RawBlock) CanotoSpec

func (*RawBlock) CanotoSpec(types ...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*RawBlock) MarshalCanoto

func (c *RawBlock) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*RawBlock) MarshalCanotoInto

func (c *RawBlock) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*RawBlock) UnmarshalCanoto

func (c *RawBlock) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*RawBlock) UnmarshalCanotoFrom

func (c *RawBlock) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*RawBlock) ValidCanoto

func (c *RawBlock) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type SignatureVerifier

type SignatureVerifier interface {
	VerifySignature(signature []byte, message []byte, publicKey []byte) error
}

SignatureVerifier verifies a cryptographic signature against a message and public key. Used to verify Approvals from validators for epoch transitions.

type SimplexEpochInfo

type SimplexEpochInfo struct {
	// PChainReferenceHeight is the P-Chain height that the StateMachine uses as a reference for the current epoch.
	// The validator set is determined based on the validators on the P-Chain at the PChainReferenceHeight.
	PChainReferenceHeight uint64 `canoto:"uint,1"`
	// EpochNumber is the current epoch number.
	// The first epoch is numbered 1, and each successive epoch is numbered according to the block sequence
	// of the sealing block of the previous epoch.
	EpochNumber uint64 `canoto:"uint,2"`
	// PrevSealingBlockHash is the hash of the sealing block of the previous epoch.
	// It is set to the hash of the zero block in the first epoch, and in subsequent epochs it is set to be
	// the hash of the sealing block of the previous epoch.
	// This is used to be able to quickly fetch and verify the sealing blocks without having to retrieve the interleaving blocks,
	// which allows to bootstrap the BLS keys of the validator set for each epoch before fully syncing the interleaving blocks.
	PrevSealingBlockHash [32]byte `canoto:"fixed bytes,3"`
	// NextPChainReferenceHeight is the P-Chain height that the StateMachine uses as a reference for the next epoch.
	// When the NextPChainReferenceHeight is > 0, it means the StateMachine is on its way to transition to a new epoch
	// in which the validator set will be based on the given P-chain height.
	// It sets the PChainReferenceHeight for the next epoch.
	NextPChainReferenceHeight uint64 `canoto:"uint,4"`
	// PrevVMBlockSeq is the block sequence of the previous block that has a VM block (inner block).
	// This is used to know on which VM block to build the next block.
	PrevVMBlockSeq uint64 `canoto:"uint,5"`
	// BlockValidationDescriptor is the metadata that describes the validator set of the next epoch.
	// It is only set in the sealing block and zero block, and nil in all other blocks.
	BlockValidationDescriptor *BlockValidationDescriptor `canoto:"pointer,6"`
	// NextEpochApprovals is the metadata that contains the approvals from validators for the next epoch.
	// It is set only in the sealing block and the blocks preceding it starting from a block that has a NextPChainReferenceHeight set.
	NextEpochApprovals *NextEpochApprovals `canoto:"pointer,7"`
	// SealingBlockSeq is the block sequence of the sealing block of the current epoch.
	// It defines the validator set of the next epoch.
	// It is set once the first Telock is built and is copied over to subsequent Telocks.
	SealingBlockSeq uint64 `canoto:"uint,8"`
	// contains filtered or unexported fields
}

SimplexEpochInfo is metadata used by the StateMachine.

func (*SimplexEpochInfo) CachedCanotoSize

func (c *SimplexEpochInfo) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*SimplexEpochInfo) CalculateCanotoCache

func (c *SimplexEpochInfo) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*SimplexEpochInfo) CanotoSpec

func (*SimplexEpochInfo) CanotoSpec(types ...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*SimplexEpochInfo) Clone

func (sei *SimplexEpochInfo) Clone() SimplexEpochInfo

Clone returns a shallow copy of the SimplexEpochInfo, skipping the canoto cache so it is safe to call while the original is being marshaled.

func (*SimplexEpochInfo) Equal

func (sei *SimplexEpochInfo) Equal(other *SimplexEpochInfo) bool

func (*SimplexEpochInfo) IsZero

func (sei *SimplexEpochInfo) IsZero() bool

func (*SimplexEpochInfo) MarshalCanoto

func (c *SimplexEpochInfo) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*SimplexEpochInfo) MarshalCanotoInto

func (c *SimplexEpochInfo) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*SimplexEpochInfo) NextState

func (sei *SimplexEpochInfo) NextState() state

NextState returns the state used to build (or verify) the block that follows the one described by the SimplexEpochInfo.

func (*SimplexEpochInfo) UnmarshalCanoto

func (c *SimplexEpochInfo) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*SimplexEpochInfo) UnmarshalCanotoFrom

func (c *SimplexEpochInfo) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*SimplexEpochInfo) ValidCanoto

func (c *SimplexEpochInfo) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type StateMachine

type StateMachine struct {
	*Config
	// contains filtered or unexported fields
}

StateMachine manages block building and verification across epoch transitions.

func NewStateMachine

func NewStateMachine(config *Config) (*StateMachine, error)

func (*StateMachine) BuildBlock

func (sm *StateMachine) BuildBlock(ctx context.Context, metadata common.ProtocolMetadata, blacklist common.Blacklist) (*StateMachineBlock, error)

BuildBlock constructs the next block on top of the given parent block, and passes in the provided simplex metadata and blacklist.

func (*StateMachine) HandleApproval

func (sm *StateMachine) HandleApproval(approval *common.ValidatorSetApproval, timestamp uint64)

func (*StateMachine) VerifyBlock

func (sm *StateMachine) VerifyBlock(ctx context.Context, block *StateMachineBlock) error

VerifyBlock validates a proposed block by checking its metadata, epoch info, and inner block against the previous block and the current state.

func (*StateMachine) WaitForPendingBlock

func (sm *StateMachine) WaitForPendingBlock(ctx context.Context, currentRoundMetadata common.ProtocolMetadata)

WaitForPendingBlock waits for either the VM to signal that a block is ready to be built, or for the state machine to determine that a block should be built immediately due to an epoch transition. In the latter case, we only wait up to MaxBlockBuildingWaitTime before returning.

type StateMachineBlock

type StateMachineBlock struct {
	// InnerBlock is the VM-level block, or nil if this is a block without an inner block (e.g., a Telock block).
	InnerBlock avalanchego.VMBlock
	// Metadata contains the state machine metadata associated with this block.
	Metadata StateMachineMetadata
}

A StateMachineBlock is a representation of a parsed OuterBlock, containing the inner block and the metadata.

func (*StateMachineBlock) Bytes

func (smb *StateMachineBlock) Bytes() []byte

func (*StateMachineBlock) Clone

func (smb *StateMachineBlock) Clone() StateMachineBlock

Clone returns a shallow copy of the block, skipping the canoto caches so it is safe to call while the original is being marshaled.

func (*StateMachineBlock) Digest

func (smb *StateMachineBlock) Digest() [32]byte

Digest returns the SHA-256 hash of the combined inner block digest and metadata digest.

func (*StateMachineBlock) SealingBlockInfo

func (smb *StateMachineBlock) SealingBlockInfo() *common.SealingBlockInfo

SealingBlockInfo returns the information derived from this block's BlockValidationDescriptor: the validator set of the next epoch and the hash of the previous sealing block. It returns the zero value for blocks that carry no descriptor (any block that is neither a sealing block nor the zero block).

func (*StateMachineBlock) Type

func (smb *StateMachineBlock) Type() BlockType

type StateMachineMetadata

type StateMachineMetadata struct {
	// SimplexEpochInfo is the metadata that the StateMachine uses for its own epoching.
	SimplexEpochInfo SimplexEpochInfo `canoto:"value,1"`
	// SimplexProtocolMetadata is the metadata that Simplex uses for its protocol, such as sequence and round number.
	SimplexProtocolMetadata common.ProtocolMetadata `canoto:"value,2"`
	// SimplexBlacklist is the metadata that Simplex uses to keep track of blacklisted nodes.
	// Blacklisted nodes do not become leaders.
	SimplexBlacklist common.Blacklist `canoto:"value,3"`
	// PChainHeight is the P-Chain height that the StateMachine sampled at the time of building the block.
	// It's used for ICM epoching, not for Simplex epoching.
	// For Simplex epoching, the P-Chain height that matters is the PChainReferenceHeight in the SimplexEpochInfo.
	PChainHeight uint64 `canoto:"uint,4"`
	// Timestamp is the time when the block is being built, in milliseconds since Unix epoch.
	Timestamp uint64 `canoto:"uint,5"`
	// ICMEpochInfo is the metadata that the StateMachine uses for ICM epoching.
	ICMEpochInfo ICMEpochInfo `canoto:"value,6"`
	// AuxiliaryInfo is application-specific information that the StateMachine doesn't need to understand,
	// but can be used by applications that care about epoch changes, such as threshold distributed public key generation.
	AuxiliaryInfo *AuxiliaryInfo `canoto:"pointer,7"`
	// contains filtered or unexported fields
}

StateMachineMetadata defines the metadata that the StateMachine uses to transition between epochs, and maintain ICM epoch information. TODO: change SimplexProtocolMetadata and SimplexBlacklist to be non-opaque types. TODO: This requires to encode the protocol metadata and blacklist using canoto.

func (*StateMachineMetadata) CachedCanotoSize

func (c *StateMachineMetadata) CachedCanotoSize() uint64

CachedCanotoSize returns the previously calculated size of the Canoto representation from CalculateCanotoCache.

If CalculateCanotoCache has not yet been called, it will return 0.

If the struct has been modified since the last call to CalculateCanotoCache, the returned size may be incorrect.

func (*StateMachineMetadata) CalculateCanotoCache

func (c *StateMachineMetadata) CalculateCanotoCache()

CalculateCanotoCache populates size and OneOf caches based on the current values in the struct.

It is not safe to copy this struct concurrently.

func (*StateMachineMetadata) CanotoSpec

func (*StateMachineMetadata) CanotoSpec(types ...reflect.Type) *canoto.Spec

CanotoSpec returns the specification of this canoto message.

func (*StateMachineMetadata) Clone

Clone returns a shallow copy of the metadata, skipping the canoto caches so it is safe to call while the original is being marshaled.

func (*StateMachineMetadata) MarshalCanoto

func (c *StateMachineMetadata) MarshalCanoto() []byte

MarshalCanoto returns the Canoto representation of this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*StateMachineMetadata) MarshalCanotoInto

func (c *StateMachineMetadata) MarshalCanotoInto(w canoto.Writer) canoto.Writer

MarshalCanotoInto writes the struct into a canoto.Writer and returns the resulting canoto.Writer. Most users should just use MarshalCanoto.

It is assumed that CalculateCanotoCache has been called since the last modification to this struct.

It is assumed that this struct is ValidCanoto.

It is not safe to copy this struct concurrently.

func (*StateMachineMetadata) UnmarshalCanoto

func (c *StateMachineMetadata) UnmarshalCanoto(bytes []byte) error

UnmarshalCanoto unmarshals a Canoto-encoded byte slice into the struct.

During parsing, the canoto cache is saved.

func (*StateMachineMetadata) UnmarshalCanotoFrom

func (c *StateMachineMetadata) UnmarshalCanotoFrom(r canoto.Reader) error

UnmarshalCanotoFrom populates the struct from a canoto.Reader. Most users should just use UnmarshalCanoto.

During parsing, the canoto cache is saved.

This function enables configuration of reader options.

func (*StateMachineMetadata) ValidCanoto

func (c *StateMachineMetadata) ValidCanoto() bool

ValidCanoto validates that the struct can be correctly marshaled into the Canoto format.

Specifically, ValidCanoto ensures: 1. All OneOfs are specified at most once. 2. All strings are valid utf-8. 3. All custom fields are ValidCanoto.

type ValidatorSetApprovals

type ValidatorSetApprovals []common.ValidatorSetApproval

func (ValidatorSetApprovals) Filter

func (ValidatorSetApprovals) UniqueByNodeID

func (vsa ValidatorSetApprovals) UniqueByNodeID() ValidatorSetApprovals

type ValidatorSetRetriever

type ValidatorSetRetriever func(pChainHeight uint64) (NodeBLSMappings, error)

ValidatorSetRetriever retrieves the validator set at a given P-chain height.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL