1use crate::services::{accounts, auth_delegate, auth_sig, producers, transact};
2use crate::{
3 method_raw, new_account_action, set_auth_service_action, set_key_action, validate_dependencies,
4 AccountNumber, Action, AnyPublicKey, Claim, ExactAccountNumber, GenesisActionData,
5 MethodNumber, PackagedService, Producer, SignedTransaction, Tapos, TimePointSec, Transaction,
6};
7use fracpack::Pack;
8use serde_bytes::ByteBuf;
9use sha2::{Digest, Sha256};
10use std::collections::HashSet;
11use std::io::{Cursor, Read, Seek};
12use std::str::FromStr;
13use wasm_bindgen::prelude::*;
14
15macro_rules! method {
16 ($name:expr) => {
17 MethodNumber::new(method_raw!($name))
18 };
19}
20
21fn set_producers_action(name: AccountNumber, key: Claim) -> Action {
22 producers::Wrapper::pack().setProducers(vec![Producer {
23 name: name,
24 auth: key,
25 }])
26}
27
28fn to_claim(key: &AnyPublicKey) -> Claim {
29 Claim {
30 service: key.key.service,
31 rawData: key.key.rawData.clone(),
32 }
33}
34
35fn without_tapos(actions: Vec<Action>, expiration: TimePointSec) -> Transaction {
36 Transaction {
37 tapos: Tapos {
38 expiration,
39 refBlockSuffix: 0,
40 flags: 0,
41 refBlockIndex: 0,
42 },
43 actions,
44 claims: vec![],
45 }
46}
47
48fn genesis_transaction<R: Read + Seek>(
49 expiration: TimePointSec,
50 service_packages: &mut [PackagedService<R>],
51) -> Result<SignedTransaction, anyhow::Error> {
52 let mut services = vec![];
53 for s in service_packages {
54 s.get_genesis(&mut services)?
55 }
56
57 let genesis_action_data = GenesisActionData {
58 memo: "".to_string(),
59 services,
60 };
61
62 let actions = vec![Action {
63 sender: AccountNumber { value: 0 },
65 service: AccountNumber { value: 0 },
66 method: method!("boot"),
67 rawData: genesis_action_data.packed().into(),
68 }];
69
70 Ok(SignedTransaction {
71 transaction: without_tapos(actions, expiration).packed().into(),
72 proofs: vec![],
73 })
74}
75
76pub fn get_initial_actions<R: Read + Seek>(
81 initial_key: &Option<AnyPublicKey>,
82 initial_producer: AccountNumber,
83 install_ui: bool,
84 service_packages: &mut [PackagedService<R>],
85 compression_level: u32,
86) -> Result<Vec<Action>, anyhow::Error> {
87 let mut actions = Vec::new();
88 let has_packages = true;
89
90 for s in &mut service_packages[..] {
91 for account in s.get_accounts() {
92 if !s.has_service(*account) {
93 actions.push(new_account_action(accounts::SERVICE, *account))
94 }
95 }
96
97 if install_ui {
98 s.reg_server(&mut actions)?;
99 s.store_data(&mut actions, compression_level)?;
100 }
101
102 s.postinstall(&mut actions)?;
103 }
104
105 actions.push(new_account_action(accounts::SERVICE, initial_producer));
107
108 let mut claim = Claim {
109 service: AccountNumber::new(0),
110 rawData: Default::default(),
111 };
112 if let Some(key) = initial_key {
113 actions.push(set_key_action(initial_producer, &key));
115 actions.push(set_auth_service_action(initial_producer, auth_sig::SERVICE));
116 claim = to_claim(&key);
117 }
118
119 actions.push(set_producers_action(initial_producer, claim));
121
122 actions.push(new_account_action(accounts::SERVICE, producers::ROOT));
123 actions.push(
124 auth_delegate::Wrapper::pack_from(producers::ROOT)
125 .setOwner(producers::PRODUCER_ACCOUNT_STRONG),
126 );
127 actions.push(set_auth_service_action(
128 producers::ROOT,
129 auth_delegate::SERVICE,
130 ));
131
132 let mut accounts_with_auth = HashSet::new();
134 for act in &actions {
135 if act.service == accounts::SERVICE && act.method == method!("setAuthServ") {
136 accounts_with_auth.insert(act.sender);
137 }
138 }
139
140 for s in &service_packages[..] {
141 for account in s.get_accounts() {
142 if !accounts_with_auth.contains(account) {
143 actions.push(auth_delegate::Wrapper::pack_from(*account).setOwner(producers::ROOT));
144 actions.push(set_auth_service_action(*account, auth_delegate::SERVICE));
145 }
146 }
147 }
148
149 if has_packages {
150 for s in &mut service_packages[..] {
151 s.commit_install(producers::ROOT, &mut actions)?;
152 }
153 }
154
155 actions.push(transact::Wrapper::pack().finishBoot());
156
157 Ok(actions)
158}
159
160pub fn create_boot_transactions<R: Read + Seek>(
178 initial_key: &Option<AnyPublicKey>,
179 initial_producer: AccountNumber,
180 install_ui: bool,
181 expiration: TimePointSec,
182 service_packages: &mut [PackagedService<R>],
183 compression_level: u32,
184) -> Result<(Vec<SignedTransaction>, Vec<SignedTransaction>), anyhow::Error> {
185 validate_dependencies(service_packages)?;
186 let mut boot_transactions = vec![genesis_transaction(expiration, service_packages)?];
187 let mut actions = get_initial_actions(
188 initial_key,
189 initial_producer,
190 install_ui,
191 service_packages,
192 compression_level,
193 )?;
194 let mut transactions = Vec::new();
195 while !actions.is_empty() {
196 let mut n = 0;
197 let mut size = 0;
198 while n < actions.len() && size < 1024 * 1024 {
199 size += actions[n].rawData.len();
200 n += 1;
201 }
202 transactions.push(SignedTransaction {
203 transaction: without_tapos(actions.drain(..n).collect(), expiration)
204 .packed()
205 .into(),
206 proofs: vec![],
207 });
208 }
209
210 let mut transaction_ids: Vec<crate::Checksum256> = Vec::new();
211 for trx in &transactions {
212 transaction_ids.push(crate::Checksum256::from(<[u8; 32]>::from(Sha256::digest(
213 &trx.transaction,
214 ))))
215 }
216 boot_transactions.push(SignedTransaction {
217 transaction: without_tapos(
218 vec![transact::Wrapper::pack().startBoot(transaction_ids)],
219 expiration,
220 )
221 .packed()
222 .into(),
223 proofs: vec![],
224 });
225 Ok((boot_transactions, transactions))
226}
227
228fn js_err<T, E: std::fmt::Display>(result: Result<T, E>) -> Result<T, JsValue> {
229 result.map_err(|e| JsValue::from_str(&e.to_string()))
230}
231
232#[wasm_bindgen]
237pub fn js_create_boot_transactions(
238 producer: String,
239 js_services: JsValue,
240) -> Result<JsValue, JsValue> {
241 let mut services: Vec<PackagedService<Cursor<&[u8]>>> = vec![];
242 let deserialized_services: Vec<ByteBuf> = js_err(serde_wasm_bindgen::from_value(js_services))?;
243 for s in &deserialized_services[..] {
244 services.push(js_err(PackagedService::new(Cursor::new(&s[..])))?);
245 }
246 let now_plus_120secs = chrono::Utc::now() + chrono::Duration::seconds(120);
247 let expiration = TimePointSec::from(now_plus_120secs);
248 let prod = js_err(ExactAccountNumber::from_str(&producer))?;
249
250 let compression_level = 4;
252 let (boot_transactions, transactions) = js_err(create_boot_transactions(
253 &None,
254 prod.into(),
255 true,
256 expiration,
257 &mut services[..],
258 compression_level,
259 ))?;
260
261 let boot_transactions = boot_transactions.packed();
262 let transactions: Vec<ByteBuf> = transactions
263 .into_iter()
264 .map(|tx| ByteBuf::from(tx.packed()))
265 .collect();
266
267 Ok(serde_wasm_bindgen::to_value(&(
268 ByteBuf::from(boot_transactions),
269 transactions,
270 ))?)
271}