1use std::collections::HashMap;
12
13use anyhow::{Result, anyhow};
14use nub::{InvocationResult, LocalKernel, ObjHash};
15use nub_arch_local::PreparedProgram;
16use nub_program::ProgramBlob;
17
18use crate::hash::content_hash;
19
20#[derive(Default)]
22pub struct FlatLocal {
23 programs: HashMap<ObjHash, ProgramBlob>,
26 state_root: ObjHash,
30}
31
32impl FlatLocal {
33 fn decode(bytes: &[u8]) -> Result<ProgramBlob> {
35 ProgramBlob::from_bytes(bytes).map_err(|e| anyhow!("decode program: {e}"))
36 }
37
38 pub fn len(&self) -> usize {
40 self.programs.len()
41 }
42
43 pub fn is_empty(&self) -> bool {
44 self.programs.is_empty()
45 }
46}
47
48impl LocalKernel for FlatLocal {
49 fn put_object(&mut self, bytes: &[u8]) -> Result<ObjHash> {
50 let program = Self::decode(bytes)?;
51 let hash = content_hash(bytes);
52 self.programs.insert(hash, program);
53 Ok(hash)
54 }
55
56 fn put_object_with_hash(&mut self, hash: ObjHash, bytes: &[u8]) -> Result<()> {
57 let program = Self::decode(bytes)?;
58 debug_assert_eq!(
59 hash,
60 content_hash(bytes),
61 "claimed hash does not match the content"
62 );
63 self.programs.insert(hash, program);
64 Ok(())
65 }
66
67 fn invoke(
68 &mut self,
69 root: ObjHash,
70 endpoint: u32,
71 args: [u64; 4],
72 initial_gas: u64,
73 ) -> Result<InvocationResult> {
74 let program = self
75 .programs
76 .get(&root)
77 .ok_or_else(|| anyhow!("no program published under {}", hex(&root)))?;
78 let endpoint = u8::try_from(endpoint)
79 .map_err(|_| anyhow!("endpoint {endpoint} out of range (flat programs use u8)"))?;
80 let prepared = PreparedProgram::new(program, endpoint, args)
81 .map_err(|e| anyhow!("prepare endpoint {endpoint}: {e}"))?;
82 let mut handler = nub_arch_local::ExitingEcallHandler;
83 Ok(nub_arch_local::run_program(
84 &prepared.spec(),
85 &mut handler,
86 initial_gas,
87 ))
88 }
89
90 fn state_root(&self) -> ObjHash {
91 self.state_root
92 }
93}
94
95fn hex(h: &ObjHash) -> String {
96 h.iter().map(|b| format!("{b:02x}")).collect()
97}