Skip to content

Commit

Permalink
[x/programs] rewording for better dev ui (#446)
Browse files Browse the repository at this point in the history
* Context -> State

* programInvoke -> invokeProgram

* expose -> public

* remove mandatory init

* lint

* rename and expose macro from sdk

* lint + merge imports

* remove simulator

* rust lint test

* remove server files
  • Loading branch information
samliok authored Sep 6, 2023
1 parent 1bf60df commit 1820476
Show file tree
Hide file tree
Showing 26 changed files with 131 additions and 139 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# extend if new rust crates are added so rust analyzer isnt confused
[workspace]
members = [
"x/programs/rust/expose_macro",
"x/programs/rust/sdk_macros",
"x/programs/rust/wasmlanche_sdk",
"x/programs/rust/examples/token",
"x/programs/rust/examples/counter",
Expand Down
1 change: 0 additions & 1 deletion x/programs/examples/lottery.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ type Lottery struct {
}

func (t *Lottery) Run(ctx context.Context) error {

meter := runtime.NewMeter(t.log, t.maxFee, t.costMap)
db := utils.NewTestDB()
store := newProgramStorage(db)
Expand Down
6 changes: 2 additions & 4 deletions x/programs/examples/lottery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import (
"github.com/stretchr/testify/require"
)

var (
//go:embed testdata/lottery.wasm
lotteryProgramBytes []byte
)
//go:embed testdata/lottery.wasm
var lotteryProgramBytes []byte

// go test -v -timeout 30s -run ^TestLotteryProgram$ github.com/ava-labs/hypersdk/x/programs/examples
func TestLotteryProgram(t *testing.T) {
Expand Down
6 changes: 2 additions & 4 deletions x/programs/examples/pokemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import (
"github.com/stretchr/testify/require"
)

var (
//go:embed testdata/pokemon.wasm
pokemonProgramBytes []byte
)
//go:embed testdata/pokemon.wasm
var pokemonProgramBytes []byte

// go test -v -timeout 30s -run ^TestPokemonProgram$ github.com/ava-labs/hypersdk/x/programs/examples
func TestPokemonProgram(t *testing.T) {
Expand Down
Binary file modified x/programs/examples/testdata/counter.wasm
Binary file not shown.
Binary file modified x/programs/examples/testdata/lottery.wasm
Binary file not shown.
Binary file modified x/programs/examples/testdata/pokemon.wasm
Binary file not shown.
Binary file modified x/programs/examples/testdata/token.wasm
Binary file not shown.
8 changes: 8 additions & 0 deletions x/programs/runtime/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package runtime

import "errors"

var ErrMissingExportedFunction = errors.New("failed to find exported function")
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ func NewInvokeModule(log logging.Logger, mu state.Mutable, meter Meter, storage

func (m *InvokeModule) Instantiate(ctx context.Context, r wazero.Runtime) error {
_, err := r.NewHostModuleBuilder(invokeModuleName).
NewFunctionBuilder().WithFunc(m.programInvokeFn).Export("program_invoke").
NewFunctionBuilder().WithFunc(m.invokeProgramFn).Export("invoke_program").
Instantiate(ctx)

return err
}

// programInvokeFn makes a call to an entry function of a program in the context of another program's ID.
func (m *InvokeModule) programInvokeFn(
// invokeProgramFn makes a call to an entry function of a program in the context of another program's ID.
func (m *InvokeModule) invokeProgramFn(
ctx context.Context,
mod api.Module,
invokeProgramID uint64,
Expand Down
19 changes: 11 additions & 8 deletions x/programs/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package runtime

import (
"context"
"errors"
"fmt"
"os"

Expand Down Expand Up @@ -52,15 +53,17 @@ func (r *runtime) Create(ctx context.Context, programBytes []byte) (uint64, erro
// get programId
programID := initProgramStorage()

// call initialize
// call initialize if it exists
result, err := r.Call(ctx, "init", uint64(programID))
if err != nil {
return 0, err
}

// check boolean result from init
if result[0] == 0 {
return 0, fmt.Errorf("failed to initialize program")
if !errors.Is(err, ErrMissingExportedFunction) {
return 0, err
}
} else {
// check boolean result from init
if result[0] == 0 {
return 0, fmt.Errorf("failed to initialize program")
}
}
return uint64(programID), nil
}
Expand Down Expand Up @@ -123,7 +126,7 @@ func (r *runtime) Call(ctx context.Context, name string, params ...uint64) ([]ui
}

if api == nil {
return nil, fmt.Errorf("failed to find exported function: %s", name)
return nil, fmt.Errorf("%w: %s", ErrMissingExportedFunction, name)
}

result, err := api.Call(ctx, params...)
Expand Down
1 change: 0 additions & 1 deletion x/programs/rust/examples/counter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
expose_macro = { version = "0.1.0", path = "../../expose_macro" }
wasmlanche_sdk = { version = "0.1.0", path = "../../wasmlanche_sdk" }

[lib]
Expand Down
24 changes: 11 additions & 13 deletions x/programs/rust/examples/counter/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
use expose_macro::expose;
use wasmlanche_sdk::store::Context;
use wasmlanche_sdk::types::Address;
use wasmlanche_sdk::{public, store::State, types::Address};

/// Initializes the program. This program maps addresses with a count.
#[expose]
fn init(ctx: Context) -> bool {
ctx.store_value("counter", &0_i64).is_ok()
#[public]
fn init(state: State) -> bool {
state.store_value("counter", &0_i64).is_ok()
}

/// Increments the count at the address by the amount.
#[expose]
fn inc(ctx: Context, to: Address, amount: i64) -> bool {
let counter = amount + value(ctx, to);
#[public]
fn inc(state: State, to: Address, amount: i64) -> bool {
let counter = amount + value(state, to);
// dont check for error/ok
ctx.store_map_value("counts", &to, &counter).is_ok()
state.store_map_value("counts", &to, &counter).is_ok()
}

/// Gets the count at the address.
#[expose]
fn value(ctx: Context, of: Address) -> i64 {
ctx.get_map_value("counts", &of).unwrap_or(0)
#[public]
fn value(state: State, of: Address) -> i64 {
state.get_map_value("counts", &of).unwrap_or(0)
}
1 change: 0 additions & 1 deletion x/programs/rust/examples/lottery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
expose_macro = { version = "0.1.0", path = "../../expose_macro" }
wasmlanche_sdk = { version = "0.1.0", path = "../../wasmlanche_sdk" }
rand = "0.8.5"
rand_chacha = "0.3.1"
Expand Down
34 changes: 13 additions & 21 deletions x/programs/rust/examples/lottery/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,37 @@
use expose_macro::expose;
use wasmlanche_sdk::store::Context;
use wasmlanche_sdk::types::Address;
use wasmlanche_sdk::{public, store::State, types::Address};

// Define the name of the token contract in the programs storage map.
static TOKEN_PROGRAM_NAME: &str = "token_contract";

/// Initializes the program.
#[expose]
fn init(_: Context) -> bool {
// Initialize the program with no fields
true
}

/// Sets the token contract address and the lotto address. This needs to be set
/// before play can be called, otherwise there is no reference contract and address.
#[expose]
fn set(ctx: Context, counter_ctx: Context, lot_address: Address) -> bool {
ctx.store_value(TOKEN_PROGRAM_NAME, &counter_ctx)
.store_value("address", &lot_address)
#[public]
fn set(state: State, lottery_state: State, lottery_address: Address) -> bool {
state
.store_value(TOKEN_PROGRAM_NAME, &lottery_state)
.store_value("address", &lottery_address)
.is_ok()
}

/// Randomly generates a number (1-100) and transfers those tokens to the player.
/// Calls the token contract(which is an external program call using invoke) to
/// transfer tokens to the player.
#[expose]
fn play(ctx: Context, player: Address) -> bool {
#[public]
fn play(state: State, player: Address) -> bool {
let num = get_random_number(player, 1);
// If win transfer to player
let call_ctx = match ctx.get_value(TOKEN_PROGRAM_NAME) {
Ok(ctx) => ctx,
let lottery_state = match state.get_value(TOKEN_PROGRAM_NAME) {
Ok(state) => state,
Err(_) => return false,
};

let Ok(lotto_addy) = ctx.get_value::<Address>("address") else {
let Ok(lotto_addy) = state.get_value::<Address>("address") else {
return false;
};

// Transfer
let _ = ctx.program_invoke(
call_ctx,
let _ = state.invoke_program(
lottery_state,
"transfer",
&[Box::new(lotto_addy), Box::new(player), Box::new(num)],
);
Expand Down
1 change: 0 additions & 1 deletion x/programs/rust/examples/pokemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
expose_macro = { version = "0.1.0", path = "../../expose_macro" }
wasmlanche_sdk = { version = "0.1.0", path = "../../wasmlanche_sdk" }
rand = "0.8.5"
rand_chacha = "0.3.1"
Expand Down
24 changes: 11 additions & 13 deletions x/programs/rust/examples/pokemon/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use expose_macro::expose;
use serde::{Deserialize, Serialize};
use wasmlanche_sdk::store::Context;
use wasmlanche_sdk::types::Address;
use wasmlanche_sdk::{public, store::State, types::Address};

#[derive(Serialize, Deserialize, Debug)]
struct Pokemon {
Expand All @@ -14,30 +12,30 @@ struct Pokemon {
// non-string keys are not supported by serde
type OwnedPokemon = Vec<Pokemon>;

#[expose]
pub fn init(ctx: Context) -> bool {
ctx.store_value("total_supply", &10_i64).is_ok()
#[public]
pub fn init(state: State) -> bool {
state.store_value("total_supply", &10_i64).is_ok()
}

#[expose]
pub fn catch(ctx: Context, player: Address) -> bool {
#[public]
pub fn catch(state: State, player: Address) -> bool {
let pokemon = Pokemon {
name: String::from("Pikachu"),
level: 1,
hp: get_random_number(player, 0) as u32,
moves: vec![String::from("Thunderbolt"), String::from("Quick Attack")],
};

let mut owned: OwnedPokemon = ctx.get_map_value("owned", &player).unwrap_or_default();
let mut owned: OwnedPokemon = state.get_map_value("owned", &player).unwrap_or_default();
owned.push(pokemon);

ctx.store_map_value("owned", &player, &owned).is_ok()
state.store_map_value("owned", &player, &owned).is_ok()
}

#[expose]
pub fn get_owned(ctx: Context, player: Address) -> bool {
#[public]
pub fn get_owned(state: State, player: Address) -> bool {
// get players pokemon and print to screen
let owned: OwnedPokemon = ctx
let owned: OwnedPokemon = state
.get_map_value("owned", &player)
.unwrap_or_else(|_| vec![]);
println!("Owned: {:?}", owned);
Expand Down
1 change: 0 additions & 1 deletion x/programs/rust/examples/token/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
expose_macro = { version = "0.1.0", path = "../../expose_macro" }
wasmlanche_sdk = { version = "0.1.0", path = "../../wasmlanche_sdk" }


Expand Down
45 changes: 23 additions & 22 deletions x/programs/rust/examples/token/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
use wasmlanche_sdk::store::Context;
use wasmlanche_sdk::types::Address;

use expose_macro::expose;
use wasmlanche_sdk::{public, store::State, types::Address};

/// Initializes the program with a name, symbol, and total supply.
#[expose]
pub fn init(ctx: Context) -> bool {
ctx.store_value("total_supply", &123456789_i64)
#[public]
pub fn init(state: State) -> bool {
state
.store_value("total_supply", &123456789_i64)
.store_value("name", "WasmCoin")
.store_value("symbol", "WACK")
.is_ok()
}

/// Gets total supply or -1 on error.
#[expose]
pub fn get_total_supply(ctx: Context) -> i64 {
ctx.get_value("total_supply").unwrap()
#[public]
pub fn get_total_supply(state: State) -> i64 {
state.get_value("total_supply").unwrap()
}

/// Adds amount coins to the recipients balance.
#[expose]
pub fn mint_to(ctx: Context, recipient: Address, amount: i64) -> bool {
let amount = amount + ctx.get_map_value("balances", &recipient).unwrap_or(0);
ctx.store_map_value("balances", &recipient, &amount).is_ok()
#[public]
pub fn mint_to(state: State, recipient: Address, amount: i64) -> bool {
let amount = amount + state.get_map_value("balances", &recipient).unwrap_or(0);
state
.store_map_value("balances", &recipient, &amount)
.is_ok()
}

/// Transfers amount coins from the sender to the recipient. Returns whether successful.
#[expose]
pub fn transfer(ctx: Context, sender: Address, recipient: Address, amount: i64) -> bool {
#[public]
pub fn transfer(state: State, sender: Address, recipient: Address, amount: i64) -> bool {
// require sender != recipient
if sender == recipient {
return false;
}
// ensure the sender has adequate balance
let sender_balance: i64 = ctx.get_map_value("balances", &sender).unwrap_or(0);
let sender_balance: i64 = state.get_map_value("balances", &sender).unwrap_or(0);
if amount < 0 || sender_balance < amount {
return false;
}
let recipient_balance: i64 = ctx.get_map_value("balances", &recipient).unwrap_or(0);
ctx.store_map_value("balances", &sender, &(sender_balance - amount))
let recipient_balance: i64 = state.get_map_value("balances", &recipient).unwrap_or(0);
state
.store_map_value("balances", &sender, &(sender_balance - amount))
.store_map_value("balances", &recipient, &(recipient_balance + amount))
.is_ok()
}

/// Gets the balance of the recipient.
#[expose]
pub fn get_balance(ctx: Context, recipient: Address) -> i64 {
ctx.get_map_value("balances", &recipient).unwrap_or(0)
#[public]
pub fn get_balance(state: State, recipient: Address) -> i64 {
state.get_map_value("balances", &recipient).unwrap_or(0)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "expose_macro"
name = "sdk_macros"
version = "0.1.0"
edition = "2021"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, parse_str, FnArg, Ident, ItemFn, Pat, PatType, Type};
/// An attribute procedural macro that can be used to expose a function to the host.
/// An attribute procedural macro that makes a function visable to the VM host.
/// It does so by wrapping the `item` tokenstream in a new function that can be called by the host.
/// The wrapper function will have the same name as the original function, but with "_guest" appended to it.
/// The wrapper functions parameters will be converted to WASM supported types. When called, the wrapper function
/// calls the original function by converting the parameters back to their intended types using .into().
#[proc_macro_attribute]
pub fn expose(_: TokenStream, item: TokenStream) -> TokenStream {
pub fn public(_: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let name = &input.sig.ident;
let input_args = &input.sig.inputs;
Expand Down Expand Up @@ -91,7 +91,7 @@ fn is_context(type_path: &std::boxed::Box<Type>) -> bool {
if let Type::Path(ref type_path) = **type_path {
let ident = &type_path.path.segments[0].ident;
let ident_str = ident.to_string();
ident_str == "Context"
ident_str == "State"
} else {
false
}
Expand Down
Loading

0 comments on commit 1820476

Please sign in to comment.