Skip to content

Commit

Permalink
Infinte loop (#47)
Browse files Browse the repository at this point in the history
* add consume fuel to config

* add fuel

* increase add fuel

* quick fix

* added Config for Runtime

* add infinite loop example

* add error

* infinite loop test

* update infinite loop

* rename

* fix tests

* clarified new fuel error variant

* updated readme, cleanup

---------

Co-authored-by: jakehemmerle <[email protected]>
  • Loading branch information
JesseAbram and jakehemmerle authored Jan 5, 2024
1 parent 60ef356 commit 0db40ea
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 27 deletions.
21 changes: 14 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,27 @@ cargo install cargo-component --version 0.2.0 &&
cargo install wasm-tools
```

## Example Program: `barebones`
## Example Program: `template-barebones`

To get started, clone this repository and build the example `barebones` program:
An example of a barebones program is at [`examples/barebones/src/lib.rs`](./examples/barebones/src/lib.rs). This example does a simple check on the length of the message to be signed.

You can compile the program by running:

```bash
git clone https://github.com/entropyxyz/constraints
cd constraints
cargo component build --release -p template-barebones --target wasm32-unknown-unknown
```

This creates the program as a Wasm component at `target/wasm32-unknown-unknown/release/template_barebones.wasm`.
This builds the program as a Wasm component at `target/wasm32-unknown-unknown/release/template_barebones.wasm`.

## Running Tests

Since this program is used in tests for the program runtime (`ec-runtime`), you can see the program get used by running `cargo test -p ec-runtime`.
Before running the runtime tests, you need to build the `template-barebones` and `infinite-loop` components. To do this, execute:

```bash
cargo component build --release -p template-barebones -p infinite-loop --target wasm32-unknown-unknown`
```

This will create the components in `target/wasm32-unknown-unknown/release/`.

## Licensing

Expand All @@ -43,4 +51,3 @@ There are some exceptions however:
`Apache License 2.0`.
Modifications made by Entropy to these crates are licensed under `AGPL-3.0`.

26 changes: 26 additions & 0 deletions examples/infinite-loop/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "infinite-loop"
version = "0.1.0"
authors = ["Entropy Cryptography <[email protected]>"]
homepage = "https://entropy.xyz/"
license = "Unlicense"
repository = "https://github.com/entropyxyz/constraints"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

# This is required to compile constraints to a wasm module
[lib]
crate-type = ["cdylib"]

[dependencies]
ec-core = { workspace = true }

# These are used by `cargo component`
[package.metadata.component]
package = "entropy:infinite-loop"

[package.metadata.component.target]
path = "../../wit"

[package.metadata.component.dependencies]
27 changes: 27 additions & 0 deletions examples/infinite-loop/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#![no_std]

extern crate alloc;
use alloc::vec::Vec;
use ec_core::{bindgen::Error, bindgen::*, export_program, prelude::*};

// TODO confirm this isn't an issue for audit
register_custom_getrandom!(always_fail);

pub struct InfiniteLoop;

impl Program for InfiniteLoop {
/// This is the only function required by the program runtime. `message` is the preimage of the curve element to be
/// signed, eg. RLP-serialized Ethereum transaction request, raw x86_64 executable, etc.
fn evaluate(_signature_request: SignatureRequest) -> Result<(), Error> {
loop {}
#[allow(unreachable_code)]
Ok(())
}

/// Since we don't use a custom hash function, we can just return `None` here.
fn custom_hash(_data: Vec<u8>) -> Option<Vec<u8>> {
None
}
}

export_program!(InfiniteLoop);
2 changes: 1 addition & 1 deletion runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ This contains the Wasm runtime for evaluaing, testing, and simulating constraint

## Running Tests

Before running the tests, you need to build the `barebones` component. Be sure to have `cargo component` installed, and run `cargo component build --release -p template-barebones --target wasm32-unknown-unknown`. This will create the files needed for testing at `target/wasm32-unknown-unknown/release/`.
Before running the tests, you need to build the `template-barebones` and `infinite-loop` components. Be sure to have `cargo component` installed, and run `cargo component build --release -p template-barebones -p infinite-loop --target wasm32-unknown-unknown`. This will create the files needed for testing at `target/wasm32-unknown-unknown/release/`.
48 changes: 35 additions & 13 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use thiserror::Error;
use wasmtime::{
component::{bindgen, Component, Linker},
Config, Engine, Result, Store,
Config as WasmtimeConfig, Engine, Result, Store,
};

/// Note, this is wasmtime's bindgen, not wit-bindgen (modules)
Expand All @@ -26,9 +26,26 @@ pub enum RuntimeError {
/// Program bytecode is not a valid WebAssembly component.
#[error("Invalid bytecode")]
InvalidBytecode,
/// Runtime error during execution.
/// Program error during execution.
#[error("Runtime error: {0}")]
Runtime(ProgramError),
/// Program exceeded fuel limits. Execute fewer instructions.
#[error("Out of fuel")]
OutOfFuel,
}

/// Config is for runtime parameters (eg instructions per program, additional runtime interfaces, etc).
pub struct Config {
/// Max number of instructions the runtime will execute before returning an error.
pub fuel: u64,
}

impl Default for Config {
fn default() -> Self {
Self {
fuel: 10_000,
}
}
}

/// Runtime allows for the execution of programs. Instantiate with `Runtime::new()`.
Expand All @@ -40,11 +57,22 @@ pub struct Runtime {

impl Default for Runtime {
fn default() -> Self {
let mut config = Config::new();
config.wasm_component_model(true);
let engine = Engine::new(&config).unwrap();
Self::new(Config::default())
}
}

impl Runtime {
pub fn new(config: Config) -> Self {
let mut wasmtime_config = WasmtimeConfig::new();
wasmtime_config
.wasm_component_model(true)
.consume_fuel(true);

let engine = Engine::new(&wasmtime_config).unwrap();
let linker = Linker::new(&engine);
let store = Store::new(&engine, ());
let mut store = Store::new(&engine, ());

store.add_fuel(config.fuel).unwrap();
Self {
engine,
linker,
Expand All @@ -53,12 +81,6 @@ impl Default for Runtime {
}
}

impl Runtime {
pub fn new() -> Self {
Self::default()
}
}

impl Runtime {
/// Evaluate a program with a given initial state.
pub fn evaluate(
Expand All @@ -77,7 +99,7 @@ impl Runtime {

bindings
.call_evaluate(&mut self.store, signature_request)
.unwrap()
.map_err(|_| RuntimeError::OutOfFuel)?
.map_err(RuntimeError::Runtime)
}

Expand Down
29 changes: 23 additions & 6 deletions runtime/tests/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ const BAREBONES_COMPONENT_WASM: &[u8] =
const CUSTOM_HASH_COMPONENT_WASM: &[u8] =
include_bytes!("../../target/wasm32-unknown-unknown/release/example_custom_hash.wasm");

/// Points to the `infinite-loop` program binary.
const INFINITE_LOOP_WASM: &[u8] =
include_bytes!("../../target/wasm32-unknown-unknown/release/infinite_loop.wasm");

use ec_runtime::{Runtime, SignatureRequest};
use blake3;

#[test]
fn test_barebones_component() {
let mut runtime = Runtime::new();
let mut runtime = Runtime::default();

// The barebones example simply validates that the length of the data to be signed is greater than 10.
let longer_than_10 = "asdfasdfasdfasdf".to_string();
Expand All @@ -24,7 +28,7 @@ fn test_barebones_component() {

#[test]
fn test_barebones_component_fails_with_data_length_less_than_10() {
let mut runtime = Runtime::new();
let mut runtime = Runtime::default();

// Since the barebones example verifies that the length of the data to be signed is greater than 10, this should fail.
let shorter_than_10 = "asdf".to_string();
Expand All @@ -39,7 +43,7 @@ fn test_barebones_component_fails_with_data_length_less_than_10() {

#[test]
fn test_empty_bytecode_fails() {
let mut runtime = Runtime::new();
let mut runtime = Runtime::default();

let signature_request = SignatureRequest {
message: vec![],
Expand All @@ -50,9 +54,22 @@ fn test_empty_bytecode_fails() {
assert_eq!(res.unwrap_err().to_string(), "Bytecode length is zero");
}

#[test]
fn test_infinite_loop() {
let mut runtime = Runtime::default();

let signature_request = SignatureRequest {
message: vec![],
auxilary_data: None,
};

let res = runtime.evaluate(INFINITE_LOOP_WASM, &signature_request);
assert_eq!(res.unwrap_err().to_string(), "Out of fuel");
}

#[test]
fn test_custom_hash() {
let mut runtime = Runtime::new();
let mut runtime = Runtime::default();

let message = "some_data_to_be_hashed".to_string().into_bytes();

Expand All @@ -69,7 +86,7 @@ fn test_custom_hash() {

#[test]
fn test_custom_hash_errors_when_returning_none() {
let mut runtime = Runtime::new();
let mut runtime = Runtime::default();

let message = "some_data_to_be_hashed".to_string().into_bytes();

Expand All @@ -85,4 +102,4 @@ fn test_custom_hash_errors_when_returning_none() {
);
}

// TODO add test for custom hash returning a vec of length != 32
// TODO add test for custom hash returning a vec of length != 32

0 comments on commit 0db40ea

Please sign in to comment.