corevm 0.1.28

The JAM CoreVM Service, a container service for running regular software on JAM.
Documentation
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
	}
}