use std::panic;
use fvm_ipld_encoding::de::DeserializeOwned;
use fvm_shared::error::ErrorNumber;
use fvm_shared::sector::WindowPoStVerifyInfo;
use super::Context;
use super::context::Memory;
use crate::kernel::ClassifyResult;
use crate::kernel::{Result, filecoin::FilecoinKernel};
use crate::syscall_error;
use anyhow::Context as _;
use anyhow::anyhow;
use fvm_ipld_encoding::from_slice;
use fvm_shared::piece::PieceInfo;
use fvm_shared::sector::{
AggregateSealVerifyProofAndInfos, RegisteredSealProof, ReplicaUpdateInfo, SealVerifyInfo,
};
use fvm_shared::sys;
trait ReadCbor {
fn read_cbor<T: DeserializeOwned>(&self, offset: u32, len: u32) -> Result<T>;
}
impl ReadCbor for Memory {
fn read_cbor<T: DeserializeOwned>(&self, offset: u32, len: u32) -> Result<T> {
let bytes = self.try_slice(offset, len)?;
match panic::catch_unwind(|| from_slice(bytes).or_error(ErrorNumber::IllegalArgument)) {
Ok(v) => v,
Err(e) => {
log::error!("panic when decoding cbor from actor: {:?}", e);
Err(syscall_error!(IllegalArgument; "panic when decoding cbor from actor").into())
}
}
}
}
pub fn compute_unsealed_sector_cid(
context: Context<'_, impl FilecoinKernel>,
proof_type: i64, pieces_off: u32, pieces_len: u32,
cid_off: u32,
cid_len: u32,
) -> Result<u32> {
let typ = RegisteredSealProof::from(proof_type);
if let RegisteredSealProof::Invalid(invalid) = typ {
return Err(syscall_error!(IllegalArgument; "invalid proof type {}", invalid).into());
}
let pieces: Vec<PieceInfo> = context.memory.read_cbor(pieces_off, pieces_len)?;
context.memory.check_bounds(cid_off, cid_len)?;
let cid = context
.kernel
.compute_unsealed_sector_cid(typ, pieces.as_slice())?;
context.memory.write_cid(&cid, cid_off, cid_len)
}
pub fn verify_post(
context: Context<'_, impl FilecoinKernel>,
info_off: u32, info_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<WindowPoStVerifyInfo>(info_off, info_len)?;
context
.kernel
.verify_post(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn verify_consensus_fault(
context: Context<'_, impl FilecoinKernel>,
h1_off: u32,
h1_len: u32,
h2_off: u32,
h2_len: u32,
extra_off: u32,
extra_len: u32,
) -> Result<sys::out::crypto::VerifyConsensusFault> {
let h1 = context.memory.try_slice(h1_off, h1_len)?;
let h2 = context.memory.try_slice(h2_off, h2_len)?;
let extra = context.memory.try_slice(extra_off, extra_len)?;
let ret = context.kernel.verify_consensus_fault(h1, h2, extra)?;
match ret {
Some(fault) => Ok(sys::out::crypto::VerifyConsensusFault {
fault: fault.fault_type as u32,
epoch: fault.epoch,
target: fault
.target
.id()
.context("kernel returned non-id target address")
.or_fatal()?,
}),
None => Ok(sys::out::crypto::VerifyConsensusFault {
fault: 0,
epoch: 0,
target: 0,
}),
}
}
pub fn verify_aggregate_seals(
context: Context<'_, impl FilecoinKernel>,
agg_off: u32, agg_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<AggregateSealVerifyProofAndInfos>(agg_off, agg_len)?;
context
.kernel
.verify_aggregate_seals(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn verify_replica_update(
context: Context<'_, impl FilecoinKernel>,
rep_off: u32, rep_len: u32,
) -> Result<i32> {
let info = context
.memory
.read_cbor::<ReplicaUpdateInfo>(rep_off, rep_len)?;
context
.kernel
.verify_replica_update(&info)
.map(|v| if v { 0 } else { -1 })
}
pub fn batch_verify_seals(
context: Context<'_, impl FilecoinKernel>,
batch_off: u32,
batch_len: u32,
result_off: u32,
) -> Result<()> {
let batch = context
.memory
.read_cbor::<Vec<SealVerifyInfo>>(batch_off, batch_len)?;
let output = context
.memory
.try_slice_mut(result_off, batch.len() as u32)?;
let result = context.kernel.batch_verify_seals(&batch)?;
if result.len() != batch.len() {
return Err(anyhow!(
"expected one result per input: {} != {}",
batch.len(),
result.len()
))
.or_fatal();
}
unsafe {
output.copy_from_slice(&*(&*result as *const [bool] as *const [u8]));
}
Ok(())
}
pub fn total_fil_circ_supply(
context: Context<'_, impl FilecoinKernel>,
) -> Result<sys::TokenAmount> {
context
.kernel
.total_fil_circ_supply()?
.try_into()
.context("circulating supply exceeds u128 limit")
.or_fatal()
}