diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 4411c256..8cecda19 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -15,11 +15,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bech32" -version = "0.7.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" +checksum = "cdcf67bb7ba7797a081cd19009948ab533af7c355d5caf1d08c777582d351e9c" [[package]] name = "bitflags" @@ -65,6 +71,7 @@ dependencies = [ "js-sys", "linked-hash-map", "noop_proc_macro", + "num-bigint", "quickcheck", "quickcheck_macros", "rand_chacha 0.1.1", @@ -124,9 +131,9 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "cryptoxide" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46212f5d1792f89c3e866fb10636139464060110c568edd7f73ab5e9f736c26d" +checksum = "b8c4fdc86023bc33b265f256ce8205329125b86c38a8a96e243a6a705b7230ec" [[package]] name = "curve25519-dalek" @@ -279,6 +286,36 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "num-bigint" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d047c1062aa51e256408c560894e5251f08925980e53cf1aa5bd00eec6512" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -293,9 +330,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -351,7 +388,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg", + "autocfg 0.1.7", "rand_core 0.3.1", ] @@ -490,9 +527,9 @@ checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" [[package]] name = "syn" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a56b62d4..a45983ac 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -27,6 +27,7 @@ hex = "0.4.0" cfg-if = "1" linked-hash-map = "0.5.3" serde_json = "1.0.57" +num-bigint = "0.4.0" # The default can't be compiled to wasm, so it's necessary to use either the 'nightly' # feature or this one clear_on_drop = { version = "0.2", features = ["no_cc"] } diff --git a/rust/src/crypto.rs b/rust/src/crypto.rs index bb8d1c2e..5f8cdff4 100644 --- a/rust/src/crypto.rs +++ b/rust/src/crypto.rs @@ -338,6 +338,57 @@ impl Deserialize for Vkey { } } +#[wasm_bindgen] +#[derive(Clone)] +pub struct Vkeys(Vec); + +#[wasm_bindgen] +impl Vkeys { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Vkey { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Vkey) { + self.0.push(elem.clone()); + } +} + +impl cbor_event::se::Serialize for Vkeys { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Vkeys { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Vkey::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Vkeys"))?; + Ok(Self(arr)) + } +} + #[wasm_bindgen] #[derive(Clone)] pub struct Vkeywitness { @@ -769,9 +820,12 @@ impl_hash_type!(ScriptHash, 28); impl_hash_type!(TransactionHash, 32); impl_hash_type!(GenesisDelegateHash, 28); impl_hash_type!(GenesisHash, 28); -impl_hash_type!(MetadataHash, 32); +impl_hash_type!(AuxiliaryDataHash, 32); +impl_hash_type!(PoolMetadataHash, 32); impl_hash_type!(VRFKeyHash, 32); impl_hash_type!(BlockHash, 32); +impl_hash_type!(DataHash, 32); +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); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4472e8e3..ba3d0448 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -43,6 +43,7 @@ pub mod fees; pub mod impl_mockchain; pub mod legacy_address; pub mod metadata; +pub mod plutus; pub mod serialization; pub mod tx_builder; pub mod typed_bytes; @@ -53,9 +54,12 @@ pub mod utils; use address::*; use crypto::*; use error::*; +use plutus::*; use metadata::*; use utils::*; +type DeltaCoin = Int; + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct UnitInterval { @@ -83,6 +87,7 @@ impl UnitInterval { } } +type SubCoin = UnitInterval; type Rational = UnitInterval; type Epoch = u32; type Slot = u32; @@ -92,7 +97,7 @@ type Slot = u32; pub struct Transaction { body: TransactionBody, witness_set: TransactionWitnessSet, - metadata: Option, + auxiliary_data: Option, } to_from_bytes!(Transaction); @@ -107,19 +112,19 @@ impl Transaction { self.witness_set.clone() } - pub fn metadata(&self) -> Option { - self.metadata.clone() + pub fn auxiliary_data(&self) -> Option { + self.auxiliary_data.clone() } pub fn new( body: &TransactionBody, witness_set: &TransactionWitnessSet, - metadata: Option, + auxiliary_data: Option, ) -> Self { Self { body: body.clone(), witness_set: witness_set.clone(), - metadata: metadata.clone(), + auxiliary_data: auxiliary_data.clone(), } } } @@ -204,6 +209,8 @@ impl Certificates { } } +pub type RequiredSigners = Ed25519KeyHashes; + #[wasm_bindgen] #[derive(Clone)] pub struct TransactionBody { @@ -214,9 +221,13 @@ pub struct TransactionBody { certs: Option, withdrawals: Option, update: Option, - metadata_hash: Option, + auxiliary_data_hash: Option, validity_start_interval: Option, mint: Option, + script_data_hash: Option, + collateral: Option, + required_signers: Option, + network_id: Option, } to_from_bytes!(TransactionBody); @@ -262,12 +273,13 @@ impl TransactionBody { pub fn update(&self) -> Option { self.update.clone() } - pub fn set_metadata_hash(&mut self, metadata_hash: &MetadataHash) { - self.metadata_hash = Some(metadata_hash.clone()) + + pub fn set_auxiliary_data_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { + self.auxiliary_data_hash = Some(auxiliary_data_hash.clone()) } - pub fn metadata_hash(&self) -> Option { - self.metadata_hash.clone() + pub fn auxiliary_data_hash(&self) -> Option { + self.auxiliary_data_hash.clone() } pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot) { @@ -275,7 +287,7 @@ impl TransactionBody { } pub fn validity_start_interval(&self) -> Option { - self.validity_start_interval + self.validity_start_interval.clone() } pub fn set_mint(&mut self, mint: &Mint) { @@ -286,12 +298,43 @@ impl TransactionBody { self.mint.clone() } + pub fn set_script_data_hash(&mut self, script_data_hash: &ScriptDataHash) { + self.script_data_hash = Some(script_data_hash.clone()) + } + + pub fn script_data_hash(&self) -> Option { + self.script_data_hash.clone() + } + + pub fn set_collateral(&mut self, collateral: &TransactionInputs) { + self.collateral = Some(collateral.clone()) + } + + pub fn collateral(&self) -> Option { + self.collateral.clone() + } + + pub fn set_required_signers(&mut self, required_signers: &RequiredSigners) { + self.required_signers = Some(required_signers.clone()) + } + + pub fn required_signers(&self) -> Option { + self.required_signers.clone() + } + + pub fn set_network_id(&mut self, network_id: &NetworkId) { + self.network_id = Some(network_id.clone()) + } + + pub fn network_id(&self) -> Option { + self.network_id.clone() + } + pub fn new( inputs: &TransactionInputs, outputs: &TransactionOutputs, fee: &Coin, - ttl: Option, - ) -> Self { + ttl: Option) -> Self { Self { inputs: inputs.clone(), outputs: outputs.clone(), @@ -300,9 +343,13 @@ impl TransactionBody { certs: None, withdrawals: None, update: None, - metadata_hash: None, + auxiliary_data_hash: None, validity_start_interval: None, mint: None, + script_data_hash: None, + collateral: None, + required_signers: None, + network_id: None, } } } @@ -339,6 +386,7 @@ impl TransactionInput { pub struct TransactionOutput { address: Address, amount: Value, + data_hash: Option, } to_from_bytes!(TransactionOutput); @@ -353,10 +401,19 @@ impl TransactionOutput { self.amount.clone() } + pub fn data_hash(&self) -> Option { + self.data_hash.clone() + } + + pub fn set_data_hash(&mut self, data_hash: &DataHash) { + self.data_hash = Some(data_hash.clone()); + } + pub fn new(address: &Address, amount: &Value) -> Self { Self { address: address.clone(), amount: amount.clone(), + data_hash: None, } } } @@ -799,26 +856,37 @@ impl Certificate { } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum MIRPot { Reserves, Treasury, } +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum MIREnum { + ToOtherPot(Coin), + ToStakeCredentials(MIRToStakeCredentials), +} + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct MoveInstantaneousReward { - pot: MIRPot, - rewards: linked_hash_map::LinkedHashMap, +pub enum MIRKind { + ToOtherPot, + ToStakeCredentials, } -to_from_bytes!(MoveInstantaneousReward); +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MIRToStakeCredentials { + rewards: linked_hash_map::LinkedHashMap, +} + +to_from_bytes!(MIRToStakeCredentials); #[wasm_bindgen] -impl MoveInstantaneousReward { - pub fn new(pot: MIRPot) -> Self { +impl MIRToStakeCredentials { + pub fn new() -> Self { Self { - pot, rewards: linked_hash_map::LinkedHashMap::new(), } } @@ -827,12 +895,12 @@ impl MoveInstantaneousReward { self.rewards.len() } - pub fn insert(&mut self, key: &StakeCredential, value: &Coin) -> Option { - self.rewards.insert(key.clone(), value.clone()) + pub fn insert(&mut self, cred: &StakeCredential, delta: &DeltaCoin) -> Option { + self.rewards.insert(cred.clone(), delta.clone()) } - pub fn get(&self, key: &StakeCredential) -> Option { - self.rewards.get(key).map(|v| v.clone()) + pub fn get(&self, cred: &StakeCredential) -> Option { + self.rewards.get(cred).map(|v| v.clone()) } pub fn keys(&self) -> StakeCredentials { @@ -845,6 +913,57 @@ impl MoveInstantaneousReward { } } +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct MoveInstantaneousReward { + pot: MIRPot, + variant: MIREnum, +} + +to_from_bytes!(MoveInstantaneousReward); + +#[wasm_bindgen] +impl MoveInstantaneousReward { + pub fn new_to_other_pot(pot: MIRPot, amount: &Coin) -> Self { + Self { + pot, + variant: MIREnum::ToOtherPot(amount.clone()), + } + } + + pub fn new_to_stake_creds(pot: MIRPot, amounts: &MIRToStakeCredentials) -> Self { + Self { + pot, + variant: MIREnum::ToStakeCredentials(amounts.clone()), + } + } + + pub fn pot(&self) -> MIRPot { + self.pot + } + + pub fn kind(&self) -> MIRKind { + match &self.variant { + MIREnum::ToOtherPot(_) => MIRKind::ToOtherPot, + MIREnum::ToStakeCredentials(_) => MIRKind::ToStakeCredentials, + } + } + + pub fn as_to_other_pot(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(amount) => Some(amount.clone()), + MIREnum::ToStakeCredentials(_) => None, + } + } + + pub fn as_to_stake_creds(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(_) => None, + MIREnum::ToStakeCredentials(amounts) => Some(amounts.clone()), + } + } +} + type Port = u16; #[wasm_bindgen] @@ -1142,7 +1261,7 @@ impl Relay { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PoolMetadata { url: URL, - metadata_hash: MetadataHash, + pool_metadata_hash: PoolMetadataHash, } to_from_bytes!(PoolMetadata); @@ -1153,14 +1272,14 @@ impl PoolMetadata { self.url.clone() } - pub fn metadata_hash(&self) -> MetadataHash { - self.metadata_hash.clone() + pub fn pool_metadata_hash(&self) -> PoolMetadataHash { + self.pool_metadata_hash.clone() } - pub fn new(url: &URL, metadata_hash: &MetadataHash) -> Self { + pub fn new(url: &URL, pool_metadata_hash: &PoolMetadataHash) -> Self { Self { url: url.clone(), - metadata_hash: metadata_hash.clone(), + pool_metadata_hash: pool_metadata_hash.clone(), } } } @@ -1253,8 +1372,11 @@ impl Withdrawals { #[derive(Clone)] pub struct TransactionWitnessSet { vkeys: Option, - scripts: Option, + native_scripts: Option, bootstraps: Option, + plutus_scripts: Option, + plutus_data: Option, + redeemers: Option, } to_from_bytes!(TransactionWitnessSet); @@ -1269,12 +1391,12 @@ impl TransactionWitnessSet { self.vkeys.clone() } - pub fn set_scripts(&mut self, scripts: &NativeScripts) { - self.scripts = Some(scripts.clone()) + pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { + self.native_scripts = Some(native_scripts.clone()) } - pub fn scripts(&self) -> Option { - self.scripts.clone() + pub fn native_scripts(&self) -> Option { + self.native_scripts.clone() } pub fn set_bootstraps(&mut self, bootstraps: &BootstrapWitnesses) { @@ -1285,11 +1407,38 @@ impl TransactionWitnessSet { self.bootstraps.clone() } + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) + } + + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_data(&mut self, plutus_data: &PlutusList) { + self.plutus_data = Some(plutus_data.clone()) + } + + pub fn plutus_data(&self) -> Option { + self.plutus_data.clone() + } + + pub fn set_redeemers(&mut self, redeemers: &Redeemers) { + self.redeemers = Some(redeemers.clone()) + } + + pub fn redeemers(&self) -> Option { + self.redeemers.clone() + } + pub fn new() -> Self { Self { vkeys: None, - scripts: None, + native_scripts: None, bootstraps: None, + plutus_scripts: None, + plutus_data: None, + redeemers: None, } } } @@ -1461,6 +1610,7 @@ to_from_bytes!(NativeScript); #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum ScriptHashNamespace { NativeScript, + // TODO: do we need to update this for Plutus? } #[wasm_bindgen] @@ -1742,6 +1892,7 @@ impl ProtocolVersions { } } + #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ProtocolParamUpdate { @@ -1762,7 +1913,13 @@ pub struct ProtocolParamUpdate { d: Option, extra_entropy: Option, protocol_version: Option, - min_utxo_value: Option, + min_pool_cost: Option, + ada_per_utxo_byte: Option, + cost_models: Option, + execution_costs: Option, + max_tx_ex_units: Option, + max_block_ex_units: Option, + max_value_size: Option, } to_from_bytes!(ProtocolParamUpdate); @@ -1889,12 +2046,60 @@ impl ProtocolParamUpdate { self.protocol_version.clone() } - pub fn set_min_utxo_value(&mut self, min_utxo_value: &Coin) { - self.min_utxo_value = Some(min_utxo_value.clone()) + pub fn set_min_pool_cost(&mut self, min_pool_cost: &Coin) { + self.min_pool_cost = Some(min_pool_cost.clone()) + } + + pub fn min_pool_cost(&self) -> Option { + self.min_pool_cost.clone() + } + + pub fn set_ada_per_utxo_byte(&mut self, ada_per_utxo_byte: &Coin) { + self.ada_per_utxo_byte = Some(ada_per_utxo_byte.clone()) + } + + pub fn ada_per_utxo_byte(&self) -> Option { + self.ada_per_utxo_byte.clone() + } + + pub fn set_cost_models(&mut self, cost_models: &Costmdls) { + self.cost_models = Some(cost_models.clone()) + } + + pub fn cost_models(&self) -> Option { + self.cost_models.clone() } - pub fn min_utxo_value(&self) -> Option { - self.min_utxo_value.clone() + pub fn set_execution_costs(&mut self, execution_costs: &ExUnitPrices) { + self.execution_costs = Some(execution_costs.clone()) + } + + pub fn execution_costs(&self) -> Option { + self.execution_costs.clone() + } + + pub fn set_max_tx_ex_units(&mut self, max_tx_ex_units: &ExUnits) { + self.max_tx_ex_units = Some(max_tx_ex_units.clone()) + } + + pub fn max_tx_ex_units(&self) -> Option { + self.max_tx_ex_units.clone() + } + + pub fn set_max_block_ex_units(&mut self, max_block_ex_units: &ExUnits) { + self.max_block_ex_units = Some(max_block_ex_units.clone()) + } + + pub fn max_block_ex_units(&self) -> Option { + self.max_block_ex_units.clone() + } + + pub fn set_max_value_size(&mut self, max_value_size: u32) { + self.max_value_size = Some(max_value_size.clone()) + } + + pub fn max_value_size(&self) -> Option { + self.max_value_size.clone() } pub fn new() -> Self { @@ -1914,7 +2119,13 @@ impl ProtocolParamUpdate { d: None, extra_entropy: None, protocol_version: None, - min_utxo_value: None, + min_pool_cost: None, + ada_per_utxo_byte: None, + cost_models: None, + execution_costs: None, + max_tx_ex_units: None, + max_block_ex_units: None, + max_value_size: None, } } } @@ -1972,10 +2183,10 @@ pub type TransactionIndexes = Vec; #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct MapTransactionIndexToTransactionMetadata(linked_hash_map::LinkedHashMap); +pub struct AuxiliaryDataSet(linked_hash_map::LinkedHashMap); #[wasm_bindgen] -impl MapTransactionIndexToTransactionMetadata { +impl AuxiliaryDataSet { pub fn new() -> Self { Self(linked_hash_map::LinkedHashMap::new()) } @@ -1984,15 +2195,15 @@ impl MapTransactionIndexToTransactionMetadata { self.0.len() } - pub fn insert(&mut self, key: TransactionIndex, value: &TransactionMetadata) -> Option { - self.0.insert(key, value.clone()) + pub fn insert(&mut self, tx_index: TransactionIndex, data: &AuxiliaryData) -> Option { + self.0.insert(tx_index, data.clone()) } - pub fn get(&self, key: TransactionIndex) -> Option { - self.0.get(&key).map(|v| v.clone()) + pub fn get(&self, tx_index: TransactionIndex) -> Option { + self.0.get(&tx_index).map(|v| v.clone()) } - pub fn keys(&self) -> TransactionIndexes { + pub fn indices(&self) -> TransactionIndexes { self.0.iter().map(|(k, _v)| k.clone()).collect::>() } } @@ -2003,7 +2214,8 @@ pub struct Block { header: Header, transaction_bodies: TransactionBodies, transaction_witness_sets: TransactionWitnessSets, - transaction_metadata_set: MapTransactionIndexToTransactionMetadata, + auxiliary_data_set: AuxiliaryDataSet, + invalid_transactions: TransactionIndexes, } to_from_bytes!(Block); @@ -2022,16 +2234,21 @@ impl Block { self.transaction_witness_sets.clone() } - pub fn transaction_metadata_set(&self) -> MapTransactionIndexToTransactionMetadata { - self.transaction_metadata_set.clone() + pub fn auxiliary_data_set(&self) -> AuxiliaryDataSet { + self.auxiliary_data_set.clone() + } + + pub fn invalid_transactions(&self) -> TransactionIndexes { + self.invalid_transactions.clone() } - pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, transaction_metadata_set: &MapTransactionIndexToTransactionMetadata) -> Self { + pub fn new(header: &Header, transaction_bodies: &TransactionBodies, transaction_witness_sets: &TransactionWitnessSets, auxiliary_data_set: &AuxiliaryDataSet, invalid_transactions: TransactionIndexes) -> Self { Self { header: header.clone(), transaction_bodies: transaction_bodies.clone(), transaction_witness_sets: transaction_witness_sets.clone(), - transaction_metadata_set: transaction_metadata_set.clone(), + auxiliary_data_set: auxiliary_data_set.clone(), + invalid_transactions: invalid_transactions, } } } @@ -2433,6 +2650,35 @@ impl Mint { } } + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum NetworkIdKind { + Testnet, + Mainnet, +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct NetworkId(NetworkIdKind); + +to_from_bytes!(NetworkId); + +#[wasm_bindgen] +impl NetworkId { + pub fn testnet() -> Self { + Self(NetworkIdKind::Testnet) + } + + pub fn mainnet() -> Self { + Self(NetworkIdKind::Mainnet) + } + + pub fn kind(&self) -> NetworkIdKind { + self.0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 085d63a0..23d83c07 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -286,17 +286,30 @@ impl GeneralTransactionMetadata { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct TransactionMetadata { - general: GeneralTransactionMetadata, +pub struct AuxiliaryData { + metadata: Option, native_scripts: Option, + plutus_scripts: Option, } -to_from_bytes!(TransactionMetadata); +to_from_bytes!(AuxiliaryData); #[wasm_bindgen] -impl TransactionMetadata { - pub fn general(&self) -> GeneralTransactionMetadata { - self.general.clone() +impl AuxiliaryData { + pub fn new() -> Self { + Self { + metadata: None, + native_scripts: None, + plutus_scripts: None, + } + } + + pub fn metadata(&self) -> Option { + self.metadata.clone() + } + + pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { + self.metadata = Some(metadata.clone()); } pub fn native_scripts(&self) -> Option { @@ -307,11 +320,12 @@ impl TransactionMetadata { self.native_scripts = Some(native_scripts.clone()) } - pub fn new(general: &GeneralTransactionMetadata) -> Self { - Self { - general: general.clone(), - native_scripts: None, - } + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) } } @@ -752,30 +766,121 @@ impl Deserialize for GeneralTransactionMetadata { } } -impl cbor_event::se::Serialize for TransactionMetadata { +impl cbor_event::se::Serialize for AuxiliaryData { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - match &self.native_scripts() { - Some(native_scripts) => { - serializer.write_array(cbor_event::Len::Len(2))?; - self.general.serialize(serializer)?; - native_scripts.serialize(serializer) - }, - None => self.general.serialize(serializer) + // we still serialize using the shelley-mary era format as it is still supported + // and it takes up less space on-chain so this should be better for scaling. + // Plus the code was already written for shelley-mary anyway + if self.metadata.is_some() && self.plutus_scripts.is_none() { + match &self.native_scripts() { + Some(native_scripts) => { + serializer.write_array(cbor_event::Len::Len(2))?; + self.metadata.as_ref().unwrap().serialize(serializer)?; + native_scripts.serialize(serializer) + }, + None => self.metadata.as_ref().unwrap().serialize(serializer), + } + } else { + // new format with plutus support + serializer.write_tag(259u64)?; + serializer.write_map(cbor_event::Len::Len( + if self.metadata.is_some() { 1 } else { 0 } + + if self.native_scripts.is_some() { 1 } else { 0 } + + if self.plutus_scripts.is_some() { 1 } else { 0 }))?; + if let Some(metadata) = &self.metadata { + serializer.write_unsigned_integer(0)?; + metadata.serialize(serializer)?; + } + if let Some(native_scripts) = &self.native_scripts { + serializer.write_unsigned_integer(1)?; + native_scripts.serialize(serializer)?; + } + if let Some(plutus_scripts) = &self.plutus_scripts { + serializer.write_unsigned_integer(2)?; + plutus_scripts.serialize(serializer)?; + } + Ok(serializer) } } } -impl Deserialize for TransactionMetadata { +impl Deserialize for AuxiliaryData { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { match raw.cbor_type()? { + // alonzo format + CBORType::Tag => { + let tag = raw.tag()?; + if tag != 259 { + return Err(DeserializeError::new("AuxiliaryData", DeserializeFailure::TagMismatch{ found: tag, expected: 259 })); + } + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut metadata = None; + let mut native_scripts = None; + let mut plutus_scripts = 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()? { + 0 => { + if metadata.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + metadata = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(GeneralTransactionMetadata::deserialize(raw)?) + })().map_err(|e| e.annotate("metadata"))?); + }, + 1 => { + if native_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + native_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("native_scripts"))?); + }, + 2 => { + if plutus_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + plutus_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_scripts"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), + }, + CBORType::Text => match raw.text()?.as_str() { + 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; + } + read_len.finish()?; + Ok(Self { + metadata, + native_scripts, + plutus_scripts, + }) + }, + // shelley mary format (still valid for alonzo) CBORType::Array => { let len = raw.array()?; let mut read_len = CBORReadLen::new(len); read_len.read_elems(2)?; - let general = (|| -> Result<_, DeserializeError> { + let metadata = (|| -> Result<_, DeserializeError> { Ok(GeneralTransactionMetadata::deserialize(raw)?) - })().map_err(|e| e.annotate("general"))?; + })().map_err(|e| e.annotate("metadata"))?; let native_scripts = (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })().map_err(|e| e.annotate("native_scripts"))?; @@ -786,18 +891,21 @@ impl Deserialize for TransactionMetadata { _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } - Ok(TransactionMetadata { - general, + Ok(Self { + metadata: Some(metadata), native_scripts: Some(native_scripts), + plutus_scripts: None, }) }, - CBORType::Map => Ok(TransactionMetadata { - general: GeneralTransactionMetadata::deserialize(raw).map_err(|e| e.annotate("general"))?, + // shelley pre-mary format (still valid for alonzo + mary) + CBORType::Map => Ok(Self { + metadata: Some(GeneralTransactionMetadata::deserialize(raw).map_err(|e| e.annotate("metadata"))?), native_scripts: None, + plutus_scripts: None, }), _ => return Err(DeserializeFailure::NoVariantMatched)? } - })().map_err(|e| e.annotate("TransactionMetadata")) + })().map_err(|e| e.annotate("AuxiliaryData")) } } @@ -933,18 +1041,29 @@ mod tests { } #[test] - fn allegra_metadata() { + fn metadata_serialize() { let mut gmd = GeneralTransactionMetadata::new(); let mdatum = TransactionMetadatum::new_text(String::from("string md")).unwrap(); gmd.insert(&to_bignum(100), &mdatum); - let md1 = TransactionMetadata::new(&gmd); - let md1_deser = TransactionMetadata::from_bytes(md1.to_bytes()).unwrap(); - assert_eq!(md1.to_bytes(), md1_deser.to_bytes()); - let mut md2 = TransactionMetadata::new(&gmd); - let mut scripts = NativeScripts::new(); - scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); - md2.set_native_scripts(&scripts); - let md2_deser = TransactionMetadata::from_bytes(md2.to_bytes()).unwrap(); - assert_eq!(md2.to_bytes(), md2_deser.to_bytes()); + let mut aux_data = AuxiliaryData::new(); + // alonzo (empty) + let ad0_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad0_deser.to_bytes()); + // pre-mary shelley + aux_data.set_metadata(&gmd); + let ad1_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad1_deser.to_bytes()); + // mary shelley + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); + aux_data.set_native_scripts(&native_scripts); + let ad2_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad2_deser.to_bytes()); + // alonzo + let mut plutus_scripts = PlutusScripts::new(); + plutus_scripts.add(&PlutusScript::new([61u8; 29].to_vec())); + aux_data.set_plutus_scripts(&plutus_scripts); + let ad3_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad3_deser.to_bytes()); } } diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs new file mode 100644 index 00000000..d1c70b89 --- /dev/null +++ b/rust/src/plutus.rs @@ -0,0 +1,1108 @@ +use std::io::{BufRead, Seek, Write}; +use super::*; + +// 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}}; + + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScript(Vec); + +to_from_bytes!(PlutusScript); + +#[wasm_bindgen] +impl PlutusScript { + pub fn new(bytes: Vec) -> PlutusScript { + Self(bytes) + } + + pub fn bytes(&self) -> Vec { + self.0.clone() + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScripts(Vec); + +to_from_bytes!(PlutusScripts); + +#[wasm_bindgen] +impl PlutusScripts { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusScript { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusScript) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ConstrPlutusData { + tag: Int, + data: PlutusList, +} + +to_from_bytes!(ConstrPlutusData); + +#[wasm_bindgen] +impl ConstrPlutusData { + pub fn tag(&self) -> Int { + self.tag.clone() + } + + pub fn data(&self) -> PlutusList { + self.data.clone() + } + + pub fn new(tag: Int, data: &PlutusList) -> Self { + Self { + tag, + data: data.clone(), + } + } +} + +impl ConstrPlutusData { + fn is_tag_compact(tag: i128) -> bool { + (tag >= 121 && tag <= 127) || (tag >= 1280 && tag <= 1400) + } + + const GENERAL_FORM_TAG: u64 = 102; +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct CostModel(std::collections::BTreeMap); + +to_from_bytes!(CostModel); + +#[wasm_bindgen] +impl CostModel { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: String, value: &BigInt) -> Option { + self.0.insert(key, value.clone()) + } + + pub fn get(&self, key: String) -> Option { + self.0.get(&key).map(|v| v.clone()) + } + + pub fn keys(&self) -> Strings { + Strings(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Costmdls(std::collections::BTreeMap); + +to_from_bytes!(Costmdls); + +#[wasm_bindgen] +impl Costmdls { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &Language, value: &CostModel) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &Language) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> Languages { + Languages(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ExUnitPrices { + mem_price: SubCoin, + step_price: SubCoin, +} + +to_from_bytes!(ExUnitPrices); + +#[wasm_bindgen] +impl ExUnitPrices { + pub fn mem_price(&self) -> SubCoin { + self.mem_price.clone() + } + + pub fn step_price(&self) -> SubCoin { + self.step_price.clone() + } + + pub fn new(mem_price: &SubCoin, step_price: &SubCoin) -> Self { + Self { + mem_price: mem_price.clone(), + step_price: step_price.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ExUnits { + // TODO: should these be u32 or BigNum? + mem: BigNum, + steps: BigNum, +} + +to_from_bytes!(ExUnits); + +#[wasm_bindgen] +impl ExUnits { + pub fn mem(&self) -> BigNum { + self.mem.clone() + } + + pub fn steps(&self) -> BigNum { + self.steps.clone() + } + + pub fn new(mem: &BigNum, steps: &BigNum) -> Self { + Self { + mem: mem.clone(), + steps: steps.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LanguageKind { + PlutusV1, +} + +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Language(LanguageKind); + +to_from_bytes!(Language); + +#[wasm_bindgen] +impl Language { + pub fn new_plutus_v1() -> Self { + Self(LanguageKind::PlutusV1) + } + + pub fn kind(&self) -> LanguageKind { + self.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Languages(Vec); + +#[wasm_bindgen] +impl Languages { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Language { + self.0[index] + } + + pub fn add(&mut self, elem: Language) { + self.0.push(elem); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusMap(std::collections::BTreeMap); + +to_from_bytes!(PlutusMap); + +#[wasm_bindgen] +impl PlutusMap { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &PlutusData, value: &PlutusData) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &PlutusData) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> PlutusList { + PlutusList(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PlutusDataKind { + ConstrPlutusData, + Map, + List, + Integer, + Bytes, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum PlutusDataEnum { + ConstrPlutusData(ConstrPlutusData), + Map(PlutusMap), + List(PlutusList), + Integer(BigInt), + Bytes(Vec), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusData(PlutusDataEnum); + +const PLUTUS_BYTES_MAX_LEN: usize = 64; + +to_from_bytes!(PlutusData); + +#[wasm_bindgen] +impl PlutusData { + pub fn new_constr_plutus_data(constr_plutus_data: &ConstrPlutusData) -> Self { + Self(PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone())) + } + + pub fn new_map(map: &PlutusMap) -> Self { + Self(PlutusDataEnum::Map(map.clone())) + } + + pub fn new_list(list: &PlutusList) -> Self { + Self(PlutusDataEnum::List(list.clone())) + } + + pub fn new_integer(integer: &BigInt) -> Self { + Self(PlutusDataEnum::Integer(integer.clone())) + } + + pub fn new_bytes(bytes: Vec) -> Result { + if bytes.len() > PLUTUS_BYTES_MAX_LEN { + Err(JsError::from_str(&format!("Max Plutus bytes too long: {}, max = {}", bytes.len(), PLUTUS_BYTES_MAX_LEN))) + } else { + Ok(Self(PlutusDataEnum::Bytes(bytes))) + } + } + + pub fn kind(&self) -> PlutusDataKind { + match &self.0 { + PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData, + PlutusDataEnum::Map(_) => PlutusDataKind::Map, + PlutusDataEnum::List(_) => PlutusDataKind::List, + PlutusDataEnum::Integer(_) => PlutusDataKind::Integer, + PlutusDataEnum::Bytes(_) => PlutusDataKind::Bytes, + } + } + + pub fn as_constr_plutus_data(&self) -> Option { + match &self.0 { + PlutusDataEnum::ConstrPlutusData(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_map(&self) -> Option { + match &self.0 { + PlutusDataEnum::Map(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_list(&self) -> Option { + match &self.0 { + PlutusDataEnum::List(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_integer(&self) -> Option { + match &self.0 { + PlutusDataEnum::Integer(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_bytes(&self) -> Option> { + match &self.0 { + PlutusDataEnum::Bytes(x) => Some(x.clone()), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusList(Vec); + +#[wasm_bindgen] +impl PlutusList { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusData { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusData) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Redeemer { + tag: RedeemerTag, + index: BigNum, + data: PlutusData, + ex_units: ExUnits, +} + +to_from_bytes!(Redeemer); + +#[wasm_bindgen] +impl Redeemer { + pub fn tag(&self) -> RedeemerTag { + self.tag.clone() + } + + pub fn index(&self) -> BigNum { + self.index.clone() + } + + pub fn data(&self) -> PlutusData { + self.data.clone() + } + + pub fn ex_units(&self) -> ExUnits { + self.ex_units.clone() + } + + pub fn new(tag: &RedeemerTag, index: &BigNum, data: &PlutusData, ex_units: &ExUnits) -> Self { + Self { + tag: tag.clone(), + index: index.clone(), + data: data.clone(), + ex_units: ex_units.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum RedeemerTagKind { + Spend, + Mint, + Cert, + Reward, +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct RedeemerTag(RedeemerTagKind); + +to_from_bytes!(RedeemerTag); + +#[wasm_bindgen] +impl RedeemerTag { + pub fn new_spend() -> Self { + Self(RedeemerTagKind::Spend) + } + + pub fn new_mint() -> Self { + Self(RedeemerTagKind::Mint) + } + + pub fn new_cert() -> Self { + Self(RedeemerTagKind::Cert) + } + + pub fn new_reward() -> Self { + Self(RedeemerTagKind::Reward) + } + + pub fn kind(&self) -> RedeemerTagKind { + self.0 + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Redeemers(Vec); + +#[wasm_bindgen] +impl Redeemers { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Redeemer { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Redeemer) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Strings(Vec); + +#[wasm_bindgen] +impl Strings { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> String { + self.0[index].clone() + } + + pub fn add(&mut self, elem: String) { + self.0.push(elem); + } +} + + + + + + + + + + +// Serialization + +use std::io::{SeekFrom}; + + +impl cbor_event::se::Serialize for PlutusScript { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for PlutusScript { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(raw.bytes()?)) + } +} + +impl cbor_event::se::Serialize for PlutusScripts { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusScripts { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(PlutusScript::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("PlutusScripts"))?; + Ok(Self(arr)) + } +} + + +// TODO: write tests for this hand-coded implementation? +impl cbor_event::se::Serialize for ConstrPlutusData { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + if Self::is_tag_compact(self.tag.0) { + // compact form + serializer.write_tag(self.tag.0 as u64)?; + self.data.serialize(serializer) + } else { + // general form + serializer.write_tag(Self::GENERAL_FORM_TAG)?; + serializer.write_array(cbor_event::Len::Len(2))?; + self.tag.serialize(serializer)?; + self.data.serialize(serializer) + } + } +} + +impl Deserialize for ConstrPlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let (tag, data) = match raw.tag()? { + // general form + Self::GENERAL_FORM_TAG => { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let tag = Int::deserialize(raw)?; + let data = (|| -> Result<_, DeserializeError> { + Ok(PlutusList::deserialize(raw)?) + })().map_err(|e| e.annotate("datas"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + (tag, data) + }, + // concise form + tag if Self::is_tag_compact(tag.into()) => (Int::new(&to_bignum(tag)), PlutusList::deserialize(raw)?), + invalid_tag => return Err(DeserializeFailure::TagMismatch{ + found: invalid_tag, + expected: Self::GENERAL_FORM_TAG, + }.into()), + }; + Ok(ConstrPlutusData{ + tag, + data, + }) + })().map_err(|e| e.annotate("ConstrPlutusData")) + } +} + +impl cbor_event::se::Serialize for CostModel { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + serializer.write_text(&key)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for CostModel { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = String::deserialize(raw)?; + let value = BigInt::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(key)).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("CostModel"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for Costmdls { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Costmdls { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = Language::deserialize(raw)?; + let value = CostModel::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("Costmdls"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for ExUnitPrices { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem_price.serialize(serializer)?; + self.step_price.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnitPrices { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem_price = (|| -> Result<_, DeserializeError> { + Ok(SubCoin::deserialize(raw)?) + })().map_err(|e| e.annotate("mem_price"))?; + let step_price = (|| -> Result<_, DeserializeError> { + Ok(SubCoin::deserialize(raw)?) + })().map_err(|e| e.annotate("step_price"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnitPrices { + mem_price, + step_price, + }) + })().map_err(|e| e.annotate("ExUnitPrices")) + } +} + +impl cbor_event::se::Serialize for ExUnits { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem.serialize(serializer)?; + self.steps.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnits { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem = (|| -> Result<_, DeserializeError> { + Ok(BigNum::deserialize(raw)?) + })().map_err(|e| e.annotate("mem"))?; + let steps = (|| -> Result<_, DeserializeError> { + Ok(BigNum::deserialize(raw)?) + })().map_err(|e| e.annotate("steps"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnits { + mem, + steps, + }) + })().map_err(|e| e.annotate("ExUnits")) + } +} + +impl cbor_event::se::Serialize for Language { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self.0 { + LanguageKind::PlutusV1 => { + serializer.write_unsigned_integer(0u64) + }, + } + } +} + +impl Deserialize for Language { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer()? { + 0 => Ok(Language::new_plutus_v1()), + _ => Err(DeserializeError::new("Language", DeserializeFailure::NoVariantMatched.into())), + } + })().map_err(|e| e.annotate("Language")) + } +} + +impl cbor_event::se::Serialize for Languages { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Languages { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Language::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Languages"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for PlutusMap { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusMap { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + let key = PlutusData::deserialize(raw)?; + let value = PlutusData::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + } + Ok(()) + })().map_err(|e| e.annotate("PlutusMap"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for PlutusDataEnum { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + PlutusDataEnum::ConstrPlutusData(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::Map(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::List(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::Integer(x) => { + x.serialize(serializer) + }, + PlutusDataEnum::Bytes(x) => { + serializer.write_bytes(&x) + }, + } + } +} + +impl Deserialize for PlutusDataEnum { + 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(ConstrPlutusData::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::ConstrPlutusData(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusMap::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::Map(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusList::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::List(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(BigInt::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::Integer(variant)), + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(raw.bytes()?) + })(raw) + { + Ok(variant) => if variant.len() <= PLUTUS_BYTES_MAX_LEN { + return Ok(PlutusDataEnum::Bytes(variant)); + } else { + return Err(DeserializeFailure::OutOfRange{ + min: 0, + max: PLUTUS_BYTES_MAX_LEN, + found: variant.len(), + }.into()); + } + Err(_) => raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(), + }; + Err(DeserializeError::new("PlutusDataEnum", DeserializeFailure::NoVariantMatched.into())) + })().map_err(|e| e.annotate("PlutusDataEnum")) + } +} + +impl cbor_event::se::Serialize for PlutusData { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for PlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PlutusDataEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for PlutusList { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusList { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(PlutusData::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("PlutusList"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for Redeemer { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.tag.serialize(serializer)?; + self.index.serialize(serializer)?; + self.data.serialize(serializer)?; + self.ex_units.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Redeemer { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let tag = (|| -> Result<_, DeserializeError> { + Ok(RedeemerTag::deserialize(raw)?) + })().map_err(|e| e.annotate("tag"))?; + let index = (|| -> Result<_, DeserializeError> { + Ok(BigNum::deserialize(raw)?) + })().map_err(|e| e.annotate("index"))?; + let data = (|| -> Result<_, DeserializeError> { + Ok(PlutusData::deserialize(raw)?) + })().map_err(|e| e.annotate("data"))?; + let ex_units = (|| -> Result<_, DeserializeError> { + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("ex_units"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(Redeemer { + tag, + index, + data, + ex_units, + }) + })().map_err(|e| e.annotate("Redeemer")) + } +} + +impl cbor_event::se::Serialize for RedeemerTagKind { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self { + RedeemerTagKind::Spend => { + serializer.write_unsigned_integer(0u64) + }, + RedeemerTagKind::Mint => { + serializer.write_unsigned_integer(1u64) + }, + RedeemerTagKind::Cert => { + serializer.write_unsigned_integer(2u64) + }, + RedeemerTagKind::Reward => { + serializer.write_unsigned_integer(3u64) + }, + } + } +} + +impl Deserialize for RedeemerTagKind { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer() { + Ok(0) => Ok(RedeemerTagKind::Spend), + Ok(1) => Ok(RedeemerTagKind::Mint), + Ok(2) => Ok(RedeemerTagKind::Cert), + Ok(3) => Ok(RedeemerTagKind::Reward), + Ok(_) | Err(_) => Err(DeserializeFailure::NoVariantMatched.into()), + } + })().map_err(|e| e.annotate("RedeemerTagEnum")) + } +} + +impl cbor_event::se::Serialize for RedeemerTag { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for RedeemerTag { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(RedeemerTagKind::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for Redeemers { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Redeemers { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(Redeemer::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Redeemers"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for Strings { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + serializer.write_text(&element)?; + } + Ok(serializer) + } +} + +impl Deserialize for Strings { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(String::deserialize(raw)?); + } + Ok(()) + })().map_err(|e| e.annotate("Strings"))?; + Ok(Self(arr)) + } +} diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 7d8d0993..e4d48984 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -59,7 +59,7 @@ impl cbor_event::se::Serialize for Transaction { serializer.write_array(cbor_event::Len::Len(3))?; self.body.serialize(serializer)?; self.witness_set.serialize(serializer)?; - match &self.metadata { + match &self.auxiliary_data { Some(x) => { x.serialize(serializer) }, @@ -94,10 +94,10 @@ impl DeserializeEmbeddedGroup for Transaction { let witness_set = (|| -> Result<_, DeserializeError> { Ok(TransactionWitnessSet::deserialize(raw)?) })().map_err(|e| e.annotate("witness_set"))?; - let metadata = (|| -> Result<_, DeserializeError> { + let auxiliary_data = (|| -> Result<_, DeserializeError> { Ok(match raw.cbor_type()? != CBORType::Special { true => { - Some(TransactionMetadata::deserialize(raw)?) + Some(AuxiliaryData::deserialize(raw)?) }, false => { if raw.special()? != CBORSpecial::Null { @@ -106,11 +106,11 @@ impl DeserializeEmbeddedGroup for Transaction { None } }) - })().map_err(|e| e.annotate("metadata"))?; + })().map_err(|e| e.annotate("auxiliary_data"))?; Ok(Transaction { body, witness_set, - metadata, + auxiliary_data, }) } } @@ -201,7 +201,7 @@ impl Deserialize for Certificates { impl cbor_event::se::Serialize for TransactionBody { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(3 + match &self.ttl { Some(x) => 1, None => 0 } + match &self.certs { Some(x) => 1, None => 0 } + match &self.withdrawals { Some(x) => 1, None => 0 } + match &self.update { Some(x) => 1, None => 0 } + match &self.metadata_hash { Some(x) => 1, None => 0 } + match &self.validity_start_interval { Some(x) => 1, None => 0 } + match &self.mint { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(3 + match &self.ttl { Some(x) => 1, None => 0 } + match &self.certs { Some(x) => 1, None => 0 } + match &self.withdrawals { Some(x) => 1, None => 0 } + match &self.update { Some(x) => 1, None => 0 } + match &self.auxiliary_data_hash { Some(x) => 1, None => 0 } + match &self.validity_start_interval { Some(x) => 1, None => 0 } + match &self.mint { Some(x) => 1, None => 0 } + match &self.script_data_hash { Some(x) => 1, None => 0 } + match &self.collateral { Some(x) => 1, None => 0 } + match &self.required_signers { Some(x) => 1, None => 0 } + match &self.network_id { Some(x) => 1, None => 0 }))?; serializer.write_unsigned_integer(0)?; self.inputs.serialize(serializer)?; serializer.write_unsigned_integer(1)?; @@ -224,7 +224,7 @@ impl cbor_event::se::Serialize for TransactionBody { serializer.write_unsigned_integer(6)?; field.serialize(serializer)?; } - if let Some(field) = &self.metadata_hash { + if let Some(field) = &self.auxiliary_data_hash { serializer.write_unsigned_integer(7)?; field.serialize(serializer)?; } @@ -236,6 +236,22 @@ impl cbor_event::se::Serialize for TransactionBody { serializer.write_unsigned_integer(9)?; field.serialize(serializer)?; } + if let Some(field) = &self.script_data_hash { + serializer.write_unsigned_integer(11)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.collateral { + serializer.write_unsigned_integer(13)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.required_signers { + serializer.write_unsigned_integer(14)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.network_id { + serializer.write_unsigned_integer(15)?; + field.serialize(serializer)?; + } Ok(serializer) } } @@ -244,8 +260,8 @@ impl Deserialize for TransactionBody { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - //let mut read_len = CBORReadLen::new(len); - //read_len.read_elems(3)?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(3)?; let mut inputs = None; let mut outputs = None; let mut fee = None; @@ -253,9 +269,13 @@ impl Deserialize for TransactionBody { let mut certs = None; let mut withdrawals = None; let mut update = None; - let mut metadata_hash = None; + let mut auxiliary_data_hash = None; let mut validity_start_interval = None; let mut mint = None; + let mut script_data_hash = None; + let mut collateral = None; + let mut required_signers = None; + let mut network_id = 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()? { @@ -289,7 +309,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); } ttl = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Slot::deserialize(raw)?) })().map_err(|e| e.annotate("ttl"))?); }, @@ -298,7 +318,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); } certs = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Certificates::deserialize(raw)?) })().map_err(|e| e.annotate("certs"))?); }, @@ -307,7 +327,7 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); } withdrawals = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Withdrawals::deserialize(raw)?) })().map_err(|e| e.annotate("withdrawals"))?); }, @@ -316,25 +336,25 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); } update = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Update::deserialize(raw)?) })().map_err(|e| e.annotate("update"))?); }, 7 => { - if metadata_hash.is_some() { + if auxiliary_data_hash.is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); } - metadata_hash = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; - Ok(MetadataHash::deserialize(raw)?) - })().map_err(|e| e.annotate("metadata_hash"))?); + auxiliary_data_hash = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(AuxiliaryDataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("auxiliary_data_hash"))?); }, 8 => { if validity_start_interval.is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); } validity_start_interval = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Slot::deserialize(raw)?) })().map_err(|e| e.annotate("validity_start_interval"))?); }, @@ -343,10 +363,46 @@ impl Deserialize for TransactionBody { return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); } mint = Some((|| -> Result<_, DeserializeError> { - //read_len.read_elems(1)?; + read_len.read_elems(1)?; Ok(Mint::deserialize(raw)?) })().map_err(|e| e.annotate("mint"))?); }, + 11 => { + if script_data_hash.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + script_data_hash = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ScriptDataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("script_data_hash"))?); + }, + 13 => { + if collateral.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + collateral = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(TransactionInputs::deserialize(raw)?) + })().map_err(|e| e.annotate("collateral"))?); + }, + 14 => { + if required_signers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + required_signers = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(RequiredSigners::deserialize(raw)?) + })().map_err(|e| e.annotate("required_signers"))?); + }, + 15 => { + if network_id.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); + } + network_id = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NetworkId::deserialize(raw)?) + })().map_err(|e| e.annotate("network_id"))?); + }, unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, CBORType::Text => match raw.text()?.as_str() { @@ -375,7 +431,7 @@ impl Deserialize for TransactionBody { Some(x) => x, None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(2)).into()), }; - //read_len.finish()?; + read_len.finish()?; Ok(Self { inputs, outputs, @@ -384,9 +440,13 @@ impl Deserialize for TransactionBody { certs, withdrawals, update, - metadata_hash, + auxiliary_data_hash, validity_start_interval, mint, + script_data_hash, + collateral, + required_signers, + network_id, }) })().map_err(|e| e.annotate("TransactionBody")) } @@ -435,13 +495,18 @@ impl DeserializeEmbeddedGroup for TransactionInput { impl cbor_event::se::Serialize for TransactionOutput { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_array(cbor_event::Len::Len(if self.data_hash.is_some() { 3 } else { 2 }))?; self.address.serialize(serializer)?; self.amount.serialize(serializer)?; + if let Some(data_hash) = &self.data_hash { + data_hash.serialize(serializer)?; + } Ok(serializer) } } +// this is used when deserializing it on its own, but the more likely case +// is when it's done via TransactionOutputs impl Deserialize for TransactionOutput { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { @@ -459,17 +524,49 @@ impl Deserialize for TransactionOutput { } } +// this is used by both TransactionOutput (on its own)'s deserialize +// but also for TransactionOutputs +// This implementation was hand-coded since cddl-codegen doesn't support deserialization +// with array-encoded types with optional fields, due to the complexity. +// This is made worse as this is a plain group... impl DeserializeEmbeddedGroup for TransactionOutput { fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { + use std::convert::TryInto; let address = (|| -> Result<_, DeserializeError> { Ok(Address::deserialize(raw)?) })().map_err(|e| e.annotate("address"))?; let amount = (|| -> Result<_, DeserializeError> { Ok(Value::deserialize(raw)?) })().map_err(|e| e.annotate("amount"))?; + // there are only two cases so far where this is used: + // 1) on its own inside of TransactionOutput's Deserialize trait (only used if someone calls to_bytes() on it) + // 2) from TransactionOutput's deserialization + // in 1) we would encounter an array-end (or track it for definite deserialization - which we don't do right now) + // and in 2) we would encounter the same OR we would encounter the next TransactionOutput in the array + // Unfortunately, both address and data hash are bytes type, so we can't just check the type, but instead + // must check the length, and backtrack if that wasn't the case. + let data_hash = match raw.cbor_type() { + Ok(cbor_event::Type::Bytes) => { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let bytes = raw.bytes().unwrap(); + if bytes.len() == DataHash::BYTE_COUNT { + Some(DataHash(bytes[..DataHash::BYTE_COUNT].try_into().unwrap())) + } else { + // This is an address of the next output in sequence, which luckily is > 32 bytes so there's no confusion + // Go to previous place in array then carry on + raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(); + None + } + }, + // not possibly a data hash + Ok(_) | + // end of input + Err(_) => None, + }; Ok(TransactionOutput { address, amount, + data_hash, }) } } @@ -1124,13 +1221,8 @@ impl Deserialize for StakeCredentials { } } -impl cbor_event::se::Serialize for MoveInstantaneousReward { +impl cbor_event::se::Serialize for MIRToStakeCredentials { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match self.pot { - MIRPot::Reserves => serializer.write_unsigned_integer(0u64), - MIRPot::Treasury => serializer.write_unsigned_integer(1u64), - }?; serializer.write_map(cbor_event::Len::Len(self.rewards.len() as u64))?; for (key, value) in &self.rewards { key.serialize(serializer)?; @@ -1140,28 +1232,57 @@ impl cbor_event::se::Serialize for MoveInstantaneousReward { } } -impl Deserialize for MoveInstantaneousReward { +impl Deserialize for MIRToStakeCredentials { fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = linked_hash_map::LinkedHashMap::new(); - let pot = (|| -> Result<_, DeserializeError> { - let outer_len = raw.array()?; - let pot = match raw.unsigned_integer()? { - 0 => MIRPot::Reserves, - 1 => MIRPot::Treasury, - n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), - }; - let len = raw.map()?; + (|| -> Result<_, DeserializeError> { + let mut table = linked_hash_map::LinkedHashMap::new();let len = raw.map()?; while match len { cbor_event::Len::Len(n) => table.len() < n as usize, cbor_event::Len::Indefinite => true, } { if raw.cbor_type()? == CBORType::Special { assert_eq!(raw.special()?, CBORSpecial::Break); break; } let key = StakeCredential::deserialize(raw)?; - let value = Coin::deserialize(raw)?; + let value = DeltaCoin::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + return Err(DeserializeFailure::DuplicateKey(Key::Str(format!("StakeCred: {} (hex bytes)", hex::encode(key.to_bytes())))).into()); } } + Ok(Self { + rewards: table + }) + })().map_err(|e| e.annotate("MIRToStakeCredentials")) + + } +} + +impl cbor_event::se::Serialize for MoveInstantaneousReward { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match self.pot { + MIRPot::Reserves => serializer.write_unsigned_integer(0u64), + MIRPot::Treasury => serializer.write_unsigned_integer(1u64), + }?; + match &self.variant { + MIREnum::ToOtherPot(amount) => amount.serialize(serializer), + MIREnum::ToStakeCredentials(amounts) => amounts.serialize(serializer), + } + } +} + +impl Deserialize for MoveInstantaneousReward { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let outer_len = raw.array()?; + let pot = match raw.unsigned_integer()? { + 0 => MIRPot::Reserves, + 1 => MIRPot::Treasury, + n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), + }; + let variant = match raw.cbor_type()? { + CBORType::UnsignedInteger => MIREnum::ToOtherPot(Coin::deserialize(raw)?), + CBORType::Map => MIREnum::ToStakeCredentials(MIRToStakeCredentials::deserialize(raw)?), + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + }; match outer_len { cbor_event::Len::Len(n) => if n != 2 { return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen(n, outer_len, "MoveInstantaneousReward")).into()) @@ -1171,12 +1292,11 @@ impl Deserialize for MoveInstantaneousReward { _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, }; - Ok(pot) - })().map_err(|e| e.annotate("MoveInstantaneousReward"))?; - Ok(Self { - pot, - rewards: table - }) + Ok(Self { + pot, + variant, + }) + })().map_err(|e| e.annotate("MoveInstantaneousReward")) } } @@ -1536,7 +1656,7 @@ impl cbor_event::se::Serialize for PoolMetadata { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(2))?; self.url.serialize(serializer)?; - self.metadata_hash.serialize(serializer)?; + self.pool_metadata_hash.serialize(serializer)?; Ok(serializer) } } @@ -1563,12 +1683,12 @@ impl DeserializeEmbeddedGroup for PoolMetadata { let url = (|| -> Result<_, DeserializeError> { Ok(URL::deserialize(raw)?) })().map_err(|e| e.annotate("url"))?; - let metadata_hash = (|| -> Result<_, DeserializeError> { - Ok(MetadataHash::deserialize(raw)?) - })().map_err(|e| e.annotate("metadata_hash"))?; + let pool_metadata_hash = (|| -> Result<_, DeserializeError> { + Ok(PoolMetadataHash::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_metadata_hash"))?; Ok(PoolMetadata { url, - metadata_hash, + pool_metadata_hash, }) } } @@ -1637,12 +1757,12 @@ impl Deserialize for Withdrawals { impl cbor_event::se::Serialize for TransactionWitnessSet { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(match &self.vkeys { Some(x) => 1, None => 0 } + match &self.scripts { Some(x) => 1, None => 0 } + match &self.bootstraps { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(match &self.vkeys { Some(x) => 1, None => 0 } + match &self.native_scripts { Some(x) => 1, None => 0 } + match &self.bootstraps { Some(x) => 1, None => 0 } + match &self.plutus_scripts { Some(x) => 1, None => 0 } + match &self.plutus_data { Some(x) => 1, None => 0 } + match &self.redeemers { Some(x) => 1, None => 0 }))?; if let Some(field) = &self.vkeys { serializer.write_unsigned_integer(0)?; field.serialize(serializer)?; } - if let Some(field) = &self.scripts { + if let Some(field) = &self.native_scripts { serializer.write_unsigned_integer(1)?; field.serialize(serializer)?; } @@ -1650,6 +1770,18 @@ impl cbor_event::se::Serialize for TransactionWitnessSet { serializer.write_unsigned_integer(2)?; field.serialize(serializer)?; } + if let Some(field) = &self.plutus_scripts { + serializer.write_unsigned_integer(3)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.plutus_data { + serializer.write_unsigned_integer(4)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.redeemers { + serializer.write_unsigned_integer(5)?; + field.serialize(serializer)?; + } Ok(serializer) } } @@ -1658,65 +1790,97 @@ impl Deserialize for TransactionWitnessSet { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - Self::deserialize_as_embedded_group(raw, len) - })().map_err(|e| e.annotate("TransactionWitnessSet")) - } -} - -impl DeserializeEmbeddedGroup for TransactionWitnessSet { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let mut vkeys = None; - let mut scripts = None; - let mut bootstraps = 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()? { - 0 => { - if vkeys.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - vkeys = Some((|| -> Result<_, DeserializeError> { - Ok(Vkeywitnesses::deserialize(raw)?) - })().map_err(|e| e.annotate("vkeys"))?); - }, - 1 => { - if scripts.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - scripts = Some((|| -> Result<_, DeserializeError> { - Ok(NativeScripts::deserialize(raw)?) - })().map_err(|e| e.annotate("scripts"))?); + let mut read_len = CBORReadLen::new(len); + let mut vkeys = None; + let mut native_scripts = None; + let mut bootstraps = None; + let mut plutus_scripts = None; + let mut plutus_data = None; + let mut redeemers = 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()? { + 0 => { + if vkeys.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + vkeys = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Vkeywitnesses::deserialize(raw)?) + })().map_err(|e| e.annotate("vkeys"))?); + }, + 1 => { + if native_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + native_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("native_scripts"))?); + }, + 2 => { + if bootstraps.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + bootstraps = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(BootstrapWitnesses::deserialize(raw)?) + })().map_err(|e| e.annotate("bootstraps"))?); + }, + 3 => { + if plutus_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + plutus_scripts = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_scripts"))?); + }, + 4 => { + if plutus_data.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + plutus_data = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusList::deserialize(raw)?) + })().map_err(|e| e.annotate("plutus_data"))?); + }, + 5 => { + if redeemers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + redeemers = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Redeemers::deserialize(raw)?) + })().map_err(|e| e.annotate("redeemers"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, - 2 => { - if bootstraps.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - bootstraps = Some((|| -> Result<_, DeserializeError> { - Ok(BootstrapWitnesses::deserialize(raw)?) - })().map_err(|e| e.annotate("bootstraps"))?); + CBORType::Text => match raw.text()?.as_str() { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), }, - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), - }, - CBORType::Text => match raw.text()?.as_str() { - 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()), + 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()), + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; } - read += 1; - } - Ok(Self { - vkeys, - scripts, - bootstraps, - }) + read_len.finish()?; + Ok(Self { + vkeys, + native_scripts, + bootstraps, + plutus_scripts, + plutus_data, + redeemers, + }) + })().map_err(|e| e.annotate("TransactionWitnessSet")) } } @@ -2354,10 +2518,9 @@ impl Deserialize for ProtocolVersions { Ok(Self(arr)) } } - impl cbor_event::se::Serialize for ProtocolParamUpdate { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(match &self.minfee_a { Some(x) => 1, None => 0 } + match &self.minfee_b { Some(x) => 1, None => 0 } + match &self.max_block_body_size { Some(x) => 1, None => 0 } + match &self.max_tx_size { Some(x) => 1, None => 0 } + match &self.max_block_header_size { Some(x) => 1, None => 0 } + match &self.key_deposit { Some(x) => 1, None => 0 } + match &self.pool_deposit { Some(x) => 1, None => 0 } + match &self.max_epoch { Some(x) => 1, None => 0 } + match &self.n_opt { Some(x) => 1, None => 0 } + match &self.pool_pledge_influence { Some(x) => 1, None => 0 } + match &self.expansion_rate { Some(x) => 1, None => 0 } + match &self.treasury_growth_rate { Some(x) => 1, None => 0 } + match &self.d { Some(x) => 1, None => 0 } + match &self.extra_entropy { Some(x) => 1, None => 0 } + match &self.protocol_version { Some(x) => 1, None => 0 } + match &self.min_utxo_value { Some(x) => 1, None => 0 }))?; + serializer.write_map(cbor_event::Len::Len(match &self.minfee_a { Some(x) => 1, None => 0 } + match &self.minfee_b { Some(x) => 1, None => 0 } + match &self.max_block_body_size { Some(x) => 1, None => 0 } + match &self.max_tx_size { Some(x) => 1, None => 0 } + match &self.max_block_header_size { Some(x) => 1, None => 0 } + match &self.key_deposit { Some(x) => 1, None => 0 } + match &self.pool_deposit { Some(x) => 1, None => 0 } + match &self.max_epoch { Some(x) => 1, None => 0 } + match &self.n_opt { Some(x) => 1, None => 0 } + match &self.pool_pledge_influence { Some(x) => 1, None => 0 } + match &self.expansion_rate { Some(x) => 1, None => 0 } + match &self.treasury_growth_rate { Some(x) => 1, None => 0 } + match &self.d { Some(x) => 1, None => 0 } + match &self.extra_entropy { Some(x) => 1, None => 0 } + match &self.protocol_version { Some(x) => 1, None => 0 } + match &self.min_pool_cost { Some(x) => 1, None => 0 } + match &self.ada_per_utxo_byte { Some(x) => 1, None => 0 } + match &self.cost_models { Some(x) => 1, None => 0 } + match &self.execution_costs { Some(x) => 1, None => 0 } + match &self.max_tx_ex_units { Some(x) => 1, None => 0 } + match &self.max_block_ex_units { Some(x) => 1, None => 0 } + match &self.max_value_size { Some(x) => 1, None => 0 }))?; if let Some(field) = &self.minfee_a { serializer.write_unsigned_integer(0)?; field.serialize(serializer)?; @@ -2418,8 +2581,32 @@ impl cbor_event::se::Serialize for ProtocolParamUpdate { serializer.write_unsigned_integer(14)?; field.serialize(serializer)?; } - if let Some(field) = &self.min_utxo_value { - serializer.write_unsigned_integer(15)?; + if let Some(field) = &self.min_pool_cost { + serializer.write_unsigned_integer(16)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.ada_per_utxo_byte { + serializer.write_unsigned_integer(17)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.cost_models { + serializer.write_unsigned_integer(18)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.execution_costs { + serializer.write_unsigned_integer(19)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_tx_ex_units { + serializer.write_unsigned_integer(20)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_block_ex_units { + serializer.write_unsigned_integer(21)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_value_size { + serializer.write_unsigned_integer(22)?; field.serialize(serializer)?; } Ok(serializer) @@ -2430,195 +2617,273 @@ impl Deserialize for ProtocolParamUpdate { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.map()?; - Self::deserialize_as_embedded_group(raw, len) - })().map_err(|e| e.annotate("ProtocolParamUpdate")) - } -} - -impl DeserializeEmbeddedGroup for ProtocolParamUpdate { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let mut minfee_a = None; - let mut minfee_b = None; - let mut max_block_body_size = None; - let mut max_tx_size = None; - let mut max_block_header_size = None; - let mut key_deposit = None; - let mut pool_deposit = None; - let mut max_epoch = None; - let mut n_opt = None; - let mut pool_pledge_influence = None; - let mut expansion_rate = None; - let mut treasury_growth_rate = None; - let mut d = None; - let mut extra_entropy = None; - let mut protocol_version = None; - let mut min_utxo_value = 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()? { - 0 => { - if minfee_a.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - minfee_a = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("minfee_a"))?); - }, - 1 => { - if minfee_b.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - minfee_b = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("minfee_b"))?); - }, - 2 => { - if max_block_body_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - max_block_body_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_block_body_size"))?); - }, - 3 => { - if max_tx_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - max_tx_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_tx_size"))?); - }, - 4 => { - if max_block_header_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); - } - max_block_header_size = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("max_block_header_size"))?); - }, - 5 => { - if key_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); - } - key_deposit = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("key_deposit"))?); - }, - 6 => { - if pool_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); - } - pool_deposit = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("pool_deposit"))?); - }, - 7 => { - if max_epoch.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); - } - max_epoch = Some((|| -> Result<_, DeserializeError> { - Ok(Epoch::deserialize(raw)?) - })().map_err(|e| e.annotate("max_epoch"))?); - }, - 8 => { - if n_opt.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); - } - n_opt = Some((|| -> Result<_, DeserializeError> { - Ok(u32::deserialize(raw)?) - })().map_err(|e| e.annotate("n_opt"))?); - }, - 9 => { - if pool_pledge_influence.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); - } - pool_pledge_influence = Some((|| -> Result<_, DeserializeError> { - Ok(Rational::deserialize(raw)?) - })().map_err(|e| e.annotate("pool_pledge_influence"))?); - }, - 10 => { - if expansion_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); - } - expansion_rate = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("expansion_rate"))?); - }, - 11 => { - if treasury_growth_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); - } - treasury_growth_rate = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("treasury_growth_rate"))?); - }, - 12 => { - if d.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); - } - d = Some((|| -> Result<_, DeserializeError> { - Ok(UnitInterval::deserialize(raw)?) - })().map_err(|e| e.annotate("d"))?); - }, - 13 => { - if extra_entropy.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); - } - extra_entropy = Some((|| -> Result<_, DeserializeError> { - Ok(Nonce::deserialize(raw)?) - })().map_err(|e| e.annotate("extra_entropy"))?); - }, - 14 => { - if protocol_version.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); - } - protocol_version = Some((|| -> Result<_, DeserializeError> { - Ok(ProtocolVersions::deserialize(raw)?) - })().map_err(|e| e.annotate("protocol_version"))?); + let mut read_len = CBORReadLen::new(len); + let mut minfee_a = None; + let mut minfee_b = None; + let mut max_block_body_size = None; + let mut max_tx_size = None; + let mut max_block_header_size = None; + let mut key_deposit = None; + let mut pool_deposit = None; + let mut max_epoch = None; + let mut n_opt = None; + let mut pool_pledge_influence = None; + let mut expansion_rate = None; + let mut treasury_growth_rate = None; + let mut d = None; + let mut extra_entropy = None; + let mut protocol_version = None; + let mut min_pool_cost = None; + let mut ada_per_utxo_byte = None; + let mut cost_models = None; + let mut execution_costs = None; + let mut max_tx_ex_units = None; + let mut max_block_ex_units = None; + let mut max_value_size = 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()? { + 0 => { + if minfee_a.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + minfee_a = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("minfee_a"))?); + }, + 1 => { + if minfee_b.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + minfee_b = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("minfee_b"))?); + }, + 2 => { + if max_block_body_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + max_block_body_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_body_size"))?); + }, + 3 => { + if max_tx_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + max_tx_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_tx_size"))?); + }, + 4 => { + if max_block_header_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + max_block_header_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_header_size"))?); + }, + 5 => { + if key_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + key_deposit = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("key_deposit"))?); + }, + 6 => { + if pool_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); + } + pool_deposit = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_deposit"))?); + }, + 7 => { + if max_epoch.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); + } + max_epoch = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })().map_err(|e| e.annotate("max_epoch"))?); + }, + 8 => { + if n_opt.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); + } + n_opt = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("n_opt"))?); + }, + 9 => { + if pool_pledge_influence.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); + } + pool_pledge_influence = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Rational::deserialize(raw)?) + })().map_err(|e| e.annotate("pool_pledge_influence"))?); + }, + 10 => { + if expansion_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); + } + expansion_rate = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("expansion_rate"))?); + }, + 11 => { + if treasury_growth_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + treasury_growth_rate = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("treasury_growth_rate"))?); + }, + 12 => { + if d.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); + } + d = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })().map_err(|e| e.annotate("d"))?); + }, + 13 => { + if extra_entropy.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + extra_entropy = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Nonce::deserialize(raw)?) + })().map_err(|e| e.annotate("extra_entropy"))?); + }, + 14 => { + if protocol_version.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + protocol_version = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ProtocolVersions::deserialize(raw)?) + })().map_err(|e| e.annotate("protocol_version"))?); + }, + 16 => { + if min_pool_cost.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); + } + min_pool_cost = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("min_pool_cost"))?); + }, + 17 => { + if ada_per_utxo_byte.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); + } + ada_per_utxo_byte = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })().map_err(|e| e.annotate("ada_per_utxo_byte"))?); + }, + 18 => { + if cost_models.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); + } + cost_models = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Costmdls::deserialize(raw)?) + })().map_err(|e| e.annotate("cost_models"))?); + }, + 19 => { + if execution_costs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(19)).into()); + } + execution_costs = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnitPrices::deserialize(raw)?) + })().map_err(|e| e.annotate("execution_costs"))?); + }, + 20 => { + if max_tx_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(20)).into()); + } + max_tx_ex_units = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("max_tx_ex_units"))?); + }, + 21 => { + if max_block_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(21)).into()); + } + max_block_ex_units = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })().map_err(|e| e.annotate("max_block_ex_units"))?); + }, + 22 => { + if max_value_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(22)).into()); + } + max_value_size = Some((|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })().map_err(|e| e.annotate("max_value_size"))?); + }, + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), }, - 15 => { - if min_utxo_value.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); - } - min_utxo_value = Some((|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })().map_err(|e| e.annotate("min_utxo_value"))?); + CBORType::Text => match raw.text()?.as_str() { + unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Str(unknown_key.to_owned())).into()), }, - unknown_key => return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()), - }, - CBORType::Text => match raw.text()?.as_str() { - 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()), + 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; - } - Ok(Self { - minfee_a, - minfee_b, - max_block_body_size, - max_tx_size, - max_block_header_size, - key_deposit, - pool_deposit, - max_epoch, - n_opt, - pool_pledge_influence, - expansion_rate, - treasury_growth_rate, - d, - extra_entropy, - protocol_version, - min_utxo_value, - }) + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + read_len.finish()?; + Ok(Self { + minfee_a, + minfee_b, + max_block_body_size, + max_tx_size, + max_block_header_size, + key_deposit, + pool_deposit, + max_epoch, + n_opt, + pool_pledge_influence, + expansion_rate, + treasury_growth_rate, + d, + extra_entropy, + protocol_version, + min_pool_cost, + ada_per_utxo_byte, + cost_models, + execution_costs, + max_tx_ex_units, + max_block_ex_units, + max_value_size, + }) + })().map_err(|e| e.annotate("ProtocolParamUpdate")) } } @@ -2678,7 +2943,7 @@ impl Deserialize for TransactionWitnessSets { } } -impl cbor_event::se::Serialize for MapTransactionIndexToTransactionMetadata { +impl cbor_event::se::Serialize for AuxiliaryDataSet { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; for (key, value) in &self.0 { @@ -2689,7 +2954,7 @@ impl cbor_event::se::Serialize for MapTransactionIndexToTransactionMetadata { } } -impl Deserialize for MapTransactionIndexToTransactionMetadata { +impl Deserialize for AuxiliaryDataSet { fn deserialize(raw: &mut Deserializer) -> Result { let mut table = linked_hash_map::LinkedHashMap::new(); (|| -> Result<_, DeserializeError> { @@ -2700,24 +2965,28 @@ impl Deserialize for MapTransactionIndexToTransactionMetadata { break; } let key = TransactionIndex::deserialize(raw)?; - let value = TransactionMetadata::deserialize(raw)?; + let value = AuxiliaryData::deserialize(raw)?; if table.insert(key.clone(), value).is_some() { return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); } } Ok(()) - })().map_err(|e| e.annotate("MapTransactionIndexToTransactionMetadata"))?; + })().map_err(|e| e.annotate("AuxiliaryDataSet"))?; Ok(Self(table)) } } impl cbor_event::se::Serialize for Block { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; + serializer.write_array(cbor_event::Len::Len(5))?; self.header.serialize(serializer)?; self.transaction_bodies.serialize(serializer)?; self.transaction_witness_sets.serialize(serializer)?; - self.transaction_metadata_set.serialize(serializer)?; + self.auxiliary_data_set.serialize(serializer)?; + serializer.write_array(cbor_event::Len::Len(self.invalid_transactions.len() as u64))?; + for element in self.invalid_transactions.iter() { + element.serialize(serializer)?; + } Ok(serializer) } } @@ -2726,42 +2995,50 @@ impl Deserialize for Block { fn deserialize(raw: &mut Deserializer) -> Result { (|| -> Result<_, DeserializeError> { let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(5)?; + let header = (|| -> Result<_, DeserializeError> { + Ok(Header::deserialize(raw)?) + })().map_err(|e| e.annotate("header"))?; + let transaction_bodies = (|| -> Result<_, DeserializeError> { + Ok(TransactionBodies::deserialize(raw)?) + })().map_err(|e| e.annotate("transaction_bodies"))?; + let transaction_witness_sets = (|| -> Result<_, DeserializeError> { + Ok(TransactionWitnessSets::deserialize(raw)?) + })().map_err(|e| e.annotate("transaction_witness_sets"))?; + let auxiliary_data_set = (|| -> Result<_, DeserializeError> { + Ok(AuxiliaryDataSet::deserialize(raw)?) + })().map_err(|e| e.annotate("auxiliary_data_set"))?; + let invalid_transactions = (|| -> Result<_, DeserializeError> { + let mut arr = Vec::new(); + let len = raw.array()?; + while match len { cbor_event::Len::Len(n) => arr.len() < n as usize, cbor_event::Len::Indefinite => true, } { + if raw.cbor_type()? == CBORType::Special { + assert_eq!(raw.special()?, CBORSpecial::Break); + break; + } + arr.push(TransactionIndex::deserialize(raw)?); + } + Ok(arr) + })().map_err(|e| e.annotate("invalid_transactions"))?; match len { - cbor_event::Len::Len(_) => /* TODO: check finite len somewhere */(), + cbor_event::Len::Len(_) => (), cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => /* it's ok */(), + CBORSpecial::Break => (), _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } - ret + Ok(Block { + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + }) })().map_err(|e| e.annotate("Block")) } } -impl DeserializeEmbeddedGroup for Block { - fn deserialize_as_embedded_group(raw: &mut Deserializer, len: cbor_event::Len) -> Result { - let header = (|| -> Result<_, DeserializeError> { - Ok(Header::deserialize(raw)?) - })().map_err(|e| e.annotate("header"))?; - let transaction_bodies = (|| -> Result<_, DeserializeError> { - Ok(TransactionBodies::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_bodies"))?; - let transaction_witness_sets = (|| -> Result<_, DeserializeError> { - Ok(TransactionWitnessSets::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_witness_sets"))?; - let transaction_metadata_set = (|| -> Result<_, DeserializeError> { - Ok(MapTransactionIndexToTransactionMetadata::deserialize(raw)?) - })().map_err(|e| e.annotate("transaction_metadata_set"))?; - Ok(Block { - header, - transaction_bodies, - transaction_witness_sets, - transaction_metadata_set, - }) - } -} - impl cbor_event::se::Serialize for Header { fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { serializer.write_array(cbor_event::Len::Len(2))?; @@ -3133,3 +3410,78 @@ impl Deserialize for Mint { } } +impl cbor_event::se::Serialize for NetworkId { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + match self.0 { + NetworkIdKind::Testnet => { + serializer.write_unsigned_integer(0u64) + }, + NetworkIdKind::Mainnet => { + serializer.write_unsigned_integer(1u64) + }, + } + } +} + +impl Deserialize for NetworkId { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer()? { + 0 => Ok(NetworkId::testnet()), + 1 => Ok(NetworkId::mainnet()), + _ => Err(DeserializeError::new("NetworkId", DeserializeFailure::NoVariantMatched.into())), + } + })().map_err(|e| e.annotate("NetworkId")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tx_output_deser() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput::new(&addr, &val); + let mut txo_dh = txo.clone(); + txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); + } + + #[test] + fn mir_deser() { + let reserves_to_pot = MoveInstantaneousReward::new_to_other_pot(MIRPot::Treasury, &Coin::from_str("143546464").unwrap()); + let reserves_to_pot_deser = MoveInstantaneousReward::from_bytes(reserves_to_pot.to_bytes()).unwrap(); + assert_eq!(reserves_to_pot.to_bytes(), reserves_to_pot_deser.to_bytes()); + let treasury_to_pot = MoveInstantaneousReward::new_to_other_pot(MIRPot::Treasury, &Coin::from_str("0").unwrap()); + let treasury_to_pot_deser = MoveInstantaneousReward::from_bytes(treasury_to_pot.to_bytes()).unwrap(); + assert_eq!(treasury_to_pot.to_bytes(), treasury_to_pot_deser.to_bytes()); + let mut stake_creds = MIRToStakeCredentials::new(); + stake_creds.insert(&StakeCredential::from_scripthash(&ScriptHash([54u8; ScriptHash::BYTE_COUNT])), &Int::new_i32(-314159265)); + let to_stake_creds = MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &stake_creds); + let to_stake_creds_deser = MoveInstantaneousReward::from_bytes(to_stake_creds.to_bytes()).unwrap(); + assert_eq!(to_stake_creds.to_bytes(), to_stake_creds_deser.to_bytes()); + + } + + #[test] + #[ignore] + fn alonzo_block() { + // this test for some reason has 2-byte pool metadata hashes so don't run this without changing that + let bytes = hex::decode("85828f03095820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c8258404fefc7c718693b57c87170ceba220382afbdd148c0a53b4a009ca63ad1f101483a6170c83a77f23d362a68dcb502802df7f98fa4f7a78b4082b211530e1234305850f770f6769ae9871d42b970fc6254bb927c2181fff45897f241bd72221d86d33c8df64c0a3c8cbb9aa52fef191d7202465c52df8d33727a38c7dc5d40864d753348a340f8afcbb3bb05d4a03f16b1080d825840fe682775f0fa232e909ddc9ec3210ea7a0ee6514cd8b0815190a08f7cef3985463152e10dfad9ed6c09b641b6c1824498e77814a7c12e03096a63cd62056446358500951ed3ef2065e4196d008b50a63bb3e2bdc9a64df67eff4e230b35429291def476684114e074357a5c834bf79eacf583b6fe9fcd1d17f3719a31de97aa4da5e4630b05421359e0b6e4a9bd76c6b920b190929582010c865acec05c84c2c0d0b889f7dbe9bf3b5561f8552da1eb286eac4ccdabc5e5820d298da3803eb9958f01c02e73f2410f2f9bb2ecbc346526b1b76772e1bdd7db500005840940f8a3696847e4a238705bdd27a345086282067b9bc5cb7b69847ca8756085844d576f59ab056c169a504320cc1eab4c11fd529482b3c57da6fa96d44635b0802005901c0a1b2ee63b357fe0b19c6bb8dc3fc865c0608a89626505c5f9aff3b74a0809ca2635e0c4235c247306987c7fd76a4a06210ebf74178e72a1faa78fb8865a69005cc6a5ab5c9b40f817c715df558af7d07b6186f0ccf31715ec2fb00980730ac166af657e6670608afe1bf651d496e01b1c7ff1eb44614d8cfd1b7e32b2c2939349236cc0ada145d8d8d7ad919ef1e60c8bbad31dbedf9f395849705a00c14a8785106aae31f55abc5b1f2089cbef16d9401f158704c1e4f740f7125cfc700a99d97d0332eacb33e4bbc8dab2872ec2b3df9e113addaebd156bfc64fdfc732614d2aedd10a58a34993b7b08c822af3aa615b6bbb9b267bc902e4f1075e194aed084ca18f8bcde1a6b094bf3f5295a0d454c0a083ed5b74f7092fc0a7346c03979a30eeea76d686e512ba48d21544ba874886cdd166cbf275b11f1f3881f4c4277c09a24b88fc6168f4578267bdc9d62cb9b78b8dfc888ccce226a177725f39e7d50930552861d1e88b7898971c780dc3b773321ba1854422b5cecead7d50e77783050eeae2cd9595b9cd91681c72e5d53bb7d12f28dec9b2847ee70a3d7781fb1133aea3b169f536ff5945ec0a76950e51beded0627bb78120617a2f0842e50e3981ae0081825820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25000d81825820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa01018183583900cb9358529df4729c3246a2a033cb9821abbfd16de4888005904abc410d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb06821864a1581ca646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520aa14a636f75747473436f696e1903e85820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25021903e70304048382008200581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb068a03581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb065820c5e21ab1c9f6022d81c3b25e3436cb7f1df77f9652ae3e1310c28e621dd87b4c0105d81e82010a581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0681581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0680826e636f6e73656e7375732e706f6f6c427b7d82068200a18200581c008b47844d92812fc30d1f0ac9b6fbf38778ccba9db8312ad9079079186e05a1581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0618640682a1581ce0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541a104190fa00008020e81581cf81ce66e0f52da5ca48193386e7511fde5b030a307b4c3681736c6f009a1581cb16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5a14a636f75747473436f696e1903e80b58209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0758209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0f0181a400818258203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da295840815671b581b4b02a30108a799a85c7f2e5487fb667e748e8fde59e466ab987ce133ecb77ffa0dc53c5804e6706e26b94e17803235da28112bc747de48ccbd70903814c4b0100002002002002000011048118bf058184000019039782191388191388a100d90103a300a40166737472696e67024562797465730382010204a10341620181820180028148470100002002006180").unwrap(); + let block = Block::from_bytes(bytes).unwrap(); + let block2 = Block::from_bytes(block.to_bytes()).unwrap(); + assert_eq!(block.to_bytes(), block2.to_bytes()); + } +} \ No newline at end of file diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index 1579b282..2adf60c9 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -85,13 +85,17 @@ fn min_fee(tx_builder: &TransactionBuilder) -> Result { }; let witness_set = TransactionWitnessSet { vkeys: vkeys, - scripts: script_keys, + native_scripts: script_keys, bootstraps: bootstrap_keys, + // TODO: plutus support? + plutus_scripts: None, + plutus_data: None, + redeemers: None, }; let full_tx = Transaction { body, witness_set, - metadata: tx_builder.metadata.clone(), + auxiliary_data: tx_builder.auxiliary_data.clone(), }; fees::min_fee(&full_tx, &tx_builder.fee_algo) } @@ -124,7 +128,7 @@ pub struct TransactionBuilder { ttl: Option, // absolute slot number certs: Option, withdrawals: Option, - metadata: Option, + auxiliary_data: Option, validity_start_interval: Option, input_types: MockWitnessSet, mint: Option, @@ -277,8 +281,8 @@ impl TransactionBuilder { }; } - pub fn set_metadata(&mut self, metadata: &TransactionMetadata) { - self.metadata = Some(metadata.clone()) + pub fn set_auxiliary_data(&mut self, auxiliary_data: &AuxiliaryData) { + self.auxiliary_data = Some(auxiliary_data.clone()) } pub fn new( @@ -299,7 +303,7 @@ impl TransactionBuilder { ttl: None, certs: None, withdrawals: None, - metadata: None, + auxiliary_data: None, input_types: MockWitnessSet { vkeys: BTreeSet::new(), scripts: BTreeSet::new(), @@ -394,6 +398,8 @@ impl TransactionBuilder { let fee_for_change = builder.fee_for_output(&TransactionOutput { address: address.clone(), amount: change_estimator.clone(), + // TODO: data hash? + data_hash: None, })?; let new_fee = fee.checked_add(&fee_for_change)?; @@ -430,6 +436,7 @@ impl TransactionBuilder { builder.add_output(&TransactionOutput { address: address.clone(), amount: change_estimator.checked_sub(&Value::new(&new_fee.clone()))?, + data_hash: None, // TODO: How do we get DataHash? })?; Ok(true) @@ -455,12 +462,17 @@ impl TransactionBuilder { certs: self.certs.clone(), withdrawals: self.withdrawals.clone(), update: None, - metadata_hash: match &self.metadata { + auxiliary_data_hash: match &self.auxiliary_data { None => None, - Some(x) => Some(utils::hash_metadata(x)), + Some(x) => Some(utils::hash_auxiliary_data(x)), }, validity_start_interval: self.validity_start_interval, mint: self.mint.clone(), + // TODO: update for use with Alonzo + script_data_hash: None, + collateral: None, + required_signers: None, + network_id: None, }) } diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 0dd182b8..cd513057 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -482,6 +482,107 @@ impl Deserialize for Int { } } +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct BigInt(num_bigint::BigInt); + +to_from_bytes!(BigInt); + +#[wasm_bindgen] +impl BigInt { + pub fn as_u64(&self) -> Option { + let (sign, u64_digits) = self.0.to_u64_digits(); + if sign == num_bigint::Sign::Minus { + return None; + } + match u64_digits.len() { + 1 => Some(to_bignum(*u64_digits.first().unwrap())), + _ => None, + } + } + + pub fn from_str(text: &str) -> Result { + use std::str::FromStr; + num_bigint::BigInt::from_str(text) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + .map(BigInt) + } + + pub fn to_str(&self) -> String { + self.0.to_string() + } +} + +impl cbor_event::se::Serialize for BigInt { + fn serialize<'se, W: Write>(&self, serializer: &'se mut Serializer) -> cbor_event::Result<&'se mut Serializer> { + let (sign, u64_digits) = self.0.to_u64_digits(); + // we use the uint/nint encodings to use a minimum of space + if u64_digits.len() == 1 { + match sign { + // uint + num_bigint::Sign::Plus | + num_bigint::Sign::NoSign => serializer.write_unsigned_integer(*u64_digits.first().unwrap())?, + // nint + num_bigint::Sign::Minus => serializer.write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64)?, + }; + } else { + let (sign, bytes) = self.0.to_bytes_be(); + match sign { + // positive bigint + num_bigint::Sign::Plus | + num_bigint::Sign::NoSign => { + serializer.write_tag(2u64)?; + serializer.write_bytes(bytes)?; + }, + // negative bigint + num_bigint::Sign::Minus => { + serializer.write_tag(3u64)?; + use std::ops::Neg; + // CBOR RFC defines this as the bytes of -n -1 + let adjusted = self.0.clone().neg().checked_sub(&num_bigint::BigInt::from(1u32)).unwrap().to_biguint().unwrap(); + serializer.write_bytes(adjusted.to_bytes_be())?; + }, + } + } + Ok(serializer) + } +} + +impl Deserialize for BigInt { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + // bigint + CBORType::Tag => { + let tag = raw.tag()?; + let bytes = raw.bytes()?; + if bytes.len() > 64 { + return Err(DeserializeFailure::OutOfRange{ found: bytes.len(), min: 0, max: 64}.into()) + } + match tag { + // positive bigint + 2 => Ok(Self(num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes))), + // negative bigint + 3 => { + // CBOR RFC defines this as the bytes of -n -1 + let initial = num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes); + use std::ops::Neg; + let adjusted = initial.checked_add(&num_bigint::BigInt::from(1u32)).unwrap().neg(); + Ok(Self(adjusted)) + }, + _ => return Err(DeserializeFailure::TagMismatch{ found: tag, expected: 2 }.into()), + } + }, + // uint + CBORType::UnsignedInteger => Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?))), + // nint + CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(raw.negative_integer()?))), + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + } + })().map_err(|e| e.annotate("BigInt")) + } +} + // we use the cbor_event::Serialize trait directly // This is only for use for plain cddl groups who need to be embedded within outer groups. @@ -607,8 +708,8 @@ pub fn make_vkey_witness( } #[wasm_bindgen] -pub fn hash_metadata(metadata: &TransactionMetadata) -> MetadataHash { - MetadataHash::from(blake2b256(&metadata.to_bytes())) +pub fn hash_auxiliary_data(auxiliary_data: &AuxiliaryData) -> AuxiliaryDataHash { + AuxiliaryDataHash::from(blake2b256(&auxiliary_data.to_bytes())) } #[wasm_bindgen] pub fn hash_transaction(tx_body: &TransactionBody) -> TransactionHash { @@ -1525,4 +1626,45 @@ mod tests { assert_eq!(a.partial_cmp(&b), None); } } -} \ No newline at end of file + + #[test] + fn bigint_serialization() { + let zero = BigInt::from_str("0").unwrap(); + let zero_rt = BigInt::from_bytes(zero.to_bytes()).unwrap(); + assert_eq!(zero.to_str(), zero_rt.to_str()); + + let pos_small = BigInt::from_str("100").unwrap(); + let pos_small_rt = BigInt::from_bytes(pos_small.to_bytes()).unwrap(); + assert_eq!(pos_small.to_str(), pos_small_rt.to_str()); + + let pos_big = BigInt::from_str("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let pos_big_rt = BigInt::from_bytes(pos_big.to_bytes()).unwrap(); + assert_eq!(pos_big.to_str(), pos_big_rt.to_str()); + + let neg_small = BigInt::from_str("-100").unwrap(); + let neg_small_rt = BigInt::from_bytes(neg_small.to_bytes()).unwrap(); + assert_eq!(neg_small.to_str(), neg_small_rt.to_str()); + + let neg_big = BigInt::from_str("-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let neg_big_rt = BigInt::from_bytes(neg_big.to_bytes()).unwrap(); + assert_eq!(neg_big.to_str(), neg_big_rt.to_str()); + + // taken from CBOR RFC examples + // negative big int + assert_eq!(hex::decode("c349010000000000000000").unwrap(), BigInt::from_str("-18446744073709551617").unwrap().to_bytes()); + // positive big int + assert_eq!(hex::decode("c249010000000000000000").unwrap(), BigInt::from_str("18446744073709551616").unwrap().to_bytes()); + // uint + assert_eq!(hex::decode("1b000000e8d4a51000").unwrap(), BigInt::from_str("1000000000000").unwrap().to_bytes()); + // nint + // we can't use this due to cbor_event actually not supporting the full NINT spectrum as it uses an i64 for some reason... + //assert_eq!(hex::decode("3bffffffffffffffff").unwrap(), BigInt::from_str("-18446744073709551616").unwrap().to_bytes()); + // this one fits in an i64 though + assert_eq!(hex::decode("3903e7").unwrap(), BigInt::from_str("-1000").unwrap().to_bytes()); + + + let x = BigInt::from_str("-18446744073709551617").unwrap(); + let x_rt = BigInt::from_bytes(x.to_bytes()).unwrap(); + assert_eq!(x.to_str(), x_rt.to_str()); + } +}