Skip to content

Commit

Permalink
feat(cheatcodes): base implementation of cheatcode system (#58)
Browse files Browse the repository at this point in the history
## Description

This PR introduces cheatcodes in Kythera. The inspiration for what the
system allows is heavily inspired from [Foundry's
cheatcodes](https://book.getfoundry.sh/cheatcodes/).


### Changelog
**Major**
- We now implement most of the components from the `ref-fvm` repo as we
need to have granular control over them for the cheatcode system to
work. This implementation is heavily inspired from [the conformance test
framework](https://github.com/filecoin-project/ref-fvm/tree/master/testing/conformance)
from @Stebalian on the `ref-fvm`
  - `KytheraMachine`: Stores override values.
- `KytheraCallManager`: Set override values when detects a `send()`
syscall to actor Id 98 (cheatcodes actor).
  - `KytheraKernel`: Actually override values at call time.
- Introducing an `OverrideContext` structure that allows us to store at
the `KytheraMachine` level different value to alter information when
cheatcodes are invoked.
- Deploying a _Cheatcode Actor_ at Actor Id 98. It contains entrypoints
corresponding to the different cheatcodes we want to be available to our
users.
- Updated the way we compile actors to get binaries. Now, all actors can
be found under `<project-root>/actors`. In this folder we find both
`actors` (available at all time) and `test-actors` (only available if
`feature=["testing"]` is enabled). This allows to have some binaries
built and used in our `kythera-lib` without actually building everything
everytime.
 
**Minor**
- Actor are no longer deployed at a random ID. Instead, we first create
an address from their new and register it on-chain.
- Set default Kythera chain Id to _1312_ 


## Links to any relevant issues or pull requests

Closes #22 

## Change checklist

- [X] I have performed a self-review of my own code
- [ ] I have made corresponding changes to the documentation
- [X] I have added tests that prove my fix is effective or that my
feature works

---------

Co-authored-by: João Oliveira <[email protected]>
Co-authored-by: PhilippeMts <[email protected]>
  • Loading branch information
3 people committed Apr 24, 2023
1 parent 9fc5e2e commit de142e0
Show file tree
Hide file tree
Showing 51 changed files with 1,917 additions and 197 deletions.
175 changes: 117 additions & 58 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ members = [
"lib",
"fvm",
"common",
"test_actors",
"test_actors/actors/*",
"actors",
"actors/actors/*",
"actors/test_actors/*",
]


Expand Down
6 changes: 5 additions & 1 deletion test_actors/Cargo.toml → actors/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[package]
name = "kythera_test_actors"
name = "kythera-actors"
description = "Kythera FVM WASM actor builder"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
authors = ["Polyphene"]
publish = false

[features]
default=[]
testing=[]
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "failed_test_actor"
name = "cheatcodes-actor"
version = "0.1.0"
edition = "2021"
publish=false
Expand All @@ -8,6 +8,8 @@ publish=false
frc42_dispatch = "3.1.0"
fvm_sdk = { version = "3.0.0" }
fvm_shared = { version = "3.1.0" }
fvm_ipld_encoding = { version = "0.3.3" }
serde = { version = "1.0.136", features = ["derive"] }

[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib"]
13 changes: 13 additions & 0 deletions actors/actors/cheatcodes_actor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Cheatcodes Test Actor

This is the actor embedded in our `kythera-fvm` to expose the cheatcodes interface.

### Cheatcodes

The following cheatcodes are exposed through the actor:
- `Epoch`: Set the `NetworkContext::epoch`
- `Warp`: Set the `NetworkContext::timestamp`
- `Fee`: Set the `NetworkContext::fee`
- `ChaindId`: Set the `NetworkContext::chain_id`
- `Prank`: Sets the **next implicit message**'s `MessageContext::caller` to be the input address
- `Trick`: Sets the **next implicit message and its sub-implicit messages**' `MessageContext::origin` to be the input address
114 changes: 114 additions & 0 deletions actors/actors/cheatcodes_actor/src/actor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// Copyright 2023 Polyphene.
// SPDX-License-Identifier: Apache-2.0, MIT

use frc42_dispatch::match_method;
use fvm_ipld_encoding::{de::DeserializeOwned, RawBytes};
use fvm_sdk::NO_DATA_BLOCK_ID;
use fvm_shared::address::Address;
use fvm_shared::error::ExitCode;

/// Deserialize message parameters into given struct.
pub fn deserialize_params<D: DeserializeOwned>(params: u32) -> D {
let params = fvm_sdk::message::params_raw(params)
.expect("Could not get message parameters")
.expect("Expected message parameters but got none");

let params = RawBytes::new(params.data);

params
.deserialize()
.expect("Should be able to deserialize message params into arguments of called method")
}

#[no_mangle]
fn invoke(input: u32) -> u32 {
let method_num = fvm_sdk::message::method_number();
match_method!(
method_num,
{
"Warp" => {
// Ensure that the message params can be deserialized.
let new_timestamp: u64 = deserialize_params(input);

Warp(new_timestamp);

NO_DATA_BLOCK_ID
},
"Epoch" => {
// Ensure that the message params can be deserialized.
let new_epoch: i64 = deserialize_params(input);

Epoch(new_epoch);

NO_DATA_BLOCK_ID
},
"Fee" => {
// Ensure that the message params can be deserialized.
let (lo, hi): (u64, u64) = deserialize_params(input);

Fee(
fvm_shared::sys::TokenAmount {
lo,
hi
}
);

NO_DATA_BLOCK_ID
},
"ChainId" => {
// Ensure that the message params can be deserialized.
let new_chain_id: u64 = deserialize_params(input);

ChainId(new_chain_id);

NO_DATA_BLOCK_ID
},
"Prank" => {
// Ensure that the message params can be deserialized.
let new_caller: Address = deserialize_params(input);

Prank(new_caller);

NO_DATA_BLOCK_ID
},
"Trick" => {
// Ensure that the message params can be deserialized.
let new_origin: Address = deserialize_params(input);

Trick(new_origin);

NO_DATA_BLOCK_ID
},
_ => {
fvm_sdk::vm::abort(
ExitCode::USR_UNHANDLED_MESSAGE.value(),
Some("Unknown method number"),
);
}
}
)
}

/// Warp the machine context to a given timestamp.
#[allow(non_snake_case)]
fn Warp(_new_timestamp: u64) {}

/// Update the machine context to a given epoch.
#[allow(non_snake_case)]
fn Epoch(_new_epoch: i64) {}

/// Update the base fee that's in effect when the machine runs.
#[allow(non_snake_case)]
fn Fee(_new_fee: fvm_shared::sys::TokenAmount) {}

/// Set a new chain Id in the machine context.
#[allow(non_snake_case)]
fn ChainId(_new_chain_id: u64) {}

/// Prank the call manager to set a pre-determined caller for the next message sent.
#[allow(non_snake_case)]
fn Prank(_new_caller: Address) {}

/// Trick the call manager to set a pre-determined origin for the next message sent.
#[allow(non_snake_case)]
fn Trick(_new_origin: Address) {}
File renamed without changes.
34 changes: 26 additions & 8 deletions test_actors/build.rs → actors/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ use std::path::Path;
use std::process::{Command, Stdio};
use std::thread;

const ACTORS: &[&str] = &[
"basic_test_actor",
"builtin_test_actor",
"constructor_setup_test_actor",
"failed_test_actor",
const ACTORS: &[&str] = &["cheatcodes-actor"];

#[cfg(feature = "testing")]
const TEST_ACTORS: &[&str] = &[
"basic-test-actor",
"basic-target-actor",
"builtins-test-actor",
"cheatcodes-test-actor",
"constructor-setup-test-actor",
];

const FILES_TO_WATCH: &[&str] = &["Cargo.toml", "src", "actors"];

fn main() -> Result<(), Box<dyn Error>> {
// Cargo executable location.
let cargo = std::env::var_os("CARGO").expect("no CARGO env var");
Expand All @@ -28,14 +34,26 @@ fn main() -> Result<(), Box<dyn Error>> {
Path::new(&std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR unset"))
.join("Cargo.toml");

for file in ["Cargo.toml", "src", "actors"] {
let mut files_to_watch = FILES_TO_WATCH.to_vec();

if cfg!(feature = "testing") {
files_to_watch = [files_to_watch, vec!["test_actors"]].concat();
}

for file in files_to_watch {
println!("cargo:rerun-if-changed={}", file);
}

// Cargo build command for all actors at once.
let mut actors = ACTORS.to_vec();

if cfg!(feature = "testing") {
actors = [ACTORS, TEST_ACTORS].concat();
}

// Cargo build command for all test_actors at once.
let mut cmd = Command::new(cargo);
cmd.arg("build")
.args(ACTORS.iter().map(|pkg| "-p=".to_owned() + pkg))
.args(actors.iter().map(|pkg| "-p=".to_owned() + pkg))
.arg("--target=wasm32-unknown-unknown")
.arg("--profile=wasm")
.arg("--locked")
Expand Down
2 changes: 1 addition & 1 deletion test_actors/src/README.md → actors/src/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This crate contains the necessary logic to build some testing actors for our Kythera toolset.
This crate contains the necessary logic to build actors for our Kythera toolset.

It was heavily inspired and copied from the implementation [over the `ref-fvm`](https://github.com/filecoin-project/ref-fvm/tree/37643fc02f0342256afecff5158c43693b5ee4f0/testing/test_actors)
done by @fridrik01.
File renamed without changes.
28 changes: 28 additions & 0 deletions actors/src/wasm_bin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2023 Polyphene.
// SPDX-License-Identifier: Apache-2.0, MIT
// Constants for wasm build artifacts.

macro_rules! wasm_bin {
($x: expr) => {
concat!(
env!("OUT_DIR"),
"/bundle/wasm32-unknown-unknown/wasm/",
$x,
".wasm"
)
};
}

pub const CHEATCODES_ACTOR_BINARY: &[u8] = include_bytes!(wasm_bin!("cheatcodes_actor"));

#[cfg(feature = "testing")]
pub mod test_actors {
// Integration tests actors.
pub const BASIC_TEST_ACTOR_BINARY: &[u8] = include_bytes!(wasm_bin!("basic_test_actor"));
pub const BASIC_TARGET_ACTOR_BINARY: &[u8] = include_bytes!(wasm_bin!("basic_target_actor"));
pub const BUILTINS_TEST_ACTOR_BINARY: &[u8] = include_bytes!(wasm_bin!("builtins_test_actor"));
pub const CHEATCODES_TEST_ACTOR_BINARY: &[u8] =
include_bytes!(wasm_bin!("cheatcodes_test_actor"));
pub const CONSTRUCTOR_SETUP_TEST_ACTOR_BINARY: &[u8] =
include_bytes!(wasm_bin!("constructor_setup_test_actor"));
}
16 changes: 16 additions & 0 deletions actors/test_actors/basic_target_actor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "basic-target-actor"
version = "0.1.0"
edition = "2021"
publish=false

[target.'cfg(target_arch = "wasm32")'.dependencies]
frc42_dispatch = "3.1.0"
fvm_sdk = { version = "3.0.0" }
fvm_shared = { version = "3.1.0" }
fvm_ipld_encoding = { version = "0.3.3" }
serde = { version = "1.0.136", features = ["derive"] }
thiserror = { version = "1.0.31" }

[lib]
crate-type = ["cdylib"]
6 changes: 6 additions & 0 deletions actors/test_actors/basic_target_actor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Basic Target Actor

This is a basic actor that serves as a target actor in some of our tests for Kythera. It is the target actor used against
our `cheatcodes_test_actor` for example. Its entrypoints are:
- `Caller`: Method that returns the value of the `MessageContext.caller`
- `Origin`: Method that returns the value of the `MessageContext.origin`
52 changes: 52 additions & 0 deletions actors/test_actors/basic_target_actor/src/actor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2023 Polyphene.
// SPDX-License-Identifier: Apache-2.0, MIT

use frc42_dispatch::match_method;
use fvm_ipld_encoding::DAG_CBOR;
use fvm_sdk as sdk;
use fvm_shared::error::ExitCode;
use sdk::sys::ErrorNumber;
use serde::ser;
use thiserror::Error;

#[derive(Error, Debug)]
enum IpldError {
#[error("ipld encoding error: {0}")]
Encoding(#[from] fvm_ipld_encoding::Error),
#[error("ipld blockstore error: {0}")]
Blockstore(#[from] ErrorNumber),
}

fn return_ipld<T>(value: &T) -> std::result::Result<u32, IpldError>
where
T: ser::Serialize + ?Sized,
{
let bytes = fvm_ipld_encoding::to_vec(value)?;
Ok(sdk::ipld::put_block(DAG_CBOR, bytes.as_slice())?)
}

#[no_mangle]
fn invoke(_input: u32) -> u32 {
let method_num = fvm_sdk::message::method_number();
match_method!(
method_num,
{
"Caller" => {
let mc_caller: u64 = unsafe { fvm_sdk::sys::vm::message_context().unwrap().caller };

return_ipld(&mc_caller).unwrap()
},
"Origin" => {
let mc_origin: u64 = unsafe { fvm_sdk::sys::vm::message_context().unwrap().origin };

return_ipld(&mc_origin).unwrap()
},
_ => {
fvm_sdk::vm::abort(
ExitCode::USR_UNHANDLED_MESSAGE.value(),
Some("Unknown method number"),
);
}
}
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "basic_test_actor"
name = "basic-test-actor"
version = "0.1.0"
edition = "2021"
publish=false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use frc42_dispatch::match_method;
use fvm_ipld_encoding::DAG_CBOR;
use fvm_sdk as sdk;
use fvm_sdk::NO_DATA_BLOCK_ID;
use fvm_shared::error::ExitCode;
use sdk::sys::ErrorNumber;
use serde::ser;
Expand All @@ -27,12 +28,25 @@ where

#[no_mangle]
fn invoke(_input: u32) -> u32 {
std::panic::set_hook(Box::new(|info| {
sdk::vm::exit(
ExitCode::USR_ASSERTION_FAILED.value(),
None,
Some(&format!("{info}")),
)
}));

let method_num = sdk::message::method_number();
match_method!(
method_num,
{
"TestOne" => return_ipld(TestOne()).unwrap(),
"TestTwo" => return_ipld(TestTwo()).unwrap(),
"TestFailed" => {
TestFailed();

NO_DATA_BLOCK_ID
},
_ => {
sdk::vm::abort(
ExitCode::USR_UNHANDLED_MESSAGE.value(),
Expand All @@ -52,3 +66,8 @@ fn TestOne() -> &'static str {
fn TestTwo() -> &'static str {
"TestTwo"
}

#[allow(non_snake_case)]
fn TestFailed() {
assert_eq!(1 + 1, 3);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "builtin_test_actor"
name = "builtins-test-actor"
version = "0.1.0"
edition = "2021"
publish=false
Expand Down
Loading

0 comments on commit de142e0

Please sign in to comment.