Skip to main content

MissingDependenciesAreRecoverable

Trait MissingDependenciesAreRecoverable 

Source
pub trait MissingDependenciesAreRecoverable:
    LockingBlobsTravelWithTheLock
    + CorrectValidatorAvailability
    + EventualSynchrony { }
Expand description

Lemma (A client can obtain everything a submission depends on). A client can always find, on the network, the data it needs in order to submit

  1. a valid block proposal — one every correct validator will accept — or
  2. a confirmed certificate, to a validator that does not yet have it;

and having found it, it can hand that data to any validator that is missing it.

The two halves are discovery and supply, and they are separate claims. Discovery is what a client does before it has a block at all; supply is what happens when a validator turns out to be behind. The second is easy once the first has happened, because building the proposal is what puts the data in the client’s own storage.

§What a valid proposal requires

Validity is not one condition, so the dependencies are not one kind. try_handle_block_proposal checks the following in order, and each check is a demand on data the validator must already hold. The right-hand column is how a client comes to hold it too.

the proposal is valid only ifthe validator needserror when it lacks ithow a client finds it
the chain exists at allthe chain description blobInactiveChainfrom the creating chain’s block
its height and parent hash continue the chain (verify_block_chaining)that chain’s certified prefixUnexpectedBlockHeightChainClient::synchronize_chain_state
its round is one this validator can accept (ProposalGate)the consensus state at that heightWrongRoundthe same, plus ChainClient::prepare_chain
it declares the chain’s current epoch (check_block_epoch)the committee for that epoch, hence the admin chain’s epoch eventEventsNotFound on the epoch streamby following the admin chain, which every client does
every incoming bundle it consumes is present in the inbox, equal, and in cursor orderthe sending chains’ message-bearing blocks, already delivered into that inboxMissingCrossChainUpdatesChainClient::find_received_certificates
its transactions execute correctlyevery blob the block publishes or reads, and every event it readsBlobsNotFound, EventsNotFounddownload_blob / download_pending_blob, and Client::sync_events_from_node

The inbox row is the one that needs care, because “valid” there means more than “the bundle exists”. remove_bundles_from_inboxes runs with must_be_present = true, so the bundle must already be in that validator’s inbox and equal to what it holds (IncomingBundlesMatchTheLocalInbox); and consumption must respect cursor order, skipping only bundles every message of which is skippable (DeliveryAndConsumptionAreOrdered). A client therefore cannot make a proposal valid by supplying a bundle in isolation: it supplies the sending chain’s blocks, and the validator derives the inbox from them itself.

§Discovery

Each row’s last column is a request to the network, not a lookup in something the client is assumed to have. The client learns of incoming messages by asking validators for their received logs (find_received_certificates), of blobs by downloading them, of events by sync_events_from_node, and of its own chain’s state by synchronizing it.

This half is quorum-dependent, and the code says so. find_received_certificates is documented as best effort: it finds only certificates confirmed among sufficiently many validators of the sending chain’s current committee — which holds “whenever a sender’s chain is still in use and is regularly upgraded to new committees”. A message from a chain that has since gone quiet across a reconfiguration is the case that is not covered, which is the availability question in super::assumptions::BlobRetention’s family rather than a defect in the supply argument below.

§Supply

Every dependency is by then available at the requester, locally. Building the proposal means the client’s own local node executed the block, and execution consumes exactly the data in the table; so holding it is a precondition of having a proposal to submit, not a coincidence. linera_core::updater states the messaging case as an invariant of local storage: it is “guaranteed to hold every block we needed to build a proposal”, because a bundle can only be consumed after its ordered message-bearing predecessors were downloaded.

For case (2) the argument is shorter: the block is certified, so its dependencies are retrievable at all (CertifiedBlockIsAvailable), and a client that processed the certificate wrote them as it went (BlockOutputsArePersisted). The blob arm of send_confirmed_certificate says so outright — “the certificate is confirmed, so the blobs must be in storage” — and treats a miss as an error rather than something to wait for.

This is a local availability claim, and it is stronger than the network-wide one: CertifiedBlockIsAvailable says the data can be obtained from some quorum, whereas here it is already in the hand of the party that must supply it. That is why the pushes read local storage and nothing else — read_certificates_for_heights, read_blobs_from_storage, get_next_height_to_preprocess — and why no class waits on a third party. ∎

The one case where local availability is not immediate is a lock the client is recovering rather than one it created: there the blobs were collected during synchronization, by LockingBlobsTravelWithTheLock.

§Two things this makes possible

A client need not follow a whole chain to supply what came from it. A chain it merely receives from is stored only at its message-bearing heights, and send_chain_information pushes exactly those, silently skipping heights it does not have; the validator executes the contiguous prefix and preprocesses any block above a gap, which is enough to deliver that block’s bundles. So a sparse chain the client never fully held is still enough to make the inbox row true at the validator.

The set to push is derived from local storage, not from the error. MissingCrossChainUpdates names only the bundles the current proposal needs and omits already-consumed ancestors the validator must execute first, so deriving the push from it would be unreliable; send_chain_information sends the whole locally-held range instead.

Why the pushes terminate. Each class carries a well-founded measure. send_confirmed_certificate latches sent_admin_chain / sent_blobs / sent_blocks, so each class is attempted once. send_block_proposal drains its blob_ids with mem::take and records publisher_chain_ids_sent per publishing chain. MissingCrossChainUpdates reports every missing bundle at once, so it too is a latch: the whole reported set is synced in one batch, and a validator that still reports missing bundles afterwards is not retried. A dependency that nobody can supply therefore surfaces as an error rather than looping.

Implementors§