1#![cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), no_std)]
6#![allow(clippy::unwrap_used)]
7
8extern crate alloc;
9
10use alloc::{format, string::ToString, vec, vec::Vec};
11use jam_bootstrap_service_common::{Instruction, ServiceRegistry, SERVICE_REGISTRY_KEY};
12use jam_pvm_common::{
13 accumulate::*,
14 refine::{export_slice, import},
15 *,
16};
17use jam_types::*;
18
19const BENCHMARK_CYCLES: u32 = 1_000;
20
21#[allow(dead_code)]
22struct Service;
23jam_pvm_common::declare_service!(Service);
24
25impl jam_pvm_common::Service for Service {
26 fn refine(
27 id: ServiceId,
28 payload: WorkPayload,
29 _package_hash: WorkPackageHash,
30 _context: RefineContext,
31 _auth_code_hash: CodeHash,
32 ) -> WorkOutput {
33 info!(target = "boot", "Bootstrap Service Refine, {id:x}h");
34
35 let mut cursor = &payload[..];
40 let mut out = vec![];
41 while !cursor.is_empty() {
42 match Instruction::decode(&mut cursor).unwrap() {
43 Instruction::Lookup { service, hash } => {
44 info!(target = "boot", "Looking up...");
45 let maybe_data = foreign_lookup(service, &hash);
46 info!(target = "boot", "Got {:?}", maybe_data);
47 if let Some(data) = maybe_data {
48 out.push(Instruction::LookedUp { data });
49 }
50 },
51 Instruction::RandomStorageRefine(input) =>
52 out.push(Instruction::RandomStorageAccumulate(
53 jam_bootstrap_service_common::test_key_vals::generate_payload(input),
54 )),
55 Instruction::Export { data } => {
56 for d in &data {
57 let r = export_slice(d);
58 info!(target = "boot", "Exported slice: {d:?} -> {r:?}");
59 }
60 out.push(Instruction::Exported { count: data.len() as _ })
61 },
62 Instruction::Import { items } => {
63 let data = items
64 .into_iter()
65 .map(|(index, len)| {
66 import(index as usize).unwrap().truncate_into_vec(len as usize)
67 })
68 .collect();
69 out.push(Instruction::Imported { data });
70 },
71 x => out.push(x),
72 };
73 }
74 debug!(target = "boot", "Returning {:?} into accumulate", out);
75 out.encode().into()
76 }
77
78 fn accumulate(now: Slot, id: ServiceId, results: Vec<AccumulateItem>) -> Option<Hash> {
79 info!(
80 target = "boot",
81 "Bootstrap Service Accumulate, {id:x}h @{now} ${}",
82 my_info().balance
83 );
84 for raw_instructions in results.into_iter().filter_map(|x| x.result.ok()) {
85 for inst in Vec::<Instruction>::decode(&mut &raw_instructions[..]).unwrap() {
86 debug!(target = "boot", "Decoded instruction: {:?}", inst);
87 match inst {
88 Instruction::CreateService {
89 code_hash,
90 code_len,
91 min_item_gas,
92 min_memo_gas,
93 endowment,
94 memo,
95 registration,
96 } => {
97 let id = create_service(
98 &code_hash,
99 code_len as usize,
100 min_item_gas,
101 min_memo_gas,
102 );
103 if let Ok(id) = id {
104 info!(target = "boot", "Created service {id:x}h");
105 set(b"created", id).expect("balance?");
106
107 if let Some(code) = lookup(&code_hash) {
108 let e = provide(id, &code);
110 info!(target = "boot", "Code provision resulted in {e:?}");
111 }
112
113 info!(target = "boot", "Attemting transfer, gas={}", gas());
114 let e = transfer(id, endowment, min_memo_gas, &memo);
115 info!(
116 target = "boot",
117 "Transfer of {endowment} with {min_memo_gas} gas resulted in {e:?}"
118 );
119 if let Some(registration) = registration {
120 let mut registry: ServiceRegistry =
121 get(SERVICE_REGISTRY_KEY).unwrap_or_default();
122 registry.update(registration, id, code_hash);
123 set(SERVICE_REGISTRY_KEY, ®istry).expect("balance?");
124 }
125 } else {
126 error!("Failed to create!");
127 }
128 },
129 Instruction::Upgrade { code_hash, min_item_gas, min_memo_gas } => {
130 upgrade(&code_hash, min_item_gas, min_memo_gas);
131 info!(target = "boot", "Upgraded!");
132 },
133 Instruction::Transfer { destination, amount, gas_limit, memo } => {
134 info!(target = "boot", "Gas remaining: {}", gas());
135 info!(
136 "Attempting transfer: {} {} {} {:?}",
137 destination, amount, gas_limit, memo
138 );
139 let e = transfer(destination, amount, gas_limit, &memo);
140 info!(target = "boot", "Result: {:?}", e);
141 (destination, amount, memo)
142 .using_encoded(|d| set_storage(b"transferred", d).expect("balance?"));
143 },
144 Instruction::Zombify { ejector } => {
145 info!(target = "boot", "Zombifying service. Ejector: #{:x}", ejector);
146 let info = my_info();
147 if info.bytes >= 81 && info.items == 2 {
148 if forget(&info.code_hash, info.bytes as usize - 81).is_ok() {
149 zombify(ejector);
150 info!(target = "boot", "Zombified");
151 } else {
152 error!("Failed to zombify - invalid code_hash?");
153 }
154 } else {
155 error!("Failed to zombify - laggards in storage/lookup?");
156 }
157 },
158 Instruction::Eject { target, code_hash } => {
159 info!(
160 target = "boot",
161 "Ejecting service #{:x} with code_hash {:?}", target, code_hash
162 );
163 let e = eject(target, &code_hash);
164 info!(target = "boot", "Result: {:?}", e);
165 },
166 Instruction::DeleteItems { storage_items } => {
167 let mut fail = 0;
168 for i in &storage_items {
169 info!(target = "boot", "Deleting item: {:?}", i);
170 if remove_storage(i).is_none() {
171 error!("Failed to remove item: {:?}", i);
172 fail += 1;
173 }
174 }
175 info!(
176 "{} items deleted successfully, {} keys not found",
177 storage_items.len() - fail,
178 fail
179 );
180 },
181 Instruction::LookedUp { data } => {
182 set_storage(b"looked_up", &data[..]).expect("balance?");
183 },
184 Instruction::Imported { data } => {
185 info!(
186 target = "boot",
187 "Imported data {:?}",
188 data.iter().cloned().map(AnyVec)
189 );
190 set_storage(b"imported", &(data.len() as u32).encode()).expect("balance?");
191 for (i, d) in data.into_iter().enumerate() {
192 set_storage(alloc::format!("import-{i}").as_bytes(), &d[..])
193 .expect("balance?");
194 }
195 },
196 Instruction::Exported { count } => {
197 info!(target = "boot", "Exported {count} items");
198 set_storage(b"exported", &count.encode()).expect("balance?");
199 },
200 Instruction::Solicit { hash, len } => {
201 solicit(&hash, len as usize).unwrap();
202 info!(target = "boot", "Solicited {hash} of length {len}");
203 set_storage(b"requested", &hash[..]).expect("balance?");
204 },
205 Instruction::Forget { hash, len } => {
206 let q = query(&hash, len as usize).unwrap();
207 info!(
208 target = "boot",
209 "Query result: {:?} (fi: {:?})",
210 q,
211 q.forget_implication(now)
212 );
213 forget(&hash, len as usize).unwrap();
214 set_storage(b"unrequested", &hash[..]).expect("balance?");
215 },
216 Instruction::Assign { core, queue } => {
217 info!(target = "boot", "Assigning core {:?} to queue {:?}", core, queue);
218 match assign(core, &queue) {
219 Ok(_) => info!(target = "boot", "Assigned!"),
220 Err(_) => error!("Failed to assign!"),
221 }
222 },
223 Instruction::Bless { manager: b, assign: a, designate: d, auto_acc } => {
224 bless(b, a, d, &auto_acc);
225 info!(
226 "Blessed services m: #{}, a: #{}. v: #{}. aa: {:?}",
227 b, a, d, auto_acc
228 );
229 },
230 Instruction::Designate { keys } => {
231 designate(&keys);
232 info!(target = "boot", "Designated keys {:?}", keys);
233 },
234 Instruction::Yield { hash } => {
235 yield_hash(&hash);
236 info!(target = "boot", "Yielded hash {:?}", hash);
237 },
238 Instruction::Provide { service_id, preimage } => {
239 let r = provide(service_id, &preimage);
240 info!(target = "boot", "Provided preimage to {service_id}: {:?}", r);
241 },
242 Instruction::Checkpoint => {
243 checkpoint();
244 info!(target = "boot", "Checkpointed!");
245 },
246 Instruction::Panic => {
247 panic!("Panic instruction executed!");
248 },
249 Instruction::RandomStorageAccumulate(result_refine) => {
250 if let Ok(keys) = result_refine {
251 let mut count: u64 = get_storage(b"count_random_storage")
252 .map(|v| u64::decode(&mut v.as_slice()).unwrap())
253 .unwrap_or(0);
254 for item in keys.items.into_iter() {
257 set_storage(&item.key[..], &item.key[..]).expect("balance?");
258 count += 1;
259 }
260 set_storage(b"count_random_storage", &count.encode()[..])
261 .expect("balance?");
262 }
263 },
264 Instruction::Benchmark =>
265 for i in 0..BENCHMARK_CYCLES {
266 set_storage(
267 format!("item-{now}-{i}").as_bytes(),
268 format!("{i}").as_bytes(),
269 )
270 .expect("balance low");
271 checkpoint();
272 },
273 i => {
274 info!(target = "boot", "Instruction not handled: {:?}", i);
275 },
276 }
277 }
278 }
279 None
280 }
281
282 fn on_transfer(_slot: Slot, _id: ServiceId, items: Vec<TransferRecord>) {
283 for TransferRecord { source, amount, memo, .. } in items.into_iter() {
284 let count = get::<u32>(b"transfer-count").unwrap_or(0);
285 set(b"transfer-count", count + 1).expect("balance?");
286 info!(
287 target = "boot",
288 "Received transfer from {source} of {amount} with memo {}",
289 alloc::string::String::from_utf8(memo.as_ref().to_vec())
290 .unwrap_or("???".to_string())
291 );
292 set_storage(
293 alloc::format!("transfer{count}").as_bytes(),
294 &(source, amount, memo).encode()[..],
295 )
296 .expect("balance?");
297 }
298 }
299}