Releasing Linera SDK v0.10
Announcing v0.10 of the Linera SDK for Rust. This version includes a multitude of changes with the goal of making Linera applications simpler to write. Here’s a quick overview:
Separating execution states from the storage state of an application
Applications now define two additional types tracking their runtime execution state: one type for the contract execution, implementing the Contract trait, and one for the service execution, implementing the Service trait. The new types should include a field with an instance of the storage state (usually a View). Previously, the storage state implemented both the Contract
and the Service
trait directly, limiting the ability to define non-persistent runtime state.
Removing the *Context arguments
Most callbacks in the Contract
and the Service
traits had a *Context
argument (e.g. OperationContext
). This is replaced by a runtime object (ContractRuntime or ServiceRuntime) passed in the new constructor of the traits Contract and Service. It is expected that the user-defined execution state implementing these traits will store the runtime object in-memory for the duration of a transaction. Runtime objects can be used to obtain information from the current execution (current block height, the authenticated signer, etc.) as well as to ask to execute actions, like sending messages or calling other applications. This allows simplifying the return values of the entry points by removing the *Outcome
types which used to hold scheduled outgoing messages.
Defining contract execution lifetime and removing “sessions”
Contract execution states now have a well-defined lifetime, which is the duration of the current block transaction. Specifically, an application’s contract type is loaded into memory when its new
constructor is called, and stays in memory until its finalize
method is called at the end of the transaction. This means that an application may be called multiple times inside the same transaction, and it will only be loaded once. This not only saves fuel, but also allows the application to keep track of what’s happening in the transaction across the calls it handles with volatile fields in the contract type that aren’t persisted to the application state.
Contracts don’t need to implement the finalize
method in most cases. A default implementation is provided that persists the application state. If a contract overrides the finalize
method, they should take care to persist their changes to the application state. The main use case for the new method is to provide a last chance to the contract to perform some final checks and abort the transaction if they don’t pass. An example usage scenario is for flash loans.
With the new functionality introduced by the finalize
method, it was possible to remove the experimental (undocumented) concept of “sessions” entirely from contracts. Sessions are useful in some cases such as flash loans, but they were hard to explain, and most applications did not need to use them. With the finalize method, the same functionality can be implemented within contracts only when needed.
Merging operations and application calls
Finally, operations and application calls were merged. When a contract receives a call from another contract, it is now handled by execute_operation
instead of a separate handle_application_call
. This simplifies the code and makes the concepts clearer. Operations are “public” entry-points, where either block proposers request them to be executed, or applications request other applications to use them (through cross-application calls). And messages are the “private” entry-points, because messages can only be sent from the same application that handles them.
Test your applications in the new Linera Devnet
Applications built on SDK 0.10 can be tested on our new Devnet instance labeled “2024-03-26”.