use super::*;
use bounded_collections::ConstU32;
const ADDRESS_SPACE_SIZE: u64 = 0x100000000_u64;
const VM_MAX_PAGE_SIZE: u32 = 0x10000;
const VM_ADDRESS_SPACE_TOP: u32 = (ADDRESS_SPACE_SIZE - VM_MAX_PAGE_SIZE as u64) as u32;
const VM_ADDR_RETURN_TO_HOST: u32 = 0xffff0000;
#[derive(Copy, Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub struct OpaqueValKeyset {
pub ed25519: OpaqueEd25519Public,
pub bandersnatch: OpaqueBandersnatchPublic,
pub metadata: OpaqueValidatorMetadata,
}
impl Default for OpaqueValKeyset {
fn default() -> Self {
Self {
ed25519: OpaqueEd25519Public::zero(),
bandersnatch: OpaqueBandersnatchPublic::zero(),
metadata: OpaqueValidatorMetadata::zero(),
}
}
}
pub type OpaqueValKeysets = FixedVec<OpaqueValKeyset, ConstU32<{ VAL_COUNT as u32 }>>;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum RootIdentifier {
Direct(SegmentTreeRoot),
Indirect(WorkPackageHash),
}
impl From<SegmentTreeRoot> for RootIdentifier {
fn from(root: SegmentTreeRoot) -> Self {
Self::Direct(root)
}
}
impl From<WorkPackageHash> for RootIdentifier {
fn from(hash: WorkPackageHash) -> Self {
Self::Indirect(hash)
}
}
impl TryFrom<RootIdentifier> for SegmentTreeRoot {
type Error = WorkPackageHash;
fn try_from(root: RootIdentifier) -> Result<Self, Self::Error> {
match root {
RootIdentifier::Direct(root) => Ok(root),
RootIdentifier::Indirect(hash) => Err(hash),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct ImportSpec {
pub root: RootIdentifier,
pub index: u16,
}
impl Encode for ImportSpec {
fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
let off = match &self.root {
RootIdentifier::Direct(root) => {
root.encode_to(dest);
0
},
RootIdentifier::Indirect(hash) => {
hash.encode_to(dest);
1 << 15
},
};
(self.index + off).encode_to(dest);
}
}
impl Decode for ImportSpec {
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
let h = Hash::decode(input)?;
let i = u16::decode(input)?;
let root = if i & (1 << 15) == 0 {
SegmentTreeRoot::from(h).into()
} else {
WorkPackageHash::from(h).into()
};
Ok(Self { root, index: i & !(1 << 15) })
}
}
#[derive(Clone, Encode, Decode, Debug)]
pub struct ExtrinsicSpec {
pub hash: ExtrinsicHash,
pub len: u32,
}
#[derive(Clone, Encode, Decode, Debug)]
pub struct GenericWorkItem<Xt> {
pub service: ServiceId,
pub code_hash: CodeHash,
pub payload: WorkPayload,
pub gas_limit: Gas,
pub import_segments: WorkItemImportsVec,
pub extrinsics: Vec<Xt>,
pub export_count: u16,
}
pub type WorkItemImportsVec = BoundedVec<ImportSpec, ConstU32<{ MAX_IMPORTS }>>;
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub struct RefineContext {
pub anchor: HeaderHash,
pub state_root: StateRootHash,
pub beefy_root: MmrPeakHash,
pub lookup_anchor: HeaderHash,
pub lookup_anchor_slot: Slot,
pub prerequisites: VecSet<WorkPackageHash>,
}
#[derive(Clone, Encode, Decode, Debug)]
pub struct GenericWorkPackage<Xt> {
pub authorization: Authorization,
pub auth_code_host: ServiceId,
pub authorizer: Authorizer,
pub context: RefineContext,
pub items: GenericWorkItems<Xt>,
}
pub type GenericWorkItems<Xt> = BoundedVec<GenericWorkItem<Xt>, ConstU32<MAX_WORK_ITEMS>>;
pub type WorkItems = GenericWorkItems<ExtrinsicSpec>;
pub type WorkItem = GenericWorkItem<ExtrinsicSpec>;
pub type FatWorkItem = GenericWorkItem<Vec<u8>>;
pub type RefWorkItem = GenericWorkItem<[u8]>;
pub type WorkPackage = GenericWorkPackage<ExtrinsicSpec>;
pub type FatWorkPackage = GenericWorkPackage<Vec<u8>>;
pub type RefWorkPackage = GenericWorkPackage<[u8]>;
#[derive(Clone, Encode, Decode, Debug)]
pub struct Authorizer {
pub code_hash: CodeHash,
pub param: AuthParam,
}
impl Authorizer {
pub fn any() -> Self {
Self { code_hash: CodeHash::zero(), param: Default::default() }
}
}
#[derive(Clone, Encode, Decode, Debug)]
pub struct PackageInfo {
pub package_hash: WorkPackageHash,
pub context: RefineContext,
pub authorizer: Authorizer,
pub auth_output: AuthOutput,
}
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub enum WorkError {
OutOfGas = 1,
Panic = 2,
BadCode = 3,
CodeOversize = 4,
}
#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
pub struct WorkResult {
pub service: ServiceId,
pub code_hash: CodeHash,
pub payload_hash: PayloadHash,
pub gas: Gas,
#[codec(encoded_as = "CompactRefineResult")]
pub result: Result<WorkOutput, WorkError>,
}
#[derive(Debug, Encode, Decode)]
pub struct OnChainContext {
pub slot: Slot,
pub id: ServiceId,
}
#[derive(Debug, Encode, Decode)]
pub struct AccumulateItem {
pub package: WorkPackageHash,
pub auth: AuthOutput,
pub payload: PayloadHash,
#[codec(encoded_as = "CompactRefineResult")]
pub result: Result<WorkOutput, WorkError>,
}
#[derive(Debug, Encode, Decode)]
pub struct AccumulateParams {
pub context: OnChainContext,
pub results: Vec<AccumulateItem>,
}
#[derive(Debug, Encode)]
pub struct AccumulateParamsRef<'a> {
pub context: OnChainContext,
pub results: &'a [AccumulateItem],
}
#[derive(Debug, Clone, Encode, Decode)]
pub struct TransferRecord {
pub source: ServiceId,
pub destination: ServiceId,
pub amount: Balance,
pub memo: Memo,
pub gas_limit: Gas,
}
impl Default for TransferRecord {
fn default() -> Self {
Self {
source: Default::default(),
destination: Default::default(),
amount: Default::default(),
memo: Memo::zero(),
gas_limit: Default::default(),
}
}
}
#[derive(Debug, Encode, Decode)]
pub struct OnTransferParams {
pub context: OnChainContext,
pub transfers: Vec<TransferRecord>,
}
#[derive(Debug, Encode)]
pub struct OnTransferParamsRef<'a> {
pub context: OnChainContext,
pub transfers: &'a [TransferRecord],
}
#[derive(Debug, Encode, Decode)]
pub struct RefineParams {
pub id: ServiceId,
pub payload: WorkPayload,
pub package_info: PackageInfo,
pub extrinsics: Vec<Vec<u8>>,
}
#[derive(Debug, Encode)]
pub struct RefineParamsRef<'a, T: 'a + core::fmt::Debug + Encode> {
pub id: ServiceId,
pub payload: &'a WorkPayload,
pub package_info: &'a PackageInfo,
pub extrinsics: &'a [T],
}
#[derive(Debug, Clone, Encode, Decode, MaxEncodedLen)]
pub struct ServiceInfo {
pub code_hash: CodeHash,
pub balance: Balance,
pub min_item_gas: Gas,
pub min_memo_gas: Gas,
pub bytes: u64,
pub items: u32,
}
impl scale::ConstEncodedLen for ServiceInfo {}
#[repr(C)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Encode, Decode, MaxEncodedLen, Default)]
pub struct InvokeArgsT<RegT> {
pub gas: i64,
pub regs: [RegT; 13],
}
impl InvokeArgsT<u32> {
pub const BYTESIZE: usize = {
let size = core::mem::size_of::<u64>() + core::mem::size_of::<u32>() * 13;
assert!(size >= 60);
size
};
pub fn from_bytes(xs: &[u8]) -> Self {
assert!(xs.len() >= Self::BYTESIZE); Self {
gas: i64::from_le_bytes([xs[0], xs[1], xs[2], xs[3], xs[4], xs[5], xs[6], xs[7]]),
regs: {
let mut n = 8;
[(); 13].map(|_| {
let value = u32::from_le_bytes([xs[n], xs[n + 1], xs[n + 2], xs[n + 3]]);
n += 4;
value
})
},
}
}
pub fn to_bytes(&self, output: &mut [u8]) {
assert!(output.len() >= Self::BYTESIZE);
output[0..8].copy_from_slice(&self.gas.to_le_bytes());
for (nth_reg, value) in self.regs.into_iter().enumerate() {
output[8 + nth_reg * 4..8 + (nth_reg + 1) * 4].copy_from_slice(&value.to_le_bytes());
}
}
}
impl InvokeArgsT<u64> {
pub const BYTESIZE: usize = {
let size = core::mem::size_of::<u64>() + core::mem::size_of::<u64>() * 13;
assert!(size >= 112);
size
};
pub fn from_bytes(xs: &[u8]) -> Self {
assert!(xs.len() >= Self::BYTESIZE); Self {
gas: i64::from_le_bytes([xs[0], xs[1], xs[2], xs[3], xs[4], xs[5], xs[6], xs[7]]),
regs: {
let mut n = 8;
[(); 13].map(|_| {
let value = u64::from_le_bytes([
xs[n],
xs[n + 1],
xs[n + 2],
xs[n + 3],
xs[n + 4],
xs[n + 5],
xs[n + 6],
xs[n + 7],
]);
n += 8;
value
})
},
}
}
pub fn to_bytes(&self, output: &mut [u8]) {
assert!(output.len() >= Self::BYTESIZE);
output[0..8].copy_from_slice(&self.gas.to_le_bytes());
for (nth_reg, value) in self.regs.into_iter().enumerate() {
output[8 + nth_reg * 8..8 + (nth_reg + 1) * 8].copy_from_slice(&value.to_le_bytes());
}
}
}
impl<RegT> InvokeArgsT<RegT>
where
RegT: Copy + Default + From<u32>,
{
pub fn new(gas: i64) -> Self {
let mut args = Self { gas, ..Self::default() };
args.set_reg(Reg::RA, RegT::from(VM_ADDR_RETURN_TO_HOST));
args.set_reg(Reg::SP, RegT::from(VM_ADDRESS_SPACE_TOP));
args
}
pub fn reg(&self, reg: Reg) -> RegT {
self.regs[reg as usize]
}
pub fn set_reg(&mut self, reg: Reg, value: RegT) {
self.regs[reg as usize] = value;
}
pub fn set_return_value<T: SetReturnValue<RegT>>(&mut self, value: T) {
value.set_return_value(self);
}
pub fn get_arg_reg(&self, i: usize) -> RegT {
self.reg(Reg::ARG_REGS[i])
}
pub fn get_arg<T: Arg<RegT>>(&mut self, i: &mut usize) -> T {
Arg::get(self, i)
}
pub fn set_arg_reg(&mut self, i: usize, value: RegT) {
self.set_reg(Reg::ARG_REGS[i], value)
}
pub fn set_arg<T: Arg<RegT>>(&mut self, i: &mut usize, value: T) {
value.set(self, i)
}
}
#[cfg(target_pointer_width = "32")]
pub type InvokeArgs = InvokeArgsT<u32>;
#[cfg(target_pointer_width = "64")]
pub type InvokeArgs = InvokeArgsT<u64>;
impl scale::ConstEncodedLen for InvokeArgsT<u32> {}
impl scale::ConstEncodedLen for InvokeArgsT<u64> {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u32)]
pub enum Reg {
RA = 0,
SP = 1,
T0 = 2,
T1 = 3,
T2 = 4,
S0 = 5,
S1 = 6,
A0 = 7,
A1 = 8,
A2 = 9,
A3 = 10,
A4 = 11,
A5 = 12,
}
impl Reg {
pub const ARG_REGS: [Reg; 9] =
[Reg::A0, Reg::A1, Reg::A2, Reg::A3, Reg::A4, Reg::A5, Reg::T0, Reg::T1, Reg::T2];
}
pub trait SetReturnValue<RegT> {
fn set_return_value(self, args: &mut InvokeArgsT<RegT>);
}
impl<RegT> SetReturnValue<RegT> for () {
fn set_return_value(self, _args: &mut InvokeArgsT<RegT>) {}
}
impl SetReturnValue<u32> for u32 {
fn set_return_value(self, args: &mut InvokeArgsT<u32>) {
args.set_reg(Reg::A0, self);
}
}
impl SetReturnValue<u32> for u64 {
fn set_return_value(self, args: &mut InvokeArgsT<u32>) {
args.set_reg(Reg::A0, self as u32);
args.set_reg(Reg::A1, (self >> 32) as u32);
}
}
impl SetReturnValue<u64> for u32 {
fn set_return_value(self, args: &mut InvokeArgsT<u64>) {
args.set_reg(Reg::A0, u64::from(self));
}
}
impl SetReturnValue<u64> for u64 {
fn set_return_value(self, args: &mut InvokeArgsT<u64>) {
args.set_reg(Reg::A0, self);
}
}
set_return_value_impl_as! {
bool => u32,
u8 => u32,
u16 => u32,
i8 => u32,
i16 => u32,
i32 => u32,
i64 => u64
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
set_return_value_impl_as! {
isize => usize
}
pub trait Arg<RegT> {
fn get(args: &InvokeArgsT<RegT>, i: &mut usize) -> Self
where
Self: Sized;
fn set(self, args: &mut InvokeArgsT<RegT>, i: &mut usize);
}
impl Arg<u32> for u32 {
fn get(args: &InvokeArgsT<u32>, i: &mut usize) -> Self {
let value = args.get_arg_reg(*i);
*i += 1;
value
}
fn set(self, args: &mut InvokeArgsT<u32>, i: &mut usize) {
args.set_arg_reg(*i, self);
*i += 1;
}
}
impl Arg<u64> for u32 {
fn get(args: &InvokeArgsT<u64>, i: &mut usize) -> Self {
let value = args.get_arg_reg(*i);
*i += 1;
value as u32
}
fn set(self, args: &mut InvokeArgsT<u64>, i: &mut usize) {
args.set_arg_reg(*i, u64::from(self));
*i += 1;
}
}
impl Arg<u32> for u64 {
fn get(args: &InvokeArgsT<u32>, i: &mut usize) -> Self {
let value_lo = args.get_arg_reg(*i);
*i += 1;
let value_hi = args.get_arg_reg(*i);
*i += 1;
(value_lo as u64) | ((value_hi as u64) << 32)
}
fn set(self, args: &mut InvokeArgsT<u32>, i: &mut usize) {
let value_lo = self as u32;
let value_hi = (self >> 32) as u32;
value_lo.set(args, i);
value_hi.set(args, i);
}
}
impl Arg<u64> for u64 {
fn get(args: &InvokeArgsT<u64>, i: &mut usize) -> Self {
let value = args.get_arg_reg(*i);
*i += 1;
value
}
fn set(self, args: &mut InvokeArgsT<u64>, i: &mut usize) {
args.set_arg_reg(*i, self);
*i += 1;
}
}
macro_rules! impl_conv_common {
($reg_ty:ty) => {
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
impl SetReturnValue<$reg_ty> for usize {
#[cfg(target_pointer_width = "32")]
fn set_return_value(self, args: &mut InvokeArgsT<$reg_ty>) {
(self as u32).set_return_value(args)
}
#[cfg(target_pointer_width = "64")]
fn set_return_value(self, args: &mut InvokeArgsT<$reg_ty>) {
(self as u64).set_return_value(args)
}
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
impl Arg<$reg_ty> for usize {
#[cfg(target_pointer_width = "32")]
fn get(args: &InvokeArgsT<$reg_ty>, i: &mut usize) -> Self {
u32::get(args, i) as usize
}
#[cfg(target_pointer_width = "64")]
fn get(args: &InvokeArgsT<$reg_ty>, i: &mut usize) -> Self {
u64::get(args, i) as usize
}
#[cfg(target_pointer_width = "32")]
fn set(self, args: &mut InvokeArgsT<$reg_ty>, i: &mut usize) {
(self as u32).set(args, i);
}
#[cfg(target_pointer_width = "64")]
fn set(self, args: &mut InvokeArgsT<$reg_ty>, i: &mut usize) {
(self as u64).set(args, i);
}
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
impl<T> Arg<$reg_ty> for *const T {
fn get(args: &InvokeArgsT<$reg_ty>, i: &mut usize) -> Self {
usize::get(args, i) as Self
}
fn set(self, args: &mut InvokeArgsT<$reg_ty>, i: &mut usize) {
(self as usize).set(args, i);
}
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
impl<T> Arg<$reg_ty> for *mut T {
fn get(args: &InvokeArgsT<$reg_ty>, i: &mut usize) -> Self {
usize::get(args, i) as Self
}
fn set(self, args: &mut InvokeArgsT<$reg_ty>, i: &mut usize) {
(self as usize).set(args, i);
}
}
impl Arg<$reg_ty> for bool {
fn get(args: &InvokeArgsT<$reg_ty>, i: &mut usize) -> Self {
u32::get(args, i) != 0
}
fn set(self, args: &mut InvokeArgsT<$reg_ty>, i: &mut usize) {
(self as u32).set(args, i);
}
}
};
}
impl_conv_common!(u32);
impl_conv_common!(u64);
arg_impl_as! {
u8 => u32,
u16 => u32,
i8 => u32,
i16 => u32,
i32 => u32,
i64 => u64
}
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
arg_impl_as! {
isize => usize
}
macro_rules! set_return_value_impl_as {
($($type:ty => $as_type:ty),+) => {
$(
impl SetReturnValue<u32> for $type {
fn set_return_value(self, args: &mut InvokeArgsT<u32>) {
(self as $as_type).set_return_value(args);
}
}
impl SetReturnValue<u64> for $type {
fn set_return_value(self, args: &mut InvokeArgsT<u64>) {
(self as $as_type).set_return_value(args);
}
}
)+
};
}
use set_return_value_impl_as;
macro_rules! arg_impl_as {
($($type:ty => $as_type:ty),+) => {
$(
impl Arg<u32> for $type {
fn get(args: &InvokeArgsT<u32>, i: &mut usize) -> Self {
<$as_type>::get(args, i) as Self
}
fn set(self, args: &mut InvokeArgsT<u32>, i: &mut usize) {
(self as $as_type).set(args, i);
}
}
impl Arg<u64> for $type {
fn get(args: &InvokeArgsT<u64>, i: &mut usize) -> Self {
<$as_type>::get(args, i) as Self
}
fn set(self, args: &mut InvokeArgsT<u64>, i: &mut usize) {
(self as $as_type).set(args, i);
}
}
)+
};
}
use arg_impl_as;
#[rustfmt::skip]
#[macro_export]
macro_rules! index_for_hostcall {
(gas) => {0};
(lookup) => {1};
(read) => {2};
(write) => {3};
(info) => {4};
(bless) => {5};
(assign) => {6};
(designate) => {7};
(checkpoint) => {8};
(new) => {9};
(upgrade) => {10};
(transfer) => {11};
(quit) => {12};
(solicit) => {13};
(forget) => {14};
(historical_lookup) => {15};
(import) => {16};
(export) => {17};
(machine) => {18};
(peek) => {19};
(poke) => {20};
(zero) => {21};
(void) => {22};
(invoke) => {23};
(expunge) => {24};
(log) => {100};
}
pub use index_for_hostcall;
struct CompactRefineResult(Result<WorkOutput, WorkError>);
struct CompactRefineResultRef<'a>(&'a Result<WorkOutput, WorkError>);
impl From<CompactRefineResult> for Result<WorkOutput, WorkError> {
fn from(value: CompactRefineResult) -> Self {
value.0
}
}
impl<'a> From<&'a Result<WorkOutput, WorkError>> for CompactRefineResultRef<'a> {
fn from(value: &'a Result<WorkOutput, WorkError>) -> Self {
CompactRefineResultRef(value)
}
}
impl<'a> scale::EncodeAsRef<'a, Result<WorkOutput, WorkError>> for CompactRefineResult {
type RefType = CompactRefineResultRef<'a>;
}
impl Encode for CompactRefineResult {
fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
CompactRefineResultRef(&self.0).encode_to(dest)
}
}
impl<'a> Encode for CompactRefineResultRef<'a> {
fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
match &self.0 {
Ok(o) => {
dest.push_byte(0);
o.encode_to(dest)
},
Err(e) => e.encode_to(dest),
}
}
}
impl Decode for CompactRefineResult {
fn decode<I: scale::Input>(input: &mut I) -> Result<Self, scale::Error> {
match input.read_byte()? {
0 => Ok(Self(Ok(WorkOutput::decode(input)?))),
e => Ok(Self(Err(WorkError::decode(&mut &[e][..])?))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invoke_args_32_can_be_converted_to_bytes() {
let args = InvokeArgsT::<u32> {
gas: 6245510980648378913,
regs: [
3364686619, 2100728089, 1436741328, 1388289407, 722770902, 1607395728, 142847709,
4089017013, 3970153417, 2326764831, 1722929565, 904569242, 3653777900,
],
};
let mut bytes = [0; core::mem::size_of::<InvokeArgsT<u32>>()];
args.to_bytes(&mut bytes);
assert_eq!(InvokeArgsT::<u32>::from_bytes(&bytes), args);
}
#[test]
fn test_invoke_args_64_can_be_converted_to_bytes() {
let args = InvokeArgsT::<u64> {
gas: 6245510980648378913,
regs: [
3897305703111539400,
5760987759726533151,
5505591633088512456,
7520817185631591303,
8571768649504054185,
7628499073207346885,
5159587593731446957,
12549592788806540823,
4259284988670782509,
1881634007696300103,
3283781648033715868,
6132298261455339512,
3612762733779559463,
],
};
let mut bytes = [0; core::mem::size_of::<InvokeArgsT<u64>>()];
args.to_bytes(&mut bytes);
assert_eq!(InvokeArgsT::<u64>::from_bytes(&bytes), args);
}
#[test]
fn compact_refine_result_codec() {
let enc_dec = |exp_res, exp_buf: &[u8]| {
let buf = CompactRefineResultRef(&exp_res).encode();
assert_eq!(buf, exp_buf);
let res = CompactRefineResult::decode(&mut &buf[..]).unwrap();
assert_eq!(res.0, exp_res);
};
enc_dec(Ok(vec![1, 2, 3].into()), &[0, 3, 1, 2, 3]);
enc_dec(Err(WorkError::OutOfGas), &[1]);
enc_dec(Err(WorkError::Panic), &[2]);
enc_dec(Err(WorkError::BadCode), &[3]);
enc_dec(Err(WorkError::CodeOversize), &[4]);
}
}