use std::{cell::RefCell, collections::HashMap};
use cid::Cid;
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared::{
address::Address, econ::TokenAmount, error::ErrorNumber, error::ExitCode, ActorID, Response,
};
use super::Syscalls;
#[derive(Clone, Default, Debug)]
pub struct TestMessage {
pub method: u64,
pub params: Option<IpldBlock>,
pub value: TokenAmount,
}
#[derive(Clone, Default, Debug)]
pub struct FakeSyscalls {
pub root: RefCell<Cid>,
pub actor_id: ActorID,
pub caller_id: RefCell<ActorID>,
pub addresses: RefCell<HashMap<Address, ActorID>>,
pub next_actor_id: RefCell<ActorID>,
pub last_message: RefCell<Option<TestMessage>>,
pub abort_next_send: RefCell<bool>,
}
impl FakeSyscalls {
pub fn set_caller_id(&self, new_id: ActorID) {
self.caller_id.replace(new_id);
}
}
impl Syscalls for FakeSyscalls {
fn root(&self) -> Result<Cid, super::NoStateError> {
Ok(*self.root.borrow())
}
fn set_root(&self, cid: &Cid) -> Result<(), super::NoStateError> {
self.root.replace(*cid);
Ok(())
}
fn receiver(&self) -> fvm_shared::ActorID {
self.actor_id
}
fn caller(&self) -> fvm_shared::ActorID {
*self.caller_id.borrow()
}
fn send(
&self,
to: &fvm_shared::address::Address,
method: fvm_shared::MethodNum,
params: Option<fvm_ipld_encoding::ipld_block::IpldBlock>,
value: fvm_shared::econ::TokenAmount,
) -> Result<Response, ErrorNumber> {
if *self.abort_next_send.borrow() {
self.abort_next_send.replace(false);
Err(ErrorNumber::AssertionFailed)
} else {
let mut map = self.addresses.borrow_mut();
match to.payload() {
fvm_shared::address::Payload::ID(_) | fvm_shared::address::Payload::Actor(_) => {
Ok(())
}
fvm_shared::address::Payload::Secp256k1(_)
| fvm_shared::address::Payload::BLS(_)
| fvm_shared::address::Payload::Delegated(_) => {
if !map.contains_key(to) {
let actor_id = self.next_actor_id.replace_with(|old| *old + 1);
map.insert(*to, actor_id);
}
Ok(())
}
}?;
let message = TestMessage { method, params: params.clone(), value };
self.last_message.replace(Some(message));
Ok(Response { exit_code: ExitCode::OK, return_data: params })
}
}
fn resolve_address(&self, addr: &Address) -> Option<ActorID> {
if let fvm_shared::address::Payload::ID(id) = addr.payload() {
return Some(*id);
}
let map = self.addresses.borrow();
map.get(addr).copied()
}
}