diff --git a/.gitignore b/.gitignore index b67c72cd..9c9695f2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,30 @@ rust/target rust/.idea binaryen/ -rust/core/target/** -rust/core/Cargo.lock -rust/wasm/target/** -rust/wasm/Cargo.lock -rust/json-gen-split/target/** +target/ + +core/target/** +core/Cargo.lock + +crypto/target/** +crypto/Cargo.lock + +crypto-wasm/target/** +crypto-wasm/Cargo.lock +crypto-wasm/pkg/** + +wasm/target/** +wasm/Cargo.lock +wasm/pkg/** + +cip25/target/** +cip25/Cargo.lock + +cip25-wasm/target/** +cip25-wasm/Cargo.lock + +json-gen-split/target/** + +cip25-json-gen/target/** + tools/metadata-cddl-checker/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..c7a1ed6d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] + +# this is for the new crate structure. The legacy code (current CML) still resides in the `rust` directory. +members = [ + "core", + "crypto", + "cip25", + "cip25-wasm", + "wasm" +] + +# exclude old crate structure to avoid error in it +exclude = [ + "rust" +] diff --git a/README.md b/README.md index b5147148..fb8f3c36 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,7 @@ We recommend using Ionic + Capacitor or an equivalent setup to have the WASM bin ## Documentation TODO + +# Crate Architecture + +For current users, the `rust/ `crate is the main version of CML and is the only one that should be used. There is a workspace in the root directory with crates like `core`, `wasm` etc, which are a part of a big refactor and will eventually replace the rust crate at some point in the future, but are still quite WIP for now. The rust crate when used for WASM builds via the npm scripts in the root repo dir will utilize the `rust/json-gen` crate here in the build scripts to generate typescript definitions for the JSON conversion. The `json-gen-split` crate is the equivalent for the new `core`/`wasm` crates and is not called anywhere from the build scripts, but will someday replace the `rust/json-gen` crate once the refactoring is completed. diff --git a/chain/Cargo.toml b/chain/Cargo.toml new file mode 100644 index 00000000..0bbaaa56 --- /dev/null +++ b/chain/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "cardano-multiplatform-lib-chain" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cardano-multiplatform-lib-core = { "path" = "../core" } +cardano-multiplatform-lib-crypto = { "path" = "../crypto" } +cbor_event = "2.2.0" +linked-hash-map = "0.5.3" +derivative = "2.2.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +schemars = "0.8.8" + +bech32 = "0.7.2" +hex = "0.4.0" +itertools = "0.10.1" +getrandom = { version = "0.2.3", features = ["js"] } +rand = "0.8.5" +fraction = "0.10.0" +base64 = "0.13" +num-bigint = "0.4.0" +num-integer = "0.1.45" +#rand_os = "0.1" +thiserror = "1.0.37" +# These can be removed if we make wasm bindings for ALL functionality here. +# This was not done right now as there is a lot of existing legacy code e.g. +# for Byron that might need to be used from WASM and might not. +# We can remove this dependency when that is decided. +# +# The other use-case here is enums. Without this two enums would need to be defined +# despite wasm_bindgen supporting C-style enums (with non-negative values) 100% +# This could possibly be resolved with macros but maybe not. + +# non-wasm +#[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies] +#rand_os = "0.1" +#noop_proc_macro = "0.3.0" + +# wasm +#[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] +wasm-bindgen = { version = "=0.2.82", features = ["serde-serialize"] } +#rand_os = { version = "0.1", features = ["wasm-bindgen"] } +#js-sys = "=0.3.59" + + +[dev-dependencies] +quickcheck = "0.9.2" +quickcheck_macros = "0.9.1" +rand_chacha = "0.3.1" diff --git a/chain/README.md b/chain/README.md new file mode 100644 index 00000000..2decf486 --- /dev/null +++ b/chain/README.md @@ -0,0 +1,4 @@ +# Chain + +This is the core cardano-multiplatform-lib crate for all on-chain data types. +This was generated from the `specs/babbage/` CDDL specs using cddl-codegen. diff --git a/rust/core/src/address.rs b/chain/src/address.rs similarity index 99% rename from rust/core/src/address.rs rename to chain/src/address.rs index 6d4ef713..95f81482 100644 --- a/rust/core/src/address.rs +++ b/chain/src/address.rs @@ -7,6 +7,7 @@ use derivative::Derivative; //use crate::genesis::network_info::NetworkInfo; use std::convert::TryInto; +use cardano_multiplatform_lib_crypto as cml_crypto; #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, Copy, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct ProtocolMagic(pub(crate) u32); @@ -249,16 +250,16 @@ impl Address { (|| -> Result { let header = data[0]; let network = header & 0x0F; - const HASH_LEN: usize = Ed25519KeyHash::BYTE_COUNT; + const HASH_LEN: usize = cml_crypto::Ed25519KeyHash::BYTE_COUNT; // should be static assert but it's maybe not worth importing a whole external crate for it now - assert_eq!(ScriptHash::BYTE_COUNT, HASH_LEN); + assert_eq!(cml_crypto::ScriptHash::BYTE_COUNT, HASH_LEN); // checks the /bit/ bit of the header for key vs scripthash then reads the credential starting at byte position /pos/ let read_addr_cred = |bit: u8, pos: usize| { let hash_bytes: [u8; HASH_LEN] = data[pos..pos+HASH_LEN].try_into().unwrap(); if header & (1 << bit) == 0 { - StakeCredential::Key(KeyStakeCredential::new(Ed25519KeyHash::from(hash_bytes))) + StakeCredential::Key(KeyStakeCredential::new(cml_crypto::Ed25519KeyHash::from(hash_bytes).into())) } else { - StakeCredential::Script(ScriptStakeCredential::new(ScriptHash::from(hash_bytes))) + StakeCredential::Script(ScriptStakeCredential::new(cml_crypto::ScriptHash::from(hash_bytes).into())) } }; fn make_encoding(bytes_encoding: Option, trailing: Option>) -> Result, DeserializeError> { diff --git a/rust/core/src/block.rs b/chain/src/block.rs similarity index 94% rename from rust/core/src/block.rs rename to chain/src/block.rs index d69c968a..1d813574 100644 --- a/rust/core/src/block.rs +++ b/chain/src/block.rs @@ -1,3 +1,5 @@ +use super::*; + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct Block { pub header: Header, @@ -46,7 +48,7 @@ pub struct HeaderBody { pub slot: u64, pub prev_hash: Option, pub issuer_vkey: Vkey, - pub vrf_vkey: VrfVkey, + pub vrf_vkey: VRFVKey, pub vrf_result: VrfCert, pub block_body_size: u64, pub block_body_hash: BlockBodyHash, @@ -57,7 +59,7 @@ pub struct HeaderBody { } impl HeaderBody { - pub fn new(block_number: u64, slot: u64, prev_hash: Option, issuer_vkey: Vkey, vrf_vkey: VrfVkey, vrf_result: VrfCert, block_body_size: u64, block_body_hash: BlockBodyHash, operational_cert: OperationalCert, protocol_version: ProtocolVersion) -> Self { + pub fn new(block_number: u64, slot: u64, prev_hash: Option, issuer_vkey: Vkey, vrf_vkey: VRFVKey, vrf_result: VrfCert, block_body_size: u64, block_body_hash: BlockBodyHash, operational_cert: OperationalCert, protocol_version: ProtocolVersion) -> Self { Self { block_number, slot, @@ -76,7 +78,7 @@ impl HeaderBody { #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct OperationalCert { - pub hot_vkey: KesVkey, + pub hot_vkey: KESVKey, pub sequence_number: u64, pub kes_period: u64, pub sigma: Ed25519Signature, @@ -85,7 +87,7 @@ pub struct OperationalCert { } impl OperationalCert { - pub fn new(hot_vkey: KesVkey, sequence_number: u64, kes_period: u64, sigma: Ed25519Signature) -> Self { + pub fn new(hot_vkey: KESVKey, sequence_number: u64, kes_period: u64, sigma: Ed25519Signature) -> Self { Self { hot_vkey, sequence_number, diff --git a/rust/core/src/cbor_encodings.rs b/chain/src/cbor_encodings.rs similarity index 99% rename from rust/core/src/cbor_encodings.rs rename to chain/src/cbor_encodings.rs index 0b1bc03b..6e272353 100644 --- a/rust/core/src/cbor_encodings.rs +++ b/chain/src/cbor_encodings.rs @@ -1,4 +1,8 @@ use super::*; +use cardano_multiplatform_lib_core::{ + serialization::{LenEncoding, StringEncoding}, +}; +use cbor_event::Sz; #[derive(Clone, Debug, Default)] pub struct AddressEncoding { @@ -65,13 +69,6 @@ pub struct BlockEncoding { pub invalid_transactions_elem_encodings: Vec>, } -#[derive(Clone, Debug, Default)] -pub struct BootstrapWitnessEncoding { - pub len_encoding: LenEncoding, - pub chain_code_encoding: StringEncoding, - pub attributes_encoding: StringEncoding, -} - #[derive(Clone, Debug, Default)] pub struct ConstrPlutusDataEncoding { pub len_encoding: LenEncoding, @@ -175,16 +172,6 @@ pub struct Ipv6Encoding { pub inner_encoding: StringEncoding, } -#[derive(Clone, Debug, Default)] -pub struct KesSignatureEncoding { - pub inner_encoding: StringEncoding, -} - -#[derive(Clone, Debug, Default)] -pub struct KesVkeyEncoding { - pub inner_encoding: StringEncoding, -} - #[derive(Clone, Debug, Default)] pub struct MoveInstantaneousRewardEncoding { pub len_encoding: LenEncoding, @@ -204,13 +191,6 @@ pub struct MultiHostNameEncoding { pub index_0_encoding: Option, } -#[derive(Clone, Debug, Default)] -pub struct Nonce1Encoding { - pub len_encoding: LenEncoding, - pub index_0_encoding: Option, - pub bytes_encoding: StringEncoding, -} - #[derive(Clone, Debug, Default)] pub struct OperationalCertEncoding { pub len_encoding: LenEncoding, @@ -393,16 +373,6 @@ pub struct ShelleyTxOutEncoding { pub len_encoding: LenEncoding, } -#[derive(Clone, Debug, Default)] -pub struct SignatureEncoding { - pub inner_encoding: StringEncoding, -} - -#[derive(Clone, Debug, Default)] -pub struct SignkeyKESEncoding { - pub inner_encoding: StringEncoding, -} - #[derive(Clone, Debug, Default)] pub struct SingleHostAddrEncoding { pub len_encoding: LenEncoding, @@ -538,6 +508,41 @@ pub struct ValueEncoding { pub multiasset_value_encodings: BTreeMap>)>, } + +#[derive(Clone, Debug, Default)] +pub struct BootstrapWitnessEncoding { + pub len_encoding: LenEncoding, + pub chain_code_encoding: StringEncoding, + pub attributes_encoding: StringEncoding, +} + +#[derive(Clone, Debug, Default)] +pub struct KesSignatureEncoding { + pub inner_encoding: StringEncoding, +} + +#[derive(Clone, Debug, Default)] +pub struct KesVkeyEncoding { + pub inner_encoding: StringEncoding, +} + +#[derive(Clone, Debug, Default)] +pub struct Nonce1Encoding { + pub len_encoding: LenEncoding, + pub index_0_encoding: Option, + pub bytes_encoding: StringEncoding, +} + +#[derive(Clone, Debug, Default)] +pub struct SignatureEncoding { + pub inner_encoding: StringEncoding, +} + +#[derive(Clone, Debug, Default)] +pub struct SignkeyKESEncoding { + pub inner_encoding: StringEncoding, +} + #[derive(Clone, Debug, Default)] pub struct VkeyEncoding { pub pubkey_bytes_encoding: StringEncoding, diff --git a/rust/core/src/certs.rs b/chain/src/certs.rs similarity index 100% rename from rust/core/src/certs.rs rename to chain/src/certs.rs diff --git a/chain/src/crypto.rs b/chain/src/crypto.rs new file mode 100644 index 00000000..5df0dc5a --- /dev/null +++ b/chain/src/crypto.rs @@ -0,0 +1,298 @@ +use super::*; + +/// Crypto-related on-chain structures. See the crypto crate for actually using these. + +use cardano_multiplatform_lib_crypto as cml_crypto; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct KesSignature { + pub inner: Vec, + #[serde(skip)] + pub encodings: Option, +} + +impl KesSignature { + pub fn get(&self) -> &Vec { + &self.inner + } + + pub fn new(inner: Vec) -> Result { + if inner.len() != 32 { + return Err(DeserializeError::new("KesSignature", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(32), max: Some(32) })); + } + Ok(Self { + inner, + encodings: None, + }) + } +} + +impl TryFrom> for KesSignature { + type Error = DeserializeError; + + fn try_from(inner: Vec) -> Result { + KesSignature::new(inner) + } +} + +impl From for Vec { + fn from(wrapper: KesSignature) -> Self { + wrapper.inner + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct Nonce1 { + pub bytes: Vec, + #[serde(skip)] + pub encodings: Option, +} + +impl Nonce1 { + pub fn new(bytes: Vec) -> Self { + Self { + bytes, + encodings: None, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub enum Nonce { + I0 { + #[serde(skip)] + i0_encoding: Option, + #[serde(skip)] + outer_len_encoding: LenEncoding, + }, + Nonce1(Nonce1), +} + +impl Nonce { + pub fn new_i0() -> Self { + Self::I0 { + i0_encoding: None, + outer_len_encoding: LenEncoding::default(), + } + } + + pub fn new_nonce1(bytes: Vec) -> Self { + Self::Nonce1(Nonce1::new(bytes)) + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct SignkeyKES { + pub inner: Vec, + #[serde(skip)] + pub encodings: Option, +} + +impl SignkeyKES { + pub fn get(&self) -> &Vec { + &self.inner + } + + pub fn new(inner: Vec) -> Result { + if inner.len() != 16 { + return Err(DeserializeError::new("SignkeyKES", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(16), max: Some(16) })); + } + Ok(Self { + inner, + encodings: None, + }) + } +} + +impl TryFrom> for SignkeyKES { + type Error = DeserializeError; + + fn try_from(inner: Vec) -> Result { + SignkeyKES::new(inner) + } +} + +impl From for Vec { + fn from(wrapper: SignkeyKES) -> Self { + wrapper.inner + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct VrfCert { + pub index_0: Vec, + pub bytes: Vec, + #[serde(skip)] + pub encodings: Option, +} + +impl VrfCert { + pub fn new(index_0: Vec, bytes: Vec) -> Self { + Self { + index_0, + bytes, + encodings: None, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct Vkeywitness { + pub vkey: Vkey, + pub signature: Ed25519Signature, + #[serde(skip)] + pub encodings: Option, +} + +impl Vkeywitness { + pub fn new(vkey: Vkey, signature: Ed25519Signature) -> Self { + Self { + vkey, + signature, + encodings: None, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct BootstrapWitness { + pub vkey: Vkey, + pub signature: Ed25519Signature, + pub chain_code: Vec, + // TODO: this should be replaced by AddrAttributes when Byron is brought over + pub attributes: Vec, + #[serde(skip)] + pub encodings: Option, +} + +impl BootstrapWitness { + pub fn new(vkey: Vkey, signature: Ed25519Signature, chain_code: Vec, attributes: Vec) -> Self { + Self { + vkey, + signature, + chain_code, + attributes, + encodings: None, + } + } + + // pub fn to_public_key(&self) -> Result { + // crypto::chain_crypto::PublicKey::::try_from(self.clone()) + // .map(crypto::Bip32PublicKey) + // .map_err(Into::into) + // } + + // pub fn to_address(&self) -> Result { + // AddressContent::try_from(self.clone()) + // .map_err(Into::into) + // } +} + +// impl TryFrom for chain_crypto::PublicKey { +// type Error = ed25519_bip32::PublicKeyError; + +// fn try_from(wit: BootstrapWitness) -> Result { +// combine_pk_and_chaincode(wit.vkey.pubkey.0, &wit.chain_code) +// } +// } + +// impl TryFrom for AddressContent { +// type Error = ed25519_bip32::PublicKeyError; + +// fn try_from(wit: BootstrapWitness) -> Result { +// let protocol_magic = wit.attributes.protocol_magic; +// let key = chain_crypto::PublicKey::::try_from(wit)?; +// let address_content = AddressContent::new_simple(&Bip32PublicKey(key), protocol_magic); +// Ok(address_content) +// } +// } + +#[derive(Debug, Clone, Derivative)] +#[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ChainCrypto { + pub primitive: T, + #[derivative(PartialEq="ignore", Ord="ignore", PartialOrd="ignore", Hash="ignore")] + pub encoding: StringEncoding, +} + +impl From for ChainCrypto { + fn from(primitive: T) -> Self { + Self { + primitive, + encoding: StringEncoding::default(), + } + } +} + +impl Serialize for ChainCrypto { + fn serialize<'se, W: std::io::Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + let data = self.primitive.to_raw_bytes(); + serializer.write_bytes_sz(&data, self.encoding.to_str_len_sz(data.len() as u64, force_canonical)) + } +} + +impl Deserialize for ChainCrypto { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result { + let (bytes, encoding) = raw.bytes_sz()?; + T::from_raw_bytes(&bytes).map(|primitive| ChainCrypto { + primitive, + encoding: encoding.into(), + }) + .map_err(|e| DeserializeFailure::InvalidStructure(Box::new(e)).into()) + })().map_err(|e| e.annotate("ChainCrypto")) + } +} + +impl cml_crypto::RawBytesEncoding for ChainCrypto { + fn to_raw_bytes(&self) -> &[u8] { + self.primitive.to_raw_bytes() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + T::from_raw_bytes(bytes).map(Into::into) + } +} + +impl serde::Serialize for ChainCrypto { + fn serialize(&self, serializer: S) -> Result + where S: serde::Serializer { + serializer.serialize_str(&self.primitive.to_raw_hex()) + } +} + +impl<'de, T: cml_crypto::RawBytesEncoding> serde::de::Deserialize<'de> for ChainCrypto { + fn deserialize(deserializer: D) -> Result where + D: serde::de::Deserializer<'de> { + let s = ::deserialize(deserializer)?; + T::from_raw_hex(&s) + .map(Into::into) + .map_err(|_e| serde::de::Error::invalid_value(serde::de::Unexpected::Str(&s), &"hex bytes for signature")) + } +} + +impl schemars::JsonSchema for ChainCrypto { + fn schema_name() -> String { String::from("ChainCrypto") } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { String::json_schema(gen) } + fn is_referenceable() -> bool { String::is_referenceable() } +} + +pub type Ed25519Signature = ChainCrypto; + +pub type Vkey = ChainCrypto; + +pub type Ed25519KeyHash = ChainCrypto; +pub type ScriptHash = ChainCrypto; +// TransactionHash is either a hash of the tx CBOR or a hash of a redeem address (genesis) +pub type TransactionHash = ChainCrypto; +pub type GenesisDelegateHash = ChainCrypto; +pub type GenesisHash = ChainCrypto; +pub type AuxiliaryDataHash = ChainCrypto; +pub type PoolMetadataHash = ChainCrypto; +pub type VRFKeyHash = ChainCrypto; +pub type BlockBodyHash = ChainCrypto; +pub type BlockHeaderHash = ChainCrypto; +pub type DataHash = ChainCrypto; +pub type ScriptDataHash = ChainCrypto; +pub type VRFVKey = ChainCrypto; +pub type KESVKey = ChainCrypto; \ No newline at end of file diff --git a/rust/core/src/legacy_address/hdpayload.rs b/chain/src/legacy_address/hdpayload.rs similarity index 100% rename from rust/core/src/legacy_address/hdpayload.rs rename to chain/src/legacy_address/hdpayload.rs diff --git a/rust/core/src/lib.rs b/chain/src/lib.rs similarity index 93% rename from rust/core/src/lib.rs rename to chain/src/lib.rs index b5065009..91c99daa 100644 --- a/rust/core/src/lib.rs +++ b/chain/src/lib.rs @@ -1,40 +1,25 @@ use std::io::{BufRead, Seek, Write}; -pub use error::*; -// This library was code-generated using an experimental CDDL to rust tool: -// https://github.com/dcSpark/cddl-codegen use cbor_event::{self, de::Deserializer, se::Serializer}; - use cbor_event::Type as CBORType; - use cbor_event::Special as CBORSpecial; - -use serialization::*; - use std::collections::BTreeMap; - use std::convert::{From, TryFrom}; -use schemars::JsonSchema; - -#[macro_use] -extern crate cfg_if; - -pub mod error; pub mod serialization; +pub mod crypto; -pub mod ordered_hash_map; -pub mod chain_core; -pub mod chain_crypto; -pub mod impl_mockchain; -//pub mod legacy_address; -pub mod typed_bytes; +use crypto::*; -use ordered_hash_map::OrderedHashMap; +//pub mod legacy_address; -use cbor_event::{Sz, LenSz, StringLenSz}; +pub use cardano_multiplatform_lib_core::{ + ordered_hash_map::OrderedHashMap, + error::{DeserializeError, DeserializeFailure}, + serialization::{Serialize, Deserialize, StringEncoding, LenEncoding}, +}; pub mod cbor_encodings; @@ -44,6 +29,9 @@ extern crate derivative; pub(crate) use derivative::Derivative; +// This library was code-generated using an experimental CDDL to rust tool: +// https://github.com/dcSpark/cddl-codegen + // TODO: for regen, change babbage's cddl to have our own names in the first place pub type AddrKeyhash = Ed25519KeyHash; @@ -200,9 +188,9 @@ pub mod certs; pub use certs::*; -pub mod crypto; +//pub mod crypto; -pub use crypto::*; +//pub use crypto::*; pub mod metadata; @@ -346,22 +334,6 @@ impl NetworkId { } } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct Nonce1 { - pub bytes: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl Nonce1 { - pub fn new(bytes: Vec) -> Self { - Self { - bytes, - encodings: None, - } - } -} - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct PositiveInterval { #[serde(skip)] @@ -569,6 +541,7 @@ impl StakeCredential { } pub fn to_raw_bytes(&self) -> &[u8] { + use cardano_multiplatform_lib_crypto::RawBytesEncoding; match self { Self::Key(key) => key.addr_keyhash.to_raw_bytes(), Self::Script(script) => script.scripthash.to_raw_bytes(), @@ -628,22 +601,4 @@ impl Value { encodings: None, } } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct Vkeywitness { - pub vkey: Vkey, - pub signature: Ed25519Signature, - #[serde(skip)] - pub encodings: Option, -} - -impl Vkeywitness { - pub fn new(vkey: Vkey, signature: Ed25519Signature) -> Self { - Self { - vkey, - signature, - encodings: None, - } - } } \ No newline at end of file diff --git a/rust/core/src/metadata.rs b/chain/src/metadata.rs similarity index 99% rename from rust/core/src/metadata.rs rename to chain/src/metadata.rs index 7a2a5e44..520ca999 100644 --- a/rust/core/src/metadata.rs +++ b/chain/src/metadata.rs @@ -1,3 +1,5 @@ +use super::*; + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct AlonzoAuxData { pub key_0: Option, diff --git a/rust/core/src/plutus.rs b/chain/src/plutus.rs similarity index 99% rename from rust/core/src/plutus.rs rename to chain/src/plutus.rs index 685f5252..d9fa6e63 100644 --- a/rust/core/src/plutus.rs +++ b/chain/src/plutus.rs @@ -1,3 +1,5 @@ +use super::*; + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, Derivative)] #[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct BigInt { diff --git a/rust/core/src/serialization.rs b/chain/src/serialization.rs similarity index 97% rename from rust/core/src/serialization.rs rename to chain/src/serialization.rs index 1be24ae5..63d8fcda 100644 --- a/rust/core/src/serialization.rs +++ b/chain/src/serialization.rs @@ -1,260 +1,18 @@ -pub struct CBORReadLen { - deser_len: cbor_event::LenSz, - read: u64, -} - -impl CBORReadLen { - pub fn new(len: cbor_event::LenSz) -> Self { - Self { - deser_len: len, - read: 0, - } - } - - // Marks {n} values as being read, and if we go past the available definite length - // given by the CBOR, we return an error. - pub fn read_elems(&mut self, count: usize) -> Result<(), DeserializeFailure> { - match self.deser_len { - cbor_event::LenSz::Len(n, _) => { - self.read += count as u64; - if self.read > n { - Err(DeserializeFailure::DefiniteLenMismatch(n, None)) - } else { - Ok(()) - } - }, - cbor_event::LenSz::Indefinite => Ok(()), - } - } - - pub fn finish(&self) -> Result<(), DeserializeFailure> { - match self.deser_len { - cbor_event::LenSz::Len(n, _) => { - if self.read == n { - Ok(()) - } else { - Err(DeserializeFailure::DefiniteLenMismatch(n, Some(self.read))) - } - }, - cbor_event::LenSz::Indefinite => Ok(()), - } - } -} - -pub trait DeserializeEmbeddedGroup { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - read_len: &mut CBORReadLen, - len: cbor_event::LenSz, - ) -> Result where Self: Sized; -} - -#[inline] -fn sz_max(sz: cbor_event::Sz) -> u64 { - match sz { - Sz::Inline => 23u64, - Sz::One => u8::MAX as u64, - Sz::Two => u16::MAX as u64, - Sz::Four => u32::MAX as u64, - Sz::Eight => u64::MAX, - } -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum LenEncoding { - Canonical, - Definite(cbor_event::Sz), - Indefinite, -} - -impl Default for LenEncoding { - fn default() -> Self { - Self::Canonical - } -} - -impl From for LenEncoding { - fn from(len_sz: cbor_event::LenSz) -> Self { - match len_sz { - cbor_event::LenSz::Len(len, sz) => if cbor_event::Sz::canonical(len) == sz { - Self::Canonical - } else { - Self::Definite(sz) - }, - cbor_event::LenSz::Indefinite => Self::Indefinite, - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum StringEncoding { - Canonical, - Indefinite(Vec<(u64, Sz)>), - Definite(Sz), -} - -impl Default for StringEncoding { - fn default() -> Self { - Self::Canonical - } -} - -impl From for StringEncoding { - fn from(len_sz: cbor_event::StringLenSz) -> Self { - match len_sz { - cbor_event::StringLenSz::Len(sz) => Self::Definite(sz), - cbor_event::StringLenSz::Indefinite(lens) => Self::Indefinite(lens), - } - } -}#[inline] -fn fit_sz(len: u64, sz: Option, force_canonical: bool) -> Sz { - match sz { - Some(sz) => if !force_canonical && len <= sz_max(sz) { - sz - } else { - Sz::canonical(len) - }, - None => Sz::canonical(len), - } -} - -impl LenEncoding { - pub fn to_len_sz(&self, len: u64, force_canonical: bool) -> cbor_event::LenSz { - if force_canonical { - cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)) - } else { - match self { - Self::Canonical => cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)), - Self::Definite(sz) => if sz_max(*sz) >= len { - cbor_event::LenSz::Len(len, *sz) - } else { - cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)) - }, - Self::Indefinite => cbor_event::LenSz::Indefinite, - } - } - } - - pub fn end<'a, W: Write + Sized>(&self, serializer: &'a mut Serializer, force_canonical: bool) -> cbor_event::Result<&'a mut Serializer> { - if !force_canonical && *self == Self::Indefinite { - serializer.write_special(CBORSpecial::Break)?; - } - Ok(serializer) - } -} - -impl StringEncoding { - pub fn to_str_len_sz(&self, len: u64, force_canonical: bool) -> cbor_event::StringLenSz { - if force_canonical { - cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)) - } else { - match self { - Self::Canonical => cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)), - Self::Definite(sz) => if sz_max(*sz) >= len { - cbor_event::StringLenSz::Len(*sz) - } else { - cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)) - }, - Self::Indefinite(lens) => cbor_event::StringLenSz::Indefinite(lens.clone()), - } - } - } -} - -pub trait Serialize { - fn serialize<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - force_canonical: bool, - ) -> cbor_event::Result<&'a mut Serializer>; - - /// Bytes of a structure using the CBOR bytes as per the CDDL spec - /// which for foo = bytes will include the CBOR bytes type/len, etc. - fn to_cbor_bytes(&self, force_canonical: bool) -> Vec { - let mut buf = Serializer::new_vec(); - self.serialize(&mut buf, force_canonical).unwrap(); - buf.finalize() - } - - /// Generic to-bytes when one does not care what kind of bytes - /// e.g. CBOR encoded or the raw inner bytes of a buffer when applicable. - /// This is auto-implemented using to_cbor_bytes but can be overrided - /// for things like signatures, hashes, etc where the CBOR bytes are not commonly used. - fn to_bytes(&self) -> Vec { - self.to_cbor_bytes(false) - } -} - -impl Serialize for T { - fn serialize<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - _force_canonical: bool, - ) -> cbor_event::Result<&'a mut Serializer> { - ::serialize(self, serializer) - } -} - -pub trait SerializeEmbeddedGroup { - fn serialize_as_embedded_group<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - force_canonical: bool, - ) -> cbor_event::Result<&'a mut Serializer>; -} - -pub trait Deserialize { - fn deserialize( - raw: &mut Deserializer, - ) -> Result where Self: Sized; - - /// from-bytes using the exact CBOR format specified in the CDDL binary spec. - /// For hashes/addresses/etc this will include the CBOR bytes type/len/etc. - fn from_cbor_bytes(data: &[u8]) -> Result where Self: Sized { - let mut raw = Deserializer::from(std::io::Cursor::new(data)); - Self::deserialize(&mut raw) - } - - /// Generic from-bytes when one does not care what kind of bytes - /// e.g. CBOR encoded or the raw inner bytes of a buffer when applicable. - /// This is auto-implemented using from_cbor_bytes but can be overrided - /// for things like signatures, hashes, etc where the CBOR bytes are not commonly used. - fn from_bytes(data: &[u8]) -> Result where Self: Sized { - Self::from_cbor_bytes(data) - } -} - -impl Deserialize for T { - fn deserialize(raw: &mut Deserializer) -> Result { - T::deserialize(raw).map_err(|e| DeserializeError::from(e)) - } -} - -// TODO: remove ToBytes / FromBytes after we regenerate the WASM wrappers. -// This is just so the existing generated to/from bytes code works -pub trait ToBytes { - fn to_bytes(&self, force_canonical: bool) -> Vec; -} - -impl ToBytes for T { - fn to_bytes(&self, _force_canonical: bool) -> Vec { - Serialize::to_bytes(self) - } -} - -pub trait FromBytes { - fn from_bytes(data: Vec) -> Result where Self: Sized; -} - -impl FromBytes for T { - fn from_bytes(data: Vec) -> Result where Self: Sized { - Deserialize::from_bytes(data.as_ref()) - } -} - use super::*; use std::io::{Seek, SeekFrom}; +pub use cardano_multiplatform_lib_core::{ + error::Key, + serialization::{ + fit_sz, + CBORReadLen, + Deserialize, + DeserializeEmbeddedGroup, + Serialize, + SerializeEmbeddedGroup, + } +}; + impl Serialize for Address { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { let raw_bytes = self.to_raw_bytes(); @@ -1017,58 +775,6 @@ impl Deserialize for Block { } } -impl Serialize for BootstrapWitness { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(4, force_canonical))?; - self.vkey.serialize(serializer, force_canonical)?; - self.signature.serialize(serializer, force_canonical)?; - serializer.write_bytes_sz(&self.chain_code, self.encodings.as_ref().map(|encs| encs.chain_code_encoding.clone()).unwrap_or_default().to_str_len_sz(self.chain_code.len() as u64, force_canonical))?; - serializer.write_bytes_sz(&self.attributes, self.encodings.as_ref().map(|encs| encs.attributes_encoding.clone()).unwrap_or_default().to_str_len_sz(self.attributes.len() as u64, force_canonical))?; - self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().end(serializer, force_canonical) - } -} - -impl Deserialize for BootstrapWitness { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array_sz()?; - let len_encoding: LenEncoding = len.into(); - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(4)?; - let vkey = (|| -> Result<_, DeserializeError> { - Ok(Vkey::deserialize(raw)?) - })().map_err(|e| e.annotate("vkey"))?; - let signature = (|| -> Result<_, DeserializeError> { - Ok(Ed25519Signature::deserialize(raw)?) - })().map_err(|e| e.annotate("signature"))?; - let (chain_code, chain_code_encoding) = (|| -> Result<_, DeserializeError> { - Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) - })().map_err(|e| e.annotate("chain_code"))?; - let (attributes, attributes_encoding) = (|| -> Result<_, DeserializeError> { - Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) - })().map_err(|e| e.annotate("attributes"))?; - match len { - cbor_event::LenSz::Len(_, _) => (), - cbor_event::LenSz::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(BootstrapWitness { - vkey, - signature, - chain_code, - attributes, - encodings: Some(BootstrapWitnessEncoding { - len_encoding, - chain_code_encoding, - attributes_encoding, - }), - }) - })().map_err(|e| e.annotate("BootstrapWitness")) - } -} - impl Serialize for Certificate { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { match self { @@ -1773,7 +1479,7 @@ impl Deserialize for HeaderBody { Ok(Vkey::deserialize(raw)?) })().map_err(|e| e.annotate("issuer_vkey"))?; let vrf_vkey = (|| -> Result<_, DeserializeError> { - Ok(VrfVkey::deserialize(raw)?) + Ok(VRFVKey::deserialize(raw)?) })().map_err(|e| e.annotate("vrf_vkey"))?; let vrf_result = (|| -> Result<_, DeserializeError> { Ok(VrfCert::deserialize(raw)?) @@ -2046,48 +1752,6 @@ impl Deserialize for Ipv6 { } } -impl Serialize for KesSignature { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) - } -} - -impl Deserialize for KesSignature { - fn deserialize(raw: &mut Deserializer) -> Result { - let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; - if inner.len() != 32 { - return Err(DeserializeError::new("KesSignature", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(32), max: Some(32) })); - } - Ok(Self { - inner, - encodings: Some(KesSignatureEncoding { - inner_encoding, - }), - }) - } -} - -impl Serialize for KesVkey { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) - } -} - -impl Deserialize for KesVkey { - fn deserialize(raw: &mut Deserializer) -> Result { - let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; - if inner.len() != 8 { - return Err(DeserializeError::new("KesVkey", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(8), max: Some(8) })); - } - Ok(Self { - inner, - encodings: Some(KesVkeyEncoding { - inner_encoding, - }), - }) - } -} - impl Serialize for Language { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { match self { @@ -2421,145 +2085,33 @@ impl Deserialize for NetworkId { (|| -> Result<_, DeserializeError> { let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let (i0_value, i0_encoding) = raw.unsigned_integer_sz()?; - if i0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); - } - Ok(Some(i0_encoding)) - })(raw) - { - Ok(i0_encoding) => return Ok(Self::I0 { - i0_encoding, - }), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let (i1_value, i1_encoding) = raw.unsigned_integer_sz()?; - if i1_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i1_value), expected: Key::Uint(1) }.into()); - } - Ok(Some(i1_encoding)) - })(raw) - { - Ok(i1_encoding) => return Ok(Self::I1 { - i1_encoding, - }), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - Err(DeserializeError::new("NetworkId", DeserializeFailure::NoVariantMatched.into())) - })().map_err(|e| e.annotate("NetworkId")) - } -} - -impl Serialize for Nonce { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - match self { - Nonce::I0{ i0_encoding, outer_len_encoding } => { - serializer.write_array_sz(outer_len_encoding.to_len_sz(1, force_canonical))?; - serializer.write_unsigned_integer_sz(0u64, fit_sz(0u64, *i0_encoding, force_canonical))?; - outer_len_encoding.end(serializer, force_canonical)?; - Ok(serializer) - }, - Nonce::Nonce1(nonce1) => nonce1.serialize(serializer, force_canonical), - } - } -} - -impl Deserialize for Nonce { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array_sz()?; - let outer_len_encoding: LenEncoding = len.into(); - let mut read_len = CBORReadLen::new(len); - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - let (i0_value, i0_encoding) = raw.unsigned_integer_sz()?; - if i0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); - } - Ok(Some(i0_encoding)) - })(raw) - { - Ok(i0_encoding) => return Ok(Self::I0 { - i0_encoding, - outer_len_encoding, - }), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(Nonce1::deserialize_as_embedded_group(raw, &mut read_len, len)?) - })(raw) - { - Ok(nonce1) => return Ok(Self::Nonce1(nonce1)), - Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), - }; - match len { - cbor_event::LenSz::Len(_, _) => (), - cbor_event::LenSz::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Err(DeserializeError::new("Nonce", DeserializeFailure::NoVariantMatched.into())) - })().map_err(|e| e.annotate("Nonce")) - } -} - -impl Serialize for Nonce1 { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(2, force_canonical))?; - self.serialize_as_embedded_group(serializer, force_canonical) - } -} - -impl SerializeEmbeddedGroup for Nonce1 { - fn serialize_as_embedded_group<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer_sz(1u64, fit_sz(1u64, self.encodings.as_ref().map(|encs| encs.index_0_encoding.clone()).unwrap_or_default(), force_canonical))?; - serializer.write_bytes_sz(&self.bytes, self.encodings.as_ref().map(|encs| encs.bytes_encoding.clone()).unwrap_or_default().to_str_len_sz(self.bytes.len() as u64, force_canonical))?; - self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().end(serializer, force_canonical) - } -} - -impl Deserialize for Nonce1 { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array_sz()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, &mut read_len, len); - match len { - cbor_event::LenSz::Len(_, _) => (), - cbor_event::LenSz::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })().map_err(|e| e.annotate("Nonce1")) - } -} - -impl DeserializeEmbeddedGroup for Nonce1 { - fn deserialize_as_embedded_group(raw: &mut Deserializer, read_len: &mut CBORReadLen, len: cbor_event::LenSz) -> Result { - let len_encoding = len.into(); - let index_0_encoding = (|| -> Result<_, DeserializeError> { - let (index_0_value, index_0_encoding) = raw.unsigned_integer_sz()?; - if index_0_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(index_0_value), expected: Key::Uint(1) }.into()); - } - Ok(Some(index_0_encoding)) - })().map_err(|e| e.annotate("index_0"))?; - let (bytes, bytes_encoding) = (|| -> Result<_, DeserializeError> { - Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) - })().map_err(|e| e.annotate("bytes"))?; - Ok(Nonce1 { - bytes, - encodings: Some(Nonce1Encoding { - len_encoding, - index_0_encoding, - bytes_encoding, - }), - }) + let (i0_value, i0_encoding) = raw.unsigned_integer_sz()?; + if i0_value != 0 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); + } + Ok(Some(i0_encoding)) + })(raw) + { + Ok(i0_encoding) => return Ok(Self::I0 { + i0_encoding, + }), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let (i1_value, i1_encoding) = raw.unsigned_integer_sz()?; + if i1_value != 1 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i1_value), expected: Key::Uint(1) }.into()); + } + Ok(Some(i1_encoding)) + })(raw) + { + Ok(i1_encoding) => return Ok(Self::I1 { + i1_encoding, + }), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("NetworkId", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("NetworkId")) } } @@ -2603,7 +2155,7 @@ impl DeserializeEmbeddedGroup for OperationalCert { fn deserialize_as_embedded_group(raw: &mut Deserializer, read_len: &mut CBORReadLen, len: cbor_event::LenSz) -> Result { let len_encoding = len.into(); let hot_vkey = (|| -> Result<_, DeserializeError> { - Ok(KesVkey::deserialize(raw)?) + Ok(KESVKey::deserialize(raw)?) })().map_err(|e| e.annotate("hot_vkey"))?; let (sequence_number, sequence_number_encoding) = (|| -> Result<_, DeserializeError> { Ok(raw.unsigned_integer_sz().map(|(x, enc)| (x, Some(enc)))?) @@ -4639,27 +4191,6 @@ impl Deserialize for ShelleyTxOut { } } -impl Serialize for SignkeyKES { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) - } -} - -impl Deserialize for SignkeyKES { - fn deserialize(raw: &mut Deserializer) -> Result { - let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; - if inner.len() != 16 { - return Err(DeserializeError::new("SignkeyKES", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(16), max: Some(16) })); - } - Ok(Self { - inner, - encodings: Some(SignkeyKESEncoding { - inner_encoding, - }), - }) - } -} - impl Serialize for SingleHostAddr { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(4, force_canonical))?; @@ -6674,28 +6205,214 @@ impl Deserialize for Value { } } -impl Serialize for Vkey { + +impl Serialize for BootstrapWitness { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(4, force_canonical))?; + self.vkey.serialize(serializer, force_canonical)?; + self.signature.serialize(serializer, force_canonical)?; + serializer.write_bytes_sz(&self.chain_code, self.encodings.as_ref().map(|encs| encs.chain_code_encoding.clone()).unwrap_or_default().to_str_len_sz(self.chain_code.len() as u64, force_canonical))?; + serializer.write_bytes_sz(&self.attributes, self.encodings.as_ref().map(|encs| encs.attributes_encoding.clone()).unwrap_or_default().to_str_len_sz(self.attributes.len() as u64, force_canonical))?; + self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().end(serializer, force_canonical) + } +} + +impl Deserialize for BootstrapWitness { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array_sz()?; + let len_encoding: LenEncoding = len.into(); + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let vkey = (|| -> Result<_, DeserializeError> { + Ok(Vkey::deserialize(raw)?) + })().map_err(|e| e.annotate("vkey"))?; + let signature = (|| -> Result<_, DeserializeError> { + Ok(Ed25519Signature::deserialize(raw)?) + })().map_err(|e| e.annotate("signature"))?; + let (chain_code, chain_code_encoding) = (|| -> Result<_, DeserializeError> { + Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) + })().map_err(|e| e.annotate("chain_code"))?; + let (attributes, attributes_encoding) = (|| -> Result<_, DeserializeError> { + Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) + })().map_err(|e| e.annotate("attributes"))?; + match len { + cbor_event::LenSz::Len(_, _) => (), + cbor_event::LenSz::Indefinite => match raw.special()? { + cbor_event::Special::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(BootstrapWitness { + vkey, + signature, + chain_code, + attributes, + encodings: Some(BootstrapWitnessEncoding { + len_encoding, + chain_code_encoding, + attributes_encoding, + }), + }) + })().map_err(|e| e.annotate("BootstrapWitness")) + } +} + +impl Serialize for KesSignature { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) + } +} + +impl Deserialize for KesSignature { + fn deserialize(raw: &mut Deserializer) -> Result { + let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; + if inner.len() != 32 { + return Err(DeserializeError::new("KesSignature", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(32), max: Some(32) })); + } + Ok(Self { + inner, + encodings: Some(KesSignatureEncoding { + inner_encoding, + }), + }) + } +} + +impl Serialize for SignkeyKES { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - let pubkey_bytes = self.pubkey.as_bytes(); - serializer.write_bytes_sz(&pubkey_bytes, self.encodings.as_ref().map(|encs| encs.pubkey_bytes_encoding.clone()).unwrap_or_default().to_str_len_sz(pubkey_bytes.len() as u64, force_canonical)) + serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) } } -impl Deserialize for Vkey { +impl Deserialize for SignkeyKES { fn deserialize(raw: &mut Deserializer) -> Result { - let (pubkey_bytes, pubkey_bytes_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; - let pubkey = chain_crypto::PublicKey::from_binary(pubkey_bytes.as_ref()) - .map(PublicKey) - .map_err(DeserializeFailure::PublicKeyError)?; + let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; + if inner.len() != 16 { + return Err(DeserializeError::new("SignkeyKES", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(16), max: Some(16) })); + } Ok(Self { - pubkey, - encodings: Some(VkeyEncoding { - pubkey_bytes_encoding, + inner, + encodings: Some(SignkeyKESEncoding { + inner_encoding, }), }) } } +impl Serialize for Nonce { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + match self { + Nonce::I0{ i0_encoding, outer_len_encoding } => { + serializer.write_array_sz(outer_len_encoding.to_len_sz(1, force_canonical))?; + serializer.write_unsigned_integer_sz(0u64, fit_sz(0u64, *i0_encoding, force_canonical))?; + outer_len_encoding.end(serializer, force_canonical)?; + Ok(serializer) + }, + Nonce::Nonce1(nonce1) => nonce1.serialize(serializer, force_canonical), + } + } +} + +impl Deserialize for Nonce { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array_sz()?; + let outer_len_encoding: LenEncoding = len.into(); + let mut read_len = CBORReadLen::new(len); + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let (i0_value, i0_encoding) = raw.unsigned_integer_sz()?; + if i0_value != 0 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(i0_value), expected: Key::Uint(0) }.into()); + } + Ok(Some(i0_encoding)) + })(raw) + { + Ok(i0_encoding) => return Ok(Self::I0 { + i0_encoding, + outer_len_encoding, + }), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(Nonce1::deserialize_as_embedded_group(raw, &mut read_len, len)?) + })(raw) + { + Ok(nonce1) => return Ok(Self::Nonce1(nonce1)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match len { + cbor_event::LenSz::Len(_, _) => (), + cbor_event::LenSz::Indefinite => match raw.special()? { + cbor_event::Special::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Err(DeserializeError::new("Nonce", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("Nonce")) + } +} + +impl Serialize for Nonce1 { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(2, force_canonical))?; + self.serialize_as_embedded_group(serializer, force_canonical) + } +} + +impl SerializeEmbeddedGroup for Nonce1 { + fn serialize_as_embedded_group<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer_sz(1u64, fit_sz(1u64, self.encodings.as_ref().map(|encs| encs.index_0_encoding.clone()).unwrap_or_default(), force_canonical))?; + serializer.write_bytes_sz(&self.bytes, self.encodings.as_ref().map(|encs| encs.bytes_encoding.clone()).unwrap_or_default().to_str_len_sz(self.bytes.len() as u64, force_canonical))?; + self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().end(serializer, force_canonical) + } +} + +impl Deserialize for Nonce1 { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array_sz()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, &mut read_len, len); + match len { + cbor_event::LenSz::Len(_, _) => (), + cbor_event::LenSz::Indefinite => match raw.special()? { + cbor_event::Special::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })().map_err(|e| e.annotate("Nonce1")) + } +} + +impl DeserializeEmbeddedGroup for Nonce1 { + fn deserialize_as_embedded_group(raw: &mut Deserializer, read_len: &mut CBORReadLen, len: cbor_event::LenSz) -> Result { + let len_encoding = len.into(); + let index_0_encoding = (|| -> Result<_, DeserializeError> { + let (index_0_value, index_0_encoding) = raw.unsigned_integer_sz()?; + if index_0_value != 1 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(index_0_value), expected: Key::Uint(1) }.into()); + } + Ok(Some(index_0_encoding)) + })().map_err(|e| e.annotate("index_0"))?; + let (bytes, bytes_encoding) = (|| -> Result<_, DeserializeError> { + Ok(raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?) + })().map_err(|e| e.annotate("bytes"))?; + Ok(Nonce1 { + bytes, + encodings: Some(Nonce1Encoding { + len_encoding, + index_0_encoding, + bytes_encoding, + }), + }) + } +} + + impl Serialize for Vkeywitness { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array_sz(self.encodings.as_ref().map(|encs| encs.len_encoding).unwrap_or_default().to_len_sz(2, force_canonical))?; @@ -6721,7 +6438,7 @@ impl Deserialize for Vkeywitness { match len { cbor_event::LenSz::Len(_, _) => (), cbor_event::LenSz::Indefinite => match raw.special()? { - CBORSpecial::Break => (), + cbor_event::Special::Break => (), _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } @@ -6761,7 +6478,7 @@ impl Deserialize for VrfCert { match len { cbor_event::LenSz::Len(_, _) => (), cbor_event::LenSz::Indefinite => match raw.special()? { - CBORSpecial::Break => (), + cbor_event::Special::Break => (), _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } @@ -6777,24 +6494,3 @@ impl Deserialize for VrfCert { })().map_err(|e| e.annotate("VrfCert")) } } - -impl Serialize for VrfVkey { - fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(&self.inner, self.encodings.as_ref().map(|encs| encs.inner_encoding.clone()).unwrap_or_default().to_str_len_sz(self.inner.len() as u64, force_canonical)) - } -} - -impl Deserialize for VrfVkey { - fn deserialize(raw: &mut Deserializer) -> Result { - let (inner, inner_encoding) = raw.bytes_sz().map(|(bytes, enc)| (bytes, StringEncoding::from(enc)))?; - if inner.len() != 8 { - return Err(DeserializeError::new("VrfVkey", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(8), max: Some(8) })); - } - Ok(Self { - inner, - encodings: Some(VrfVkeyEncoding { - inner_encoding, - }), - }) - } -} \ No newline at end of file diff --git a/rust/core/src/transaction.rs b/chain/src/transaction.rs similarity index 99% rename from rust/core/src/transaction.rs rename to chain/src/transaction.rs index e3ed97da..a7f855f8 100644 --- a/rust/core/src/transaction.rs +++ b/chain/src/transaction.rs @@ -1,3 +1,5 @@ +use super::*; + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct AlonzoTxOut { pub address: Address, diff --git a/cip25-json-gen/Cargo.toml b/cip25-json-gen/Cargo.toml new file mode 100644 index 00000000..8d440282 --- /dev/null +++ b/cip25-json-gen/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cddl-lib-json-schema-gen" +version = "0.0.1" +edition = "2018" + + +[dependencies] +serde_json = "1.0.57" +schemars = "0.8.8" +core = { path = "../core", package = "cip25-lib" } diff --git a/cip25-json-gen/src/main.rs b/cip25-json-gen/src/main.rs new file mode 100644 index 00000000..0cb14363 --- /dev/null +++ b/cip25-json-gen/src/main.rs @@ -0,0 +1,21 @@ +use core::*; + +fn main() { + macro_rules! gen_json_schema { + ($name:ident) => { + let dest_path = std::path::Path::new(&"schemas").join(&format!("{}.json", stringify!($name))); + std::fs::write(&dest_path, serde_json::to_string_pretty(&schemars::schema_for!($name)).unwrap()).unwrap(); + } + } + let schema_path = std::path::Path::new(&"schemas"); + if !schema_path.exists() { + std::fs::create_dir(schema_path).unwrap(); + } + gen_json_schema!(FilesDetails); + gen_json_schema!(LabelMetadata); + gen_json_schema!(LabelMetadataV2); + gen_json_schema!(Metadata); + gen_json_schema!(MetadataDetails); + gen_json_schema!(String64); + gen_json_schema!(String64OrArrString64); +} diff --git a/rust/wasm/Cargo.toml b/cip25-wasm/Cargo.toml similarity index 66% rename from rust/wasm/Cargo.toml rename to cip25-wasm/Cargo.toml index 852de1b8..f6f5eb51 100644 --- a/rust/wasm/Cargo.toml +++ b/cip25-wasm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "cardano-multiplatform-lib-wasm" +name = "cardano-multiplatform-lib-cip25-wasm" version = "0.1.0" edition = "2018" @@ -7,7 +7,7 @@ edition = "2018" crate-type = ["cdylib", "rlib"] [dependencies] -core = { path = "../core", package = "cardano-multiplatform-lib-core" } +core = { path = "../cip25", package = "cardano-multiplatform-lib-cip25" } cbor_event = "2.2.0" wasm-bindgen = { version = "0.2", features=["serde-serialize"] } linked-hash-map = "0.5.3" diff --git a/cip25-wasm/src/lib.rs b/cip25-wasm/src/lib.rs new file mode 100644 index 00000000..277e1dde --- /dev/null +++ b/cip25-wasm/src/lib.rs @@ -0,0 +1,833 @@ +use wasm_bindgen::prelude::*; + +// TODO: remove after regen +use core::{ToBytes, FromBytes}; + +use std::collections::BTreeMap; + +pub type AssetNameV1 = String64; + +pub type AssetNameV2 = Vec; + +pub type PolicyIdV1 = String64; + +pub type PolicyIdV2 = Vec; + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct MapAssetNameV2ToMetadataDetails(BTreeMap); + +#[wasm_bindgen] + +impl MapAssetNameV2ToMetadataDetails { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: AssetNameV2, value: &MetadataDetails) -> Option { + self.0.insert(key, value.clone().into()).map(|v| v.clone().into()) + } + + pub fn get(&self, key: AssetNameV2) -> Option { + self.0.get(&key).map(|v| v.clone().into()) + } + + pub fn keys(&self) -> AssetNameV2s { + AssetNameV2s(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +impl From> for MapAssetNameV2ToMetadataDetails { + fn from(native: BTreeMap) -> Self { + Self(native) + } +} + +impl std::convert::Into> for MapAssetNameV2ToMetadataDetails { + fn into(self) -> BTreeMap { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct AssetNameV2s(Vec); + +#[wasm_bindgen] + +impl AssetNameV2s { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> AssetNameV2 { + self.0[index].clone() + } + + pub fn add(&mut self, elem: AssetNameV2) { + self.0.push(elem); + } +} + +impl From> for AssetNameV2s { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for AssetNameV2s { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct String64s(Vec); + +#[wasm_bindgen] + +impl String64s { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> String64 { + self.0[index].clone().into() + } + + pub fn add(&mut self, elem: &String64) { + self.0.push(elem.clone().into()); + } +} + +impl From> for String64s { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for String64s { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct FilesDetailss(Vec); + +#[wasm_bindgen] + +impl FilesDetailss { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> FilesDetails { + self.0[index].clone().into() + } + + pub fn add(&mut self, elem: &FilesDetails) { + self.0.push(elem.clone().into()); + } +} + +impl From> for FilesDetailss { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for FilesDetailss { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct Data(BTreeMap>); + +#[wasm_bindgen] + +impl Data { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: PolicyIdV2, value: &MapAssetNameV2ToMetadataDetails) -> Option { + self.0.insert(key, value.clone().into()).map(|v| v.clone().into()) + } + + pub fn get(&self, key: PolicyIdV2) -> Option { + self.0.get(&key).map(|v| v.clone().into()) + } + + pub fn keys(&self) -> PolicyIdV2s { + PolicyIdV2s(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +impl From>> for Data { + fn from(native: BTreeMap>) -> Self { + Self(native) + } +} + +impl std::convert::Into>> for Data { + fn into(self) -> BTreeMap> { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct FilesDetails(core::FilesDetails); + +#[wasm_bindgen] + +impl FilesDetails { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn name(&self) -> String64 { + self.0.name.clone().into() + } + + pub fn media_type(&self) -> String64 { + self.0.media_type.clone().into() + } + + pub fn src(&self) -> String64OrArrString64 { + self.0.src.clone().into() + } + + pub fn new(name: &String64, media_type: &String64, src: &String64OrArrString64) -> Self { + Self(core::FilesDetails::new(name.clone().into(), media_type.clone().into(), src.clone().into())) + } +} + +impl From for FilesDetails { + fn from(native: core::FilesDetails) -> Self { + Self(native) + } +} + +impl From for core::FilesDetails { + fn from(wasm: FilesDetails) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct MapPolicyIdV1ToMapAssetNameV1ToMetadataDetails(BTreeMap>); + +#[wasm_bindgen] + +impl MapPolicyIdV1ToMapAssetNameV1ToMetadataDetails { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &PolicyIdV1, value: &MapAssetNameV1ToMetadataDetails) -> Option { + self.0.insert(key.clone().into(), value.clone().into()).map(|v| v.clone().into()) + } + + pub fn get(&self, key: &PolicyIdV1) -> Option { + self.0.get(&key.0).map(|v| v.clone().into()) + } + + pub fn keys(&self) -> PolicyIdV1s { + PolicyIdV1s(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +impl From>> for MapPolicyIdV1ToMapAssetNameV1ToMetadataDetails { + fn from(native: BTreeMap>) -> Self { + Self(native) + } +} + +impl std::convert::Into>> for MapPolicyIdV1ToMapAssetNameV1ToMetadataDetails { + fn into(self) -> BTreeMap> { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct PolicyIdV1s(Vec); + +#[wasm_bindgen] + +impl PolicyIdV1s { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PolicyIdV1 { + self.0[index].clone().into() + } + + pub fn add(&mut self, elem: &PolicyIdV1) { + self.0.push(elem.clone().into()); + } +} + +impl From> for PolicyIdV1s { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for PolicyIdV1s { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct MapAssetNameV1ToMetadataDetails(BTreeMap); + +#[wasm_bindgen] + +impl MapAssetNameV1ToMetadataDetails { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &AssetNameV1, value: &MetadataDetails) -> Option { + self.0.insert(key.clone().into(), value.clone().into()).map(|v| v.clone().into()) + } + + pub fn get(&self, key: &AssetNameV1) -> Option { + self.0.get(&key.0).map(|v| v.clone().into()) + } + + pub fn keys(&self) -> AssetNameV1s { + AssetNameV1s(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +impl From> for MapAssetNameV1ToMetadataDetails { + fn from(native: BTreeMap) -> Self { + Self(native) + } +} + +impl std::convert::Into> for MapAssetNameV1ToMetadataDetails { + fn into(self) -> BTreeMap { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct AssetNameV1s(Vec); + +#[wasm_bindgen] + +impl AssetNameV1s { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> AssetNameV1 { + self.0[index].clone().into() + } + + pub fn add(&mut self, elem: &AssetNameV1) { + self.0.push(elem.clone().into()); + } +} + +impl From> for AssetNameV1s { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for AssetNameV1s { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct PolicyIdV2s(Vec); + +#[wasm_bindgen] + +impl PolicyIdV2s { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PolicyIdV2 { + self.0[index].clone() + } + + pub fn add(&mut self, elem: PolicyIdV2) { + self.0.push(elem); + } +} + +impl From> for PolicyIdV2s { + fn from(native: Vec) -> Self { + Self(native) + } +} + +impl std::convert::Into> for PolicyIdV2s { + fn into(self) -> Vec { + self.0 + } +} + +#[wasm_bindgen] + +pub enum LabelMetadataKind { + LabelMetadataV1, + LabelMetadataV2, +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct LabelMetadata(core::LabelMetadata); + +#[wasm_bindgen] + +impl LabelMetadata { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn new_label_metadata_v1(label_metadata_v1: LabelMetadataV1) -> Self { + Self(core::LabelMetadata::new_label_metadata_v1(label_metadata_v1.clone().into())) + } + + pub fn new_label_metadata_v2(label_metadata_v2: &LabelMetadataV2) -> Self { + Self(core::LabelMetadata::new_label_metadata_v2(label_metadata_v2.clone().into())) + } + + pub fn kind(&self) -> LabelMetadataKind { + match &self.0 { + core::LabelMetadata::LabelMetadataV1(_) => LabelMetadataKind::LabelMetadataV1, + core::LabelMetadata::LabelMetadataV2(_) => LabelMetadataKind::LabelMetadataV2, + } + } + + pub fn as_label_metadata_v1(&self) -> Option { + match &self.0 { + core::LabelMetadata::LabelMetadataV1(label_metadata_v1) => Some(label_metadata_v1.clone().into()), + _ => None, + } + } + + pub fn as_label_metadata_v2(&self) -> Option { + match &self.0 { + core::LabelMetadata::LabelMetadataV2(label_metadata_v2) => Some(label_metadata_v2.clone().into()), + _ => None, + } + } +} + +impl From for LabelMetadata { + fn from(native: core::LabelMetadata) -> Self { + Self(native) + } +} + +impl From for core::LabelMetadata { + fn from(wasm: LabelMetadata) -> Self { + wasm.0 + } +} + +type LabelMetadataV1 = MapPolicyIdV1ToMapAssetNameV1ToMetadataDetails; + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct LabelMetadataV2(core::LabelMetadataV2); + +#[wasm_bindgen] + +impl LabelMetadataV2 { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn data(&self) -> Data { + self.0.data.clone().into() + } + + pub fn new(data: Data) -> Self { + Self(core::LabelMetadataV2::new(data.clone().into())) + } +} + +impl From for LabelMetadataV2 { + fn from(native: core::LabelMetadataV2) -> Self { + Self(native) + } +} + +impl From for core::LabelMetadataV2 { + fn from(wasm: LabelMetadataV2) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct Metadata(core::Metadata); + +#[wasm_bindgen] + +impl Metadata { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn key_721(&self) -> LabelMetadata { + self.0.key_721.clone().into() + } + + pub fn new(key_721: &LabelMetadata) -> Self { + Self(core::Metadata::new(key_721.clone().into())) + } +} + +impl From for Metadata { + fn from(native: core::Metadata) -> Self { + Self(native) + } +} + +impl From for core::Metadata { + fn from(wasm: Metadata) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct MetadataDetails(core::MetadataDetails); + +#[wasm_bindgen] + +impl MetadataDetails { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn name(&self) -> String64 { + self.0.name.clone().into() + } + + pub fn image(&self) -> String64OrArrString64 { + self.0.image.clone().into() + } + + pub fn set_media_type(&mut self, media_type: &String64) { + self.0.media_type = Some(media_type.clone().into()) + } + + pub fn media_type(&self) -> Option { + self.0.media_type.clone().map(std::convert::Into::into) + } + + pub fn set_description(&mut self, description: &String64OrArrString64) { + self.0.description = Some(description.clone().into()) + } + + pub fn description(&self) -> Option { + self.0.description.clone().map(std::convert::Into::into) + } + + pub fn set_files(&mut self, files: &FilesDetailss) { + self.0.files = Some(files.clone().into()) + } + + pub fn files(&self) -> Option { + self.0.files.clone().map(std::convert::Into::into) + } + + pub fn new(name: &String64, image: &String64OrArrString64) -> Self { + Self(core::MetadataDetails::new(name.clone().into(), image.clone().into())) + } +} + +impl From for MetadataDetails { + fn from(native: core::MetadataDetails) -> Self { + Self(native) + } +} + +impl From for core::MetadataDetails { + fn from(wasm: MetadataDetails) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct String64(core::String64); + +#[wasm_bindgen] + +impl String64 { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn get(&self) -> String { + self.0.get().clone().clone() + } +} + +impl From for String64 { + fn from(native: core::String64) -> Self { + Self(native) + } +} + +impl From for core::String64 { + fn from(wasm: String64) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] + +pub enum String64OrArrString64Kind { + String64, + ArrString64, +} + +#[wasm_bindgen] + +#[derive(Clone, Debug)] +pub struct String64OrArrString64(core::String64OrArrString64); + +#[wasm_bindgen] + +impl String64OrArrString64 { + pub fn to_bytes(&self) -> Vec { + ToBytes::to_bytes(&self.0) + } + + pub fn from_bytes(data: Vec) -> Result { + FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn new_string64(string64: &String64) -> Self { + Self(core::String64OrArrString64::new_string64(string64.clone().into())) + } + + pub fn new_arr_string64(arr_string64: &String64s) -> Self { + Self(core::String64OrArrString64::new_arr_string64(arr_string64.clone().into())) + } + + pub fn kind(&self) -> String64OrArrString64Kind { + match &self.0 { + core::String64OrArrString64::String64(_) => String64OrArrString64Kind::String64, + core::String64OrArrString64::ArrString64(_) => String64OrArrString64Kind::ArrString64, + } + } + + pub fn as_string64(&self) -> Option { + match &self.0 { + core::String64OrArrString64::String64(string64) => Some(string64.clone().into()), + _ => None, + } + } + + pub fn as_arr_string64(&self) -> Option { + match &self.0 { + core::String64OrArrString64::ArrString64(arr_string64) => Some(arr_string64.clone().into()), + _ => None, + } + } +} + +impl From for String64OrArrString64 { + fn from(native: core::String64OrArrString64) -> Self { + Self(native) + } +} + +impl From for core::String64OrArrString64 { + fn from(wasm: String64OrArrString64) -> Self { + wasm.0 + } +} \ No newline at end of file diff --git a/cip25/Cargo.toml b/cip25/Cargo.toml new file mode 100644 index 00000000..368e3185 --- /dev/null +++ b/cip25/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cardano-multiplatform-lib-cip25" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cardano-multiplatform-lib-core = { "path" = "../core" } +cbor_event = "2.2.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +schemars = "0.8.8" diff --git a/cip25/src/lib.rs b/cip25/src/lib.rs new file mode 100644 index 00000000..92ae8ca8 --- /dev/null +++ b/cip25/src/lib.rs @@ -0,0 +1,164 @@ +use std::io::{BufRead, Seek, Write}; + +pub use cardano_multiplatform_lib_core::{ + serialization::*, + error::*, +}; + +// This library was code-generated using an experimental CDDL to rust tool: +// https://github.com/Emurgo/cddl-codegen + +use cbor_event::{self, de::Deserializer, se::{Serialize, Serializer}}; + +use cbor_event::Type as CBORType; + +use cbor_event::Special as CBORSpecial; + + +pub mod serialization; + +use std::collections::BTreeMap; + +use std::convert::{From, TryFrom}; + +pub type AssetNameV1 = String64; + +pub type AssetNameV2 = Vec; + +pub type Data = BTreeMap>; + +pub type LabelMetadataV1 = BTreeMap>; + +pub type PolicyIdV1 = String64; + +pub type PolicyIdV1s = Vec; + +pub type PolicyIdV2 = Vec; + +pub type PolicyIdV2s = Vec; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct FilesDetails { + pub name: String64, + pub media_type: String64, + pub src: String64OrArrString64, +} + +impl FilesDetails { + pub fn new(name: String64, media_type: String64, src: String64OrArrString64) -> Self { + Self { + name, + media_type, + src, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub enum LabelMetadata { + LabelMetadataV1(LabelMetadataV1), + LabelMetadataV2(LabelMetadataV2), +} + +impl LabelMetadata { + pub fn new_label_metadata_v1(label_metadata_v1: LabelMetadataV1) -> Self { + Self::LabelMetadataV1(label_metadata_v1) + } + + pub fn new_label_metadata_v2(label_metadata_v2: LabelMetadataV2) -> Self { + Self::LabelMetadataV2(label_metadata_v2) + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct LabelMetadataV2 { + pub data: Data, +} + +impl LabelMetadataV2 { + pub fn new(data: Data) -> Self { + Self { + data, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct Metadata { + pub key_721: LabelMetadata, +} + +impl Metadata { + pub fn new(key_721: LabelMetadata) -> Self { + Self { + key_721, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct MetadataDetails { + pub name: String64, + pub image: String64OrArrString64, + pub media_type: Option, + pub description: Option, + pub files: Option>, +} + +impl MetadataDetails { + pub fn new(name: String64, image: String64OrArrString64) -> Self { + Self { + name, + image, + media_type: None, + description: None, + files: None, + } + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, Eq, PartialEq, Ord, PartialOrd)] +pub struct String64(String); + +impl String64 { + pub fn get(&self) -> &String { + &self.0 + } + + pub fn new(inner: String) -> Result { + if inner.len() > 64 { + return Err(DeserializeError::new("String64", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(0), max: Some(64) })); + } + Ok(Self(inner)) + } +} + +impl TryFrom for String64 { + type Error = DeserializeError; + + fn try_from(inner: String) -> Result { + String64::new(inner) + } +} + +impl From for String { + fn from(wrapper: String64) -> Self { + wrapper.0 + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub enum String64OrArrString64 { + String64(String64), + ArrString64(Vec), +} + +impl String64OrArrString64 { + pub fn new_string64(string64: String64) -> Self { + Self::String64(string64) + } + + pub fn new_arr_string64(arr_string64: Vec) -> Self { + Self::ArrString64(arr_string64) + } +} diff --git a/cip25/src/serialization.rs b/cip25/src/serialization.rs new file mode 100644 index 00000000..1571bc1b --- /dev/null +++ b/cip25/src/serialization.rs @@ -0,0 +1,528 @@ +use super::*; +use std::io::{Seek, SeekFrom}; + +impl cbor_event::se::Serialize for FilesDetails { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(3))?; + serializer.write_text(&"src")?; + self.src.serialize(serializer)?; + serializer.write_text(&"name")?; + self.name.serialize(serializer)?; + serializer.write_text(&"mediaType")?; + self.media_type.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for FilesDetails { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(match len { + cbor_event::Len::Len(n) => cbor_event::LenSz::Len(n, cbor_event::Sz::canonical(n)), + cbor_event::Len::Indefinite => cbor_event::LenSz::Indefinite, + }); + read_len.read_elems(3)?; + let mut src = None; + let mut name = None; + let mut media_type = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), + }, + CBORType::Text => match raw.text()?.as_str() { + "src" => { + if src.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("src".into())).into()); + } + src = Some((|| -> Result<_, DeserializeError> { + Ok(String64OrArrString64::deserialize(raw)?) + })().map_err(|e| e.annotate("src"))?); + }, + "name" => { + if name.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("name".into())).into()); + } + name = Some((|| -> Result<_, DeserializeError> { + Ok(String64::deserialize(raw)?) + })().map_err(|e| e.annotate("name"))?); + }, + "mediaType" => { + if media_type.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("mediaType".into())).into()); + } + media_type = Some((|| -> Result<_, DeserializeError> { + Ok(String64::deserialize(raw)?) + })().map_err(|e| e.annotate("media_type"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + let name = match name { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("name"))).into()), + }; + let media_type = match media_type { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("mediaType"))).into()), + }; + let src = match src { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("src"))).into()), + }; + (); + Ok(Self { + name, + media_type, + src, + }) + })().map_err(|e| e.annotate("FilesDetails")) + } +} + +impl cbor_event::se::Serialize for LabelMetadata { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + LabelMetadata::LabelMetadataV1(label_metadata_v1) => { + serializer.write_map(cbor_event::Len::Len(label_metadata_v1.len() as u64))?; + for (key, value) in label_metadata_v1.iter() { + key.serialize(serializer)?; + serializer.write_map(cbor_event::Len::Len(value.len() as u64))?; + for (key, value) in value.iter() { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + } + Ok(serializer) + }, + LabelMetadata::LabelMetadataV2(label_metadata_v2) => { + label_metadata_v2.serialize(serializer) + }, + } + } +} + +impl Deserialize for LabelMetadata { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let mut label_metadata_v1_table = BTreeMap::new(); + let label_metadata_v1_len = raw.map()?; + while match label_metadata_v1_len { cbor_event::Len::Len(n) => label_metadata_v1_table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let label_metadata_v1_key = String64::deserialize(raw)?; + let mut label_metadata_v1_value_table = BTreeMap::new(); + let label_metadata_v1_value_len = raw.map()?; + while match label_metadata_v1_value_len { cbor_event::Len::Len(n) => label_metadata_v1_value_table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let label_metadata_v1_value_key = String64::deserialize(raw)?; + let label_metadata_v1_value_value = MetadataDetails::deserialize(raw)?; + if label_metadata_v1_value_table.insert(label_metadata_v1_value_key.clone(), label_metadata_v1_value_value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + let label_metadata_v1_value = label_metadata_v1_value_table; + if label_metadata_v1_table.insert(label_metadata_v1_key.clone(), label_metadata_v1_value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(label_metadata_v1_table) + })(raw) + { + Ok(label_metadata_v1) => return Ok(Self::LabelMetadataV1(label_metadata_v1)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(LabelMetadataV2::deserialize(raw)?) + })(raw) + { + Ok(label_metadata_v2) => return Ok(Self::LabelMetadataV2(label_metadata_v2)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("LabelMetadata", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("LabelMetadata")) + } +} + +impl cbor_event::se::Serialize for LabelMetadataV2 { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(2))?; + serializer.write_text(&"data")?; + serializer.write_map(cbor_event::Len::Len(self.data.len() as u64))?; + for (key, value) in self.data.iter() { + serializer.write_bytes(&key)?; + serializer.write_map(cbor_event::Len::Len(value.len() as u64))?; + for (key, value) in value.iter() { + serializer.write_bytes(&key)?; + value.serialize(serializer)?; + } + } + serializer.write_text(&"version")?; + serializer.write_unsigned_integer(2u64)?; + Ok(serializer) + } +} + +impl Deserialize for LabelMetadataV2 { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(match len { + cbor_event::Len::Len(n) => cbor_event::LenSz::Len(n, cbor_event::Sz::canonical(n)), + cbor_event::Len::Indefinite => cbor_event::LenSz::Indefinite, + }); + read_len.read_elems(2)?; + let mut data = None; + let mut version_present = false; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), + }, + CBORType::Text => match raw.text()?.as_str() { + "data" => { + if data.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("data".into())).into()); + } + data = Some((|| -> Result<_, DeserializeError> { + let mut data_table = BTreeMap::new(); + let data_len = raw.map()?; + while match data_len { cbor_event::Len::Len(n) => data_table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let data_key = raw.bytes()? as Vec; + let mut data_value_table = BTreeMap::new(); + let data_value_len = raw.map()?; + while match data_value_len { cbor_event::Len::Len(n) => data_value_table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let data_value_key = raw.bytes()? as Vec; + let data_value_value = MetadataDetails::deserialize(raw)?; + if data_value_table.insert(data_value_key.clone(), data_value_value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + let data_value = data_value_table; + if data_table.insert(data_key.clone(), data_value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(data_table) + })().map_err(|e| e.annotate("data"))?); + }, + "version" => { + if version_present { + return Err(DeserializeFailure::DuplicateKey(Key::Str("version".into())).into()); + } + version_present = (|| -> Result<_, DeserializeError> { + let version_value = raw.unsigned_integer()?; + if version_value != 2 { + return Err(DeserializeFailure::FixedValueMismatch{ found: Key::Uint(version_value), expected: Key::Uint(2) }.into()); + } + Ok(true) + })().map_err(|e| e.annotate("version"))?; + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + let data = match data { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("data"))).into()), + }; + if !version_present { + return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("version"))).into()); + } + (); + Ok(Self { + data, + }) + })().map_err(|e| e.annotate("LabelMetadataV2")) + } +} + +impl cbor_event::se::Serialize for Metadata { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(1))?; + serializer.write_unsigned_integer(721u64)?; + self.key_721.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Metadata { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(match len { + cbor_event::Len::Len(n) => cbor_event::LenSz::Len(n, cbor_event::Sz::canonical(n)), + cbor_event::Len::Indefinite => cbor_event::LenSz::Indefinite, + }); + read_len.read_elems(1)?; + let mut key_721 = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 721 => { + if key_721.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(721)).into()); + } + key_721 = Some((|| -> Result<_, DeserializeError> { + Ok(LabelMetadata::deserialize(raw)?) + })().map_err(|e| e.annotate("key_721"))?); + }, + _unknown_key => /* we must be permissive as we are looking at a subset of metadata here */(), + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + _other_type => /* we must be permissive as we are looking at a subset of metadata here */(), + } + read += 1; + } + let key_721 = match key_721 { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(721)).into()), + }; + (); + Ok(Self { + key_721, + }) + })().map_err(|e| e.annotate("Metadata")) + } +} + +impl cbor_event::se::Serialize for MetadataDetails { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(2 + match &self.media_type { Some(_) => 1, None => 0 } + match &self.description { Some(_) => 1, None => 0 } + match &self.files { Some(_) => 1, None => 0 }))?; + serializer.write_text(&"name")?; + self.name.serialize(serializer)?; + if let Some(field) = &self.files { + serializer.write_text(&"files")?; + serializer.write_array(cbor_event::Len::Len(field.len() as u64))?; + for element in field.iter() { + element.serialize(serializer)?; + } + } + serializer.write_text(&"image")?; + self.image.serialize(serializer)?; + if let Some(field) = &self.media_type { + serializer.write_text(&"mediaType")?; + field.serialize(serializer)?; + } + if let Some(field) = &self.description { + serializer.write_text(&"description")?; + field.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MetadataDetails { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(match len { + cbor_event::Len::Len(n) => cbor_event::LenSz::Len(n, cbor_event::Sz::canonical(n)), + cbor_event::Len::Indefinite => cbor_event::LenSz::Indefinite, + }); + read_len.read_elems(2)?; + let mut name = None; + let mut files = None; + let mut image = None; + let mut media_type = None; + let mut description = None; + let mut read = 0; + while match len { cbor_event::Len::Len(n) => read < n as usize, cbor_event::Len::Indefinite => true, } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), + }, + CBORType::Text => match raw.text()?.as_str() { + "name" => { + if name.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("name".into())).into()); + } + name = Some((|| -> Result<_, DeserializeError> { + Ok(String64::deserialize(raw)?) + })().map_err(|e| e.annotate("name"))?); + }, + "files" => { + if files.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("files".into())).into()); + } + files = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + let mut files_arr = Vec::new(); + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => files_arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + files_arr.push(FilesDetails::deserialize(raw)?); + } + Ok(files_arr) + })().map_err(|e| e.annotate("files"))?); + }, + "image" => { + if image.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("image".into())).into()); + } + image = Some((|| -> Result<_, DeserializeError> { + Ok(String64OrArrString64::deserialize(raw)?) + })().map_err(|e| e.annotate("image"))?); + }, + "mediaType" => { + if media_type.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("mediaType".into())).into()); + } + media_type = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(String64::deserialize(raw)?) + })().map_err(|e| e.annotate("media_type"))?); + }, + "description" => { + if description.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str("description".into())).into()); + } + description = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(String64OrArrString64::deserialize(raw)?) + })().map_err(|e| e.annotate("description"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => return Err(DeserializeFailure::BreakInDefiniteLen.into()), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + let name = match name { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("name"))).into()), + }; + let image = match image { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Str(String::from("image"))).into()), + }; + read_len.finish()?; + Ok(Self { + name, + image, + media_type, + description, + files, + }) + })().map_err(|e| e.annotate("MetadataDetails")) + } +} + +impl cbor_event::se::Serialize for String64 { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_text(&self.0) + } +} + +impl Deserialize for String64 { + fn deserialize(raw: &mut Deserializer) -> Result { + let inner = raw.text()? as String; + if inner.len() > 64 { + return Err(DeserializeError::new("String64", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(0), max: Some(64) })); + } + Ok(Self(inner)) + } +} + +impl cbor_event::se::Serialize for String64OrArrString64 { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + String64OrArrString64::String64(string64) => { + string64.serialize(serializer) + }, + String64OrArrString64::ArrString64(arr_string64) => { + serializer.write_array(cbor_event::Len::Len(arr_string64.len() as u64))?; + for element in arr_string64.iter() { + element.serialize(serializer)?; + } + Ok(serializer) + }, + } + } +} + +impl Deserialize for String64OrArrString64 { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(String64::deserialize(raw)?) + })(raw) + { + Ok(string64) => return Ok(Self::String64(string64)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + let mut arr_string64_arr = Vec::new(); + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr_string64_arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr_string64_arr.push(String64::deserialize(raw)?); + } + Ok(arr_string64_arr) + })(raw) + { + Ok(arr_string64) => return Ok(Self::ArrString64(arr_string64)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("String64OrArrString64", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("String64OrArrString64")) + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..f2d5db4a --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "cardano-multiplatform-lib-core" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cbor_event = "2.2.0" +linked-hash-map = "0.5.3" +derivative = "2.2.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +schemars = "0.8.8" + +bech32 = "0.7.2" +hex = "0.4.0" +itertools = "0.10.1" +getrandom = { version = "0.2.3", features = ["js"] } +rand = "0.8.5" +fraction = "0.10.0" +base64 = "0.13" +num-bigint = "0.4.0" +num-integer = "0.1.45" +#rand_os = "0.1" +#thiserror = "1.0.37" +cfg-if = "1" + +# These can be removed if we make wasm bindings for ALL functionality here. +# This was not done right now as there is a lot of existing legacy code e.g. +# for Byron that might need to be used from WASM and might not. +# We can remove this dependency when that is decided. +# +# The other use-case here is enums. Without this two enums would need to be defined +# despite wasm_bindgen supporting C-style enums (with non-negative values) 100% +# This could possibly be resolved with macros but maybe not. + +# non-wasm +#[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies] +#rand_os = "0.1" +#noop_proc_macro = "0.3.0" + +# wasm +#[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] +wasm-bindgen = { version = "=0.2.82", features = ["serde-serialize"] } +#rand_os = { version = "0.1", features = ["wasm-bindgen"] } +#js-sys = "=0.3.59" + + +[dev-dependencies] +quickcheck = "0.9.2" +quickcheck_macros = "0.9.1" +rand_chacha = "0.3.1" diff --git a/core/README.md b/core/README.md new file mode 100644 index 00000000..bb0063ce --- /dev/null +++ b/core/README.md @@ -0,0 +1,3 @@ +# Core + +The most core types for cardano-multiplatform-lib. Used for core serialization types, errors, etc that will be used by other consuming crates within cardano-multiplatform-lib. End users should never have to use this crate directly. diff --git a/rust/core/src/error.rs b/core/src/error.rs similarity index 92% rename from rust/core/src/error.rs rename to core/src/error.rs index 92ebff82..d174d432 100644 --- a/rust/core/src/error.rs +++ b/core/src/error.rs @@ -1,7 +1,3 @@ -use std::io::{BufRead, Seek}; -use crate::serialization::CBORReadLen; -use crate::chain_crypto; - #[derive(Debug)] pub enum Key { Str(String), @@ -30,6 +26,8 @@ pub enum DeserializeFailure { found: Key, expected: Key, }, + /// Invalid internal structure imposed on top of the CBOR format + InvalidStructure(Box), MandatoryFieldMissing(Key), NoVariantMatched, RangeCheck{ @@ -37,8 +35,6 @@ pub enum DeserializeFailure { min: Option, max: Option, }, - PublicKeyError(chain_crypto::PublicKeyError), - SignatureError(chain_crypto::SignatureError), TagMismatch{ found: u64, expected: u64, @@ -94,6 +90,7 @@ impl std::fmt::Display for DeserializeError { DeserializeFailure::EndingBreakMissing => write!(f, "Missing ending CBOR Break"), DeserializeFailure::ExpectedNull => write!(f, "Expected null, found other type"), DeserializeFailure::FixedValueMismatch{ found, expected } => write!(f, "Expected fixed value {} found {}", expected, found), + DeserializeFailure::InvalidStructure(e) => write!(f, "Invalid internal structure: {}", e), DeserializeFailure::MandatoryFieldMissing(key) => write!(f, "Mandatory field {} not found", key), DeserializeFailure::NoVariantMatched => write!(f, "No variant matched"), DeserializeFailure::RangeCheck{ found, min, max } => match (min, max) { @@ -102,8 +99,6 @@ impl std::fmt::Display for DeserializeError { (None, Some(max)) => write!(f, "{} not at most {}", found, max), (None, None) => write!(f, "invalid range (no min nor max specified)"), }, - DeserializeFailure::PublicKeyError(e) => write!(f, "PublicKeyError error: {}", e), - DeserializeFailure::SignatureError(e) => write!(f, "Signature error: {}", e), DeserializeFailure::TagMismatch{ found, expected } => write!(f, "Expected tag {}, found {}", expected, found), DeserializeFailure::UnknownKey(key) => write!(f, "Found unexpected key {}", key), DeserializeFailure::UnexpectedKeyType(ty) => write!(f, "Found unexpected key of CBOR type {:?}", ty), diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..326605b4 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,7 @@ +pub use error::*; +pub mod error; + +pub mod serialization; + +pub mod ordered_hash_map; + diff --git a/rust/core/src/ordered_hash_map.rs b/core/src/ordered_hash_map.rs similarity index 100% rename from rust/core/src/ordered_hash_map.rs rename to core/src/ordered_hash_map.rs diff --git a/core/src/serialization.rs b/core/src/serialization.rs new file mode 100644 index 00000000..33873480 --- /dev/null +++ b/core/src/serialization.rs @@ -0,0 +1,259 @@ +use crate::error::{DeserializeError, DeserializeFailure}; +use std::io::{Seek, BufRead, Write}; +use cbor_event::{Sz, de::Deserializer, se::Serializer}; + +pub struct CBORReadLen { + deser_len: cbor_event::LenSz, + read: u64, +} + +impl CBORReadLen { + pub fn new(len: cbor_event::LenSz) -> Self { + Self { + deser_len: len, + read: 0, + } + } + + // Marks {n} values as being read, and if we go past the available definite length + // given by the CBOR, we return an error. + pub fn read_elems(&mut self, count: usize) -> Result<(), DeserializeFailure> { + match self.deser_len { + cbor_event::LenSz::Len(n, _) => { + self.read += count as u64; + if self.read > n { + Err(DeserializeFailure::DefiniteLenMismatch(n, None)) + } else { + Ok(()) + } + }, + cbor_event::LenSz::Indefinite => Ok(()), + } + } + + pub fn finish(&self) -> Result<(), DeserializeFailure> { + match self.deser_len { + cbor_event::LenSz::Len(n, _) => { + if self.read == n { + Ok(()) + } else { + Err(DeserializeFailure::DefiniteLenMismatch(n, Some(self.read))) + } + }, + cbor_event::LenSz::Indefinite => Ok(()), + } + } +} + +pub trait DeserializeEmbeddedGroup { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + read_len: &mut CBORReadLen, + len: cbor_event::LenSz, + ) -> Result where Self: Sized; +} + +#[inline] +pub fn sz_max(sz: cbor_event::Sz) -> u64 { + match sz { + Sz::Inline => 23u64, + Sz::One => u8::MAX as u64, + Sz::Two => u16::MAX as u64, + Sz::Four => u32::MAX as u64, + Sz::Eight => u64::MAX, + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum LenEncoding { + Canonical, + Definite(cbor_event::Sz), + Indefinite, +} + +impl Default for LenEncoding { + fn default() -> Self { + Self::Canonical + } +} + +impl From for LenEncoding { + fn from(len_sz: cbor_event::LenSz) -> Self { + match len_sz { + cbor_event::LenSz::Len(len, sz) => if cbor_event::Sz::canonical(len) == sz { + Self::Canonical + } else { + Self::Definite(sz) + }, + cbor_event::LenSz::Indefinite => Self::Indefinite, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum StringEncoding { + Canonical, + Indefinite(Vec<(u64, Sz)>), + Definite(Sz), +} + +impl Default for StringEncoding { + fn default() -> Self { + Self::Canonical + } +} + +impl From for StringEncoding { + fn from(len_sz: cbor_event::StringLenSz) -> Self { + match len_sz { + cbor_event::StringLenSz::Len(sz) => Self::Definite(sz), + cbor_event::StringLenSz::Indefinite(lens) => Self::Indefinite(lens), + } + } +} + +#[inline] +pub fn fit_sz(len: u64, sz: Option, force_canonical: bool) -> Sz { + match sz { + Some(sz) => if !force_canonical && len <= sz_max(sz) { + sz + } else { + Sz::canonical(len) + }, + None => Sz::canonical(len), + } +} + +impl LenEncoding { + pub fn to_len_sz(&self, len: u64, force_canonical: bool) -> cbor_event::LenSz { + if force_canonical { + cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)) + } else { + match self { + Self::Canonical => cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)), + Self::Definite(sz) => if sz_max(*sz) >= len { + cbor_event::LenSz::Len(len, *sz) + } else { + cbor_event::LenSz::Len(len, cbor_event::Sz::canonical(len)) + }, + Self::Indefinite => cbor_event::LenSz::Indefinite, + } + } + } + + pub fn end<'a, W: Write + Sized>(&self, serializer: &'a mut Serializer, force_canonical: bool) -> cbor_event::Result<&'a mut Serializer> { + if !force_canonical && *self == Self::Indefinite { + serializer.write_special(cbor_event::Special::Break)?; + } + Ok(serializer) + } +} + +impl StringEncoding { + pub fn to_str_len_sz(&self, len: u64, force_canonical: bool) -> cbor_event::StringLenSz { + if force_canonical { + cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)) + } else { + match self { + Self::Canonical => cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)), + Self::Definite(sz) => if sz_max(*sz) >= len { + cbor_event::StringLenSz::Len(*sz) + } else { + cbor_event::StringLenSz::Len(cbor_event::Sz::canonical(len)) + }, + Self::Indefinite(lens) => cbor_event::StringLenSz::Indefinite(lens.clone()), + } + } + } +} + +pub trait Serialize { + fn serialize<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + force_canonical: bool, + ) -> cbor_event::Result<&'a mut Serializer>; + + /// Bytes of a structure using the CBOR bytes as per the CDDL spec + /// which for foo = bytes will include the CBOR bytes type/len, etc. + fn to_cbor_bytes(&self, force_canonical: bool) -> Vec { + let mut buf = Serializer::new_vec(); + self.serialize(&mut buf, force_canonical).unwrap(); + buf.finalize() + } + + /// Generic to-bytes when one does not care what kind of bytes + /// e.g. CBOR encoded or the raw inner bytes of a buffer when applicable. + /// This is auto-implemented using to_cbor_bytes but can be overrided + /// for things like signatures, hashes, etc where the CBOR bytes are not commonly used. + fn to_bytes(&self) -> Vec { + self.to_cbor_bytes(false) + } +} + +impl Serialize for T { + fn serialize<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + _force_canonical: bool, + ) -> cbor_event::Result<&'a mut Serializer> { + ::serialize(self, serializer) + } +} + +pub trait SerializeEmbeddedGroup { + fn serialize_as_embedded_group<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + force_canonical: bool, + ) -> cbor_event::Result<&'a mut Serializer>; +} + +pub trait Deserialize { + fn deserialize( + raw: &mut Deserializer, + ) -> Result where Self: Sized; + + /// from-bytes using the exact CBOR format specified in the CDDL binary spec. + /// For hashes/addresses/etc this will include the CBOR bytes type/len/etc. + fn from_cbor_bytes(data: &[u8]) -> Result where Self: Sized { + let mut raw = Deserializer::from(std::io::Cursor::new(data)); + Self::deserialize(&mut raw) + } + + /// Generic from-bytes when one does not care what kind of bytes + /// e.g. CBOR encoded or the raw inner bytes of a buffer when applicable. + /// This is auto-implemented using from_cbor_bytes but can be overrided + /// for things like signatures, hashes, etc where the CBOR bytes are not commonly used. + fn from_bytes(data: &[u8]) -> Result where Self: Sized { + Self::from_cbor_bytes(data) + } +} + +impl Deserialize for T { + fn deserialize(raw: &mut Deserializer) -> Result { + T::deserialize(raw).map_err(|e| DeserializeError::from(e)) + } +} + +// TODO: remove ToBytes / FromBytes after we regenerate the WASM wrappers. +// This is just so the existing generated to/from bytes code works +pub trait ToBytes { + fn to_bytes(&self) -> Vec; +} + +impl ToBytes for T { + fn to_bytes(&self) -> Vec { + Serialize::to_bytes(self) + } +} + +pub trait FromBytes { + fn from_bytes(data: Vec) -> Result where Self: Sized; +} + +impl FromBytes for T { + fn from_bytes(data: Vec) -> Result where Self: Sized { + Deserialize::from_bytes(data.as_ref()) + } +} \ No newline at end of file diff --git a/crypto-wasm/Cargo.toml b/crypto-wasm/Cargo.toml new file mode 100644 index 00000000..b46d164f --- /dev/null +++ b/crypto-wasm/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cardano-multiplatform-lib-crypto-wasm" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +#core = { path = "../chain", package = "cardano-multiplatform-lib-chain" } +core_crypto = { path = "../crypto", package = "cardano-multiplatform-lib-crypto" } +cbor_event = "2.2.0" +wasm-bindgen = { version = "0.2", features=["serde-serialize"] } +linked-hash-map = "0.5.3" +serde_json = "1.0.57" diff --git a/crypto-wasm/README.md b/crypto-wasm/README.md new file mode 100644 index 00000000..93181372 --- /dev/null +++ b/crypto-wasm/README.md @@ -0,0 +1,3 @@ +# crypto-wasm + +WASM bindings covering the `crypto-wasm` crate. On-chain crypto primitives are still in the `wasm` crate. diff --git a/crypto-wasm/src/lib.rs b/crypto-wasm/src/lib.rs new file mode 100644 index 00000000..33f260a4 --- /dev/null +++ b/crypto-wasm/src/lib.rs @@ -0,0 +1,403 @@ +use wasm_bindgen::prelude::{wasm_bindgen, JsError}; + +use core_crypto::RawBytesEncoding; + +#[wasm_bindgen] +pub struct Bip32PrivateKey(core_crypto::Bip32PrivateKey); + +#[wasm_bindgen] +impl Bip32PrivateKey { + /// derive this private key with the given index. + /// + /// # Security considerations + /// + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive(&self, index: u32) -> Self { + Self(self.0.derive(index)) + } + + /// 128-byte xprv a key format in Cardano that some software still uses or requires + /// the traditional 96-byte xprv is simply encoded as + /// prv | chaincode + /// however, because some software may not know how to compute a public key from a private key, + /// the 128-byte inlines the public key in the following format + /// prv | pub | chaincode + /// so be careful if you see the term "xprv" as it could refer to either one + /// our library does not require the pub (instead we compute the pub key when needed) + pub fn from_128_xprv(bytes: &[u8]) -> Result { + core_crypto::Bip32PrivateKey::from_128_xprv(bytes).map(Self).map_err(Into::into) + } + /// see from_128_xprv + pub fn to_128_xprv(&self) -> Vec { + self.0.to_128_xprv() + } + + pub fn generate_ed25519_bip32() -> Bip32PrivateKey { + Self(core_crypto::Bip32PrivateKey::generate_ed25519_bip32()) + } + + pub fn to_raw_key(&self) -> PrivateKey { + self.0.to_raw_key().into() + } + + pub fn to_public(&self) -> Bip32PublicKey { + Bip32PublicKey(self.0.to_public()) + } + + pub fn from_raw_bytes(bytes: &[u8]) -> Result { + core_crypto::Bip32PrivateKey::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn from_bech32(bech32_str: &str) -> Result { + core_crypto::Bip32PrivateKey::from_bech32(bech32_str).map(Self).map_err(Into::into) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32() + } + + pub fn from_bip39_entropy(entropy: &[u8], password: &[u8]) -> Self { + Self(core_crypto::Bip32PrivateKey::from_bip39_entropy(entropy, password)) + } + + pub fn chaincode(&self) -> Vec { + self.0.chaincode() + } +} + +impl From for Bip32PrivateKey { + fn from(inner: core_crypto::Bip32PrivateKey) -> Self { + Self(inner) + } +} + +impl From for core_crypto::Bip32PrivateKey { + fn from(wrapper: Bip32PrivateKey) -> Self { + wrapper.0 + } +} + + +#[wasm_bindgen] +pub struct Bip32PublicKey(core_crypto::Bip32PublicKey); + +#[wasm_bindgen] +impl Bip32PublicKey { + /// derive this public key with the given index. + /// + /// # Errors + /// + /// If the index is not a soft derivation index (< 0x80000000) then + /// calling this method will fail. + /// + /// # Security considerations + /// + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive(&self, index: u32) -> Result { + self.0.derive(index).map(Self).map_err(Into::into) + } + + pub fn to_raw_key(&self) -> PublicKey { + PublicKey(self.0.to_raw_key()) + } + + pub fn from_raw_bytes(bytes: &[u8]) -> Result { + core_crypto::Bip32PublicKey::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn from_bech32(bech32_str: &str) -> Result { + core_crypto::Bip32PublicKey::from_bech32(bech32_str).map(Self).map_err(Into::into) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32() + } + + pub fn chaincode(&self) -> Vec { + self.0.chaincode() + } +} + +impl From for Bip32PublicKey { + fn from(inner: core_crypto::Bip32PublicKey) -> Self { + Self(inner) + } +} + +impl From for core_crypto::Bip32PublicKey { + fn from(wrapper: Bip32PublicKey) -> Self { + wrapper.0 + } +} + +#[wasm_bindgen] +pub struct PrivateKey(core_crypto::PrivateKey); + +#[wasm_bindgen] +impl PrivateKey { + pub fn to_public(&self) -> PublicKey { + PublicKey(self.0.to_public()) + } + + pub fn generate_ed25519() -> Self { + Self(core_crypto::PrivateKey::generate_ed25519()) + } + + pub fn generate_ed25519extended() -> Self { + Self(core_crypto::PrivateKey::generate_ed25519extended()) + } + + /// Get private key from its bech32 representation + /// ```javascript + /// PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); + /// ``` + /// For an extended 25519 key + /// ```javascript + /// PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); + /// ``` + pub fn from_bech32(bech32_str: &str) -> Result { + core_crypto::PrivateKey::from_bech32(bech32_str).map(Self).map_err(Into::into) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32() + } + + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn from_extended_bytes(bytes: &[u8]) -> Result { + core_crypto::PrivateKey::from_extended_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn from_normal_bytes(bytes: &[u8]) -> Result { + core_crypto::PrivateKey::from_normal_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn sign(&self, message: &[u8]) -> Ed25519Signature { + Ed25519Signature(self.0.sign(message).into()) + } +} + +impl From for PrivateKey { + fn from(inner: core_crypto::PrivateKey) -> Self { + Self(inner) + } +} + +impl From for core_crypto::PrivateKey { + fn from(wrapper: PrivateKey) -> Self { + wrapper.0 + } +} + + +/// ED25519 key used as public key +#[wasm_bindgen] +pub struct PublicKey(core_crypto::PublicKey); + +#[wasm_bindgen] +impl PublicKey { + /// Get public key from its bech32 representation + /// Example: + /// ```javascript + /// const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); + /// ``` + pub fn from_bech32(bech32_str: &str) -> Result { + core_crypto::PublicKey::from_bech32(bech32_str).map(Self).map_err(Into::into) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32() + } + + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + core_crypto::PublicKey::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool { + self.0.verify(data, &signature.0) + } + + pub fn hash(&self) -> Ed25519KeyHash { + Ed25519KeyHash(self.0.hash().into()) + } +} + +impl From for PublicKey { + fn from(inner: core_crypto::PublicKey) -> Self { + Self(inner) + } +} + +impl From for core_crypto::PublicKey { + fn from(wrapper: PublicKey) -> Self { + wrapper.0 + } +} + +macro_rules! impl_signature { + ($name:ident) => { + #[wasm_bindgen] + #[derive(Debug, Clone)] + pub struct $name(core_crypto::$name); + + #[wasm_bindgen] + impl $name { + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32() + } + + pub fn to_hex(&self) -> String { + self.0.to_raw_hex() + } + + pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { + core_crypto::$name::from_bech32(bech32_str) + .map(Into::into) + .map(Self) + .map_err(Into::into) + } + + pub fn from_hex(input: &str) -> Result<$name, JsError> { + core_crypto::$name::from_raw_hex(input) + .map(Into::into) + .map(Self) + .map_err(Into::into) + } + + pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { + core_crypto::$name::from_raw_bytes(bytes) + .map(Self) + .map_err(Into::into) + } + } + + + impl From for $name { + fn from(inner: core_crypto::$name) -> Self { + Self(inner) + } + } + + impl From<$name> for core_crypto::$name { + fn from(wrapper: $name) -> core_crypto::$name { + wrapper.0 + } + } + }; +} + +impl_signature!(Ed25519Signature); + +macro_rules! impl_hash_type { + ($name:ident) => { + #[wasm_bindgen] + #[derive(Debug, Clone)] + pub struct $name(core_crypto::$name); + + #[wasm_bindgen] + impl $name { + pub fn to_raw_bytes(&self) -> Vec { + self.0.to_raw_bytes().to_vec() + } + + pub fn to_bech32(&self, prefix: &str) -> Result { + self.0.to_bech32(prefix).map_err(Into::into) + } + + pub fn to_hex(&self) -> String { + self.0.to_raw_hex() + } + + pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { + core_crypto::$name::from_bech32(bech32_str) + .map(Into::into) + .map(Self) + .map_err(Into::into) + } + + pub fn from_hex(input: &str) -> Result<$name, JsError> { + core_crypto::$name::from_raw_hex(input).map(Self).map_err(Into::into) + } + + pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { + core_crypto::$name::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + } + + impl From for $name { + fn from(inner: core_crypto::$name) -> Self { + Self(inner) + } + } + + impl From<$name> for core_crypto::$name { + fn from(wrapper: $name) -> core_crypto::$name { + wrapper.0 + } + } + }; +} + +impl_hash_type!(Ed25519KeyHash); +impl_hash_type!(ScriptHash); +// TransactionHash is either a hash of the tx CBOR or a hash of a redeem address (genesis) +impl_hash_type!(TransactionHash); +impl_hash_type!(GenesisDelegateHash); +impl_hash_type!(GenesisHash); +impl_hash_type!(AuxiliaryDataHash); +impl_hash_type!(PoolMetadataHash); +impl_hash_type!(VRFKeyHash); +impl_hash_type!(BlockBodyHash); +impl_hash_type!(BlockHeaderHash); +impl_hash_type!(DataHash); +impl_hash_type!(ScriptDataHash); +// We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) +impl_hash_type!(VRFVKey); +impl_hash_type!(KESVKey); \ No newline at end of file diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml new file mode 100644 index 00000000..8deb9b51 --- /dev/null +++ b/crypto/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "cardano-multiplatform-lib-crypto" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +cardano-multiplatform-lib-core = { "path" = "../core" } +cbor_event = "2.2.0" +cryptoxide = "0.4.2" +ed25519-bip32 = "0.4.1" +sha2 = "^0.9" +digest = "^0.9" +bech32 = "0.7.2" +hex = "0.4.0" +thiserror = "1.0.37" +rand = "0.8.5" +cfg-if = "1" + +derivative = "2.2.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.57" +schemars = "0.8.8" diff --git a/crypto/README.md b/crypto/README.md new file mode 100644 index 00000000..99ecccb1 --- /dev/null +++ b/crypto/README.md @@ -0,0 +1,3 @@ +# Crypto + +Cryptography-related components for cardano-serialization-lib. diff --git a/rust/core/src/chain_core/abor.rs b/crypto/src/chain_core/abor.rs similarity index 100% rename from rust/core/src/chain_core/abor.rs rename to crypto/src/chain_core/abor.rs diff --git a/rust/core/src/chain_core/mempack.rs b/crypto/src/chain_core/mempack.rs similarity index 100% rename from rust/core/src/chain_core/mempack.rs rename to crypto/src/chain_core/mempack.rs diff --git a/rust/core/src/chain_core/mod.rs b/crypto/src/chain_core/mod.rs similarity index 100% rename from rust/core/src/chain_core/mod.rs rename to crypto/src/chain_core/mod.rs diff --git a/rust/core/src/chain_core/packer.rs b/crypto/src/chain_core/packer.rs similarity index 100% rename from rust/core/src/chain_core/packer.rs rename to crypto/src/chain_core/packer.rs diff --git a/rust/core/src/chain_core/property.rs b/crypto/src/chain_core/property.rs similarity index 100% rename from rust/core/src/chain_core/property.rs rename to crypto/src/chain_core/property.rs diff --git a/rust/core/src/chain_crypto/algorithms/ed25519.rs b/crypto/src/chain_crypto/algorithms/ed25519.rs similarity index 100% rename from rust/core/src/chain_crypto/algorithms/ed25519.rs rename to crypto/src/chain_crypto/algorithms/ed25519.rs diff --git a/rust/core/src/chain_crypto/algorithms/ed25519_derive.rs b/crypto/src/chain_crypto/algorithms/ed25519_derive.rs similarity index 100% rename from rust/core/src/chain_crypto/algorithms/ed25519_derive.rs rename to crypto/src/chain_crypto/algorithms/ed25519_derive.rs diff --git a/rust/core/src/chain_crypto/algorithms/ed25519_extended.rs b/crypto/src/chain_crypto/algorithms/ed25519_extended.rs similarity index 100% rename from rust/core/src/chain_crypto/algorithms/ed25519_extended.rs rename to crypto/src/chain_crypto/algorithms/ed25519_extended.rs diff --git a/rust/core/src/chain_crypto/algorithms/legacy_daedalus.rs b/crypto/src/chain_crypto/algorithms/legacy_daedalus.rs similarity index 100% rename from rust/core/src/chain_crypto/algorithms/legacy_daedalus.rs rename to crypto/src/chain_crypto/algorithms/legacy_daedalus.rs diff --git a/rust/core/src/chain_crypto/algorithms/mod.rs b/crypto/src/chain_crypto/algorithms/mod.rs similarity index 100% rename from rust/core/src/chain_crypto/algorithms/mod.rs rename to crypto/src/chain_crypto/algorithms/mod.rs diff --git a/rust/core/src/chain_crypto/bech32.rs b/crypto/src/chain_crypto/bech32.rs similarity index 100% rename from rust/core/src/chain_crypto/bech32.rs rename to crypto/src/chain_crypto/bech32.rs diff --git a/rust/core/src/chain_crypto/derive.rs b/crypto/src/chain_crypto/derive.rs similarity index 100% rename from rust/core/src/chain_crypto/derive.rs rename to crypto/src/chain_crypto/derive.rs diff --git a/rust/core/src/chain_crypto/digest.rs b/crypto/src/chain_crypto/digest.rs similarity index 100% rename from rust/core/src/chain_crypto/digest.rs rename to crypto/src/chain_crypto/digest.rs diff --git a/rust/core/src/chain_crypto/hash.rs b/crypto/src/chain_crypto/hash.rs similarity index 100% rename from rust/core/src/chain_crypto/hash.rs rename to crypto/src/chain_crypto/hash.rs diff --git a/rust/core/src/chain_crypto/key.rs b/crypto/src/chain_crypto/key.rs similarity index 100% rename from rust/core/src/chain_crypto/key.rs rename to crypto/src/chain_crypto/key.rs diff --git a/rust/core/src/chain_crypto/mod.rs b/crypto/src/chain_crypto/mod.rs similarity index 100% rename from rust/core/src/chain_crypto/mod.rs rename to crypto/src/chain_crypto/mod.rs diff --git a/rust/core/src/chain_crypto/securemem.rs b/crypto/src/chain_crypto/securemem.rs similarity index 100% rename from rust/core/src/chain_crypto/securemem.rs rename to crypto/src/chain_crypto/securemem.rs diff --git a/rust/core/src/chain_crypto/sign.rs b/crypto/src/chain_crypto/sign.rs similarity index 100% rename from rust/core/src/chain_crypto/sign.rs rename to crypto/src/chain_crypto/sign.rs diff --git a/rust/core/src/chain_crypto/testing.rs b/crypto/src/chain_crypto/testing.rs similarity index 100% rename from rust/core/src/chain_crypto/testing.rs rename to crypto/src/chain_crypto/testing.rs diff --git a/rust/core/src/impl_mockchain/key.rs b/crypto/src/impl_mockchain/key.rs similarity index 100% rename from rust/core/src/impl_mockchain/key.rs rename to crypto/src/impl_mockchain/key.rs diff --git a/rust/core/src/impl_mockchain/mod.rs b/crypto/src/impl_mockchain/mod.rs similarity index 100% rename from rust/core/src/impl_mockchain/mod.rs rename to crypto/src/impl_mockchain/mod.rs diff --git a/rust/core/src/crypto.rs b/crypto/src/lib.rs similarity index 51% rename from rust/core/src/crypto.rs rename to crypto/src/lib.rs index e3be7352..7e2d491f 100644 --- a/rust/core/src/crypto.rs +++ b/crypto/src/lib.rs @@ -1,14 +1,43 @@ //use crate::byron::{AddrAttributes, AddressContent}; use crate::chain_crypto::{ bech32::Bech32, - derive::combine_pk_and_chaincode +// derive::combine_pk_and_chaincode +}; +pub use cardano_multiplatform_lib_core::{ + error::{DeserializeError, DeserializeFailure}, + serialization::{Serialize, Deserialize, StringEncoding} }; -pub use crate::serialization::{Deserialize, StringEncoding}; pub use derivative::{Derivative}; use cryptoxide::blake2b::Blake2b; use impl_mockchain::key; use rand::rngs::OsRng; -use super::*; +use std::convert::{From}; +use cbor_event::{self}; + +// brought over from old IOHK code +mod chain_core; +mod chain_crypto; +mod impl_mockchain; +mod typed_bytes; + +// used in chain_core / chain_crypto +#[macro_use] +extern crate cfg_if; + +pub trait RawBytesEncoding { + fn to_raw_bytes(&self) -> &[u8]; + + fn from_raw_bytes(bytes: &[u8]) -> Result where Self: Sized; + + fn to_raw_hex(&self) -> String { + hex::encode(self.to_raw_bytes()) + } + + fn from_raw_hex(hex_str: &str) -> Result where Self: Sized { + let bytes = hex::decode(hex_str)?; + Self::from_raw_bytes(bytes.as_ref()) + } +} #[derive(Debug, thiserror::Error)] pub enum CryptoError { @@ -17,7 +46,7 @@ pub enum CryptoError { #[error("ByronError: {0}")] Hex(#[from] hex::FromHexError), #[error("Deserialization: {0}")] - Deserialization(#[from] error::DeserializeError), + Deserialization(#[from] DeserializeError), #[error("SecretKeyError: {0}")] SecretKey(#[from] chain_crypto::SecretKeyError), #[error("PublicKeyError: {0}")] @@ -26,6 +55,8 @@ pub enum CryptoError { SignatureFromStr(#[from] chain_crypto::SignatureFromStrError), #[error("BootStrapCombine: {0}")] BootstrapCombine(#[from] ed25519_bip32::PublicKeyError), + #[error("SignatureError: {0}")] + SignatureError(#[from] chain_crypto::SignatureError), } // otherwise with 2 Froms (bech32::Error -> chain_crypto::bech32::Error -> CryptoError) @@ -36,228 +67,13 @@ impl From for CryptoError { } } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct KesSignature { - pub inner: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl KesSignature { - pub fn get(&self) -> &Vec { - &self.inner - } - - pub fn new(inner: Vec) -> Result { - if inner.len() != 32 { - return Err(DeserializeError::new("KesSignature", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(32), max: Some(32) })); - } - Ok(Self { - inner, - encodings: None, - }) - } -} - -impl TryFrom> for KesSignature { - type Error = DeserializeError; - - fn try_from(inner: Vec) -> Result { - KesSignature::new(inner) - } -} - -impl From for Vec { - fn from(wrapper: KesSignature) -> Self { - wrapper.inner - } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct KesVkey { - pub inner: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl KesVkey { - pub fn get(&self) -> &Vec { - &self.inner - } - - pub fn new(inner: Vec) -> Result { - if inner.len() != 8 { - return Err(DeserializeError::new("KesVkey", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(8), max: Some(8) })); - } - Ok(Self { - inner, - encodings: None, - }) - } -} - -impl TryFrom> for KesVkey { - type Error = DeserializeError; - - fn try_from(inner: Vec) -> Result { - KesVkey::new(inner) - } -} - -impl From for Vec { - fn from(wrapper: KesVkey) -> Self { - wrapper.inner - } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub enum Nonce { - I0 { - #[serde(skip)] - i0_encoding: Option, - #[serde(skip)] - outer_len_encoding: LenEncoding, - } - , - Nonce1(Nonce1), -} - -impl Nonce { - pub fn new_i0() -> Self { - Self::I0 { - i0_encoding: None, - outer_len_encoding: LenEncoding::default(), - } - } - - pub fn new_nonce1(bytes: Vec) -> Self { - Self::Nonce1(Nonce1::new(bytes)) - } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct SignkeyKES { - pub inner: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl SignkeyKES { - pub fn get(&self) -> &Vec { - &self.inner - } - - pub fn new(inner: Vec) -> Result { - if inner.len() != 16 { - return Err(DeserializeError::new("SignkeyKES", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(16), max: Some(16) })); - } - Ok(Self { - inner, - encodings: None, - }) - } -} - -impl TryFrom> for SignkeyKES { - type Error = DeserializeError; - - fn try_from(inner: Vec) -> Result { - SignkeyKES::new(inner) - } -} - -impl From for Vec { - fn from(wrapper: SignkeyKES) -> Self { - wrapper.inner - } -} - -/// On-chain verification key (pubkey) -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct Vkey { - pub pubkey: PublicKey, - #[serde(skip)] - pub encodings: Option, -} - -impl Vkey { - pub fn new(pubkey: PublicKey) -> Self { - Self { - pubkey, - encodings: None, - } - } -} - -impl From for Vkey { - fn from(pubkey: PublicKey) -> Self { - Self::new(pubkey) - } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct VrfCert { - pub index_0: Vec, - pub bytes: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl VrfCert { - pub fn new(index_0: Vec, bytes: Vec) -> Self { - Self { - index_0, - bytes, - encodings: None, - } - } -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct VrfVkey { - pub inner: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl VrfVkey { - pub fn get(&self) -> &Vec { - &self.inner - } - - pub fn new(inner: Vec) -> Result { - if inner.len() != 8 { - return Err(DeserializeError::new("VrfVkey", DeserializeFailure::RangeCheck{ found: inner.len(), min: Some(8), max: Some(8) })); - } - Ok(Self { - inner, - encodings: None, - }) - } -} - -impl TryFrom> for VrfVkey { - type Error = DeserializeError; - - fn try_from(inner: Vec) -> Result { - VrfVkey::new(inner) - } -} - -impl From for Vec { - fn from(wrapper: VrfVkey) -> Self { - wrapper.inner - } -} - - -pub (crate) fn blake2b224(data: &[u8]) -> [u8; 28] { +pub fn blake2b224(data: &[u8]) -> [u8; 28] { let mut out = [0; 28]; Blake2b::blake2b(&mut out, data, &[]); out } -pub (crate) fn blake2b256(data: &[u8]) -> [u8; 32] { +pub fn blake2b256(data: &[u8]) -> [u8; 32] { let mut out = [0; 32]; Blake2b::blake2b(&mut out, data, &[]); out @@ -305,12 +121,14 @@ impl Bip32PrivateKey { buf[0..64].clone_from_slice(&bytes[0..64]); buf[64..96].clone_from_slice(&bytes[96..128]); - Bip32PrivateKey::from_bytes(&buf) + Bip32PrivateKey::from_raw_bytes(&buf) } /// see from_128_xprv pub fn to_128_xprv(&self) -> Vec { - let prv_key = self.to_raw_key().as_bytes(); - let pub_key = self.to_public().to_raw_key().as_bytes(); + let raw_key = self.to_raw_key(); + let prv_key = raw_key.to_raw_bytes(); + let raw_pub_key = self.to_public().to_raw_key(); + let pub_key = raw_pub_key.to_raw_bytes(); let cc = self.chaincode(); let mut buf = [0; 128]; @@ -334,16 +152,6 @@ impl Bip32PrivateKey { Bip32PublicKey(self.0.to_public().into()) } - pub fn from_bytes(bytes: &[u8]) -> Result { - chain_crypto::SecretKey::::from_binary(bytes) - .map_err(Into::into) - .map(Bip32PrivateKey) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - pub fn from_bech32(bech32_str: &str) -> Result { chain_crypto::SecretKey::try_from_bech32_str(&bech32_str) .map(Bip32PrivateKey) @@ -365,7 +173,19 @@ impl Bip32PrivateKey { } } -#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, JsonSchema)] +impl RawBytesEncoding for Bip32PrivateKey { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + chain_crypto::SecretKey::::from_binary(bytes) + .map_err(Into::into) + .map(Bip32PrivateKey) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema)] pub struct Bip32PublicKey(pub(crate) chain_crypto::PublicKey); impl Bip32PublicKey { @@ -402,16 +222,6 @@ impl Bip32PublicKey { PublicKey(chain_crypto::derive::to_raw_pk(&self.0)) } - pub fn from_bytes(bytes: &[u8]) -> Result { - chain_crypto::PublicKey::::from_binary(bytes) - .map_err(Into::into) - .map(Bip32PublicKey) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - pub fn from_bech32(bech32_str: &str) -> Result { chain_crypto::PublicKey::try_from_bech32_str(&bech32_str) .map(Bip32PublicKey) @@ -429,6 +239,18 @@ impl Bip32PublicKey { } } +impl RawBytesEncoding for Bip32PublicKey { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + chain_crypto::PublicKey::::from_binary(bytes) + .map_err(Into::into) + .map(Bip32PublicKey) + } +} + pub struct PrivateKey(key::EitherEd25519SecretKey); impl From for PrivateKey { @@ -478,13 +300,6 @@ impl PrivateKey { } } - pub fn as_bytes(&self) -> Vec { - match self.0 { - key::EitherEd25519SecretKey::Normal(ref secret) => secret.as_ref().to_vec(), - key::EitherEd25519SecretKey::Extended(ref secret) => secret.as_ref().to_vec(), - } - } - pub fn from_extended_bytes(bytes: &[u8]) -> Result { chain_crypto::SecretKey::from_binary(bytes) .map(key::EitherEd25519SecretKey::Extended) @@ -504,8 +319,22 @@ impl PrivateKey { } } +impl RawBytesEncoding for PrivateKey { + fn to_raw_bytes(&self) -> &[u8] { + match self.0 { + key::EitherEd25519SecretKey::Normal(ref secret) => secret.as_ref(), + key::EitherEd25519SecretKey::Extended(ref secret) => secret.as_ref(), + } + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + Self::from_normal_bytes(bytes) + .or_else(|_| Self::from_extended_bytes(bytes)) + } +} + /// ED25519 key used as public key -#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, schemars::JsonSchema)] pub struct PublicKey(pub(crate) chain_crypto::PublicKey); impl From> for PublicKey { @@ -530,141 +359,68 @@ impl PublicKey { self.0.to_bech32_str() } - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - chain_crypto::PublicKey::from_binary(bytes) - .map_err(Into::into) - .map(PublicKey) - } - pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool { - signature.sig.verify_slice(&self.0, data) == chain_crypto::Verification::Success + signature.0.verify_slice(&self.0, data) == chain_crypto::Verification::Success } pub fn hash(&self) -> Ed25519KeyHash { - Ed25519KeyHash::from(blake2b224(self.as_bytes().as_ref())) + Ed25519KeyHash::from(blake2b224(self.to_raw_bytes().as_ref())) } } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct BootstrapWitness { - pub vkey: Vkey, - pub signature: Ed25519Signature, - pub chain_code: Vec, - // TODO: this should be replaced by AddrAttributes when Byron is brought over - pub attributes: Vec, - #[serde(skip)] - pub encodings: Option, -} - -impl BootstrapWitness { - pub fn new(vkey: Vkey, signature: Ed25519Signature, chain_code: Vec, attributes: Vec) -> Self { - Self { - vkey, - signature, - chain_code, - attributes, - encodings: None, - } +impl RawBytesEncoding for PublicKey { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() } - pub fn to_public_key(&self) -> Result { - chain_crypto::PublicKey::::try_from(self.clone()) - .map(Bip32PublicKey) + fn from_raw_bytes(bytes: &[u8]) -> Result { + chain_crypto::PublicKey::from_binary(bytes) .map_err(Into::into) - } - - // pub fn to_address(&self) -> Result { - // AddressContent::try_from(self.clone()) - // .map_err(Into::into) - // } -} - -impl TryFrom for chain_crypto::PublicKey { - type Error = ed25519_bip32::PublicKeyError; - - fn try_from(wit: BootstrapWitness) -> Result { - combine_pk_and_chaincode(wit.vkey.pubkey.0, &wit.chain_code) + .map(PublicKey) } } -// impl TryFrom for AddressContent { -// type Error = ed25519_bip32::PublicKeyError; -// fn try_from(wit: BootstrapWitness) -> Result { -// let protocol_magic = wit.attributes.protocol_magic; -// let key = chain_crypto::PublicKey::::try_from(wit)?; -// let address_content = AddressContent::new_simple(&Bip32PublicKey(key), protocol_magic); -// Ok(address_content) -// } -// } macro_rules! impl_signature { ($name:ident, $signee_type:ty, $verifier_type:ty) => { #[derive(Debug, Clone)] - pub struct $name{ - sig: chain_crypto::Signature<$signee_type, $verifier_type>, - encoding: StringEncoding, - } + pub struct $name(chain_crypto::Signature<$signee_type, $verifier_type>); impl $name { - pub fn to_raw_bytes(&self) -> Vec { - self.sig.as_ref().to_vec() - } - pub fn to_bech32(&self) -> String { use crate::chain_crypto::bech32::Bech32; - self.sig.to_bech32_str() + self.0.to_bech32_str() } pub fn to_hex(&self) -> String { - hex::encode(&self.sig.as_ref()) + hex::encode(&self.0.as_ref()) } - pub fn from_bech32(bech32_str: &str) -> Result<$name, CryptoError> { + pub fn from_bech32(bech32_str: &str) -> Result { use crate::chain_crypto::bech32::Bech32; - let sig = chain_crypto::Signature::try_from_bech32_str(&bech32_str)?; - Ok($name { - sig, - encoding: StringEncoding::default(), - }) + chain_crypto::Signature::try_from_bech32_str(&bech32_str) + .map(Self) + .map_err(Into::into) } - pub fn from_hex(input: &str) -> Result<$name, CryptoError> { + pub fn from_hex(input: &str) -> Result { use crate::chain_core::property::FromStr; - let sig = chain_crypto::Signature::from_str(input)?; - Ok($name { - sig, - encoding: StringEncoding::default(), - }) - } - - pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, DeserializeError> { - let sig = chain_crypto::Signature::from_binary(bytes.as_ref()) - .map_err(|e| DeserializeError::new(stringify!($name), DeserializeFailure::SignatureError(e)))?; - Ok($name { - sig, - encoding: StringEncoding::default(), - }) + chain_crypto::Signature::from_str(input) + .map(Self) + .map_err(Into::into) } - } + } - impl Serialize for $name { - fn serialize<'se, W: std::io::Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(self.sig.as_ref(), self.encoding.to_str_len_sz(self.sig.as_ref().len() as u64, force_canonical)) + impl RawBytesEncoding for $name { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() } - } - impl Deserialize for $name { - fn deserialize(raw: &mut Deserializer) -> Result { - let (bytes, encoding) = raw.bytes_sz()?; - Ok(Self { - sig: chain_crypto::Signature::from_binary(bytes.as_ref()).map_err(DeserializeFailure::SignatureError)?, - encoding: encoding.into(), - }) + fn from_raw_bytes(bytes: &[u8]) -> Result { + chain_crypto::Signature::from_binary(bytes.as_ref()) + .map(Self) + .map_err(Into::into) } } @@ -683,7 +439,7 @@ macro_rules! impl_signature { } } - impl JsonSchema for $name { + impl schemars::JsonSchema for $name { fn schema_name() -> String { String::from(stringify!($name)) } fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { String::json_schema(gen) } fn is_referenceable() -> bool { String::is_referenceable() } @@ -691,16 +447,13 @@ macro_rules! impl_signature { impl std::hash::Hash for $name { fn hash(&self, state: &mut H) { - self.sig.as_ref().hash(state) + self.0.as_ref().hash(state) } } impl From> for $name { fn from(sig: chain_crypto::Signature<$signee_type, $verifier_type>) -> Self { - Self { - sig, - encoding: StringEncoding::default(), - } + Self(sig) } } }; @@ -709,45 +462,18 @@ macro_rules! impl_signature { impl_signature!(Ed25519Signature, Vec, chain_crypto::Ed25519); macro_rules! impl_hash_type { ($name:ident, $byte_count:expr) => { - #[derive(Debug, Clone, derivative::Derivative)] - #[derivative(Eq, PartialEq, Ord, PartialOrd, Hash)] - pub struct $name{ - pub (crate) data: [u8; $byte_count], - #[derivative(PartialEq="ignore", Ord="ignore", PartialOrd="ignore", Hash="ignore")] - encoding: StringEncoding, - } + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] + pub struct $name([u8; $byte_count]); impl $name { pub const BYTE_COUNT: usize = $byte_count; - // hash types are the only types in this library to not give the entire CBOR structure. - // There is no CBOR binary tag here just the raw hash bytes. - pub fn to_raw_bytes(&self) -> &[u8] { - self.data.as_ref() - } - - // hash types are the only types in this library to not expect the entire CBOR structure. - // There is no CBOR binary tag here just the raw hash bytes. - pub fn from_raw_bytes(bytes: &[u8]) -> Result { - use std::convert::TryInto; - match bytes.len() { - $byte_count => Ok($name { - data: bytes[..$byte_count].try_into().unwrap(), - encoding: StringEncoding::default(), - }), - other_len => { - let cbor_error = cbor_event::Error::WrongLen($byte_count, cbor_event::Len::Len(other_len as u64), "hash length"); - Err(DeserializeError::new(stringify!($name), DeserializeFailure::CBOR(cbor_error))) - }, - } - } - pub fn to_bech32(&self, prefix: &str) -> Result { use bech32::ToBase32; - bech32::encode(&prefix, self.to_raw_bytes().to_base32()) + bech32::encode(&prefix, self.0.as_ref().to_base32()) .map_err(Into::into) } - + pub fn from_bech32(bech_str: &str) -> Result<$name, CryptoError> { let (_hrp, u5data) = bech32::decode(bech_str) .map_err(chain_crypto::bech32::Error::Bech32Malformed)?; @@ -756,107 +482,40 @@ macro_rules! impl_hash_type { Self::from_raw_bytes(&data) .map_err(Into::into) } - - pub fn to_hex(&self) -> String { - hex::encode(&self.data) - } - - pub fn from_hex(hex: &str) -> Result<$name, CryptoError> { - let bytes = hex::decode(hex)?; - Ok(Self::from_raw_bytes(&bytes)?) - } } impl std::fmt::Display for $name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_hex()) + write!(f, "{}", self.to_raw_hex()) } } impl From<[u8; $byte_count]> for $name { fn from(bytes: [u8; $byte_count]) -> Self { - Self { - data: bytes, - encoding: StringEncoding::default(), - } + Self(bytes) } } - impl Serialize for $name { - fn serialize<'se, W: std::io::Write>(&self, serializer: &'se mut Serializer, force_canonical: bool) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes_sz(&self.data, self.encoding.to_str_len_sz(self.data.len() as u64, force_canonical)) + impl RawBytesEncoding for $name { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() } - fn to_bytes(&self) -> Vec { - self.to_raw_bytes().to_vec() - } - } - - impl Deserialize for $name { - fn deserialize(raw: &mut Deserializer) -> Result { + fn from_raw_bytes(bytes: &[u8]) -> Result { use std::convert::TryInto; - (|| -> Result { - let (bytes, encoding) = raw.bytes_sz()?; - if bytes.len() != $byte_count { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen($byte_count, cbor_event::Len::Len(bytes.len() as u64), "hash length")).into()); - } - Ok($name { - data: bytes[..$byte_count].try_into().unwrap(), - encoding: encoding.into(), - }) - })().map_err(|e| e.annotate(stringify!($name))) - } - - fn from_bytes(data: &[u8]) -> Result { - Self::from_raw_bytes(data) - } - } - - impl serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - serializer.serialize_str(&self.to_hex()) - } - } - - impl <'de> serde::de::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result where - D: serde::de::Deserializer<'de> { - let s = ::deserialize(deserializer)?; - $name::from_hex(&s).map_err(|_e| serde::de::Error::invalid_value(serde::de::Unexpected::Str(&s), &"hex bytes for hash")) + match bytes.len() { + $byte_count => Ok($name(bytes[..$byte_count].try_into().unwrap())), + other_len => { + let cbor_error = cbor_event::Error::WrongLen($byte_count, cbor_event::Len::Len(other_len as u64), "hash length"); + Err(DeserializeError::new(stringify!($name), DeserializeFailure::CBOR(cbor_error)).into()) + }, + } } } - - impl JsonSchema for $name { - fn schema_name() -> String { String::from(stringify!($name)) } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { String::json_schema(gen) } - fn is_referenceable() -> bool { String::is_referenceable() } - } } } pub(crate) use impl_hash_type; - -pub struct LegacyDaedalusPrivateKey(pub (crate) chain_crypto::SecretKey); - -impl LegacyDaedalusPrivateKey { - pub fn from_bytes(bytes: &[u8]) -> Result { - chain_crypto::SecretKey::::from_binary(bytes) - .map(LegacyDaedalusPrivateKey) - .map_err(|e| e.into()) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn chaincode(&self) -> Vec { - const ED25519_PRIVATE_KEY_LENGTH: usize = 64; - const XPRV_SIZE: usize = 96; - self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() - } -} - impl_hash_type!(Ed25519KeyHash, 28); impl_hash_type!(ScriptHash, 28); // TransactionHash is either a hash of the tx CBOR or a hash of a redeem address (genesis) @@ -873,5 +532,28 @@ impl_hash_type!(ScriptDataHash, 32); // We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) impl_hash_type!(VRFVKey, 32); impl_hash_type!(KESVKey, 32); -// same for this signature +// same for this signature (but lots of traits aren't implemented for [u8; 448] so we can't) //impl_hash_type!(KESSignature, 448); + + +pub struct LegacyDaedalusPrivateKey(pub (crate) chain_crypto::SecretKey); + +impl LegacyDaedalusPrivateKey { + pub fn chaincode(&self) -> Vec { + const ED25519_PRIVATE_KEY_LENGTH: usize = 64; + const XPRV_SIZE: usize = 96; + self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() + } +} + +impl RawBytesEncoding for LegacyDaedalusPrivateKey { + fn to_raw_bytes(&self) -> &[u8] { + self.0.as_ref() + } + + fn from_raw_bytes(bytes: &[u8]) -> Result { + chain_crypto::SecretKey::::from_binary(bytes) + .map(LegacyDaedalusPrivateKey) + .map_err(|e| e.into()) + } +} diff --git a/rust/core/src/typed_bytes/builder.rs b/crypto/src/typed_bytes/builder.rs similarity index 100% rename from rust/core/src/typed_bytes/builder.rs rename to crypto/src/typed_bytes/builder.rs diff --git a/rust/core/src/typed_bytes/mod.rs b/crypto/src/typed_bytes/mod.rs similarity index 100% rename from rust/core/src/typed_bytes/mod.rs rename to crypto/src/typed_bytes/mod.rs diff --git a/rust/json-gen-split/Cargo.toml b/json-gen-split/Cargo.toml similarity index 100% rename from rust/json-gen-split/Cargo.toml rename to json-gen-split/Cargo.toml diff --git a/rust/json-gen-split/src/main.rs b/json-gen-split/src/main.rs similarity index 100% rename from rust/json-gen-split/src/main.rs rename to json-gen-split/src/main.rs diff --git a/rust/wasm/src/crypto.rs b/rust/wasm/src/crypto.rs deleted file mode 100644 index afbac87d..00000000 --- a/rust/wasm/src/crypto.rs +++ /dev/null @@ -1,600 +0,0 @@ -use super::*; - -use core::CryptoError; - -#[wasm_bindgen] -pub struct Bip32PrivateKey(core::Bip32PrivateKey); - -#[wasm_bindgen] -impl Bip32PrivateKey { - /// derive this private key with the given index. - /// - /// # Security considerations - /// - /// * hard derivation index cannot be soft derived with the public key - /// - /// # Hard derivation vs Soft derivation - /// - /// If you pass an index below 0x80000000 then it is a soft derivation. - /// The advantage of soft derivation is that it is possible to derive the - /// public key too. I.e. derivation the private key with a soft derivation - /// index and then retrieving the associated public key is equivalent to - /// deriving the public key associated to the parent private key. - /// - /// Hard derivation index does not allow public key derivation. - /// - /// This is why deriving the private key should not fail while deriving - /// the public key may fail (if the derivation index is invalid). - /// - pub fn derive(&self, index: u32) -> Self { - Self(self.0.derive(index)) - } - - /// 128-byte xprv a key format in Cardano that some software still uses or requires - /// the traditional 96-byte xprv is simply encoded as - /// prv | chaincode - /// however, because some software may not know how to compute a public key from a private key, - /// the 128-byte inlines the public key in the following format - /// prv | pub | chaincode - /// so be careful if you see the term "xprv" as it could refer to either one - /// our library does not require the pub (instead we compute the pub key when needed) - pub fn from_128_xprv(bytes: &[u8]) -> Result { - core::Bip32PrivateKey::from_128_xprv(bytes).map(Self).map_err(Into::into) - } - /// see from_128_xprv - pub fn to_128_xprv(&self) -> Vec { - self.0.to_128_xprv() - } - - pub fn generate_ed25519_bip32() -> Bip32PrivateKey { - Self(core::Bip32PrivateKey::generate_ed25519_bip32()) - } - - pub fn to_raw_key(&self) -> PrivateKey { - self.0.to_raw_key().into() - } - - pub fn to_public(&self) -> Bip32PublicKey { - Bip32PublicKey(self.0.to_public()) - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - core::Bip32PrivateKey::from_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_bytes() - } - - pub fn from_bech32(bech32_str: &str) -> Result { - core::Bip32PrivateKey::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32() - } - - pub fn from_bip39_entropy(entropy: &[u8], password: &[u8]) -> Self { - Self(core::Bip32PrivateKey::from_bip39_entropy(entropy, password)) - } - - pub fn chaincode(&self) -> Vec { - self.0.chaincode() - } -} - -impl From for Bip32PrivateKey { - fn from(inner: core::Bip32PrivateKey) -> Self { - Self(inner) - } -} - -impl From for core::Bip32PrivateKey { - fn from(wrapper: Bip32PrivateKey) -> Self { - wrapper.0 - } -} - - -#[wasm_bindgen] -pub struct Bip32PublicKey(core::Bip32PublicKey); - -#[wasm_bindgen] -impl Bip32PublicKey { - /// derive this public key with the given index. - /// - /// # Errors - /// - /// If the index is not a soft derivation index (< 0x80000000) then - /// calling this method will fail. - /// - /// # Security considerations - /// - /// * hard derivation index cannot be soft derived with the public key - /// - /// # Hard derivation vs Soft derivation - /// - /// If you pass an index below 0x80000000 then it is a soft derivation. - /// The advantage of soft derivation is that it is possible to derive the - /// public key too. I.e. derivation the private key with a soft derivation - /// index and then retrieving the associated public key is equivalent to - /// deriving the public key associated to the parent private key. - /// - /// Hard derivation index does not allow public key derivation. - /// - /// This is why deriving the private key should not fail while deriving - /// the public key may fail (if the derivation index is invalid). - /// - pub fn derive(&self, index: u32) -> Result { - self.0.derive(index).map(Self).map_err(Into::into) - } - - pub fn to_raw_key(&self) -> PublicKey { - PublicKey(self.0.to_raw_key()) - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - core::Bip32PublicKey::from_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_bytes() - } - - pub fn from_bech32(bech32_str: &str) -> Result { - core::Bip32PublicKey::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32() - } - - pub fn chaincode(&self) -> Vec { - self.0.chaincode() - } -} - -impl From for Bip32PublicKey { - fn from(inner: core::Bip32PublicKey) -> Self { - Self(inner) - } -} - -impl From for core::Bip32PublicKey { - fn from(wrapper: Bip32PublicKey) -> Self { - wrapper.0 - } -} - -#[wasm_bindgen] -pub struct PrivateKey(core::PrivateKey); - -#[wasm_bindgen] -impl PrivateKey { - pub fn to_public(&self) -> PublicKey { - PublicKey(self.0.to_public()) - } - - pub fn generate_ed25519() -> Self { - Self(core::PrivateKey::generate_ed25519()) - } - - pub fn generate_ed25519extended() -> Self { - Self(core::PrivateKey::generate_ed25519extended()) - } - - /// Get private key from its bech32 representation - /// ```javascript - /// PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); - /// ``` - /// For an extended 25519 key - /// ```javascript - /// PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); - /// ``` - pub fn from_bech32(bech32_str: &str) -> Result { - core::PrivateKey::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32() - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_bytes() - } - - pub fn from_extended_bytes(bytes: &[u8]) -> Result { - core::PrivateKey::from_extended_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn from_normal_bytes(bytes: &[u8]) -> Result { - core::PrivateKey::from_normal_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn sign(&self, message: &[u8]) -> Ed25519Signature { - self.0.sign(message).into() - } -} - -impl From for PrivateKey { - fn from(inner: core::PrivateKey) -> Self { - Self(inner) - } -} - -impl From for core::PrivateKey { - fn from(wrapper: PrivateKey) -> Self { - wrapper.0 - } -} - - -/// ED25519 key used as public key -#[wasm_bindgen] -pub struct PublicKey(core::PublicKey); - -#[wasm_bindgen] -impl PublicKey { - /// Get public key from its bech32 representation - /// Example: - /// ```javascript - /// const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); - /// ``` - pub fn from_bech32(bech32_str: &str) -> Result { - core::PublicKey::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32() - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_bytes() - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - core::PublicKey::from_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool { - self.0.verify(data, &signature.0) - } - - pub fn hash(&self) -> Ed25519KeyHash { - self.0.hash().into() - } -} - -impl From for PublicKey { - fn from(inner: core::PublicKey) -> Self { - Self(inner) - } -} - -impl From for core::PublicKey { - fn from(wrapper: PublicKey) -> Self { - wrapper.0 - } -} - -macro_rules! impl_signature { - ($name:ident) => { - #[wasm_bindgen] - #[derive(Debug, Clone)] - pub struct $name(core::$name); - - #[wasm_bindgen] - impl $name { - pub fn to_raw_bytes(&self) -> Vec { - self.0.to_raw_bytes().to_vec() - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32() - } - - pub fn to_hex(&self) -> String { - self.0.to_hex() - } - - pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { - core::$name::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn from_hex(input: &str) -> Result<$name, JsError> { - core::$name::from_hex(input).map(Self).map_err(Into::into) - } - - pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { - core::$name::from_raw_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result<$name, JsValue> { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } - } - - - impl From for $name { - fn from(inner: core::$name) -> Self { - Self(inner) - } - } - - impl From<$name> for core::$name { - fn from(wrapper: $name) -> core::$name { - wrapper.0 - } - } - }; -} - -impl_signature!(Ed25519Signature); - -macro_rules! impl_hash_type { - ($name:ident) => { - #[wasm_bindgen] - #[derive(Debug, Clone)] - pub struct $name(core::$name); - - #[wasm_bindgen] - impl $name { - pub fn to_raw_bytes(&self) -> Vec { - self.0.to_raw_bytes().to_vec() - } - - pub fn to_bech32(&self, prefix: &str) -> Result { - self.0.to_bech32(prefix).map_err(Into::into) - } - - pub fn to_hex(&self) -> String { - self.0.to_hex() - } - - pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { - core::$name::from_bech32(bech32_str).map(Self).map_err(Into::into) - } - - pub fn from_hex(input: &str) -> Result<$name, JsError> { - core::$name::from_hex(input).map(Self).map_err(Into::into) - } - - pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { - core::$name::from_raw_bytes(bytes).map(Self).map_err(Into::into) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result<$name, JsValue> { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } - } - - impl From for $name { - fn from(inner: core::$name) -> Self { - Self(inner) - } - } - - impl From<$name> for core::$name { - fn from(wrapper: $name) -> core::$name { - wrapper.0 - } - } - }; -} - -impl_hash_type!(Ed25519KeyHash); -impl_hash_type!(ScriptHash); -// TransactionHash is either a hash of the tx CBOR or a hash of a redeem address (genesis) -impl_hash_type!(TransactionHash); -impl_hash_type!(GenesisDelegateHash); -impl_hash_type!(GenesisHash); -impl_hash_type!(AuxiliaryDataHash); -impl_hash_type!(PoolMetadataHash); -impl_hash_type!(VRFKeyHash); -impl_hash_type!(BlockBodyHash); -impl_hash_type!(BlockHeaderHash); -impl_hash_type!(DataHash); -impl_hash_type!(ScriptDataHash); -// We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) -impl_hash_type!(VRFVKey); -impl_hash_type!(KESVKey); - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct KesSignature(pub(crate) core::KesSignature); - -#[wasm_bindgen] -impl KesSignature { - pub fn to_bytes(&self, force_canonical: bool) -> Vec { - use core::serialization::ToBytes; - ToBytes::to_bytes(&self.0, force_canonical) - } - - pub fn from_bytes(data: Vec) -> Result { - use core::serialization::FromBytes; - FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } - - pub fn get(&self) -> Vec { - self.0.get().clone().clone() - } -} - -impl From for KesSignature { - fn from(native: core::KesSignature) -> Self { - Self(native) - } -} - -impl From for core::KesSignature { - fn from(wasm: KesSignature) -> Self { - wasm.0 - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct Vkey(pub(crate) core::Vkey); - -#[wasm_bindgen] -impl Vkey { - pub fn to_bytes(&self, force_canonical: bool) -> Vec { - use core::serialization::ToBytes; - ToBytes::to_bytes(&self.0, force_canonical) - } - - pub fn from_bytes(data: Vec) -> Result { - use core::serialization::FromBytes; - FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } -} - -impl From for Vkey { - fn from(native: core::Vkey) -> Self { - Self(native) - } -} - -impl From for core::Vkey { - fn from(wasm: Vkey) -> Self { - wasm.0 - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct VrfCert(pub(crate) core::VrfCert); - -#[wasm_bindgen] -impl VrfCert { - pub fn to_bytes(&self, force_canonical: bool) -> Vec { - use core::serialization::ToBytes; - ToBytes::to_bytes(&self.0, force_canonical) - } - - pub fn from_bytes(data: Vec) -> Result { - use core::serialization::FromBytes; - FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } - - pub fn index_0(&self) -> Vec { - self.0.index_0.clone() - } - - pub fn bytes(&self) -> Vec { - self.0.bytes.clone() - } - - pub fn new(index_0: Vec, bytes: Vec) -> Self { - Self(core::VrfCert::new(index_0, bytes)) - } -} - -impl From for VrfCert { - fn from(native: core::VrfCert) -> Self { - Self(native) - } -} - -impl From for core::VrfCert { - fn from(wasm: VrfCert) -> Self { - wasm.0 - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct VrfVkey(pub(crate) core::VrfVkey); - -#[wasm_bindgen] -impl VrfVkey { - pub fn to_bytes(&self, force_canonical: bool) -> Vec { - use core::serialization::ToBytes; - ToBytes::to_bytes(&self.0, force_canonical) - } - - pub fn from_bytes(data: Vec) -> Result { - use core::serialization::FromBytes; - FromBytes::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) - } - - pub fn to_json(&self) -> Result { - serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) - } - - pub fn to_json_value(&self) -> Result { - JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) - } - - pub fn from_json(json: &str) -> Result { - serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) - } - - pub fn get(&self) -> Vec { - self.0.get().clone().clone() - } -} - -impl From for VrfVkey { - fn from(native: core::VrfVkey) -> Self { - Self(native) - } -} - -impl From for core::VrfVkey { - fn from(wasm: VrfVkey) -> Self { - wasm.0 - } -} \ No newline at end of file diff --git a/specs/cip25.cddl b/specs/cip25.cddl new file mode 100644 index 00000000..f3fb44b9 --- /dev/null +++ b/specs/cip25.cddl @@ -0,0 +1,31 @@ +string64 = text .size (0..64) + +policy_id_v1 = string64 +asset_name_v1 = string64 ; utf-8 + +policy_id_v2 = bytes ; no longer in text +asset_name_v2 = bytes ; no longer in text and utf-8 + +files_details = + { + name : string64, + mediaType : string64, + src : string64 / [* string64] + } + +metadata_details = + { + name : string64, + image : string64 / [* string64], + ? mediaType : string64, + ? description : string64 / [* string64], + ? files : [* files_details] + } + +label_metadata_v1 = { * policy_id_v1 => { * asset_name_v1 => metadata_details } } +data = { * policy_id_v2 => { * asset_name_v2 => metadata_details } } +label_metadata_v2 = { data: data, version: 2 } ; version 2 + +label_metadata = label_metadata_v1 / label_metadata_v2 + +metadata = { 721 : label_metadata } diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml new file mode 100644 index 00000000..43fc4df3 --- /dev/null +++ b/wasm/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cardano-multiplatform-lib-wasm" +version = "0.1.0" +edition = "2018" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +core = { path = "../chain", package = "cardano-multiplatform-lib-chain" } +# TODO: remove this dependency if possible to reduce confusion? maybe pub export necessary things in crypto-wasm? +core_crypto = { path = "../crypto", package = "cardano-multiplatform-lib-crypto" } +wasm_crypto = { path = "../crypto-wasm", package = "cardano-multiplatform-lib-crypto-wasm" } +cbor_event = "2.2.0" +wasm-bindgen = { version = "0.2", features=["serde-serialize"] } +linked-hash-map = "0.5.3" +serde_json = "1.0.57" diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 00000000..6fd1aebb --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,3 @@ +# wasm + +WASM bindings covering the `chain` crate. Includes the `crypto` crate as a dependency so you do not need to use both as both are output in WASM builds. diff --git a/rust/wasm/src/address.rs b/wasm/src/address.rs similarity index 100% rename from rust/wasm/src/address.rs rename to wasm/src/address.rs diff --git a/rust/wasm/src/block.rs b/wasm/src/block.rs similarity index 100% rename from rust/wasm/src/block.rs rename to wasm/src/block.rs diff --git a/rust/wasm/src/certs.rs b/wasm/src/certs.rs similarity index 100% rename from rust/wasm/src/certs.rs rename to wasm/src/certs.rs diff --git a/wasm/src/crypto.rs b/wasm/src/crypto.rs new file mode 100644 index 00000000..07d142fe --- /dev/null +++ b/wasm/src/crypto.rs @@ -0,0 +1,228 @@ +use super::*; + +// for to/from bytes +use core::serialization::{Serialize, Deserialize}; +use core_crypto::RawBytesEncoding; + +// unfortunately concat_idents isn't in stable so we just take in two params +// as to not cause name collisions in the final WASM build in the chain wasm bindings +// TODO: embed the type into comments (looks like it's likely possible using another macro) +// so that we have less generic documentation +macro_rules! impl_chain_crypto { + ($name:ident, $primitive:ident) => { + /// On-chain cryptographic primitive + #[wasm_bindgen] + #[derive(Debug, Clone)] + pub struct $name(core::crypto::ChainCrypto); + + #[wasm_bindgen] + impl $name { + /// Get the underlying cryptographic primitive represented here + pub fn primitive(&self) -> wasm_crypto::$primitive { + self.0.primitive.clone().into() + } + + /// Make a default-encoded on-chain cryptographic type based on the primitive + pub fn new(primitive: &wasm_crypto::$primitive) -> Self { + primitive.clone().into() + } + + pub fn to_cbor_bytes(&self, force_canonical: bool) -> Vec { + self.0.to_cbor_bytes(force_canonical) + } + + pub fn from_raw_bytes(bytes: &[u8]) -> Result<$name, JsError> { + core::crypto::ChainCrypto::::from_raw_bytes(bytes).map(Self).map_err(Into::into) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result<$name, JsValue> { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + } + + // chain-crypto (rust) <-> chain-crypto (wasm) + impl From> for $name { + fn from(inner: core::crypto::ChainCrypto) -> Self { + Self(inner) + } + } + + impl From<$name> for core::crypto::ChainCrypto { + fn from(wrapper: $name) -> core::crypto::ChainCrypto { + wrapper.0 + } + } + + // crypto (wasm) <-> chain-crypto (wasm) + impl From for $name { + fn from(primitive: wasm_crypto::$primitive) -> Self { + Self(core_crypto::$primitive::from(primitive).into()) + } + } + + impl From<$name> for wasm_crypto::$primitive { + fn from(wrapper: $name) -> wasm_crypto::$primitive { + wrapper.0.primitive.into() + } + } + }; +} + +impl_chain_crypto!(Ed25519SignatureOnChain, Ed25519Signature); +impl_chain_crypto!(Ed25519KeyHashOnChain, Ed25519KeyHash); +impl_chain_crypto!(ScriptHashOnChain, ScriptHash); +// TransactionHash is either a hash of the tx CBOR or a hash of a redeem address (genesis) +impl_chain_crypto!(TransactionHashOnChain, TransactionHash); +impl_chain_crypto!(GenesisDelegateHashOnChain, GenesisDelegateHash); +impl_chain_crypto!(GenesisHashOnChain, GenesisHash); +impl_chain_crypto!(AuxiliaryDataHashOnChain, AuxiliaryDataHash); +impl_chain_crypto!(PoolMetadataHashOnChain, PoolMetadataHash); +impl_chain_crypto!(VRFKeyHashOnChain, VRFKeyHash); +impl_chain_crypto!(BlockBodyHashOnChain, BlockBodyHash); +impl_chain_crypto!(BlockHeaderHashOnChain, BlockHeaderHash); +impl_chain_crypto!(DataHashOnChain, DataHash); +impl_chain_crypto!(ScriptDataHashOnChain, ScriptDataHash); +// We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) +impl_chain_crypto!(VRFVKeyOnChain, VRFVKey); +impl_chain_crypto!(KESVKeyOnChain, KESVKey); + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct KesSignature(pub(crate) core::crypto::KesSignature); + +#[wasm_bindgen] +impl KesSignature { + pub fn to_bytes(&self) -> Vec { + Serialize::to_bytes(&self.0) + } + + pub fn from_bytes(data: &[u8]) -> Result { + Deserialize::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn get(&self) -> Vec { + self.0.get().clone().clone() + } +} + +impl From for KesSignature { + fn from(native: core::crypto::KesSignature) -> Self { + Self(native) + } +} + +impl From for core::crypto::KesSignature { + fn from(wasm: KesSignature) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct Vkey(pub(crate) core::crypto::Vkey); + +#[wasm_bindgen] +impl Vkey { + pub fn to_bytes(&self) -> Vec { + Serialize::to_bytes(&self.0) + } + + pub fn from_bytes(data: &[u8]) -> Result { + Deserialize::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } +} + +impl From for Vkey { + fn from(native: core::crypto::Vkey) -> Self { + Self(native) + } +} + +impl From for core::crypto::Vkey { + fn from(wasm: Vkey) -> Self { + wasm.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct VrfCert(pub(crate) core::crypto::VrfCert); + +#[wasm_bindgen] +impl VrfCert { + pub fn to_bytes(&self) -> Vec { + Serialize::to_bytes(&self.0) + } + + pub fn from_bytes(data: &[u8]) -> Result { + Deserialize::from_bytes(data).map(Self).map_err(|e| JsValue::from_str(&format!("from_bytes: {}", e))) + } + + pub fn to_json(&self) -> Result { + serde_json::to_string_pretty(&self.0).map_err(|e| JsValue::from_str(&format!("to_json: {}", e))) + } + + pub fn to_json_value(&self) -> Result { + JsValue::from_serde(&self.0).map_err(|e| JsValue::from_str(&format!("to_js_value: {}", e))) + } + + pub fn from_json(json: &str) -> Result { + serde_json::from_str(json).map(Self).map_err(|e| JsValue::from_str(&format!("from_json: {}", e))) + } + + pub fn index_0(&self) -> Vec { + self.0.index_0.clone() + } + + pub fn bytes(&self) -> Vec { + self.0.bytes.clone() + } + + pub fn new(index_0: Vec, bytes: Vec) -> Self { + Self(core::crypto::VrfCert::new(index_0, bytes)) + } +} + +impl From for VrfCert { + fn from(native: core::crypto::VrfCert) -> Self { + Self(native) + } +} + +impl From for core::crypto::VrfCert { + fn from(wasm: VrfCert) -> Self { + wasm.0 + } +} diff --git a/rust/wasm/src/lib.rs b/wasm/src/lib.rs similarity index 71% rename from rust/wasm/src/lib.rs rename to wasm/src/lib.rs index 1afa8e75..02900680 100644 --- a/rust/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -6,9 +6,11 @@ use crypto::*; use wasm_bindgen::prelude::{wasm_bindgen, JsValue, JsError}; +// this is actually chain pulling in the core definition. +// when we regenerate we might want to change the module naming use core::{ // this can now be directly exposed as it implements std::error::Error - error::DeserializeError, + DeserializeError, }; #[wasm_bindgen] @@ -16,11 +18,11 @@ pub struct StakeCredential(core::StakeCredential); #[wasm_bindgen] impl StakeCredential { - pub fn new_key(addr_keyhash: &Ed25519KeyHash) -> Self { + pub fn new_key(addr_keyhash: &Ed25519KeyHashOnChain) -> Self { core::StakeCredential::new_key(addr_keyhash.clone().into()).into() } - pub fn new_script(scripthash: &ScriptHash) -> Self { + pub fn new_script(scripthash: &ScriptHashOnChain) -> Self { core::StakeCredential::new_script(scripthash.clone().into()).into() } } diff --git a/rust/wasm/src/metadata.rs b/wasm/src/metadata.rs similarity index 100% rename from rust/wasm/src/metadata.rs rename to wasm/src/metadata.rs diff --git a/rust/wasm/src/plutus.rs b/wasm/src/plutus.rs similarity index 100% rename from rust/wasm/src/plutus.rs rename to wasm/src/plutus.rs diff --git a/rust/wasm/src/transaction.rs b/wasm/src/transaction.rs similarity index 100% rename from rust/wasm/src/transaction.rs rename to wasm/src/transaction.rs