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::*;
13use jam_types::*;
14
15const BENCHMARK_CYCLES: u32 = 1_000;
16
17pub struct Service;
18jam_pvm_common::declare_service!(Service);
19
20impl jam_pvm_common::Service for Service {
21 fn refine(
22 _core_index: CoreIndex,
23 _item_index: usize,
24 service_id: ServiceId,
25 payload: WorkPayload,
26 _package_hash: WorkPackageHash,
27 ) -> WorkOutput {
28 use refine::*;
29 info!(target = "boot", "Bootstrap Service Refine, {service_id:x}h");
30
31 let mut cursor = &payload[..];
32 let mut out = vec![];
33 while !cursor.is_empty() {
34 match Instruction::decode(&mut cursor).unwrap() {
35 Instruction::Lookup { service, hash, eager } if eager => {
36 let maybe_data = foreign_lookup(service, &hash);
37 info!(target = "boot", "Eager lookup, got {:?}", maybe_data);
38 out.push(Instruction::LookedUp { data: maybe_data });
39 },
40 Instruction::RandomStorageRefine(input) =>
41 out.push(Instruction::RandomStorageAccumulate(
42 jam_bootstrap_service_common::test_key_vals::generate_payload(input),
43 )),
44 Instruction::Export { data } => {
45 for d in &data {
46 let r = export_slice(d);
47 info!(target = "boot", "Exported slice: {d:?} -> {r:?}");
48 }
49 out.push(Instruction::Exported { count: data.len() as _ })
50 },
51 Instruction::Import { items } => {
52 let data = items
53 .into_iter()
54 .map(|(index, len)| {
55 import(index as usize).unwrap().truncate_into_vec(len as usize)
56 })
57 .collect();
58 out.push(Instruction::Imported { data });
59 },
60 x => out.push(x),
61 };
62 }
63 debug!(target = "boot", "Returning {:?} into accumulate", out);
64 out.encode().into()
65 }
66
67 fn accumulate(now: Slot, id: ServiceId, _item_count: usize) -> Option<Hash> {
68 use accumulate::*;
69 info!(
70 target = "boot",
71 "Bootstrap Service Accumulate, {id:x}h @{now} ${}",
72 my_info().balance
73 );
74 for item in accumulate::accumulate_items() {
75 match item {
76 AccumulateItem::WorkItem(r) => on_work_item(r, now),
77 AccumulateItem::Transfer(t) => on_transfer(t),
78 }
79 }
80 None
81 }
82}
83
84fn on_work_item(item: WorkItemRecord, now: Slot) {
85 use accumulate::*;
86
87 let Ok(raw_instructions) = item.result else { return };
88
89 for inst in Vec::<Instruction>::decode(&mut &raw_instructions[..]).unwrap() {
90 debug!(target = "boot", "Decoded instruction: {:?}", inst);
91 match inst {
92 Instruction::CreateService {
93 code_hash,
94 code_len,
95 min_item_gas,
96 min_memo_gas,
97 endowment,
98 memo,
99 registration,
100 } => {
101 let id = create_service(&code_hash, code_len as usize, min_item_gas, min_memo_gas);
102 if let Ok(id) = id {
103 info!(
104 target = "boot",
105 "Created service {id:x}h with code_hash {}",
106 AnyHash(*code_hash)
107 );
108 set(b"created", id).expect("balance?");
109
110 if let Some(code) = lookup(&code_hash) {
111 let e = provide(id, &code);
113 info!(target = "boot", "Code provision resulted in {e:?}");
114 }
115
116 info!(target = "boot", "Attempting transfer, gas={}", gas());
117 let e = transfer(id, endowment, min_memo_gas, &memo);
118 info!(
119 target = "boot",
120 "Transfer of {endowment} with {min_memo_gas} gas resulted in {e:?}"
121 );
122 if let Some(registration) = registration {
123 let mut registry: ServiceRegistry =
124 get(SERVICE_REGISTRY_KEY).unwrap_or_default();
125 registry.update(registration, id, code_hash);
126 set(SERVICE_REGISTRY_KEY, ®istry).expect("balance?");
127 }
128 } else {
129 error!("Failed to create!");
130 }
131 },
132 Instruction::Upgrade { code_hash, min_item_gas, min_memo_gas } => {
133 upgrade(&code_hash, min_item_gas, min_memo_gas);
134 info!(target = "boot", "Upgraded!");
135 },
136 Instruction::Transfer { destination, amount, gas_limit, memo } => {
137 info!(target = "boot", "Gas remaining: {}", gas());
138 info!("Attempting transfer: {} {} {} {:?}", destination, amount, gas_limit, memo);
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::Imported { data } => {
182 info!(target = "boot", "Imported data {:?}", data.iter().cloned().map(AnyVec));
183 set_storage(b"imported", &(data.len() as u32).encode()).expect("balance?");
184 for (i, d) in data.into_iter().enumerate() {
185 set_storage(alloc::format!("import-{i}").as_bytes(), &d[..]).expect("balance?");
186 }
187 },
188 Instruction::Exported { count } => {
189 info!(target = "boot", "Exported {count} items");
190 set_storage(b"exported", &count.encode()).expect("balance?");
191 },
192 Instruction::Solicit { hash, len } => {
193 solicit(&hash, len as usize).unwrap();
194 info!(target = "boot", "Solicited {hash} of length {len}");
195 set_storage(b"requested", &hash[..]).expect("balance?");
196 },
197 Instruction::Forget { hash, len } => {
198 let q = query(&hash, len as usize).unwrap();
199 info!(
200 target = "boot",
201 "Query result: {:?} (fi: {:?})",
202 q,
203 q.forget_implication(now)
204 );
205 forget(&hash, len as usize).unwrap();
206 set_storage(b"unrequested", &hash[..]).expect("balance?");
207 },
208 Instruction::Lookup { service, hash, .. } => {
209 let maybe_data = foreign_lookup(service, &hash);
210 info!(target = "boot", "Lookup, got {:?}", maybe_data);
211 if let Some(data) = maybe_data {
212 set_storage(b"looked_up", &data[..]).expect("balance?");
213 } else {
214 remove_storage(b"looked_up");
215 }
216 },
217 Instruction::LookedUp { data } =>
218 if let Some(data) = data {
219 set_storage(b"looked_up", &data[..]).expect("balance?");
220 } else {
221 remove_storage(b"looked_up");
222 },
223 Instruction::Assign { core, queue, assigner } => {
224 info!(target = "boot", "Assigning core {:?} to queue {:?}", core, queue);
225 match assign(core, &queue, assigner) {
226 Ok(_) => info!(target = "boot", "Assigned!"),
227 Err(_) => error!("Failed to assign!"),
228 }
229 },
230 Instruction::Bless { manager, assign, designate, register, auto_acc } => {
231 bless(manager, assign, designate, register, &auto_acc);
232 info!("Blessed services m: #{manager}, a: #{assign}, v: #{designate}, r: #{register}, aa: {auto_acc:?}");
233 },
234 Instruction::Designate { keys } => match designate(&keys) {
235 Ok(_) => info!(target = "boot", "Designated keys {:?}", keys),
236 Err(_) => error!("Failed to designate"),
237 },
238 Instruction::Yield { hash } => {
239 yield_hash(&hash);
240 info!(target = "boot", "Yielded hash {:?}", hash);
241 },
242 Instruction::Provide { service_id, preimage } => {
243 let r = provide(service_id, &preimage);
244 info!(target = "boot", "Provided preimage to {service_id}: {:?}", r);
245 },
246 Instruction::Checkpoint => {
247 checkpoint();
248 info!(target = "boot", "Checkpointed!");
249 },
250 Instruction::Panic => {
251 panic!("Panic instruction executed!");
252 },
253 Instruction::RandomStorageAccumulate(result_refine) => {
254 if let Ok(keys) = result_refine {
255 let mut count: u64 = get_storage(b"count_random_storage")
256 .map(|v| u64::decode(&mut v.as_slice()).unwrap())
257 .unwrap_or(0);
258 for item in keys.items.into_iter() {
261 set_storage(&item.key[..], &item.key[..]).expect("balance?");
262 count += 1;
263 }
264 set_storage(b"count_random_storage", &count.encode()[..]).expect("balance?");
265 }
266 },
267 Instruction::Benchmark =>
268 for i in 0..BENCHMARK_CYCLES {
269 set_storage(format!("item-{now}-{i}").as_bytes(), format!("{i}").as_bytes())
270 .expect("balance low");
271 checkpoint();
272 },
273 i => {
274 info!(target = "boot", "Instruction not handled: {:?}", i);
275 },
276 }
277 }
278}
279
280fn on_transfer(item: TransferRecord) {
281 use accumulate::*;
282 let TransferRecord { source, amount, memo, .. } = item;
283 let count = get::<u32>(b"transfer-count").unwrap_or(0);
284 set(b"transfer-count", count + 1).expect("balance?");
285 info!(
286 target = "boot",
287 "Received transfer from {source} of {amount} with memo {}",
288 alloc::string::String::from_utf8(memo.as_ref().to_vec()).unwrap_or("???".to_string())
289 );
290 set_storage(alloc::format!("transfer{count}").as_bytes(), &(source, amount, memo).encode()[..])
291 .expect("balance?");
292}
293
294pub const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");