1use aluvm::LibSite;
25use amplify::confinement::SmallVec;
26use amplify::num::u256;
27use chrono::{DateTime, Utc};
28use strict_encoding::TypeName;
29use strict_types::{StrictVal, TypeSystem};
30use ultrasonic::{
31 fe256, AuthToken, CallId, CellAddr, CodexId, Consensus, Contract, ContractId, ContractMeta, ContractName, Genesis,
32 Identity, Input, Operation, StateCell, StateData, StateValue,
33};
34
35use crate::{Api, Articles, DataCell, MethodName, Schema, StateAtom, StateName};
36
37#[derive(Clone, PartialEq, Eq, Debug)]
38#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39pub struct NamedState<T> {
40 pub name: StateName,
41 #[cfg_attr(feature = "serde", serde(flatten))]
42 pub state: T,
43}
44
45#[derive(Clone, PartialEq, Eq, Debug)]
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47pub struct CoreParams {
48 pub method: MethodName,
49 pub global: Vec<NamedState<StateAtom>>,
50 pub owned: Vec<NamedState<DataCell>>,
51}
52
53#[derive(Clone, PartialEq, Eq, Debug)]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55pub struct IssueParams {
56 pub name: TypeName,
57 pub consensus: Consensus,
58 pub testnet: bool,
59 pub timestamp: Option<DateTime<Utc>>,
60 #[cfg_attr(feature = "serde", serde(flatten))]
61 pub core: CoreParams,
62}
63
64impl Schema {
65 pub fn start_issue(self, method: impl Into<MethodName>, consensus: Consensus, testnet: bool) -> IssueBuilder {
66 let builder = Builder::new(self.call_id(method));
67 IssueBuilder { builder, schema: self, testnet, consensus }
68 }
69
70 pub fn start_issue_mainnet(self, method: impl Into<MethodName>, consensus: Consensus) -> IssueBuilder {
71 self.start_issue(method, consensus, false)
72 }
73 pub fn start_issue_testnet(self, method: impl Into<MethodName>, consensus: Consensus) -> IssueBuilder {
74 self.start_issue(method, consensus, true)
75 }
76
77 pub fn issue(self, params: IssueParams) -> Articles {
78 let mut builder = self.start_issue(params.core.method, params.consensus, params.testnet);
79
80 for NamedState { name, state } in params.core.global {
81 builder = builder.append(name, state.verified, state.unverified)
82 }
83 for NamedState { name, state } in params.core.owned {
84 builder = builder.assign(name, state.auth, state.data, state.lock)
85 }
86
87 let timestamp = params.timestamp.unwrap_or_else(Utc::now).timestamp();
88 builder.finish(params.name, timestamp)
89 }
90}
91
92#[derive(Clone, Debug)]
93pub struct IssueBuilder {
94 builder: Builder,
95 schema: Schema,
96 testnet: bool,
97 consensus: Consensus,
98}
99
100impl IssueBuilder {
101 pub fn append(mut self, name: impl Into<StateName>, data: StrictVal, raw: Option<StrictVal>) -> Self {
102 self.builder = self
103 .builder
104 .add_immutable(name, data, raw, &self.schema.default_api, &self.schema.types);
105 self
106 }
107
108 pub fn assign(
109 mut self,
110 name: impl Into<StateName>,
111 auth: AuthToken,
112 data: StrictVal,
113 lock: Option<LibSite>,
114 ) -> Self {
115 self.builder =
116 self.builder
117 .add_destructible(name, auth, data, lock, &self.schema.default_api, &self.schema.types);
118 self
119 }
120
121 pub fn finish(self, name: impl Into<TypeName>, timestamp: i64) -> Articles {
122 let meta = ContractMeta {
123 consensus: self.consensus,
124 testnet: self.testnet,
125 reserved: zero!(),
126 timestamp,
127 name: ContractName::Named(name.into()),
128 issuer: Identity::default(),
129 };
130 let genesis = self.builder.issue_genesis(self.schema.codex.codex_id());
131 let contract = Contract {
132 version: default!(),
133 meta,
134 codex: self.schema.codex.clone(),
135 genesis,
136 };
137 Articles { contract, contract_sigs: none!(), schema: self.schema }
138 }
139}
140
141#[derive(Clone, Debug)]
142pub struct Builder {
143 call_id: CallId,
144 destructible: SmallVec<StateCell>,
145 immutable: SmallVec<StateData>,
146}
147
148impl Builder {
149 pub fn new(call_id: CallId) -> Self { Builder { call_id, destructible: none!(), immutable: none!() } }
150
151 pub fn add_immutable(
152 mut self,
153 name: impl Into<StateName>,
154 data: StrictVal,
155 raw: Option<StrictVal>,
156 api: &Api,
157 sys: &TypeSystem,
158 ) -> Self {
159 let data = api.build_immutable(name, data, raw, sys);
160 self.immutable.push(data).expect("too many state elements");
161 self
162 }
163
164 pub fn add_destructible(
165 mut self,
166 name: impl Into<StateName>,
167 auth: AuthToken,
168 data: StrictVal,
169 lock: Option<LibSite>,
170 api: &Api,
171 sys: &TypeSystem,
172 ) -> Self {
173 let data = api.build_destructible(name, data, sys);
174 let cell = StateCell { data, auth, lock };
175 self.destructible
176 .push(cell)
177 .expect("too many state elements");
178 self
179 }
180
181 pub fn issue_genesis(self, codex_id: CodexId) -> Genesis {
182 Genesis {
183 codex_id,
184 call_id: self.call_id,
185 nonce: fe256::from(u256::ZERO),
186 blank1: zero!(),
187 blank2: zero!(),
188 destructible: self.destructible,
189 immutable: self.immutable,
190 reserved: zero!(),
191 }
192 }
193}
194
195#[derive(Clone, Debug)]
196pub struct BuilderRef<'c> {
197 type_system: &'c TypeSystem,
198 api: &'c Api,
199 inner: Builder,
200}
201
202impl<'c> BuilderRef<'c> {
203 pub fn new(api: &'c Api, call_id: CallId, sys: &'c TypeSystem) -> Self {
204 BuilderRef { type_system: sys, api, inner: Builder::new(call_id) }
205 }
206
207 pub fn add_immutable(mut self, name: impl Into<StateName>, data: StrictVal, raw: Option<StrictVal>) -> Self {
208 self.inner = self
209 .inner
210 .add_immutable(name, data, raw, self.api, self.type_system);
211 self
212 }
213
214 pub fn add_destructible(
215 mut self,
216 name: impl Into<StateName>,
217 auth: AuthToken,
218 data: StrictVal,
219 lock: Option<LibSite>,
220 ) -> Self {
221 self.inner = self
222 .inner
223 .add_destructible(name, auth, data, lock, self.api, self.type_system);
224 self
225 }
226
227 pub fn issue_genesis(self, codex_id: CodexId) -> Genesis { self.inner.issue_genesis(codex_id) }
228}
229
230#[derive(Clone, Debug)]
231pub struct OpBuilder {
232 contract_id: ContractId,
233 destroying: SmallVec<Input>,
234 reading: SmallVec<CellAddr>,
235 inner: Builder,
236}
237
238impl OpBuilder {
239 pub fn new(contract_id: ContractId, call_id: CallId) -> Self {
240 let inner = Builder::new(call_id);
241 Self { contract_id, destroying: none!(), reading: none!(), inner }
242 }
243
244 pub fn add_immutable(
245 mut self,
246 name: impl Into<StateName>,
247 data: StrictVal,
248 raw: Option<StrictVal>,
249 api: &Api,
250 sys: &TypeSystem,
251 ) -> Self {
252 self.inner = self.inner.add_immutable(name, data, raw, api, sys);
253 self
254 }
255
256 pub fn add_destructible(
257 mut self,
258 name: impl Into<StateName>,
259 auth: AuthToken,
260 data: StrictVal,
261 lock: Option<LibSite>,
262 api: &Api,
263 sys: &TypeSystem,
264 ) -> Self {
265 self.inner = self
266 .inner
267 .add_destructible(name, auth, data, lock, api, sys);
268 self
269 }
270
271 pub fn access(mut self, addr: CellAddr) -> Self {
272 self.reading
273 .push(addr)
274 .expect("number of read memory cells exceeds 64k limit");
275 self
276 }
277
278 pub fn destroy(mut self, addr: CellAddr, witness: StrictVal) -> Self {
279 let input = Input { addr, witness: StateValue::None };
281 self.destroying
282 .push(input)
283 .expect("number of inputs exceeds 64k limit");
284 self
285 }
286
287 pub fn finalize(self) -> Operation {
288 Operation {
289 contract_id: self.contract_id,
290 call_id: self.inner.call_id,
291 nonce: fe256::from(u256::ZERO),
292 destroying: self.destroying,
293 reading: self.reading,
294 destructible: self.inner.destructible,
295 immutable: self.inner.immutable,
296 reserved: zero!(),
297 }
298 }
299}
300
301#[derive(Clone, Debug)]
302pub struct OpBuilderRef<'c> {
303 type_system: &'c TypeSystem,
304 api: &'c Api,
305 inner: OpBuilder,
306}
307
308impl<'c> OpBuilderRef<'c> {
309 pub fn new(api: &'c Api, contract_id: ContractId, call_id: CallId, sys: &'c TypeSystem) -> Self {
310 let inner = OpBuilder::new(contract_id, call_id);
311 Self { api, type_system: sys, inner }
312 }
313
314 pub fn add_immutable(mut self, name: impl Into<StateName>, data: StrictVal, raw: Option<StrictVal>) -> Self {
315 self.inner = self
316 .inner
317 .add_immutable(name, data, raw, self.api, self.type_system);
318 self
319 }
320
321 pub fn add_destructible(
322 mut self,
323 name: impl Into<StateName>,
324 auth: AuthToken,
325 data: StrictVal,
326 lock: Option<LibSite>,
327 ) -> Self {
328 self.inner = self
329 .inner
330 .add_destructible(name, auth, data, lock, self.api, self.type_system);
331 self
332 }
333
334 pub fn access(mut self, addr: CellAddr) -> Self {
335 self.inner = self.inner.access(addr);
336 self
337 }
338
339 pub fn destroy(mut self, addr: CellAddr, witness: StrictVal) -> Self {
340 self.inner = self.inner.destroy(addr, witness);
341 self
342 }
343
344 pub fn finalize(self) -> Operation { self.inner.finalize() }
345}