use crate::{JamAccumulateOps, JamLogger, JamOuterVm};
use alloc::vec::Vec;
use bytes::Bytes;
use codec::DecodeAll;
use corevm_engine::{AccumulateEngine, Engine, OuterVm, WorkPackageParams};
use corevm_host::{CoreVmExtrinsics, CoreVmPayload, ExecEnv, InputChunk};
use ed25519_consensus::VerificationKey;
use jam_pvm_common::{accumulate, refine};
use jam_types::{
AccumulateItem, CoreIndex, Encode, Hash, SegmentBytes, ServiceId, Slot, TransferRecord,
WorkItemRecord, WorkOutput, WorkPackageHash, WorkPayload, SEGMENT_LEN,
};
use log::debug;
pub struct Service;
jam_pvm_common::declare_service!(Service);
impl jam_pvm_common::Service for Service {
fn refine(
_core_index: CoreIndex,
_item_index: usize,
_service_id: ServiceId,
payload: WorkPayload,
_package_hash: WorkPackageHash,
) -> WorkOutput {
JamLogger::init();
let mut outer_vm = JamOuterVm;
let payload =
CoreVmPayload::decode_all(&mut &payload[..]).expect("Failed to decode payload");
let exec = outer_vm.read_file(&payload.exec_ref).expect("Failed to read ExecEnv");
let exec = ExecEnv::decode_all(&mut &exec[..]).expect("Failed to decoder ExecEnv");
let key = exec
.input_key
.map(VerificationKey::try_from)
.transpose()
.expect("Invalid input key");
let extrinsics = CoreVmExtrinsics {
imported_memory_pages: {
let extrinsic0 = refine::extrinsic(0).unwrap_or_default();
let extrinsic0 = Bytes::from(extrinsic0);
let (segments, remainder) = extrinsic0.as_chunks::<SEGMENT_LEN>();
assert!(remainder.is_empty());
let pages: Vec<SegmentBytes> = segments
.iter()
.map(|slice| {
extrinsic0
.slice_ref(slice)
.try_into()
.expect("Chunk length equals segment length")
})
.collect();
pages
},
incoming_service_messages: refine::extrinsic(1)
.map(|xt| {
DecodeAll::decode_all(&mut &xt[..])
.expect("Failed to decode incoming service messages (extrinsic 1)")
})
.unwrap_or_default(),
input_chunks: refine::extrinsic(2)
.map(|xt| {
let key = key.expect("Verification key is unset");
InputChunk::decode_multiple_signed(xt.into(), &key)
.expect("Failed to decode input buffers (extrinsic 3)")
})
.unwrap_or_default(),
};
let work_package = refine::work_package();
assert_eq!(1, work_package.items.len());
let params = WorkPackageParams {
encoded_size: work_package.encoded_size() as u32,
auth_output_size: refine::auth_trace().len() as u32,
export_count: work_package.items[0].export_count,
};
let engine =
Engine::new(payload, extrinsics, params, outer_vm).expect("Failed to init engine");
let (output, ..) = engine.run().expect("Failed to execute the code");
WorkOutput(output.encode())
}
fn accumulate(slot: Slot, id: ServiceId, _item_count: usize) -> Option<Hash> {
use AccumulateItem::*;
JamLogger::init();
let items = accumulate::accumulate_items();
let mut engine = AccumulateEngine::new(JamAccumulateOps, slot, id);
let results = engine.run(&items);
for (result, item) in results.into_iter().zip(items) {
match item {
WorkItem(WorkItemRecord { package, .. }) => match result {
Ok(()) => debug!("Accumulated package {package}"),
Err(e) => debug!("Failed to accumulate package {package}: {e:?}"),
},
Transfer(TransferRecord { amount, source, destination, .. }) => match result {
Ok(()) => debug!("Transferred {amount} from {source:x} to {destination:x}",),
Err(e) => debug!(
"Failed to transfer {amount} from {source:x} to {destination:x}: {e:?}",
),
},
}
}
None
}
}