Skip to content

Commit

Permalink
Add 'fvm-utils/' from commit '368ad258a82204d0004b7321f1015373fb4d17b9'
Browse files Browse the repository at this point in the history
git-subtree-dir: fvm-utils
git-subtree-mainline: d3a0057
git-subtree-split: 368ad25
  • Loading branch information
adlrocha committed Dec 6, 2023
2 parents d3a0057 + 368ad25 commit e1c5fa2
Show file tree
Hide file tree
Showing 46 changed files with 5,205 additions and 0 deletions.
35 changes: 35 additions & 0 deletions fvm-utils/.github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Changes

<!--
Please provide a brief but specific list of changes made, describe the changes
in functionality rather than the changes in code.
-->

-
-
-

## Tests

<!--
Details on how to run tests relevant to the changes within this pull request.
-->

```
```

## Issues

<!--
Please link any issues that this pull request is related to and use the GitHub
supported format for automatically closing issues (ie, closes #123, fixes #123)
-->

-
58 changes: 58 additions & 0 deletions fvm-utils/.github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Tests

on:
push:
branches:
- "main"
pull_request:
branches:
- '**'

jobs:
test:
name: Test Suite
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
target: wasm32-unknown-unknown
toolchain: nightly-2022-10-03
override: true
- run: cargo b --all --release
- run: cargo t --all --release

fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
target: wasm32-unknown-unknown
toolchain: stable
override: true
- run: rustup component add rustfmt
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
target: wasm32-unknown-unknown
toolchain: stable
override: true
- run: rustup component add clippy
- uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
3 changes: 3 additions & 0 deletions fvm-utils/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
/Cargo.lock
.idea
16 changes: 16 additions & 0 deletions fvm-utils/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "fvm-utils"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fil_actors_runtime = { path = "./runtime", features = ["test_utils", "fil-actor"] }
primitives = { path = "primitives" }

[workspace]
members = [
"runtime",
"primitives",
"example",
]
10 changes: 10 additions & 0 deletions fvm-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# fvm-utils
This repo contains a series of crates that serve as utils for fvm development and testing.
Here are some simple breakdowns:
- runtime: Contains the runtime wrapper for communicating with `fvm`. It provides some
handy utility functions such as `transaction` and `verification`.
- primitives: Contains typed version of `fvm` primitives such as `cid` and `hamt`.
- example: Contains a sample user defined actor using `runtime` and `primitive` crate which
one can deploy to `fvm` as wasm binary.

For more details, please refer to each crate's `README.md`.
36 changes: 36 additions & 0 deletions fvm-utils/example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
edition = "2021"
name = "fil_actor_example"
version = "0.1.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fil_actors_runtime = {path = "../runtime", features = ["test_utils", "fil-actor"]}
primitives = {path = "../primitives"}

frc42_dispatch = "3.2.0"
fvm_ipld_blockstore = "0.1.1"
fvm_ipld_encoding = "0.3.3"
fvm_ipld_hamt = "0.5.1"
fvm_shared = {version = "=3.2.0", default-features = false}

anyhow = "1.0.56"
cid = {version = "0.8.3", default-features = false, features = ["serde-codec"]}
log = "0.4.14"
num-derive = "0.3.3"
num-traits = "0.2.14"
serde = {version = "1.0.136", features = ["derive"]}

[dev-dependencies]
base64 = "0.13.0"
blake2b_simd = "1.0.0"
libsecp256k1 = {version = "0.7"}
num-traits = "0.2"
rand = "0.8"
rand_chacha = "0.3"
wabt = "0.10.0"
wasmtime = "0.35.2"

[build-dependencies]
wasm-builder = "3.0.1"
wasmtime = "0.35.2"
26 changes: 26 additions & 0 deletions fvm-utils/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# fil-actor-example
Sample fvm actor based on `fvm-utils`. To create your own user defined actor, define
`Actor` that implements `ActorCode` trait. At the same time, you can create the wasm
entrypoint with the following short cut:
```rust
#[no_mangle]
pub fn invoke(param: u32) -> u32 {
runtime::fvm::trampoline::<Actor>(param)
}
```
See `src/lib.rs` for more details.

## Build
To compile:
```shell
cargo build
```
You should be able to see the `fil_actor_example.compact.wasm` compiled generated.

Set up a local fvm according to this [tutorial](https://lotus.filecoin.io/lotus/developers/local-network/).

## Test
To trigger unit tests, perform the following:
```shell
cargo test
```
12 changes: 12 additions & 0 deletions fvm-utils/example/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
use wasm_builder::WasmBuilder;
WasmBuilder::new()
.with_current_project()
.import_memory()
.append_to_rust_flags("-Ctarget-feature=+crt-static")
.append_to_rust_flags("-Cpanic=abort")
.append_to_rust_flags("-Coverflow-checks=true")
.append_to_rust_flags("-Clto=true")
.append_to_rust_flags("-Copt-level=z")
.build()
}
124 changes: 124 additions & 0 deletions fvm-utils/example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
mod state;

use crate::state::{State, UserPersistParam};
use fil_actors_runtime::runtime::{ActorCode, Runtime};
use fil_actors_runtime::{
actor_dispatch, actor_error, restrict_internal_api, runtime, ActorDowncast, ActorError,
INIT_ACTOR_ADDR,
};
use fvm_shared::error::ExitCode;
use fvm_shared::{MethodNum, METHOD_CONSTRUCTOR};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;

#[no_mangle]
pub fn invoke(param: u32) -> u32 {
runtime::fvm::trampoline::<Actor>(param)
}

/// SCA actor methods available
#[derive(FromPrimitive)]
#[repr(u64)]
pub enum Method {
/// Constructor for Storage Power Actor
Constructor = METHOD_CONSTRUCTOR,
Persist = frc42_dispatch::method_hash!("Persist"),
}

pub struct Actor;

impl Actor {
/// Constructor for SCA actor
fn constructor(rt: &mut impl Runtime) -> Result<(), ActorError> {
rt.validate_immediate_caller_is(std::iter::once(&INIT_ACTOR_ADDR))?;
let st = State::new(rt.store()).map_err(|e| {
e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to create actor state")
})?;
rt.create(&st)?;
Ok(())
}

/// Persists some bytes to storage
fn persist(rt: &mut impl Runtime, param: UserPersistParam) -> Result<(), ActorError> {
let caller = rt.message().caller();

rt.validate_immediate_caller_accept_any()?;

rt.transaction(|st: &mut State, rt| {
st.upsert_user(&caller, param.name, rt.store())
.map_err(|e| {
e.downcast_default(
ExitCode::USR_ILLEGAL_STATE,
"Failed to create SCA actor state",
)
})?;
Ok(())
})?;

Ok(())
}
}

impl ActorCode for Actor {
type Methods = Method;
actor_dispatch! {
Constructor => constructor,
Persist => persist,
}
}

#[cfg(test)]
mod test {
use crate::{Actor, Method, State, UserPersistParam};
use fil_actors_runtime::test_utils::{MockRuntime, INIT_ACTOR_CODE_ID};
use fil_actors_runtime::INIT_ACTOR_ADDR;
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared::address::Address;
use fvm_shared::MethodNum;

#[test]
fn constructor_works() {
let mut rt = new_runtime();

rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);

rt.call::<Actor>(Method::Constructor as MethodNum, None)
.unwrap();

rt.verify()
}

#[test]
fn persists_works() {
let mut rt = new_runtime();

rt.set_caller(*INIT_ACTOR_CODE_ID, INIT_ACTOR_ADDR);
rt.expect_validate_caller_addr(vec![INIT_ACTOR_ADDR]);

rt.call::<Actor>(Method::Constructor as MethodNum, None)
.unwrap();

rt.expect_validate_caller_any();
rt.call::<Actor>(
Method::Persist as MethodNum,
IpldBlock::serialize_cbor(&UserPersistParam {
name: String::from("sample"),
})
.unwrap(),
)
.unwrap();

rt.verify();
let state: State = rt.get_state();
assert_eq!(state.call_count, 1);
}

fn new_runtime() -> MockRuntime {
MockRuntime {
receiver: Address::new_id(1),
caller: INIT_ACTOR_ADDR,
..Default::default()
}
}
}
57 changes: 57 additions & 0 deletions fvm-utils/example/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_hamt::BytesKey;
use fvm_shared::address::Address;
use primitives::{TCid, THamt};
use serde::{Deserialize, Serialize};

/// Sample struct for user persistence
#[derive(Serialize, Deserialize)]
pub struct UserPersistParam {
pub name: String,
}

/// User data storage struct
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct User {
pub name: String,
pub owner: Address,
}

/// The state storage struct, persisted in BlockStore
#[derive(Serialize, Deserialize)]
pub struct State {
pub call_count: usize,
pub typed_hamt: TCid<THamt<Cid, User>>,
}

impl State {
pub fn new<BS: Blockstore>(store: &BS) -> anyhow::Result<Self> {
Ok(State {
call_count: 0,
typed_hamt: TCid::new_hamt(store)?,
})
}

pub fn upsert_user<BS: Blockstore>(
&mut self,
address: &Address,
name: String,
store: &BS,
) -> anyhow::Result<()> {
let key = BytesKey::from(address.to_bytes());
let mut hamt = self.typed_hamt.load(store)?;
hamt.set(
key,
User {
owner: *address,
name,
},
)?;

self.call_count += 1;
self.typed_hamt.flush(hamt)?;

Ok(())
}
}
Loading

0 comments on commit e1c5fa2

Please sign in to comment.