Skip to content

Commit

Permalink
Casper backend (#27)
Browse files Browse the repository at this point in the history
Backends rewrite.
Livenet docs.

Co-authored-by: Krzysztof Pobiarżyn <[email protected]>

---------

Co-authored-by: Krzysztof Pobiarżyn <[email protected]>
  • Loading branch information
kubaplas and kpob authored Feb 5, 2024
1 parent 34c252e commit 77059fd
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 71 deletions.
9 changes: 5 additions & 4 deletions docusaurus/docs/backends/01-what-is-a-backend.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ sidebar_position: 1

You can think of a backend as a target platform for your smart contract.
This can be a piece of code allowing you to quickly check your code - like [OdraVM](02-mock-vm.md),
or a complete virtual machine, spinning up a blockchain for you - like [CasperVM](03-casper.md).
a complete virtual machine, spinning up a blockchain for you - like [CasperVM](03-casper.md),
or even a real blockchain - when using [Livenet backend](03-casper.md).

Each backend has to come with two parts that Odra requires - the Contract Env and the Test Env.
Each backend has to come with two parts that Odra requires - the Contract Env and the Host Env.

## Contract Env
The Contract Env is a simple interface that each backend needs to implement,
Expand All @@ -24,10 +25,10 @@ instead of messing with the blockchain internals.
Other functions from Contract Env include handling transfers, addresses, block time, errors and events.

## Host Env
Similarly to the Contract Env, the Test Env exposes a set of functions that allows the communication with
Similarly to the Contract Env, the Host Env exposes a set of functions that allows the communication with
the backend from the outside world - really useful for implementing tests.

This ranges from interacting with the blockchain - like deploying and calling the contracts,
This ranges from interacting with the blockchain - like deploying new, loading existing and calling the contracts,
to the more test-oriented - handling errors, forwarding the block time, etc.

## What's next
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The OdraVM is a simple implementation of a mock backend with a minimal set of fe
the code written in Odra without compiling the contract to the target architecture and spinning up the
blockchain.

Thanks to OdraVM tests run a lot faster than other backends and debugging is a lot simpler.
Thanks to OdraVM tests run a lot faster than other backends. You can even debug the code in real time -
simply use your IDE's debug functionality.

## Usage
The OdraVM is the default backend for Odra framework, so each time you run
Expand All @@ -22,8 +23,7 @@ You are running your code against it.
## Architecture
OdraVM consists of two main parts: the `Contract Register` and the `State`.

The `Contract Register` is just a list of contracts deployed onto the OdraVM, identified by an `Address`.
Each time we call the contract, we call its instance stored in the `Contract Register`.
The `Contract Register` is a list of contracts deployed onto the OdraVM, identified by an `Address`.

Contracts and Test Env functions can modify the `State` of the OdraVM.

Expand All @@ -35,7 +35,7 @@ OdraVM starts instantly and runs the tests in the native speed.
## Execution

When the OdraVM backend is enabled, the `#[odra::module]` macro is responsible for converting
your `pub` functions into a list of Entrypoints, which are put into a Contract Container.
your `pub` functions into a list of Entrypoints, which are put into a `Contract Container`.
When the contract is deployed, its Container registered into a Registry under an address.
During the contract call, OdraVM finds an Entrypoint and executes the code.

Expand Down
90 changes: 27 additions & 63 deletions docusaurus/docs/backends/03-casper.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,6 @@ The Casper backend allows you to compile your contracts into WASM files which ca
onto [Casper Blockchain](https://casper.network/)
and lets you to easily run them against [Casper's Execution Engine][casper_engine] locally.

## Types

A struct to be written into the storage must implement `OdraType` which is defined as follow:

```rust
pub trait OdraType:
casper_types::CLTyped +
casper_types::bytesrepr::ToBytes +
casper_types::bytesrepr::FromBytes {}
```

The other exposed types are:

* `CallArgs` - wraps around casper's [`RuntimeArgs`][runtime_args];
* `Balance` - U512 type alias;
* `BlockTime` - u64 type alias;
* `Address` - an enum that encapsulates casper's [`AccountHash`][account_hash] and [`ContractPackageHash`][contract_package_hash]

## Contract Env

As with any other backend, Casper Backend must implement the same features, but some do not have native support. Let's take a closer look at how Odra overcomes these hindrances.
Expand All @@ -41,21 +23,13 @@ Under the hood, Odra integrates with [Casper Event Standard] and creates a few [

Besides that, all the events the contract emits are registered - events schemas are written to the storage under the `__events_schema` key.

:::note
Don't forget to expose events in the module using `#[odra::module(events = [...])]`.
:::

So, `Events` are nothing different from any other data stored by a contract.

A struct to be an event must implement `SerializableEvent` which is defined as follow:
A struct to be an event must implement traits defined by [Casper Event Standard], thankfully you can derive them using `#[derive(Event)]`.

```rust
pub trait SerializableEvent:
odra_types::event::OdraEvent +
casper_types::CLTyped +
casper_types::bytesrepr::ToBytes +
casper_types::bytesrepr::FromBytes {}
```
:::note
Don't forget to expose events in the module using `#[odra::module(events = [...])]`.
:::

### Payable
The first Odra idiom is a `Contract Main Purse`. It is a purse associated with a contract. The purse is created lazily - when the first transfer to the contract occurs, a proper `URef` and a purse are created and stored under the `__contract_main_purse` key.
Expand All @@ -65,7 +39,7 @@ Casper does not allow direct transfers from an account to a contract, so Odra co
Behind the scenes, Odra handles an account-contract transfer via a cargo purse when a function is marked as payable.
If under the way something goes wrong with the transfer, the contract reverts.

The transferred amount can be read inside the contract by calling `contract_env::attached_value()`.
The transferred amount can be read inside the contract by calling `self.env().attached_value()`.

:::note
Odra expects the `cargo_purse` runtime argument to be attached to a contract call.
Expand All @@ -80,11 +54,12 @@ Odra adds an extra abstraction layer - in a contract `ExecutionError`s are defin
Casper equips developers with very low-level tooling, which can be cumbersome for newcomers.
If you want to check who called the contract or its address, you can not do it off-hand - you must analyze the call stack.

The `contract_env::self_address()` function takes the first element of the callstack ([`runtime::get_call_stack()`][callstack]) and casts it to `Address`.
The `self.env().self_address()` function takes the first element of the callstack ([`runtime::get_call_stack()`][callstack]) and casts it to `Address`.

The `contract_env::caller()` function takes the second element of the call stack (`runtime::get_call_stack()`) and casts it to `Address`.
The `self.env().caller()` function takes the second element of the call stack (`runtime::get_call_stack()`) and casts it to `Address`.

As mentioned in the [Payable] section, to store CSPR, each contract creates its purse. To read the contract balance, you call `contract_env::self_balance`, which checks the balance of the purse stored under `__contract_main_purse`.
As mentioned in the [Payable] section, to store CSPR, each contract creates its purse. To read the contract balance,
you call `self.env().self_balance()`, which checks the balance of the purse stored under `__contract_main_purse`.

## Test Env
Test environment allows you to test wasm contracts before you deploy them onto the testnet or livenet. It is built on top of the `Casper Execution Engine`.
Expand All @@ -109,41 +84,32 @@ If you want to just generate a wasm file, simply run:
cargo odra build -b casper
```

## Constructors

Let's define a basic Odra module that includes a constructor:
## Deploying a contract to Casper network

There would be no point in writing a contract if you couldn't deploy it to the blockchain.
You can do it in two ways: provided by the Casper itself: using the `casper-client` tool
or using the Odra's Livenet integration.

```rust
#[odra::module]
struct Counter {
value: Variable<u32>
}
Let's explore the first option to better understand the process.

#[odra::module]
impl Counter {
#[odra(init)]
pub initialize(&mut self, value: u32) {
self.value.set(value);
}
}
```
Read more about constructors [here](../advanced/04-attributes.md#init).
:::note
If you wish, you can skip the following section and jump to the [Livenet integration](04-livenet.md).
:::

### WASM arguments

When deploying a new contract you have to specify following arguments.
When deploying a new contract you can pass some arguments to it.
Every contract written in Odra expects those arguments to be set:

Required arguments:
- `odra_cfg_package_hash_key_name` - `String` type. The key under which the package hash of the contract will be stored.
- `odra_cfg_allow_key_override` - `Bool` type. If `true` and the key specified in `odra_cfg_package_hash_key_name` already exists, it will be overwritten.
- `odra_cfg_is_upgradable` - `Bool` type. If `true`, the contract will be deployed as upgradable.

Optional arguments:
- `odra_cfg_constructor` - `String` type. If the contract has the constructor entry point marked with `#[odra(init)]`, this should be set to the constructor name.
- constructor arguments that match entry point set in `odra_cfg_constructor`.
Additionally, if required by the contract, you can pass constructor arguments.

## Contract Deploys
When working with the test env via `cargo odra` or when using
[Livenet integration](04-livenet.md) this is handled automatically. However, if you rather use
`casper-client` directly, you have to pass them manually:

### Example: Deploy Counter

Expand All @@ -160,7 +126,6 @@ casper-client put-deploy \
--session-arg "odra_cfg_package_hash_key_name:string:'counter_package_hash'" \
--session-arg "odra_cfg_allow_key_override:bool:'true'" \
--session-arg "odra_cfg_is_upgradable:bool:'true'" \
--session-arg "odra_cfg_constructor:string:'initialize'" \
--session-arg "value:u32:42"
```

Expand Down Expand Up @@ -189,7 +154,6 @@ casper-client put-deploy \
--session-arg "odra_cfg_package_hash_key_name:string:'my_nft'" \
--session-arg "odra_cfg_allow_key_override:bool:'false'" \
--session-arg "odra_cfg_is_upgradable:bool:'true'" \
--session-arg "odra_cfg_constructor:string:'init'" \
--session-arg "name:string:'MyNFT'" \
--session-arg "symbol:string:'NFT'" \
--session-arg "base_uri:string:'https://example.com/'"
Expand Down Expand Up @@ -253,7 +217,7 @@ Result of `to_bytes` on [CasperPackageHash].
- `entry_point` - `String` type. The name of the entry point you want to call.
- `args` - `Bytes` type. It is a serialized [RuntimeArgs] with the arguments you want to pass
to the entry point. To be specific it is the result of `to_bytes` method wrapped with [Bytes] type.
- `attached_value`. `Option<U512>` type. The amount of CSPR you want to attach to the call.
- `attached_value`. `U512` type. The amount of CSPR you want to attach to the call.
- `amount`. `U512` type. Should be the same value as `attached_value` if not `None`.
It is a special Casper argument that enables the access to account's main purse.

Expand Down Expand Up @@ -284,9 +248,9 @@ graph TD;
[deploy]: https://docs.rs/casper-execution-engine/latest/casper_execution_engine/core/engine_state/deploy_item/struct.DeployItem.html
[Casper Event Standard]: https://github.com/make-software/casper-event-standard
[Casper's 'Writing On-Chain Code']: https://docs.casper.network/writing-contracts/
[proxy_caller.wasm]: https://github.com/odradev/odra/blob/release/0.4.0/odra-casper/livenet/resources/proxy_caller.wasm
[proxy_caller.wasm]: https://github.com/odradev/odra/blob/release/0.8.0/odra-casper/test-vm/resources/proxy_caller.wasm
[CasperPackageHash]: https://docs.rs/casper-types/latest/casper_types/contracts/struct.ContractPackageHash.html
[RuntimeArgs]: https://docs.rs/casper-types/latest/casper_types/runtime_args/struct.RuntimeArgs.html
[Bytes]: https://docs.rs/casper-types/latest/casper_types/bytesrepr/struct.Bytes.html
[ERC721]: https://github.com/odradev/odra/blob/release/0.5.0/modules/src/erc721_token.rs
[ERC1155]: https://github.com/odradev/odra/blob/release/0.5.0/modules/src/erc1155_token.rs
[ERC721]: https://github.com/odradev/odra/blob/release/0.8.0/modules/src/erc721_token.rs
[ERC1155]: https://github.com/odradev/odra/blob/release/0.8.0/modules/src/erc1155_token.rs
Loading

0 comments on commit 77059fd

Please sign in to comment.