Skip to content

Commit

Permalink
Update to SDK v0.11.1 (#105)
Browse files Browse the repository at this point in the history
* Rename "initialize" to "instantiate"

Use the new terminology used by the SDK.

* Remove `.cargo/config.toml` reference

It is not created by the SDK anymore, and specifying the Wasm target
should be done explicitly when building.

* Remove obsolete associated types from ABI

They were moved to the `Contract` and `Service` traits instead.

* Update "Contract" section

Tweak the example Counter contract to use the new SDK version.

* Update the "Service" section

Tweak the example Counter service to use the new SDK version.

* Update "Cross-Chain Messages" section

Tweak the description and examples to use the new SDK version.

* Tweak "Calling other Applications" section

Clarify which application is the dependency and add links to the
mentioned examples.

* Update the "Writing Tests" section

Remove all the now obsolete information about running tests for specific
platforms, and update the examples to use the updated SDK.

* Update the "Contract Finalization" section

Use the updated SDK and the new `load`/`store` terminology.

* Update "Applications that Handle Assets" example

Use the updated `Contract` trait implementation.

* Update `linera-protocol` submodule to 0.11.1

And update the release branch and version files.

* Add symlink to Cargo.lock + improve README

---------

Co-authored-by: Janito Vaqueiro Ferreira Filho <[email protected]>
  • Loading branch information
ma2bd and jvff authored May 20, 2024
1 parent 65eb4f5 commit 3abfe78
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 318 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ cd linera-protocol
git fetch origin
git checkout $(git rev-parse $REMOTE_BRANCH)
cargo clean
cargo build --locked -p linera-sdk
cargo build --locked -p linera-sdk --features test,wasmer
cd ..
mdbook test -L linera-protocol/target/debug/deps
git commit -a
Expand Down
2 changes: 1 addition & 1 deletion RELEASE_BRANCH
Original file line number Diff line number Diff line change
@@ -1 +1 @@
devnet_2024_03_26
devnet_2024_05_07
2 changes: 1 addition & 1 deletion RELEASE_VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.10.3
0.11.1
2 changes: 1 addition & 1 deletion linera-protocol
Submodule linera-protocol updated 423 files
4 changes: 2 additions & 2 deletions src/advanced_topics/assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The
does this:

```rust,ignore
async fn execute_operation(&mut self, operation: Operation) -> Result<(), Self::Error> {
async fn execute_operation(&mut self, operation: Operation) -> Self::Response {
match operation {
// ...
Operation::CloseChain => {
Expand All @@ -38,7 +38,7 @@ does this:
}
self.runtime
.close_chain()
.map_err(|_| MatchingEngineError::CloseChainError)?;
.expect("The application does not have permissions to close the chain.");
}
}
}
Expand Down
67 changes: 21 additions & 46 deletions src/advanced_topics/contract_finalize.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,16 @@
# Contract Finalization

When a transaction finishes executing successfully, there's a final step where
all loaded application contracts are allowed to `finalize`, similarly to
executing a destructor. The default implementation of `finalize` just persists
the application's state:

```rust,ignore
/// Finishes the execution of the current transaction.
async fn finalize(&mut self) -> Result<(), Self::Error> {
Self::Storage::store(self.state_mut()).await;
Ok(())
}
```

Applications may want to override the `finalize` method, which allows performing
some clean-up operations after execution finished. While finalizing, contracts
may send messages, read and write to the state, but are not allowed to call
other applications, because they are all also finalizing.

> If `finalize` is overriden, the default implementation is **not** executed, so
> the developer must ensure that the application's state is persisted correctly.
While finalizing, contracts can force the transaction to fail by panicking or
returning an error. The block is then rejected, even if the entire transaction's
operation had succeeded before `finalize` was called. This allows a contract to
all loaded application contracts have their `Contract::store` implementation
called. This can be seen to be similar to executing a destructor. In that sense,
applications may want to perform some final operations after execution finished.
While finalizing, contracts may send messages, read and write to the state, but
are not allowed to call other applications, because they are all also in the
process of finalizing.

While finalizing, contracts can force the transaction to fail by panicking. The
block is then rejected, even if the entire transaction's operation had succeeded
before the application's `Contract::store` was called. This allows a contract to
reject transactions if other applications don't follow any required constraints
it establishes after it responds to a cross-application call.

Expand All @@ -38,36 +25,26 @@ pub struct MyContract {
active_sessions: HashSet<ApplicationId>;
}
#[async_trait]
impl Contract for MyContract {
type Error = MyError;
type State = MyState;
type Storage = ViewStateStorage<Self>;
type Message = ();
type InstantiationArgument = ();
type Parameters = ();
async fn load(runtime: ContractRuntime<Self>) -> Self {
let state = MyState::load(ViewStorageContext::from(runtime.key_value_store()))
.await
.expect("Failed to load state");
async fn new(state: Self::State, runtime: ContractRuntime<Self>) -> Result<Self, Self::Error> {
MyContract {
state,
runtime,
active_sessions: HashSet::new(),
}
}
fn state_mut(&mut self) -> &mut Self::State {
&mut self.state
}
async fn initialize(
&mut self,
argument: Self::InitializationArgument,
) -> Result<(), Self::Error> {
Ok(())
}
async fn instantiate(&mut self, (): Self::InstantiationArgument) {}
async fn execute_operation(
&mut self,
operation: Self::Operation,
) -> Result<Self::Response, Self::Error> {
async fn execute_operation(&mut self, operation: Self::Operation) -> Self::Response {
let caller = self.runtime
.authenticated_caller_id()
.expect("Missing caller ID");
Expand All @@ -92,15 +69,13 @@ impl Contract for MyContract {
unreachable!("This example doesn't support messages");
}
async fn finalize(&mut self) -> Result<(), Self::Error> {
async fn store(&mut self) {
assert!(
self.active_sessions.is_empty(),
"Some sessions have not ended"
);
Self::Storage::store(self.state_mut()).await;
Ok(())
self.state.save().await.expect("Failed to save state");
}
}
```
6 changes: 3 additions & 3 deletions src/core_concepts/applications.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ execute user applications. Currently, the [Linera SDK](../sdk.md) is focused on
the [Rust](https://www.rust-lang.org/) programming language.

Linera applications are structured using the familiar notion of **Rust crate**:
the external interfaces of an application (including initialization parameters,
the external interfaces of an application (including instantiation parameters,
operations and messages) generally go into the library part of its crate, while
the core of each application is compiled into binary files for the Wasm
architecture.
Expand All @@ -26,7 +26,7 @@ flexible:
2. The bytecode is published to the network on a microchain, and assigned an
identifier.
3. A user can create a new application instance, by providing the bytecode
identifier and initialization arguments. This process returns an application
identifier and instantiation arguments. This process returns an application
identifier which can be used to reference and interact with the application.
4. The same bytecode identifier can be used as many times needed by as many
users needed to create distinct applications.
Expand All @@ -38,7 +38,7 @@ and an application can be published with a single command:
linera publish-and-create <contract-path> <service-path> <init-args>
```

This will publish the bytecode as well as initialize the application for you.
This will publish the bytecode as well as instantiate the application for you.

## Anatomy of an Application

Expand Down
6 changes: 1 addition & 5 deletions src/sdk/abi.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,13 @@ contract. Each type represents a specific part of the contract's behavior:
All these types must implement the `Serialize`, `DeserializeOwned`, `Send`,
`Sync`, `Debug` traits, and have a `'static` lifetime.

In our example, we would like to change our `InitializationArgument`,
`Operation` to `u64`, like so:
In our example, we would like to change our `Operation` to `u64`, like so:

```rust
# extern crate linera_base;
# use linera_base::abi::ContractAbi;
# struct CounterAbi;
impl ContractAbi for CounterAbi {
type InitializationArgument = u64;
type Parameters = ();
type Operation = u64;
type Response = ();
}
Expand Down Expand Up @@ -79,6 +76,5 @@ For our Counter example, we'll be using GraphQL to query our application so our
impl ServiceAbi for CounterAbi {
type Query = async_graphql::Request;
type QueryResponse = async_graphql::Response;
type Parameters = ();
}
```
15 changes: 10 additions & 5 deletions src/sdk/composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ they are refunded.
If Alice used the `fungible` example to create a Pugecoin application (with an
impressionable pug as its mascot), then Bob can create a `crowd-funding`
application, use Pugecoin's application ID as `CrowdFundingAbi::Parameters`, and
specify in `CrowdFundingAbi::InitializationArgument` that his campaign will run
specify in `CrowdFundingAbi::InstantiationArgument` that his campaign will run
for one week and has a target of 1000 Pugecoins.

Now let's say Carol wants to pledge 10 Pugecoin tokens to Bob's campaign.

First she needs to make sure she has his crowd-funding application on her chain,
e.g. using the `linera request-application` command. This will automatically
also register Alice's application on her chain, because it is a dependency of
Bob's.
also register Alice's Pugecoin application on her chain, because it is a
dependency of Bob's.

Now she can make her pledge by running the `linera service` and making a query
to Bob's application:
Expand Down Expand Up @@ -94,5 +94,10 @@ indirectly by signing her block. The crowd-funding application now makes a note
in its application state on Bob's chain that Carol has pledged 10 Pugecoin
tokens.

For the complete code please take a look at the `crowd-funding` and `fungible`
applications in the `examples` folder in `linera-protocol`.
For the complete code please take a look at the
[`crowd-funding`](https://github.com/linera-io/linera-protocol/blob/{{#include
../../.git/modules/linera-protocol/HEAD}}/examples/crowd-funding/src/contract.rs)
and the
[`fungible`](https://github.com/linera-io/linera-protocol/blob/{{#include
../../.git/modules/linera-protocol/HEAD}}/examples/fungible/src/contract.rs)
application contracts in the `examples` folder in `linera-protocol`.
Loading

0 comments on commit 3abfe78

Please sign in to comment.