1use std::fmt::{self, Display, Formatter};
2
3use itertools::Itertools;
4use serde::{Deserialize, Serialize};
5
6use casper_hashing::Digest;
7use casper_types::{
8 bytesrepr::{self, ToBytes},
9 crypto, PublicKey, SecretKey, Signature, URef, U512,
10};
11
12use crate::{
13 types::{ExecutableDeployItem, TimeDiff, Timestamp},
14 Error, TransferTarget,
15};
16
17pub const MAX_SERIALIZED_SIZE_OF_DEPLOY: u32 = 1_024 * 1_024;
22
23#[derive(
25 Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug,
26)]
27#[serde(deny_unknown_fields)]
28pub struct DeployHash(Digest);
29
30impl DeployHash {
31 pub fn new(digest: Digest) -> Self {
33 DeployHash(digest)
34 }
35
36 pub fn inner(&self) -> Digest {
38 self.0
39 }
40}
41
42impl Display for DeployHash {
43 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
44 write!(formatter, "{}", self.0)
45 }
46}
47
48impl ToBytes for DeployHash {
49 fn write_bytes(&self, buffer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
50 self.0.write_bytes(buffer)
51 }
52
53 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
54 self.0.to_bytes()
55 }
56
57 fn serialized_length(&self) -> usize {
58 self.0.serialized_length()
59 }
60}
61
62#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
64#[serde(deny_unknown_fields)]
65pub struct DeployHeader {
66 account: PublicKey,
67 timestamp: Timestamp,
68 ttl: TimeDiff,
69 gas_price: u64,
70 body_hash: Digest,
71 dependencies: Vec<DeployHash>,
72 chain_name: String,
73}
74
75impl DeployHeader {
76 pub fn account(&self) -> &PublicKey {
78 &self.account
79 }
80
81 pub fn timestamp(&self) -> Timestamp {
83 self.timestamp
84 }
85
86 pub fn ttl(&self) -> TimeDiff {
88 self.ttl
89 }
90
91 pub fn gas_price(&self) -> u64 {
93 self.gas_price
94 }
95
96 pub fn body_hash(&self) -> Digest {
98 self.body_hash
99 }
100
101 pub fn dependencies(&self) -> impl Iterator<Item = &DeployHash> {
103 self.dependencies.iter()
104 }
105
106 pub fn chain_name(&self) -> &str {
108 &self.chain_name
109 }
110}
111
112impl Display for DeployHeader {
113 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
114 write!(
115 formatter,
116 "deploy header {{ account {}, timestamp {}, ttl {}, body hash {}, chain name {} }}",
117 self.account, self.timestamp, self.ttl, self.body_hash, self.chain_name,
118 )
119 }
120}
121
122impl ToBytes for DeployHeader {
123 fn write_bytes(&self, buffer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
124 self.account.write_bytes(buffer)?;
125 self.timestamp.write_bytes(buffer)?;
126 self.ttl.write_bytes(buffer)?;
127 self.gas_price.write_bytes(buffer)?;
128 self.body_hash.write_bytes(buffer)?;
129 self.dependencies.write_bytes(buffer)?;
130 self.chain_name.write_bytes(buffer)
131 }
132
133 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
134 let mut buffer = vec![];
135 self.write_bytes(&mut buffer)?;
136 Ok(buffer)
137 }
138
139 fn serialized_length(&self) -> usize {
140 self.account.serialized_length()
141 + self.timestamp.serialized_length()
142 + self.ttl.serialized_length()
143 + self.gas_price.serialized_length()
144 + self.body_hash.serialized_length()
145 + self.dependencies.serialized_length()
146 + self.chain_name.serialized_length()
147 }
148}
149
150#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
152#[serde(deny_unknown_fields)]
153pub struct Approval {
154 signer: PublicKey,
155 signature: Signature,
156}
157
158impl Approval {
159 pub fn signer(&self) -> &PublicKey {
161 &self.signer
162 }
163
164 pub fn signature(&self) -> &Signature {
166 &self.signature
167 }
168}
169
170impl ToBytes for Approval {
171 fn write_bytes(&self, buffer: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
172 self.signer.write_bytes(buffer)?;
173 self.signature.write_bytes(buffer)
174 }
175
176 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
177 let mut buffer = vec![];
178 self.write_bytes(&mut buffer)?;
179 Ok(buffer)
180 }
181
182 fn serialized_length(&self) -> usize {
183 self.signer.serialized_length() + self.signature.serialized_length()
184 }
185}
186
187#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize, Debug)]
191#[serde(deny_unknown_fields)]
192pub struct Deploy {
193 hash: DeployHash,
194 header: DeployHeader,
195 payment: ExecutableDeployItem,
196 session: ExecutableDeployItem,
197 approvals: Vec<Approval>,
198}
199
200impl Deploy {
201 pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000);
203 pub const DEFAULT_GAS_PRICE: u64 = 1;
205
206 #[allow(clippy::too_many_arguments)]
208 fn new(
209 timestamp: Timestamp,
210 ttl: TimeDiff,
211 gas_price: u64,
212 dependencies: Vec<DeployHash>,
213 chain_name: String,
214 payment: ExecutableDeployItem,
215 session: ExecutableDeployItem,
216 secret_key: &SecretKey,
217 account: Option<PublicKey>,
218 ) -> Deploy {
219 let serialized_body = serialize_body(&payment, &session);
220 let body_hash = Digest::hash(serialized_body);
221
222 let account = account.unwrap_or_else(|| PublicKey::from(secret_key));
223
224 let dependencies = dependencies.into_iter().unique().collect();
226 let header = DeployHeader {
227 account,
228 timestamp,
229 ttl,
230 gas_price,
231 body_hash,
232 dependencies,
233 chain_name,
234 };
235 let serialized_header = header
236 .to_bytes()
237 .unwrap_or_else(|error| panic!("should serialize deploy header: {}", error));
238 let hash = DeployHash(Digest::hash(serialized_header));
239
240 let mut deploy = Deploy {
241 hash,
242 header,
243 payment,
244 session,
245 approvals: vec![],
246 };
247
248 deploy.sign(secret_key);
249 deploy
250 }
251
252 pub fn sign(&mut self, secret_key: &SecretKey) {
254 let signer = PublicKey::from(secret_key);
255 let signature = crypto::sign(self.hash.0, secret_key, &signer);
256 let approval = Approval { signer, signature };
257 self.approvals.push(approval);
258 }
259
260 pub fn is_valid_size(&self, max_deploy_size: u32) -> Result<(), Error> {
262 let deploy_size = self.header.serialized_length()
263 + self.hash.serialized_length()
264 + self.payment.serialized_length()
265 + self.session.serialized_length()
266 + self.approvals.serialized_length();
267 if deploy_size > max_deploy_size as usize {
268 return Err(Error::DeploySizeTooLarge {
269 max_deploy_size,
270 actual_deploy_size: deploy_size,
271 });
272 }
273 Ok(())
274 }
275
276 pub fn id(&self) -> &DeployHash {
278 &self.hash
279 }
280
281 pub fn header(&self) -> &DeployHeader {
283 &self.header
284 }
285
286 pub fn payment(&self) -> &ExecutableDeployItem {
288 &self.payment
289 }
290
291 pub fn session(&self) -> &ExecutableDeployItem {
293 &self.session
294 }
295
296 pub fn approvals(&self) -> &[Approval] {
298 &self.approvals
299 }
300}
301
302impl Display for Deploy {
303 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
304 write!(
305 formatter,
306 "deploy {{ {}, account {}, timestamp {}, ttl {}, body hash {}, chain name {} }}",
307 self.hash,
308 self.header.account,
309 self.header.timestamp,
310 self.header.ttl,
311 self.header.body_hash,
312 self.header.chain_name
313 )
314 }
315}
316
317fn serialize_body(payment: &ExecutableDeployItem, session: &ExecutableDeployItem) -> Vec<u8> {
318 let mut buffer = payment
319 .to_bytes()
320 .unwrap_or_else(|error| panic!("should serialize payment code: {}", error));
321 buffer.extend(
322 session
323 .to_bytes()
324 .unwrap_or_else(|error| panic!("should serialize session code: {}", error)),
325 );
326 buffer
327}
328
329pub struct DeployBuilder<'a> {
331 account: Option<PublicKey>,
332 timestamp: Timestamp,
333 ttl: TimeDiff,
334 gas_price: u64,
335 dependencies: Vec<DeployHash>,
336 chain_name: String,
337 payment: Option<ExecutableDeployItem>,
338 session: ExecutableDeployItem,
339 secret_key: &'a SecretKey,
340}
341
342impl<'a> DeployBuilder<'a> {
343 pub fn new<C: Into<String>>(
351 chain_name: C,
352 session: ExecutableDeployItem,
353 secret_key: &'a SecretKey,
354 ) -> Self {
355 DeployBuilder {
356 account: None,
357 timestamp: Timestamp::now(),
358 ttl: Deploy::DEFAULT_TTL,
359 gas_price: Deploy::DEFAULT_GAS_PRICE,
360 dependencies: vec![],
361 chain_name: chain_name.into(),
362 payment: None,
363 session,
364 secret_key,
365 }
366 }
367
368 pub fn new_transfer<C: Into<String>, A: Into<U512>>(
378 chain_name: C,
379 amount: A,
380 maybe_source: Option<URef>,
381 target: TransferTarget,
382 maybe_transfer_id: Option<u64>,
383 secret_key: &'a SecretKey,
384 ) -> Self {
385 let session =
386 ExecutableDeployItem::new_transfer(amount, maybe_source, target, maybe_transfer_id);
387 DeployBuilder::new(chain_name, session, secret_key)
388 }
389
390 pub fn with_standard_payment<A: Into<U512>>(mut self, amount: A) -> Self {
392 self.payment = Some(ExecutableDeployItem::new_standard_payment(amount));
393 self
394 }
395
396 pub fn with_payment(mut self, payment: ExecutableDeployItem) -> Self {
398 self.payment = Some(payment);
399 self
400 }
401
402 pub fn with_account(mut self, account: PublicKey) -> Self {
407 self.account = Some(account);
408 self
409 }
410
411 pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self {
416 self.timestamp = timestamp;
417 self
418 }
419
420 pub fn with_ttl(mut self, ttl: TimeDiff) -> Self {
424 self.ttl = ttl;
425 self
426 }
427
428 pub fn build(self) -> Result<Deploy, Error> {
432 let payment = self.payment.ok_or(Error::DeployMissingPaymentCode)?;
433 let deploy = Deploy::new(
434 self.timestamp,
435 self.ttl,
436 self.gas_price,
437 self.dependencies,
438 self.chain_name,
439 payment,
440 self.session,
441 self.secret_key,
442 self.account,
443 );
444 Ok(deploy)
445 }
446}