mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-03-14 13:01:10 +03:00
* add V5 deserialization test + fixes * clarify comment * upwrap fix during v4 deserialization * further unwrap removal
332 lines
10 KiB
Rust
332 lines
10 KiB
Rust
// Copyright 2021 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.
|
|
|
|
//! This module contains old slate versions and conversions to the newest slate version
|
|
//! Used for serialization and deserialization of slates in a backwards compatible way.
|
|
//! Versions earlier than V3 are removed for the 4.0.0 release, but versioning code
|
|
//! remains for future needs
|
|
|
|
use crate::slate::Slate;
|
|
use crate::slate_versions::v4::{CoinbaseV4, SlateV4};
|
|
use crate::slate_versions::v4_bin::SlateV4Bin;
|
|
use crate::slate_versions::v5::{CoinbaseV5, SlateV5};
|
|
use crate::slate_versions::v5_bin::SlateV5Bin;
|
|
use crate::types::CbData;
|
|
use crate::Error;
|
|
use std::convert::TryFrom;
|
|
|
|
pub mod ser;
|
|
|
|
#[allow(missing_docs)]
|
|
pub mod v4;
|
|
#[allow(missing_docs)]
|
|
pub mod v4_bin;
|
|
#[allow(missing_docs)]
|
|
pub mod v5;
|
|
#[allow(missing_docs)]
|
|
pub mod v5_bin;
|
|
|
|
/// The most recent version of the slate
|
|
pub const CURRENT_SLATE_VERSION: u16 = 5;
|
|
|
|
/// The grin block header this slate is intended to be compatible with
|
|
pub const GRIN_BLOCK_HEADER_VERSION: u16 = 3;
|
|
|
|
/// Existing versions of the slate
|
|
#[derive(EnumIter, Serialize, Deserialize, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
|
pub enum SlateVersion {
|
|
/// V5 (Most Current)
|
|
V5,
|
|
/// V4
|
|
V4,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
#[serde(untagged)]
|
|
/// Versions are ordered newest to oldest so serde attempts to
|
|
/// deserialize newer versions first, then falls back to older versions.
|
|
pub enum VersionedSlate {
|
|
/// Current (5.0.0 Onwards?)
|
|
V5(SlateV5),
|
|
/// Current (4.0.0)
|
|
V4(SlateV4),
|
|
}
|
|
|
|
impl VersionedSlate {
|
|
/// Return slate version
|
|
pub fn version(&self) -> SlateVersion {
|
|
match *self {
|
|
VersionedSlate::V5(_) => SlateVersion::V4,
|
|
VersionedSlate::V4(_) => SlateVersion::V4,
|
|
}
|
|
}
|
|
|
|
/// convert this slate type to a specified older version
|
|
pub fn into_version(slate: Slate, version: SlateVersion) -> Result<VersionedSlate, Error> {
|
|
match version {
|
|
SlateVersion::V5 => Ok(VersionedSlate::V5(slate.into())),
|
|
SlateVersion::V4 => Ok(VersionedSlate::V4(slate.into())),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<VersionedSlate> for Slate {
|
|
fn from(slate: VersionedSlate) -> Slate {
|
|
match slate {
|
|
VersionedSlate::V5(s) => Slate::from(s),
|
|
VersionedSlate::V4(s) => Slate::from(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[serde(untagged)]
|
|
/// Binary versions, can only be parsed 1:1 into the appropriate
|
|
/// version, and VersionedSlate can up/downgrade from there
|
|
/// NB (IMPORTANT): Ensure the slates are listed in reverse chronological
|
|
/// order (latest first)
|
|
pub enum VersionedBinSlate {
|
|
/// Version 5, binary
|
|
V5(SlateV5Bin),
|
|
/// Version 4, binary
|
|
V4(SlateV4Bin),
|
|
}
|
|
|
|
impl TryFrom<VersionedSlate> for VersionedBinSlate {
|
|
type Error = Error;
|
|
fn try_from(slate: VersionedSlate) -> Result<VersionedBinSlate, Error> {
|
|
match slate {
|
|
VersionedSlate::V5(s) => Ok(VersionedBinSlate::V5(SlateV5Bin(s))),
|
|
VersionedSlate::V4(s) => Ok(VersionedBinSlate::V4(SlateV4Bin(s))),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<VersionedBinSlate> for VersionedSlate {
|
|
fn from(slate: VersionedBinSlate) -> VersionedSlate {
|
|
match slate {
|
|
VersionedBinSlate::V5(s) => VersionedSlate::V5(s.0),
|
|
VersionedBinSlate::V4(s) => VersionedSlate::V4(s.0),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
#[serde(untagged)]
|
|
/// Versions are ordered newest to oldest so serde attempts to
|
|
/// deserialize newer versions first, then falls back to older versions.
|
|
pub enum VersionedCoinbase {
|
|
/// Current supported coinbase version.
|
|
V5(CoinbaseV5),
|
|
/// Previous version (no difference)
|
|
V4(CoinbaseV4),
|
|
}
|
|
|
|
impl VersionedCoinbase {
|
|
/// convert this coinbase data to a specific versioned representation for the json api.
|
|
pub fn into_version(cb: CbData, version: SlateVersion) -> VersionedCoinbase {
|
|
match version {
|
|
SlateVersion::V5 => VersionedCoinbase::V5(cb.into()),
|
|
SlateVersion::V4 => VersionedCoinbase::V4(cb.into()),
|
|
}
|
|
}
|
|
}
|
|
#[cfg(test)]
|
|
pub mod tests {
|
|
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::{KernelFeaturesArgs, ParticipantData, PaymentInfo, PaymentMemo};
|
|
use crate::slate_versions::v5::{CommitsV5, SlateV5};
|
|
use crate::{
|
|
slate, Error, Slate, Slatepacker, SlatepackerArgs, VersionedBinSlate, VersionedSlate,
|
|
};
|
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
|
use ed25519_dalek::PublicKey as DalekPublicKey;
|
|
use ed25519_dalek::Signature as DalekSignature;
|
|
use grin_core::global::{set_local_chain_type, ChainTypes};
|
|
use grin_keychain::{ExtKeychain, Keychain, SwitchCommitmentType};
|
|
use std::convert::TryInto;
|
|
|
|
// Populate a test internal slate with all fields to test conversions
|
|
pub fn populate_test_slate() -> Result<Slate, Error> {
|
|
let keychain = ExtKeychain::from_random_seed(true).unwrap();
|
|
let switch = SwitchCommitmentType::Regular;
|
|
|
|
let mut slate_internal = Slate::blank(2, false);
|
|
let id1 = ExtKeychain::derive_key_id(1, 1, 0, 0, 0);
|
|
let id2 = ExtKeychain::derive_key_id(1, 1, 1, 0, 0);
|
|
let skey1 = keychain.derive_key(0, &id1, switch).unwrap();
|
|
let skey2 = keychain.derive_key(0, &id2, switch).unwrap();
|
|
let xs = PublicKey::from_secret_key(keychain.secp(), &skey1).unwrap();
|
|
let nonce = PublicKey::from_secret_key(keychain.secp(), &skey2).unwrap();
|
|
|
|
let part = ParticipantData {
|
|
public_blind_excess: xs,
|
|
public_nonce: nonce,
|
|
part_sig: None,
|
|
};
|
|
let part2 = ParticipantData {
|
|
public_blind_excess: xs,
|
|
public_nonce: nonce,
|
|
part_sig: Some(Signature::from_raw_data(&[11; 64]).unwrap()),
|
|
};
|
|
slate_internal.participant_data.push(part.clone());
|
|
slate_internal.participant_data.push(part2);
|
|
slate_internal.participant_data.push(part);
|
|
|
|
// Another temp slate to convert commit data into internal 'transaction' like data
|
|
// add some random commit data
|
|
let slate_tmp = Slate::blank(1, false);
|
|
let mut v5 = SlateV5::from(slate_tmp);
|
|
|
|
let com1 = CommitsV5 {
|
|
f: OutputFeatures::Plain.into(),
|
|
c: Commitment::from_vec([3u8; 1].to_vec()),
|
|
p: None,
|
|
};
|
|
let com2 = CommitsV5 {
|
|
f: OutputFeatures::Plain.into(),
|
|
c: Commitment::from_vec([4u8; 1].to_vec()),
|
|
p: Some(RangeProof::zero()),
|
|
};
|
|
|
|
let mut coms = vec![];
|
|
coms.push(com1.clone());
|
|
coms.push(com1.clone());
|
|
coms.push(com1.clone());
|
|
coms.push(com2);
|
|
|
|
v5.coms = Some(coms);
|
|
|
|
slate_internal.tx = slate::tx_from_slate_v5(&v5);
|
|
|
|
// basic fields
|
|
slate_internal.amount = 23820323;
|
|
slate_internal.kernel_features = 1;
|
|
slate_internal.num_participants = 2;
|
|
slate_internal.kernel_features_args = Some(KernelFeaturesArgs {
|
|
lock_height: 2323223,
|
|
});
|
|
|
|
// current style payment proof
|
|
let raw_pubkey_str = "d03c09e9c19bb74aa9ea44e0fe5ae237a9bf40bddf0941064a80913a4459c8bb";
|
|
let b = from_hex(raw_pubkey_str).unwrap();
|
|
let d_pkey = DalekPublicKey::from_bytes(&b).unwrap();
|
|
// Need to remove milliseconds component for comparison. Won't be serialized
|
|
let ts = NaiveDateTime::from_timestamp_opt(Utc::now().timestamp(), 0).unwrap();
|
|
let ts = DateTime::<Utc>::from_utc(ts, Utc);
|
|
let pm = PaymentMemo {
|
|
memo_type: 1,
|
|
memo: [9; 32],
|
|
};
|
|
|
|
let psig = DalekSignature::from_bytes(&[0u8; 64]).unwrap();
|
|
slate_internal.payment_proof = Some(PaymentInfo {
|
|
sender_address: Some(d_pkey.clone()),
|
|
receiver_address: d_pkey.clone(),
|
|
timestamp: ts.clone(),
|
|
promise_signature: Some(psig),
|
|
memo: Some(pm),
|
|
});
|
|
|
|
Ok(slate_internal)
|
|
}
|
|
|
|
#[test]
|
|
fn ser_deser_current_slate() -> Result<(), Error> {
|
|
let slate_internal = populate_test_slate()?;
|
|
// Serialize slate into slatepack
|
|
let slatepacker_args = SlatepackerArgs {
|
|
sender: None,
|
|
recipients: vec![],
|
|
dec_key: None,
|
|
};
|
|
|
|
let slate_packer = Slatepacker::new(slatepacker_args);
|
|
let slate_packed = slate_packer.create_slatepack(&slate_internal).unwrap();
|
|
|
|
let slate_unpacked = slate_packer.get_slate(&slate_packed).unwrap();
|
|
|
|
// Just verifying payment proof for now, extend later to cover EQ for full slate if needs
|
|
// be
|
|
assert_eq!(slate_internal.payment_proof, slate_unpacked.payment_proof);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn slatepack_version_v4_v5() -> Result<(), Error> {
|
|
set_local_chain_type(ChainTypes::Mainnet);
|
|
|
|
// Convert V5 slate into V4 slate, check result
|
|
let slate_internal = populate_test_slate()?;
|
|
let v5 = VersionedSlate::V5(slate_internal.clone().into());
|
|
let v4 = VersionedSlate::V4(slate_internal.into());
|
|
|
|
let v5_converted: Slate = v5.into();
|
|
let v4_converted: Slate = v4.into();
|
|
|
|
assert!(v5_converted.payment_proof.as_ref().unwrap().memo.is_some());
|
|
|
|
// Converted from v4 will not have memos and ts will be zeroed out
|
|
assert!(v4_converted.payment_proof.as_ref().unwrap().memo.is_none());
|
|
assert_eq!(
|
|
v4_converted
|
|
.payment_proof
|
|
.as_ref()
|
|
.unwrap()
|
|
.timestamp
|
|
.timestamp(),
|
|
0
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn slatepack_version_v4_v5_bin() -> Result<(), Error> {
|
|
set_local_chain_type(ChainTypes::Mainnet);
|
|
|
|
// Convert V5 slate into V4 slate, check result
|
|
let slate_internal = populate_test_slate()?;
|
|
let v5 = VersionedSlate::V5(slate_internal.clone().into());
|
|
let v5_bin: VersionedBinSlate = v5.try_into().unwrap();
|
|
|
|
let v4 = VersionedSlate::V4(slate_internal.into());
|
|
let v4_bin: VersionedBinSlate = v4.try_into().unwrap();
|
|
|
|
let v5_versioned: VersionedSlate = v5_bin.into();
|
|
let v4_versioned: VersionedSlate = v4_bin.into();
|
|
|
|
let v5_converted: Slate = v5_versioned.into();
|
|
let v4_converted: Slate = v4_versioned.into();
|
|
|
|
assert!(v5_converted.payment_proof.as_ref().unwrap().memo.is_some());
|
|
// Converted from v4 will not have memos and ts will be zeroed out
|
|
assert!(v4_converted.payment_proof.as_ref().unwrap().memo.is_none());
|
|
assert_eq!(
|
|
v4_converted
|
|
.payment_proof
|
|
.as_ref()
|
|
.unwrap()
|
|
.timestamp
|
|
.timestamp(),
|
|
0
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
}
|