1use crate::{Jam, key};
4use anyhow::Result;
5use service::{
6 ServiceId,
7 api::{
8 AccumulateArgs, AccumulateState, Accumulated, AuthorizeArgs, Reason, RefineArgs,
9 ValidatorData,
10 },
11 service::{
12 Privileges, RefineLoad, ServiceAccount, WorkExecResult, WorkPackage, WorkResult,
13 result::Executed,
14 },
15 vm::Operand,
16};
17use std::collections::BTreeMap;
18
19#[derive(Debug, Default)]
21pub struct ExecutionInfo {
22 pub refine_gas: u64,
24
25 pub accumulate_gas: u64,
27
28 pub accounts: BTreeMap<ServiceId, ServiceAccount>,
30}
31
32impl ExecutionInfo {
33 pub fn new(acc: Vec<Accumulated>) -> Self {
35 let mut info = Self::default();
36 for acc in acc.iter() {
37 info.accumulate_gas += acc.gas;
38
39 info.accounts = acc.context.accounts.clone();
41 }
42
43 info
44 }
45
46 pub fn get_storage<V: serde::de::DeserializeOwned>(
48 &self,
49 service: ServiceId,
50 key: &[u8],
51 ) -> Option<V> {
52 let account = self.accounts.get(&service)?;
53 let vkey = key.to_vec();
54 let key = key::storage(service, &vkey);
55 let encoded = account.storage.get(key.as_ref())?;
56 codec::decode(encoded).ok()
57 }
58}
59
60impl Jam {
61 pub fn execute(&mut self, service: ServiceId, payload: Vec<u8>) -> Result<ExecutionInfo> {
65 let package = self.send(service, payload)?;
66 let result = self.refine(&package)?;
67 Ok(ExecutionInfo::new(self.accumulate(result)?))
68 }
69
70 #[tracing::instrument(name = "authorize", skip_all)]
72 pub fn authorize(&mut self, work: &WorkPackage, core_idx: u16) -> Result<Executed> {
73 tracing::debug!(
74 "service={}, code=0x{}",
75 work.auth_code_host,
76 hex::encode(work.auth_code_hash)
77 );
78
79 spacevm::authorize(AuthorizeArgs {
80 package: work.clone(),
81 core_idx,
82 accounts: self.chain.accounts.clone(),
83 timeslot: self.chain.best.slot,
84 })
85 }
86
87 #[tracing::instrument(name = "refine", skip_all)]
91 pub fn refine(&mut self, work: &WorkPackage) -> Result<Vec<WorkResult>> {
92 tracing::debug!("package: items={}", work.items.len());
93 if work.items.is_empty() {
94 anyhow::bail!("no work items");
95 }
96
97 let mut result = Vec::new();
98 for (index, item) in work.items.iter().enumerate() {
99 let refined = spacevm::refine(RefineArgs {
100 accounts: self.chain.accounts.clone(),
101 core: 0,
102 index: 0,
103 package: work.clone(),
104 export_offset: 0,
105 timeslot: self.chain.best.slot,
106 auth_output: Default::default(),
107 all_imports: Default::default(),
108 })?;
109
110 if !matches!(refined.executed.exec, WorkExecResult::Ok(_)) {
111 return Err(anyhow::anyhow!(
112 "work item {index} refine failed: {:?}",
113 refined.executed.exec
114 ));
115 }
116
117 result.push(WorkResult {
118 service_id: item.service,
119 code_hash: item.code_hash,
120 payload_hash: Default::default(),
121 accumulate_gas: Default::default(),
122 result: refined.executed.exec,
123 refine_load: RefineLoad {
124 gas_used: refined.executed.gas,
125 imports: Default::default(),
126 extrinsic_count: Default::default(),
127 extrinsic_size: Default::default(),
128 exports: Default::default(),
129 },
130 });
131 }
132
133 Ok(result)
134 }
135
136 #[tracing::instrument(name = "accumulate", skip_all)]
142 pub fn accumulate(&mut self, results: Vec<WorkResult>) -> Result<Vec<Accumulated>> {
143 tracing::debug!("work: items={}", results.len());
144 if results.is_empty() {
145 anyhow::bail!("no results");
146 }
147
148 let accounts = self.chain.accounts.clone();
149 let mut state = AccumulateState {
150 accounts,
151 validators: [ValidatorData {
152 bandersnatch: Default::default(),
153 ed25519: Default::default(),
154 bls: [0; 144],
155 metadata: [0; 128],
156 }; 6],
157 authorization: Default::default(),
158 privileges: Privileges::default(),
159 entropy: Default::default(),
160 };
161
162 let service = results.first().expect("checked").service_id;
163 let operands = {
164 let mut operands = vec![];
165 for work in results.iter() {
166 operands.push(Operand {
167 package: Default::default(),
168 exports_root: Default::default(),
169 authorizer_hash: Default::default(),
170 auth_output: Default::default(),
171 payload: work.payload_hash,
172 gas: work.accumulate_gas,
173 data: work.result.clone(),
174 });
175 }
176 operands
177 };
178
179 let accumulated = spacevm::accumulate(AccumulateArgs {
181 context: state,
182 timeslot: self.chain.best.slot,
183 service,
184 gas: 1_000_000_000,
185 operands,
186 })?;
187
188 if !matches!(accumulated.reason, Reason::Halt) {
189 anyhow::bail!("accumulate failed: {:?}", accumulated.reason);
190 }
191 state = accumulated.context.clone();
192 self.chain.accounts = state.accounts;
193 Ok(vec![accumulated])
194 }
195}