diff --git a/libwallet/src/slate.rs b/libwallet/src/slate.rs index 3cd6255..9cd2715 100644 --- a/libwallet/src/slate.rs +++ b/libwallet/src/slate.rs @@ -42,8 +42,8 @@ use crate::slate_versions::v4::{ SlateStateV4, SlateV4, VersionCompatInfoV4, }; use crate::slate_versions::v5::{ - CommitsV5, KernelFeaturesArgsV5, OutputFeaturesV5, ParticipantDataV5, PaymentInfoV5, - SlateStateV5, SlateV5, VersionCompatInfoV5, + KernelFeaturesArgsV5, ParticipantDataV5, PaymentInfoV5, SlateStateV5, SlateV5, + VersionCompatInfoV5, }; use crate::slate_versions::VersionedSlate; use crate::slate_versions::{CURRENT_SLATE_VERSION, GRIN_BLOCK_HEADER_VERSION}; @@ -1032,72 +1032,6 @@ impl From<&Slate> for SlateV5 { } } -impl From<&Slate> for Option> { - fn from(slate: &Slate) -> Self { - slate_commitments(slate) - } -} - -fn slate_commitments(slate: &Slate) -> Option> -where - C: for<'a> From<&'a Input> + for<'a> From<&'a Output>, -{ - let tx = slate.tx.as_ref()?; - let mut commitments = vec![]; - match tx.inputs() { - Inputs::CommitOnly(_) => panic!("commit only inputs unsupported"), - Inputs::FeaturesAndCommit(ref inputs) => { - for input in inputs { - commitments.push(input.into()); - } - } - } - for output in tx.outputs() { - commitments.push(output.into()); - } - Some(commitments) -} - -impl From<&ParticipantData> for ParticipantDataV5 { - fn from(data: &ParticipantData) -> ParticipantDataV5 { - let ParticipantData { - public_blind_excess, - public_nonce, - part_sig, - } = data; - let public_blind_excess = *public_blind_excess; - let public_nonce = *public_nonce; - let part_sig = *part_sig; - ParticipantDataV5 { - xs: public_blind_excess, - nonce: public_nonce, - part: part_sig, - } - } -} - -impl From<&SlateState> for SlateStateV5 { - fn from(data: &SlateState) -> SlateStateV5 { - match data { - SlateState::Unknown => SlateStateV5::Unknown, - SlateState::Standard1 => SlateStateV5::Standard1, - SlateState::Standard2 => SlateStateV5::Standard2, - SlateState::Standard3 => SlateStateV5::Standard3, - SlateState::Invoice1 => SlateStateV5::Invoice1, - SlateState::Invoice2 => SlateStateV5::Invoice2, - SlateState::Invoice3 => SlateStateV5::Invoice3, - } - } -} - -impl From<&KernelFeaturesArgs> for KernelFeaturesArgsV5 { - fn from(data: &KernelFeaturesArgs) -> KernelFeaturesArgsV5 { - let KernelFeaturesArgs { lock_height } = data; - let lock_hgt = *lock_height; - KernelFeaturesArgsV5 { lock_hgt } - } -} - impl From<&VersionCompatInfo> for VersionCompatInfoV5 { fn from(data: &VersionCompatInfo) -> VersionCompatInfoV5 { let VersionCompatInfo { @@ -1141,16 +1075,6 @@ impl From<&PaymentInfo> for PaymentInfoV5 { } } -impl From for OutputFeaturesV5 { - fn from(of: OutputFeatures) -> OutputFeaturesV5 { - let index = match of { - OutputFeatures::Plain => 0, - OutputFeatures::Coinbase => 1, - }; - OutputFeaturesV5(index) - } -} - ///// V5 impl From for Slate { fn from(slate: SlateV5) -> Slate { @@ -1273,46 +1197,6 @@ impl From<&SlateV5> for Option { } } -impl From<&ParticipantDataV5> for ParticipantData { - fn from(data: &ParticipantDataV5) -> ParticipantData { - let ParticipantDataV5 { - xs: public_blind_excess, - nonce: public_nonce, - part: part_sig, - } = data; - let public_blind_excess = *public_blind_excess; - let public_nonce = *public_nonce; - let part_sig = *part_sig; - ParticipantData { - public_blind_excess, - public_nonce, - part_sig, - } - } -} - -impl From<&KernelFeaturesArgsV5> for KernelFeaturesArgs { - fn from(data: &KernelFeaturesArgsV5) -> KernelFeaturesArgs { - let KernelFeaturesArgsV5 { lock_hgt } = data; - let lock_height = *lock_hgt; - KernelFeaturesArgs { lock_height } - } -} - -impl From<&SlateStateV5> for SlateState { - fn from(data: &SlateStateV5) -> SlateState { - match data { - SlateStateV5::Unknown => SlateState::Unknown, - SlateStateV5::Standard1 => SlateState::Standard1, - SlateStateV5::Standard2 => SlateState::Standard2, - SlateStateV5::Standard3 => SlateState::Standard3, - SlateStateV5::Invoice1 => SlateState::Invoice1, - SlateStateV5::Invoice2 => SlateState::Invoice2, - SlateStateV5::Invoice3 => SlateState::Invoice3, - } - } -} - impl From<&VersionCompatInfoV5> for VersionCompatInfo { fn from(data: &VersionCompatInfoV5) -> VersionCompatInfo { let VersionCompatInfoV5 { @@ -1355,15 +1239,6 @@ impl From<&PaymentInfoV5> for PaymentInfo { } } -impl From for OutputFeatures { - fn from(of: OutputFeaturesV5) -> OutputFeatures { - match of.0 { - 1 => OutputFeatures::Coinbase, - 0 | _ => OutputFeatures::Plain, - } - } -} - ///////// V4 impl TryFrom for SlateV4 { type Error = Error; @@ -1430,7 +1305,20 @@ impl TryFrom<&Slate> for SlateV4 { impl From<&Slate> for Option> { fn from(slate: &Slate) -> Self { - slate_commitments(slate) + let tx = slate.tx.as_ref()?; + let mut commitments = vec![]; + match tx.inputs() { + Inputs::CommitOnly(_) => panic!("commit only inputs unsupported"), + Inputs::FeaturesAndCommit(ref inputs) => { + for input in inputs { + commitments.push(input.into()); + } + } + } + for output in tx.outputs() { + commitments.push(output.into()); + } + Some(commitments) } } diff --git a/libwallet/src/slate_versions/common.rs b/libwallet/src/slate_versions/common.rs new file mode 100644 index 0000000..0d3e870 --- /dev/null +++ b/libwallet/src/slate_versions/common.rs @@ -0,0 +1,207 @@ +// Copyright 2026 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Wire types shared by V4 and V5 slates + +use crate::grin_core::core::{Input, Output, TxKernel}; +use crate::grin_core::libtx::secp_ser; +use crate::grin_keychain::Identifier; +use crate::grin_util::secp; +use crate::grin_util::secp::key::PublicKey; +use crate::grin_util::secp::pedersen::{Commitment, RangeProof}; +use crate::grin_util::secp::Signature; +use crate::{slate_versions::ser, CbData}; + +/// Slate state definition +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum SlateState { + /// Unknown, coming from earlier versions of the slate + Unknown, + /// Standard flow, freshly init + Standard1, + /// Standard flow, return journey + Standard2, + /// Standard flow, ready for transaction posting + Standard3, + /// Invoice flow, freshly init + Invoice1, + /// Invoice flow, return journey + Invoice2, + /// Invoice flow, ready for tranasction posting + Invoice3, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +/// Kernel features arguments definition +pub struct KernelFeaturesArgs { + /// Lock height, for HeightLocked + pub lock_hgt: u64, +} + +/// Participant keys and optional partial signature +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct ParticipantData { + /// Public key corresponding to private blinding factor + #[serde(with = "secp_ser::pubkey_serde")] + pub xs: PublicKey, + /// Public key corresponding to private nonce + #[serde(with = "secp_ser::pubkey_serde")] + pub nonce: PublicKey, + /// Public partial signature + #[serde(default = "default_part_sig_none")] + #[serde(skip_serializing_if = "Option::is_none")] + #[serde(with = "secp_ser::option_sig_serde")] + pub part: Option, +} + +fn default_part_sig_none() -> Option { + None +} + +/// Input commitment or output with its range proof +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub struct Commits { + /// Options for an output's structure or use + #[serde(default = "default_output_feature")] + #[serde(skip_serializing_if = "output_feature_is_plain")] + pub f: OutputFeatures, + /// The homomorphic commitment representing the output amount + #[serde( + serialize_with = "secp_ser::as_hex", + deserialize_with = "secp_ser::commitment_from_hex" + )] + pub c: Commitment, + /// A proof that the commitment is in the right range + /// Only applies for transaction outputs + #[serde(with = "ser::option_rangeproof_hex")] + #[serde(default = "default_range_proof")] + #[serde(skip_serializing_if = "Option::is_none")] + pub p: Option, +} + +impl From<&Output> for Commits { + fn from(out: &Output) -> Commits { + Commits { + f: out.features().into(), + c: out.commitment(), + p: Some(out.proof()), + } + } +} + +// This will need to be reworked once we no longer support input features with "commit only" inputs. +impl From<&Input> for Commits { + fn from(input: &Input) -> Commits { + Commits { + f: input.features.into(), + c: input.commitment(), + p: None, + } + } +} + +fn default_output_feature() -> OutputFeatures { + OutputFeatures(0) +} + +fn output_feature_is_plain(o: &OutputFeatures) -> bool { + o.0 == 0 +} + +/// Output feature byte +#[derive(Serialize, Deserialize, Copy, Debug, Clone, PartialEq, Eq)] +pub struct OutputFeatures(pub u8); + +/// Whether all signature bytes are zero +pub fn sig_is_blank(s: &secp::Signature) -> bool { + for b in s.to_raw_data().iter() { + if *b != 0 { + return false; + } + } + true +} + +fn default_range_proof() -> Option { + None +} + +/// A mining node requests new coinbase via the foreign api every time a new candidate block is built. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Coinbase { + /// Output + output: CbOutput, + /// Kernel + kernel: CbKernel, + /// Key Id + key_id: Option, +} + +impl From for Coinbase { + fn from(cb: CbData) -> Coinbase { + Coinbase { + output: CbOutput::from(&cb.output), + kernel: CbKernel::from(&cb.kernel), + key_id: cb.key_id, + } + } +} + +impl From<&Output> for CbOutput { + fn from(output: &Output) -> CbOutput { + CbOutput { + features: CbOutputFeatures::Coinbase, + commit: output.commitment(), + proof: output.proof(), + } + } +} + +impl From<&TxKernel> for CbKernel { + fn from(kernel: &TxKernel) -> CbKernel { + CbKernel { + features: CbKernelFeatures::Coinbase, + excess: kernel.excess, + excess_sig: kernel.excess_sig, + } + } +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +enum CbOutputFeatures { + Coinbase, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +enum CbKernelFeatures { + Coinbase, +} + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +struct CbOutput { + features: CbOutputFeatures, + #[serde(serialize_with = "secp_ser::as_hex")] + commit: Commitment, + #[serde(serialize_with = "secp_ser::as_hex")] + proof: RangeProof, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct CbKernel { + features: CbKernelFeatures, + #[serde(serialize_with = "secp_ser::as_hex")] + excess: Commitment, + #[serde(with = "secp_ser::sig_serde")] + excess_sig: secp::Signature, +} diff --git a/libwallet/src/slate_versions/mod.rs b/libwallet/src/slate_versions/mod.rs index d7df717..0429855 100644 --- a/libwallet/src/slate_versions/mod.rs +++ b/libwallet/src/slate_versions/mod.rs @@ -26,6 +26,7 @@ use crate::types::CbData; use crate::Error; use std::convert::TryFrom; +mod common; pub mod ser; #[allow(missing_docs)] @@ -605,6 +606,40 @@ pub mod tests { Ok(()) } + #[test] + fn slate_states() { + use crate::grin_core::ser::{deserialize_default, serialize_default}; + use crate::slate::SlateState; + use crate::slate_versions::v5::SlateStateV5; + + for (state, label, byte) in [ + (SlateState::Unknown, "NA", 0), + (SlateState::Standard1, "S1", 1), + (SlateState::Standard2, "S2", 2), + (SlateState::Standard3, "S3", 3), + (SlateState::Invoice1, "I1", 4), + (SlateState::Invoice2, "I2", 5), + (SlateState::Invoice3, "I3", 6), + ] { + let mut slate = Slate::blank(2, false); + slate.state = state; + for version in [SlateVersion::V4, SlateVersion::V5] { + let versioned = VersionedSlate::into_version(slate.clone(), version).unwrap(); + let json = serde_json::to_value(&versioned).unwrap(); + assert_eq!(json["sta"], label); + let recovered: Slate = serde_json::from_value::(json) + .unwrap() + .into(); + assert_eq!(recovered.state, slate.state); + } + let mut bytes = vec![]; + serialize_default(&mut bytes, &SlateStateV5::from(&slate.state)).unwrap(); + assert_eq!(bytes, vec![byte]); + let recovered: SlateStateV5 = deserialize_default(&mut &bytes[..]).unwrap(); + assert_eq!(SlateState::from(&recovered), slate.state); + } + } + #[test] fn slate_version_numbers() { for (number, version) in [(4, SlateVersion::V4), (5, SlateVersion::V5)] { diff --git a/libwallet/src/slate_versions/ser.rs b/libwallet/src/slate_versions/ser.rs index 55aa381..75d4e63 100644 --- a/libwallet/src/slate_versions/ser.rs +++ b/libwallet/src/slate_versions/ser.rs @@ -682,50 +682,7 @@ pub mod slate_state_v4 { } } -/// Serializes slates 'state' field - V5 -pub mod slate_state_v5 { - use serde::de::Error; - use serde::{Deserialize, Deserializer, Serializer}; - - use crate::slate_versions::v5::SlateStateV5; - - /// - pub fn serialize(st: &SlateStateV5, serializer: S) -> Result - where - S: Serializer, - { - let label = match st { - SlateStateV5::Unknown => "NA", - SlateStateV5::Standard1 => "S1", - SlateStateV5::Standard2 => "S2", - SlateStateV5::Standard3 => "S3", - SlateStateV5::Invoice1 => "I1", - SlateStateV5::Invoice2 => "I2", - SlateStateV5::Invoice3 => "I3", - }; - serializer.serialize_str(label) - } - - /// - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - String::deserialize(deserializer).and_then(|s| { - let retval = match s.as_str() { - "NA" => SlateStateV5::Unknown, - "S1" => SlateStateV5::Standard1, - "S2" => SlateStateV5::Standard2, - "S3" => SlateStateV5::Standard3, - "I1" => SlateStateV5::Invoice1, - "I2" => SlateStateV5::Invoice2, - "I3" => SlateStateV5::Invoice3, - _ => return Err(Error::custom("Invalid Slate state")), - }; - Ok(retval) - }) - } -} +pub use slate_state_v4 as slate_state_v5; /// Serializes an secp256k1 pubkey to base64 pub mod uuid_base64 { diff --git a/libwallet/src/slate_versions/v4.rs b/libwallet/src/slate_versions/v4.rs index a3ba1b1..2325404 100644 --- a/libwallet/src/slate_versions/v4.rs +++ b/libwallet/src/slate_versions/v4.rs @@ -54,18 +54,19 @@ //! * `rsig` may be omitted if it has not yet been filled out use crate::grin_core::core::FeeFields; -use crate::grin_core::core::{Input, Output, TxKernel}; use crate::grin_core::libtx::secp_ser; -use crate::grin_keychain::{BlindingFactor, Identifier}; -use crate::grin_util::secp; -use crate::grin_util::secp::key::PublicKey; -use crate::grin_util::secp::pedersen::{Commitment, RangeProof}; -use crate::grin_util::secp::Signature; -use crate::{slate_versions::ser, CbData}; +use crate::grin_keychain::BlindingFactor; +use crate::slate_versions::ser; use ed25519_dalek::Signature as DalekSignature; use ed25519_dalek::VerifyingKey as DalekPublicKey; use uuid::Uuid; +pub use crate::slate_versions::common::{ + sig_is_blank, Coinbase as CoinbaseV4, Commits as CommitsV4, + KernelFeaturesArgs as KernelFeaturesArgsV4, OutputFeatures as OutputFeaturesV4, + ParticipantData as ParticipantDataV4, SlateState as SlateStateV4, +}; + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SlateV4 { // Required Fields @@ -163,32 +164,6 @@ fn default_kernel_features_none() -> Option { None } -/// Slate state definition -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum SlateStateV4 { - /// Unknown, coming from earlier versions of the slate - Unknown, - /// Standard flow, freshly init - Standard1, - /// Standard flow, return journey - Standard2, - /// Standard flow, ready for transaction posting - Standard3, - /// Invoice flow, freshly init - Invoice1, - ///Invoice flow, return journey - Invoice2, - /// Invoice flow, ready for tranasction posting - Invoice3, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -/// Kernel features arguments definition -pub struct KernelFeaturesArgsV4 { - /// Lock height, for HeightLocked - pub lock_hgt: u64, -} - #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct VersionCompatInfoV4 { /// The current version of the slate format @@ -197,25 +172,6 @@ pub struct VersionCompatInfoV4 { pub block_header_version: u16, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct ParticipantDataV4 { - /// Public key corresponding to private blinding factor - #[serde(with = "secp_ser::pubkey_serde")] - pub xs: PublicKey, - /// Public key corresponding to private nonce - #[serde(with = "secp_ser::pubkey_serde")] - pub nonce: PublicKey, - /// Public partial signature - #[serde(default = "default_part_sig_none")] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "secp_ser::option_sig_serde")] - pub part: Option, -} - -fn default_part_sig_none() -> Option { - None -} - #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct PaymentInfoV4 { #[serde(with = "ser::dalek_pubkey_serde")] @@ -232,71 +188,6 @@ fn default_receiver_signature_none() -> Option { None } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub struct CommitsV4 { - /// Options for an output's structure or use - #[serde(default = "default_output_feature")] - #[serde(skip_serializing_if = "output_feature_is_plain")] - pub f: OutputFeaturesV4, - /// The homomorphic commitment representing the output amount - #[serde( - serialize_with = "secp_ser::as_hex", - deserialize_with = "secp_ser::commitment_from_hex" - )] - pub c: Commitment, - /// A proof that the commitment is in the right range - /// Only applies for transaction outputs - #[serde(with = "ser::option_rangeproof_hex")] - #[serde(default = "default_range_proof")] - #[serde(skip_serializing_if = "Option::is_none")] - pub p: Option, -} - -impl From<&Output> for CommitsV4 { - fn from(out: &Output) -> CommitsV4 { - CommitsV4 { - f: out.features().into(), - c: out.commitment(), - p: Some(out.proof()), - } - } -} - -// This will need to be reworked once we no longer support input features with "commit only" inputs. -impl From<&Input> for CommitsV4 { - fn from(input: &Input) -> CommitsV4 { - CommitsV4 { - f: input.features.into(), - c: input.commitment(), - p: None, - } - } -} - -fn default_output_feature() -> OutputFeaturesV4 { - OutputFeaturesV4(0) -} - -fn output_feature_is_plain(o: &OutputFeaturesV4) -> bool { - o.0 == 0 -} - -#[derive(Serialize, Deserialize, Copy, Debug, Clone, PartialEq, Eq)] -pub struct OutputFeaturesV4(pub u8); - -pub fn sig_is_blank(s: &secp::Signature) -> bool { - for b in s.to_raw_data().iter() { - if *b != 0 { - return false; - } - } - true -} - -fn default_range_proof() -> Option { - None -} - fn u64_is_blank(u: &u64) -> bool { *u == 0 } @@ -316,72 +207,3 @@ fn fee_is_zero(f: &FeeFields) -> bool { fn default_fee() -> FeeFields { FeeFields::zero() } - -/// A mining node requests new coinbase via the foreign api every time a new candidate block is built. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CoinbaseV4 { - /// Output - output: CbOutputV4, - /// Kernel - kernel: CbKernelV4, - /// Key Id - key_id: Option, -} - -impl From for CoinbaseV4 { - fn from(cb: CbData) -> CoinbaseV4 { - CoinbaseV4 { - output: CbOutputV4::from(&cb.output), - kernel: CbKernelV4::from(&cb.kernel), - key_id: cb.key_id, - } - } -} - -impl From<&Output> for CbOutputV4 { - fn from(output: &Output) -> CbOutputV4 { - CbOutputV4 { - features: CbOutputFeatures::Coinbase, - commit: output.commitment(), - proof: output.proof(), - } - } -} - -impl From<&TxKernel> for CbKernelV4 { - fn from(kernel: &TxKernel) -> CbKernelV4 { - CbKernelV4 { - features: CbKernelFeatures::Coinbase, - excess: kernel.excess, - excess_sig: kernel.excess_sig, - } - } -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -enum CbOutputFeatures { - Coinbase, -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -enum CbKernelFeatures { - Coinbase, -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -struct CbOutputV4 { - features: CbOutputFeatures, - #[serde(serialize_with = "secp_ser::as_hex")] - commit: Commitment, - #[serde(serialize_with = "secp_ser::as_hex")] - proof: RangeProof, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -struct CbKernelV4 { - features: CbKernelFeatures, - #[serde(serialize_with = "secp_ser::as_hex")] - excess: Commitment, - #[serde(with = "secp_ser::sig_serde")] - excess_sig: secp::Signature, -} diff --git a/libwallet/src/slate_versions/v4_bin.rs b/libwallet/src/slate_versions/v4_bin.rs index c660bbe..3e1760c 100644 --- a/libwallet/src/slate_versions/v4_bin.rs +++ b/libwallet/src/slate_versions/v4_bin.rs @@ -175,8 +175,8 @@ impl Readable for SlateOptFields { } } -struct SigsWrap(Vec); -struct SigsWrapRef<'a>(&'a Vec); +pub(super) struct SigsWrap(pub(super) Vec); +pub(super) struct SigsWrapRef<'a>(pub(super) &'a Vec); impl<'a> Writeable for SigsWrapRef<'a> { fn write(&self, writer: &mut W) -> Result<(), grin_ser::Error> { @@ -278,8 +278,8 @@ impl Readable for SlateOptStructs { } } -struct ComsWrap(Vec); -struct ComsWrapRef<'a>(&'a Vec); +pub(super) struct ComsWrap(pub(super) Vec); +pub(super) struct ComsWrapRef<'a>(pub(super) &'a Vec); impl<'a> Writeable for ComsWrapRef<'a> { fn write(&self, writer: &mut W) -> Result<(), grin_ser::Error> { diff --git a/libwallet/src/slate_versions/v5.rs b/libwallet/src/slate_versions/v5.rs index b621e03..d5860ba 100644 --- a/libwallet/src/slate_versions/v5.rs +++ b/libwallet/src/slate_versions/v5.rs @@ -28,21 +28,23 @@ //! * `memo` adds optional payment details use crate::grin_core::core::FeeFields; -use crate::grin_core::core::{Input, Output, TxKernel}; use crate::grin_core::libtx::secp_ser; -use crate::grin_keychain::{BlindingFactor, Identifier}; -use crate::grin_util::secp; -use crate::grin_util::secp::key::PublicKey; -use crate::grin_util::secp::pedersen::{Commitment, RangeProof}; -use crate::grin_util::secp::Signature; +use crate::grin_keychain::BlindingFactor; use crate::slate::{PaymentMemo, PaymentProofType}; -use crate::{slate_versions::ser, CbData}; +use crate::slate_versions::ser; use chrono::prelude::{DateTime, Utc}; use ed25519_dalek::Signature as DalekSignature; use ed25519_dalek::VerifyingKey as DalekPublicKey; use serde_with::TimestampSeconds; use uuid::Uuid; +// These fields have the same representation in V4 and V5 +pub use crate::slate_versions::common::{ + sig_is_blank, Coinbase as CoinbaseV5, Commits as CommitsV5, + KernelFeaturesArgs as KernelFeaturesArgsV5, OutputFeatures as OutputFeaturesV5, + ParticipantData as ParticipantDataV5, SlateState as SlateStateV5, +}; + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct SlateV5 { // Required Fields @@ -140,32 +142,6 @@ fn default_kernel_features_none() -> Option { None } -/// Slate state definition -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum SlateStateV5 { - /// Unknown, coming from earlier versions of the slate - Unknown, - /// Standard flow, freshly init - Standard1, - /// Standard flow, return journey - Standard2, - /// Standard flow, ready for transaction posting - Standard3, - /// Invoice flow, freshly init - Invoice1, - ///Invoice flow, return journey - Invoice2, - /// Invoice flow, ready for tranasction posting - Invoice3, -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -/// Kernel features arguments definition -pub struct KernelFeaturesArgsV5 { - /// Lock height, for HeightLocked - pub lock_hgt: u64, -} - #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct VersionCompatInfoV5 { /// The current version of the slate format @@ -174,25 +150,6 @@ pub struct VersionCompatInfoV5 { pub block_header_version: u16, } -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct ParticipantDataV5 { - /// Public key corresponding to private blinding factor - #[serde(with = "secp_ser::pubkey_serde")] - pub xs: PublicKey, - /// Public key corresponding to private nonce - #[serde(with = "secp_ser::pubkey_serde")] - pub nonce: PublicKey, - /// Public partial signature - #[serde(default = "default_part_sig_none")] - #[serde(skip_serializing_if = "Option::is_none")] - #[serde(with = "secp_ser::option_sig_serde")] - pub part: Option, -} - -fn default_part_sig_none() -> Option { - None -} - #[serde_as] #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] pub struct PaymentInfoV5 { @@ -218,71 +175,6 @@ fn default_promise_signature_none() -> Option { None } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub struct CommitsV5 { - /// Options for an output's structure or use - #[serde(default = "default_output_feature")] - #[serde(skip_serializing_if = "output_feature_is_plain")] - pub f: OutputFeaturesV5, - /// The homomorphic commitment representing the output amount - #[serde( - serialize_with = "secp_ser::as_hex", - deserialize_with = "secp_ser::commitment_from_hex" - )] - pub c: Commitment, - /// A proof that the commitment is in the right range - /// Only applies for transaction outputs - #[serde(with = "ser::option_rangeproof_hex")] - #[serde(default = "default_range_proof")] - #[serde(skip_serializing_if = "Option::is_none")] - pub p: Option, -} - -impl From<&Output> for CommitsV5 { - fn from(out: &Output) -> CommitsV5 { - CommitsV5 { - f: out.features().into(), - c: out.commitment(), - p: Some(out.proof()), - } - } -} - -// This will need to be reworked once we no longer support input features with "commit only" inputs. -impl From<&Input> for CommitsV5 { - fn from(input: &Input) -> CommitsV5 { - CommitsV5 { - f: input.features.into(), - c: input.commitment(), - p: None, - } - } -} - -fn default_output_feature() -> OutputFeaturesV5 { - OutputFeaturesV5(0) -} - -fn output_feature_is_plain(o: &OutputFeaturesV5) -> bool { - o.0 == 0 -} - -#[derive(Serialize, Deserialize, Copy, Debug, Clone, PartialEq, Eq)] -pub struct OutputFeaturesV5(pub u8); - -pub fn sig_is_blank(s: &secp::Signature) -> bool { - for b in s.to_raw_data().iter() { - if *b != 0 { - return false; - } - } - true -} - -fn default_range_proof() -> Option { - None -} - fn u64_is_blank(u: &u64) -> bool { *u == 0 } @@ -302,72 +194,3 @@ fn fee_is_zero(f: &FeeFields) -> bool { fn default_fee() -> FeeFields { FeeFields::zero() } - -/// A mining node requests new coinbase via the foreign api every time a new candidate block is built. -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CoinbaseV5 { - /// Output - output: CbOutputV5, - /// Kernel - kernel: CbKernelV5, - /// Key Id - key_id: Option, -} - -impl From for CoinbaseV5 { - fn from(cb: CbData) -> CoinbaseV5 { - CoinbaseV5 { - output: CbOutputV5::from(&cb.output), - kernel: CbKernelV5::from(&cb.kernel), - key_id: cb.key_id, - } - } -} - -impl From<&Output> for CbOutputV5 { - fn from(output: &Output) -> CbOutputV5 { - CbOutputV5 { - features: CbOutputFeatures::Coinbase, - commit: output.commitment(), - proof: output.proof(), - } - } -} - -impl From<&TxKernel> for CbKernelV5 { - fn from(kernel: &TxKernel) -> CbKernelV5 { - CbKernelV5 { - features: CbKernelFeatures::Coinbase, - excess: kernel.excess, - excess_sig: kernel.excess_sig, - } - } -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -enum CbOutputFeatures { - Coinbase, -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -enum CbKernelFeatures { - Coinbase, -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -struct CbOutputV5 { - features: CbOutputFeatures, - #[serde(serialize_with = "secp_ser::as_hex")] - commit: Commitment, - #[serde(serialize_with = "secp_ser::as_hex")] - proof: RangeProof, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -struct CbKernelV5 { - features: CbKernelFeatures, - #[serde(serialize_with = "secp_ser::as_hex")] - excess: Commitment, - #[serde(with = "secp_ser::sig_serde")] - excess_sig: secp::Signature, -} diff --git a/libwallet/src/slate_versions/v5_bin.rs b/libwallet/src/slate_versions/v5_bin.rs index dd6c0fc..3a67282 100644 --- a/libwallet/src/slate_versions/v5_bin.rs +++ b/libwallet/src/slate_versions/v5_bin.rs @@ -14,13 +14,9 @@ //! Wraps a V5 Slate into a V5 Binary slate -use crate::grin_core::core::transaction::OutputFeatures; use crate::grin_core::ser as grin_ser; use crate::grin_core::ser::{Readable, Reader, Writeable, Writer}; use crate::grin_keychain::BlindingFactor; -use crate::grin_util::secp::key::PublicKey; -use crate::grin_util::secp::pedersen::{Commitment, RangeProof}; -use crate::grin_util::secp::Signature; use crate::slate::{PaymentMemo, PaymentProofType}; use chrono::DateTime; use ed25519_dalek::Signature as DalekSignature; @@ -28,90 +24,12 @@ use ed25519_dalek::VerifyingKey as DalekPublicKey; use std::convert::TryFrom; use crate::slate_versions::v5::{ - CommitsV5, KernelFeaturesArgsV5, ParticipantDataV5, PaymentInfoV5, SlateStateV5, SlateV5, - VersionCompatInfoV5, + CommitsV5, KernelFeaturesArgsV5, PaymentInfoV5, SlateStateV5, SlateV5, VersionCompatInfoV5, }; -use crate::slate_versions::v4_bin::{SlateOptFields, UuidWrap}; - -impl Writeable for SlateStateV5 { - fn write(&self, writer: &mut W) -> Result<(), grin_ser::Error> { - let b = match self { - SlateStateV5::Unknown => 0, - SlateStateV5::Standard1 => 1, - SlateStateV5::Standard2 => 2, - SlateStateV5::Standard3 => 3, - SlateStateV5::Invoice1 => 4, - SlateStateV5::Invoice2 => 5, - SlateStateV5::Invoice3 => 6, - }; - writer.write_u8(b) - } -} - -impl Readable for SlateStateV5 { - fn read(reader: &mut R) -> Result { - let b = reader.read_u8()?; - let sta = match b { - 0 => SlateStateV5::Unknown, - 1 => SlateStateV5::Standard1, - 2 => SlateStateV5::Standard2, - 3 => SlateStateV5::Standard3, - 4 => SlateStateV5::Invoice1, - 5 => SlateStateV5::Invoice2, - 6 => SlateStateV5::Invoice3, - _ => SlateStateV5::Unknown, - }; - Ok(sta) - } -} - -struct SigsWrap(Vec); -struct SigsWrapRef<'a>(&'a Vec); - -impl<'a> Writeable for SigsWrapRef<'a> { - fn write(&self, writer: &mut W) -> Result<(), grin_ser::Error> { - writer.write_u8(self.0.len() as u8)?; - for s in self.0.iter() { - //0 means part sig is not yet included - //1 means part sig included - if s.part.is_some() { - writer.write_u8(1)?; - } else { - writer.write_u8(0)?; - } - s.xs.write(writer)?; - s.nonce.write(writer)?; - if let Some(s) = s.part { - s.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for SigsWrap { - fn read(reader: &mut R) -> Result { - let sigs_len = reader.read_u8()?; - let sigs = { - let mut ret = vec![]; - for _ in 0..sigs_len as usize { - let has_partial = reader.read_u8()?; - let c = ParticipantDataV5 { - xs: PublicKey::read(reader)?, - nonce: PublicKey::read(reader)?, - part: match has_partial { - 1 => Some(Signature::read(reader)?), - 0 | _ => None, - }, - }; - ret.push(c); - } - ret - }; - Ok(SigsWrap(sigs)) - } -} +use crate::slate_versions::v4_bin::{ + ComsWrap, ComsWrapRef, SigsWrap, SigsWrapRef, SlateOptFields, UuidWrap, +}; /// Serialization of optional structs struct SlateOptStructsRef<'a> { @@ -169,53 +87,6 @@ impl Readable for SlateOptStructs { } } -struct ComsWrap(Vec); -struct ComsWrapRef<'a>(&'a Vec); - -impl<'a> Writeable for ComsWrapRef<'a> { - fn write(&self, writer: &mut W) -> Result<(), grin_ser::Error> { - writer.write_u16(self.0.len() as u16)?; - for o in self.0.iter() { - //0 means input - //1 means output with proof - if o.p.is_some() { - writer.write_u8(1)?; - } else { - writer.write_u8(0)?; - } - OutputFeatures::from(o.f).write(writer)?; - o.c.write(writer)?; - if let Some(p) = o.p { - p.write(writer)?; - } - } - Ok(()) - } -} - -impl Readable for ComsWrap { - fn read(reader: &mut R) -> Result { - let coms_len = reader.read_u16()?; - let coms = { - let mut ret = vec![]; - for _ in 0..coms_len as usize { - let is_output = reader.read_u8()?; - let c = CommitsV5 { - f: OutputFeatures::read(reader)?.into(), - c: Commitment::read(reader)?, - p: match is_output { - 1 => Some(RangeProof::read(reader)?), - 0 | _ => None, - }, - }; - ret.push(c); - } - ret - }; - Ok(ComsWrap(coms)) - } -} - struct ProofWrap(PaymentInfoV5); struct ProofWrapRef<'a>(&'a PaymentInfoV5); @@ -485,8 +356,12 @@ impl Readable for SlateV5Bin { #[test] fn slate_v5_serialize_deserialize() { + use crate::grin_core::core::transaction::OutputFeatures; use crate::grin_util::from_hex; use crate::grin_util::secp::key::PublicKey; + use crate::grin_util::secp::pedersen::{Commitment, RangeProof}; + use crate::grin_util::secp::Signature; + use crate::slate_versions::v5::ParticipantDataV5; use crate::Slate; use chrono::Utc; use grin_core::global::{set_local_chain_type, ChainTypes};