diff --git a/core/src/syscall/host.rs b/core/src/syscall/host.rs index f68990a7..5e7c3e9d 100644 --- a/core/src/syscall/host.rs +++ b/core/src/syscall/host.rs @@ -155,8 +155,8 @@ impl SyscallHostCall { } if !chunks.remainder().is_empty() { - let mut value = [0u8; 4]; - value.copy_from_slice(chunks.remainder()); + let mut value = ctx.rt.mr(output_ptr).to_le_bytes(); + value[..chunks.remainder().len()].copy_from_slice(chunks.remainder()); ctx.rt.mw(output_ptr, u32::from_le_bytes(value)); } }; diff --git a/examples/wallet/program/src/main.rs b/examples/wallet/program/src/main.rs index a9f78781..0ce1764e 100644 --- a/examples/wallet/program/src/main.rs +++ b/examples/wallet/program/src/main.rs @@ -6,7 +6,7 @@ extern crate alloc; use athena_interface::Address; use athena_vm::entrypoint; use athena_vm_declare::{callable, template}; -use athena_vm_sdk::wallet::{SpendArguments, WalletProgram}; +use athena_vm_sdk::wallet::{ProxyArguments, SpendArguments, WalletProgram}; use athena_vm_sdk::{call, spawn, Pubkey, VerifiableTemplate}; use parity_scale_codec::{Decode, Encode}; @@ -39,8 +39,9 @@ impl WalletProgram for Wallet { call(args.recipient, None, None, args.amount); } - fn proxy(&self, _destination: Address, _args: &[u8]) { - unimplemented!(); + #[callable] + fn proxy(&self, args: ProxyArguments) -> alloc::vec::Vec { + call(args.destination, args.args, args.method, args.amount) } #[callable] diff --git a/examples/wallet/script/src/main.rs b/examples/wallet/script/src/main.rs index e4f0b785..fc50a6f8 100644 --- a/examples/wallet/script/src/main.rs +++ b/examples/wallet/script/src/main.rs @@ -107,15 +107,19 @@ mod tests { payload::ExecutionPayloadBuilder, payload::Payload, Address, AthenaMessage, Balance, Encode, MessageKind, MethodSelector, StatusCode, }; + use athena_interface::{AthenaContext, Decode, ExecutionResult}; use athena_runner::vm::AthenaRevision; use athena_runner::AthenaVm; use athena_sdk::host::MockHostInterface; use athena_sdk::{AthenaStdin, ExecutionClient}; + use athena_vm_sdk::wallet::ProxyArguments; use athena_vm_sdk::Pubkey; use ed25519_dalek::ed25519::signature::Signer; use ed25519_dalek::SigningKey; use rand::rngs::OsRng; + use crate::{ADDRESS_ALICE, ADDRESS_CHARLIE}; + fn setup_logger() { let _ = tracing_subscriber::fmt() .with_test_writer() @@ -306,4 +310,51 @@ mod tests { assert!(gas_cost.is_some()); assert_eq!(result.read::(), amount); } + + #[test] + fn proxying_calls() { + setup_logger(); + + let mut stdin = AthenaStdin::new(); + let owner = Pubkey::default(); + stdin.write_slice(&owner.0); + let args = ProxyArguments { + destination: ADDRESS_CHARLIE, + method: Some(MethodSelector::from("athexp_fibonacci")), + args: Some(vec![1, 2, 3, 4]), + amount: 100, + }; + stdin.write_vec(args.encode()); + + let sender = ADDRESS_ALICE; + let context = AthenaContext::new(sender, Address::default(), 0); + let mut host = MockHostInterface::new(); + host.expect_call().returning(move |msg| { + assert_eq!(sender, msg.sender); + assert_eq!(args.destination, msg.recipient); + assert_eq!(args.amount, msg.value); + + let input = msg.input_data.unwrap(); + let payload = Payload::decode(&mut input.as_slice()).unwrap(); + assert_eq!(args.method, payload.selector); + assert_eq!(args.args, Some(payload.input)); + + ExecutionResult::new(StatusCode::Success, 1_000_000, Some(vec![5, 6, 7, 8, 9])) + }); + + let selector = MethodSelector::from("athexp_proxy"); + let result = ExecutionClient::new().execute_function( + super::ELF, + &selector, + stdin, + Some(&mut host), + Some(25000000), + Some(context), + ); + let (result, gas_cost) = result.unwrap(); + assert!(gas_cost.is_some()); + + let output = Vec::::decode(&mut result.as_slice()).unwrap(); + assert_eq!(vec![5, 6, 7, 8, 9], output); + } } diff --git a/tests/entrypoint/src/main.rs b/tests/entrypoint/src/main.rs index d22499cc..350bfa46 100644 --- a/tests/entrypoint/src/main.rs +++ b/tests/entrypoint/src/main.rs @@ -1,5 +1,8 @@ #![no_main] -use athena_vm::{entrypoint, types::Address}; +use athena_vm::{ + entrypoint, + types::{Address, MethodSelector}, +}; use athena_vm_declare::{callable, template}; use athena_vm_sdk::call; use std::io::Read; @@ -22,7 +25,7 @@ impl EntrypointTest { let address = Address::from(address); // recursive call to self - call(address, None, Some("athexp_test2"), 0); + call(address, None, Some(MethodSelector::from("athexp_test2")), 0); } #[callable] diff --git a/vm/sdk/src/call.rs b/vm/sdk/src/call.rs index 0d0792c3..a0c88717 100644 --- a/vm/sdk/src/call.rs +++ b/vm/sdk/src/call.rs @@ -8,11 +8,11 @@ use athena_interface::{payload::Payload, Address, Balance, Encode, MethodSelecto pub fn call( address: Address, input: Option>, - method: Option<&str>, + method: Option, amount: Balance, ) -> Vec { let payload = Payload { - selector: method.map(MethodSelector::from), + selector: method, input: input.unwrap_or_default(), }; let input_payload = payload.encode(); diff --git a/vm/sdk/src/wallet.rs b/vm/sdk/src/wallet.rs index 4b8fd4ec..daf0e98a 100644 --- a/vm/sdk/src/wallet.rs +++ b/vm/sdk/src/wallet.rs @@ -11,10 +11,18 @@ pub struct SpendArguments { pub amount: u64, } +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +pub struct ProxyArguments { + pub destination: Address, + pub method: Option, + pub args: Option>, + pub amount: u64, +} + pub trait WalletProgram { fn spawn(owner: Pubkey) -> Address; fn spend(&self, args: SpendArguments); - fn proxy(&self, destination: Address, args: &[u8]); + fn proxy(&self, args: ProxyArguments) -> Vec; fn deploy(&self, code: Vec) -> Address; fn max_spend(&self, args: SpendArguments) -> u64; }