use crate::actor_error_v14;
use crate::v14::runtime::builtins::Type;
use crate::v14::{ActorContext, ActorError};
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared4::address::Address;
use fvm_shared4::METHOD_SEND;
use fvm_shared4::{ActorID, MethodNum};
use std::fmt::{Display, Formatter};
use crate::v14::runtime::Runtime;
pub const HAMT_BIT_WIDTH: u32 = 5;
pub const FIRST_ACTOR_SPECIFIC_EXIT_CODE: u32 = 32;
pub fn resolve_to_actor_id(
rt: &impl Runtime,
address: &Address,
check_existence: bool,
) -> Result<ActorID, ActorError> {
let mut actor_id = None;
if let Some(id) = rt.resolve_address(address) {
actor_id = Some(id)
} else {
extract_send_result(rt.send_simple(
address,
METHOD_SEND,
Default::default(),
Default::default(),
))
.with_context(|| format!("failed to send zero balance to address {}", address))?;
if let Some(id) = rt.resolve_address(address) {
actor_id = Some(id)
}
}
if let Some(id) = actor_id {
if check_existence {
rt.get_actor_code_cid(&id)
.ok_or_else(|| actor_error_v14!(not_found, "no code for address {}", address))?;
}
return Ok(id);
}
Err(actor_error_v14!(
illegal_argument,
"failed to resolve or initialize address {}",
address
))
}
pub const FIRST_EXPORTED_METHOD_NUMBER: MethodNum = 1 << 24;
pub fn restrict_internal_api<RT>(rt: &RT, method: MethodNum) -> Result<(), ActorError>
where
RT: Runtime,
{
if method >= FIRST_EXPORTED_METHOD_NUMBER {
return Ok(());
}
let caller = rt.message().caller();
let code_cid = rt.get_actor_code_cid(&caller.id().unwrap());
match code_cid {
None => {
return Err(
actor_error_v14!(forbidden; "no code for caller {} of method {}", caller, method),
);
}
Some(code_cid) => {
let builtin_type = rt.resolve_builtin_actor_type(&code_cid);
match builtin_type {
None | Some(Type::EVM) => {
return Err(
actor_error_v14!(forbidden; "caller {} of method {} must be built-in", caller, method),
);
}
Some(_) => {}
}
}
}
Ok(())
}
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct SendError(pub fvm_shared4::error::ErrorNumber);
impl From<SendError> for fvm_shared4::error::ErrorNumber {
fn from(s: SendError) -> fvm_shared4::error::ErrorNumber {
s.0
}
}
impl From<SendError> for ActorError {
fn from(s: SendError) -> ActorError {
match s.0 {
fvm_shared4::error::ErrorNumber::NotFound => {
actor_error_v14!(unspecified; "receiver not found")
}
fvm_shared4::error::ErrorNumber::InsufficientFunds => {
actor_error_v14!(insufficient_funds; "not enough funds")
}
fvm_shared4::error::ErrorNumber::LimitExceeded => {
actor_error_v14!(assertion_failed; "recursion limit exceeded")
}
fvm_shared4::error::ErrorNumber::ReadOnly => ActorError::unchecked(
fvm_shared4::error::ExitCode::USR_READ_ONLY,
"attempted to mutate state while in readonly mode".into(),
),
err => {
actor_error_v14!(assertion_failed; "unexpected error: {}", err)
}
}
}
}
impl Display for SendError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "send failed with error number {}", self.0)
}
}
pub fn extract_send_result(
res: Result<fvm_shared4::Response, SendError>,
) -> Result<Option<IpldBlock>, ActorError> {
let ret = res?;
if ret.exit_code.is_success() {
Ok(ret.return_data)
} else {
Err(ActorError::checked(
ret.exit_code,
format!("send aborted with code {}", ret.exit_code),
ret.return_data,
))
}
}