1use alloc::{format, string::String, vec::Vec};
2use core::{
3 array::TryFromSliceError,
4 convert::TryFrom,
5 fmt::{self, Debug, Display, Formatter},
6};
7
8#[cfg(feature = "datasize")]
9use datasize::DataSize;
10#[cfg(any(feature = "testing", test))]
11use rand::{
12 distributions::{Distribution, Standard},
13 Rng,
14};
15#[cfg(feature = "json-schema")]
16use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
17use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
18
19use crate::{
20 addressable_entity, bytesrepr,
21 bytesrepr::{Bytes, Error, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
22 checksummed_hex, uref, CLType, CLTyped, HashAddr,
23};
24
25const BYTE_CODE_MAX_DISPLAY_LEN: usize = 16;
26const KEY_HASH_LENGTH: usize = 32;
27const WASM_STRING_PREFIX: &str = "byte-code-";
28
29const BYTE_CODE_PREFIX: &str = "byte-code-";
30const V1_WASM_PREFIX: &str = "v1-wasm-";
31const V2_WASM_PREFIX: &str = "v2-wasm-";
32const EMPTY_PREFIX: &str = "empty-";
33
34#[derive(Debug)]
36pub struct TryFromSliceForContractHashError(());
37
38#[derive(Debug)]
39#[non_exhaustive]
40pub enum FromStrError {
41 InvalidPrefix,
42 Hex(base16::DecodeError),
43 Hash(TryFromSliceError),
44 AccountHash(addressable_entity::FromAccountHashStrError),
45 URef(uref::FromStrError),
46}
47
48impl From<base16::DecodeError> for FromStrError {
49 fn from(error: base16::DecodeError) -> Self {
50 FromStrError::Hex(error)
51 }
52}
53
54impl From<TryFromSliceError> for FromStrError {
55 fn from(error: TryFromSliceError) -> Self {
56 FromStrError::Hash(error)
57 }
58}
59
60impl From<addressable_entity::FromAccountHashStrError> for FromStrError {
61 fn from(error: addressable_entity::FromAccountHashStrError) -> Self {
62 FromStrError::AccountHash(error)
63 }
64}
65
66impl From<uref::FromStrError> for FromStrError {
67 fn from(error: uref::FromStrError) -> Self {
68 FromStrError::URef(error)
69 }
70}
71
72impl Display for FromStrError {
73 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
74 match self {
75 FromStrError::InvalidPrefix => write!(f, "invalid prefix"),
76 FromStrError::Hex(error) => write!(f, "decode from hex: {}", error),
77 FromStrError::Hash(error) => write!(f, "hash from string error: {}", error),
78 FromStrError::AccountHash(error) => {
79 write!(f, "account hash from string error: {:?}", error)
80 }
81 FromStrError::URef(error) => write!(f, "uref from string error: {:?}", error),
82 }
83 }
84}
85
86#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
88#[cfg_attr(feature = "datasize", derive(DataSize))]
89#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
90pub enum ByteCodeAddr {
91 V1CasperWasm(HashAddr),
93 V2CasperWasm(HashAddr),
95 Empty,
97}
98
99impl ByteCodeAddr {
100 pub const fn new_wasm_addr(hash_addr: HashAddr) -> Self {
102 Self::V1CasperWasm(hash_addr)
103 }
104
105 pub fn tag(&self) -> ByteCodeKind {
107 match self {
108 Self::Empty => ByteCodeKind::Empty,
109 Self::V1CasperWasm(_) => ByteCodeKind::V1CasperWasm,
110 Self::V2CasperWasm(_) => ByteCodeKind::V2CasperWasm,
111 }
112 }
113
114 pub fn to_formatted_string(&self) -> String {
116 format!("{}", self)
117 }
118
119 pub fn from_formatted_string(input: &str) -> Result<Self, FromStrError> {
122 if let Some(byte_code) = input.strip_prefix(BYTE_CODE_PREFIX) {
123 let (addr_str, tag) = if let Some(str) = byte_code.strip_prefix(EMPTY_PREFIX) {
124 (str, ByteCodeKind::Empty)
125 } else if let Some(str) = byte_code.strip_prefix(V1_WASM_PREFIX) {
126 (str, ByteCodeKind::V1CasperWasm)
127 } else if let Some(str) = byte_code.strip_prefix(V2_WASM_PREFIX) {
128 (str, ByteCodeKind::V2CasperWasm)
129 } else {
130 return Err(FromStrError::InvalidPrefix);
131 };
132 let addr = checksummed_hex::decode(addr_str).map_err(FromStrError::Hex)?;
133 let byte_code_addr = HashAddr::try_from(addr.as_ref()).map_err(FromStrError::Hash)?;
134 return match tag {
135 ByteCodeKind::V1CasperWasm => Ok(ByteCodeAddr::V1CasperWasm(byte_code_addr)),
136 ByteCodeKind::V2CasperWasm => Ok(ByteCodeAddr::V2CasperWasm(byte_code_addr)),
137 ByteCodeKind::Empty => Ok(ByteCodeAddr::Empty),
138 };
139 }
140
141 Err(FromStrError::InvalidPrefix)
142 }
143}
144
145impl ToBytes for ByteCodeAddr {
146 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
147 let mut buffer = bytesrepr::allocate_buffer(self)?;
148 self.write_bytes(&mut buffer)?;
149 Ok(buffer)
150 }
151
152 fn serialized_length(&self) -> usize {
153 U8_SERIALIZED_LENGTH
154 + match self {
155 Self::Empty => 0,
156 Self::V1CasperWasm(_) => KEY_HASH_LENGTH,
157 Self::V2CasperWasm(_) => KEY_HASH_LENGTH,
158 }
159 }
160
161 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
162 match self {
163 Self::Empty => writer.push(self.tag() as u8),
164 Self::V1CasperWasm(addr) => {
165 writer.push(self.tag() as u8);
166 writer.extend(addr.to_bytes()?);
167 }
168 Self::V2CasperWasm(addr) => {
169 writer.push(self.tag() as u8);
170 writer.extend(addr.to_bytes()?);
171 }
172 }
173 Ok(())
174 }
175}
176
177impl FromBytes for ByteCodeAddr {
178 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
179 let (tag, remainder): (ByteCodeKind, &[u8]) = FromBytes::from_bytes(bytes)?;
180 match tag {
181 ByteCodeKind::Empty => Ok((ByteCodeAddr::Empty, remainder)),
182 ByteCodeKind::V1CasperWasm => {
183 let (addr, remainder) = HashAddr::from_bytes(remainder)?;
184 Ok((ByteCodeAddr::new_wasm_addr(addr), remainder))
185 }
186 ByteCodeKind::V2CasperWasm => {
187 let (addr, remainder) = HashAddr::from_bytes(remainder)?;
188 Ok((ByteCodeAddr::V2CasperWasm(addr), remainder))
189 }
190 }
191 }
192}
193
194impl Display for ByteCodeAddr {
195 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
196 match self {
197 ByteCodeAddr::V1CasperWasm(addr) => {
198 write!(
199 f,
200 "{}{}{}",
201 BYTE_CODE_PREFIX,
202 V1_WASM_PREFIX,
203 base16::encode_lower(&addr)
204 )
205 }
206 ByteCodeAddr::V2CasperWasm(addr) => {
207 write!(
208 f,
209 "{}{}{}",
210 BYTE_CODE_PREFIX,
211 V2_WASM_PREFIX,
212 base16::encode_lower(&addr)
213 )
214 }
215 ByteCodeAddr::Empty => {
216 write!(
217 f,
218 "{}{}{}",
219 BYTE_CODE_PREFIX,
220 EMPTY_PREFIX,
221 base16::encode_lower(&[0u8; 32])
222 )
223 }
224 }
225 }
226}
227
228impl Debug for ByteCodeAddr {
229 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
230 match self {
231 ByteCodeAddr::V1CasperWasm(addr) => {
232 write!(f, "ByteCodeAddr::V1CasperWasm({:?})", addr)
233 }
234 ByteCodeAddr::V2CasperWasm(addr) => {
235 write!(f, "ByteCodeAddr::V2CasperWasm({:?})", addr)
236 }
237 ByteCodeAddr::Empty => {
238 write!(f, "ByteCodeAddr::Empty")
239 }
240 }
241 }
242}
243
244#[cfg(any(feature = "testing", test))]
245impl Distribution<ByteCodeAddr> for Standard {
246 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> ByteCodeAddr {
247 match rng.gen_range(0..=2) {
248 0 => ByteCodeAddr::Empty,
249 1 => ByteCodeAddr::V1CasperWasm(rng.gen()),
250 2 => ByteCodeAddr::V2CasperWasm(rng.gen()),
251 _ => unreachable!(),
252 }
253 }
254}
255
256#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
259#[cfg_attr(feature = "datasize", derive(DataSize))]
260pub struct ByteCodeHash(HashAddr);
261
262impl ByteCodeHash {
263 pub const fn new(value: HashAddr) -> ByteCodeHash {
265 ByteCodeHash(value)
266 }
267
268 pub fn value(&self) -> HashAddr {
270 self.0
271 }
272
273 pub fn as_bytes(&self) -> &[u8] {
275 &self.0
276 }
277
278 pub fn to_formatted_string(self) -> String {
280 format!("{}{}", WASM_STRING_PREFIX, base16::encode_lower(&self.0),)
281 }
282
283 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
286 let remainder = input
287 .strip_prefix(WASM_STRING_PREFIX)
288 .ok_or(FromStrError::InvalidPrefix)?;
289 let bytes = HashAddr::try_from(checksummed_hex::decode(remainder)?.as_ref())?;
290 Ok(ByteCodeHash(bytes))
291 }
292}
293
294impl Default for ByteCodeHash {
295 fn default() -> Self {
296 Self::new([0u8; KEY_HASH_LENGTH])
297 }
298}
299
300impl Display for ByteCodeHash {
301 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
302 write!(f, "{}", base16::encode_lower(&self.0))
303 }
304}
305
306impl Debug for ByteCodeHash {
307 fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
308 write!(f, "ByteCodeHash({})", base16::encode_lower(&self.0))
309 }
310}
311
312impl CLTyped for ByteCodeHash {
313 fn cl_type() -> CLType {
314 CLType::ByteArray(KEY_HASH_LENGTH as u32)
315 }
316}
317
318impl ToBytes for ByteCodeHash {
319 #[inline(always)]
320 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
321 self.0.to_bytes()
322 }
323
324 #[inline(always)]
325 fn serialized_length(&self) -> usize {
326 self.0.serialized_length()
327 }
328
329 #[inline(always)]
330 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
331 self.0.write_bytes(writer)
332 }
333}
334
335impl FromBytes for ByteCodeHash {
336 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
337 let (bytes, rem) = FromBytes::from_bytes(bytes)?;
338 Ok((ByteCodeHash::new(bytes), rem))
339 }
340}
341
342impl From<[u8; KEY_HASH_LENGTH]> for ByteCodeHash {
343 fn from(bytes: [u8; KEY_HASH_LENGTH]) -> Self {
344 ByteCodeHash(bytes)
345 }
346}
347
348impl Serialize for ByteCodeHash {
349 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
350 if serializer.is_human_readable() {
351 self.to_formatted_string().serialize(serializer)
352 } else {
353 self.0.serialize(serializer)
354 }
355 }
356}
357
358impl<'de> Deserialize<'de> for ByteCodeHash {
359 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
360 if deserializer.is_human_readable() {
361 let formatted_string = String::deserialize(deserializer)?;
362 ByteCodeHash::from_formatted_str(&formatted_string).map_err(SerdeError::custom)
363 } else {
364 let bytes = HashAddr::deserialize(deserializer)?;
365 Ok(ByteCodeHash(bytes))
366 }
367 }
368}
369
370impl AsRef<[u8]> for ByteCodeHash {
371 fn as_ref(&self) -> &[u8] {
372 self.0.as_ref()
373 }
374}
375
376impl TryFrom<&[u8]> for ByteCodeHash {
377 type Error = TryFromSliceForContractHashError;
378
379 fn try_from(bytes: &[u8]) -> Result<Self, TryFromSliceForContractHashError> {
380 HashAddr::try_from(bytes)
381 .map(ByteCodeHash::new)
382 .map_err(|_| TryFromSliceForContractHashError(()))
383 }
384}
385
386impl TryFrom<&Vec<u8>> for ByteCodeHash {
387 type Error = TryFromSliceForContractHashError;
388
389 fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
390 HashAddr::try_from(bytes as &[u8])
391 .map(ByteCodeHash::new)
392 .map_err(|_| TryFromSliceForContractHashError(()))
393 }
394}
395
396#[cfg(feature = "json-schema")]
397impl JsonSchema for ByteCodeHash {
398 fn schema_name() -> String {
399 String::from("ByteCodeHash")
400 }
401
402 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
403 let schema = gen.subschema_for::<String>();
404 let mut schema_object = schema.into_object();
405 schema_object.metadata().description =
406 Some("The hash address of the contract wasm".to_string());
407 schema_object.into()
408 }
409}
410
411#[repr(u8)]
413#[cfg_attr(feature = "datasize", derive(DataSize))]
414#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
415#[derive(PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash, Serialize, Deserialize)]
416pub enum ByteCodeKind {
417 Empty = 0,
419 V1CasperWasm = 1,
421 V2CasperWasm = 2,
423}
424
425impl ToBytes for ByteCodeKind {
426 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
427 (*self as u8).to_bytes()
428 }
429
430 fn serialized_length(&self) -> usize {
431 U8_SERIALIZED_LENGTH
432 }
433
434 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
435 (*self as u8).write_bytes(writer)
436 }
437}
438
439impl FromBytes for ByteCodeKind {
440 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
441 let (byte_code_kind, remainder) = u8::from_bytes(bytes)?;
442 match byte_code_kind {
443 byte_code_kind if byte_code_kind == ByteCodeKind::Empty as u8 => {
444 Ok((ByteCodeKind::Empty, remainder))
445 }
446 byte_code_kind if byte_code_kind == ByteCodeKind::V1CasperWasm as u8 => {
447 Ok((ByteCodeKind::V1CasperWasm, remainder))
448 }
449 byte_code_kind if byte_code_kind == ByteCodeKind::V2CasperWasm as u8 => {
450 Ok((ByteCodeKind::V2CasperWasm, remainder))
451 }
452 _ => Err(Error::Formatting),
453 }
454 }
455}
456
457impl Display for ByteCodeKind {
458 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
459 match self {
460 ByteCodeKind::Empty => {
461 write!(f, "empty")
462 }
463 ByteCodeKind::V1CasperWasm => {
464 write!(f, "v1-casper-wasm")
465 }
466 ByteCodeKind::V2CasperWasm => {
467 write!(f, "v2-casper-wasm")
468 }
469 }
470 }
471}
472
473#[cfg(any(feature = "testing", test))]
474impl Distribution<ByteCodeKind> for Standard {
475 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> ByteCodeKind {
476 match rng.gen_range(0..=2) {
477 0 => ByteCodeKind::Empty,
478 1 => ByteCodeKind::V1CasperWasm,
479 2 => ByteCodeKind::V2CasperWasm,
480 _ => unreachable!(),
481 }
482 }
483}
484
485#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
487#[cfg_attr(feature = "datasize", derive(DataSize))]
488#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
489pub struct ByteCode {
490 kind: ByteCodeKind,
491 bytes: Bytes,
492}
493
494impl Debug for ByteCode {
495 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
496 if self.bytes.len() > BYTE_CODE_MAX_DISPLAY_LEN {
497 write!(
498 f,
499 "ByteCode(0x{}...)",
500 base16::encode_lower(&self.bytes[..BYTE_CODE_MAX_DISPLAY_LEN])
501 )
502 } else {
503 write!(f, "ByteCode(0x{})", base16::encode_lower(&self.bytes))
504 }
505 }
506}
507
508impl ByteCode {
509 pub fn new(kind: ByteCodeKind, bytes: Vec<u8>) -> Self {
511 ByteCode {
512 kind,
513 bytes: bytes.into(),
514 }
515 }
516
517 pub fn take_bytes(self) -> Vec<u8> {
519 self.bytes.into()
520 }
521
522 pub fn bytes(&self) -> &[u8] {
524 self.bytes.as_ref()
525 }
526
527 pub fn kind(&self) -> ByteCodeKind {
529 self.kind
530 }
531}
532
533impl ToBytes for ByteCode {
534 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
535 let mut buffer = bytesrepr::allocate_buffer(self)?;
536 self.write_bytes(&mut buffer)?;
537 Ok(buffer)
538 }
539
540 fn serialized_length(&self) -> usize {
541 self.kind.serialized_length() + self.bytes.serialized_length()
542 }
543
544 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), Error> {
545 self.kind.write_bytes(writer)?;
546 self.bytes.write_bytes(writer)?;
547 Ok(())
548 }
549}
550
551impl FromBytes for ByteCode {
552 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
553 let (kind, remainder) = ByteCodeKind::from_bytes(bytes)?;
554 let (bytes, remainder) = Bytes::from_bytes(remainder)?;
555 Ok((ByteCode { kind, bytes }, remainder))
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use rand::RngCore;
562
563 use super::*;
564 use crate::testing::TestRng;
565
566 #[test]
567 fn debug_repr_of_short_wasm() {
568 const SIZE: usize = 8;
569 let wasm_bytes = vec![0; SIZE];
570 let byte_code = ByteCode::new(ByteCodeKind::V1CasperWasm, wasm_bytes);
571 assert_eq!(format!("{:?}", byte_code), "ByteCode(0x0000000000000000)");
572 }
573
574 #[test]
575 fn debug_repr_of_long_wasm() {
576 const SIZE: usize = 65;
577 let wasm_bytes = vec![0; SIZE];
578 let byte_code = ByteCode::new(ByteCodeKind::V1CasperWasm, wasm_bytes);
579 assert_eq!(
581 format!("{:?}", byte_code),
582 "ByteCode(0x00000000000000000000000000000000...)"
583 );
584 }
585
586 #[test]
587 fn byte_code_bytesrepr_roundtrip() {
588 let rng = &mut TestRng::new();
589 let byte_code = ByteCode::new(rng.gen(), vec![]);
590 bytesrepr::test_serialization_roundtrip(&byte_code);
591
592 let mut buffer = vec![0u8; rng.gen_range(1..100)];
593 rng.fill_bytes(buffer.as_mut());
594 let byte_code = ByteCode::new(rng.gen(), buffer);
595 bytesrepr::test_serialization_roundtrip(&byte_code);
596 }
597
598 #[test]
599 fn contract_wasm_hash_from_slice() {
600 let bytes: Vec<u8> = (0..32).collect();
601 let byte_code_hash = HashAddr::try_from(&bytes[..]).expect("should create byte code hash");
602 let contract_hash = ByteCodeHash::new(byte_code_hash);
603 assert_eq!(&bytes, &contract_hash.as_bytes());
604 }
605
606 #[test]
607 fn contract_wasm_hash_from_str() {
608 let byte_code_hash = ByteCodeHash([3; KEY_HASH_LENGTH]);
609 let encoded = byte_code_hash.to_formatted_string();
610 let decoded = ByteCodeHash::from_formatted_str(&encoded).unwrap();
611 assert_eq!(byte_code_hash, decoded);
612
613 let invalid_prefix =
614 "contractwasm-0000000000000000000000000000000000000000000000000000000000000000";
615 assert!(ByteCodeHash::from_formatted_str(invalid_prefix).is_err());
616
617 let short_addr =
618 "contract-wasm-00000000000000000000000000000000000000000000000000000000000000";
619 assert!(ByteCodeHash::from_formatted_str(short_addr).is_err());
620
621 let long_addr =
622 "contract-wasm-000000000000000000000000000000000000000000000000000000000000000000";
623 assert!(ByteCodeHash::from_formatted_str(long_addr).is_err());
624
625 let invalid_hex =
626 "contract-wasm-000000000000000000000000000000000000000000000000000000000000000g";
627 assert!(ByteCodeHash::from_formatted_str(invalid_hex).is_err());
628 }
629
630 #[test]
631 fn byte_code_addr_from_str() {
632 let empty_addr = ByteCodeAddr::Empty;
633 let encoded = empty_addr.to_formatted_string();
634 let decoded = ByteCodeAddr::from_formatted_string(&encoded).unwrap();
635 assert_eq!(empty_addr, decoded);
636
637 let wasm_addr = ByteCodeAddr::V1CasperWasm([3; 32]);
638 let encoded = wasm_addr.to_formatted_string();
639 let decoded = ByteCodeAddr::from_formatted_string(&encoded).unwrap();
640 assert_eq!(wasm_addr, decoded);
641 }
642
643 #[test]
644 fn byte_code_serialization_roundtrip() {
645 let rng = &mut TestRng::new();
646 let wasm_addr = ByteCodeAddr::V1CasperWasm(rng.gen());
647 bytesrepr::test_serialization_roundtrip(&wasm_addr);
648
649 let empty_addr = ByteCodeAddr::Empty;
650 bytesrepr::test_serialization_roundtrip(&empty_addr);
651 }
652
653 #[test]
654 fn contract_wasm_hash_bytesrepr_roundtrip() {
655 let rng = &mut TestRng::new();
656 let byte_code_hash = ByteCodeHash(rng.gen());
657 bytesrepr::test_serialization_roundtrip(&byte_code_hash);
658 }
659
660 #[test]
661 fn contract_wasm_hash_bincode_roundtrip() {
662 let rng = &mut TestRng::new();
663 let byte_code_hash = ByteCodeHash(rng.gen());
664 let serialized = bincode::serialize(&byte_code_hash).unwrap();
665 let deserialized = bincode::deserialize(&serialized).unwrap();
666 assert_eq!(byte_code_hash, deserialized)
667 }
668
669 #[test]
670 fn contract_wasm_hash_json_roundtrip() {
671 let rng = &mut TestRng::new();
672 let byte_code_hash = ByteCodeHash(rng.gen());
673 let json_string = serde_json::to_string_pretty(&byte_code_hash).unwrap();
674 let decoded = serde_json::from_str(&json_string).unwrap();
675 assert_eq!(byte_code_hash, decoded)
676 }
677}