Files
grin-wallet/libwallet/src/slate_versions/v5.rs
T
2026-09-08 18:29:15 +02:00

375 lines
10 KiB
Rust

// Copyright 2023 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.
//! Contains V5 of the slate (version as yet undetermined)
//!
//! V5 is experimental and should remain so until the relevant RFCs are accepted.
//!
//! Changes from V4:
//! #### Top-Level Slate Struct
//! *
//! #### PaymentInfoV5
//! * `saddr`, i.e. `sender_address` in main Slate becomes optional
//! * `rsig` is renamed to `psig`, corresponding to rename of `receiver_signature` to `promise_signature` in main Slate
//!
//! * `ptype` identifies the payment proof type
//! * `ts` adds an optional timestamp, serialized as seconds since the epoch
//! * `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::slate::{PaymentMemo, PaymentProofType};
use crate::{slate_versions::ser, CbData};
use chrono::prelude::{DateTime, Utc};
use ed25519_dalek::Signature as DalekSignature;
use ed25519_dalek::VerifyingKey as DalekPublicKey;
use serde_with::TimestampSeconds;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SlateV5 {
// Required Fields
/// Versioning info
#[serde(with = "ser::version_info_v5")]
pub ver: VersionCompatInfoV5,
/// Unique transaction ID, selected by sender
pub id: Uuid,
/// Slate state
#[serde(with = "ser::slate_state_v5")]
pub sta: SlateStateV5,
/// Offset, modified by each participant inserting inputs
/// as the transaction progresses
#[serde(
serialize_with = "secp_ser::as_hex",
deserialize_with = "secp_ser::blind_from_hex"
)]
#[serde(default = "default_offset_zero")]
#[serde(skip_serializing_if = "offset_is_zero")]
pub off: BlindingFactor,
// Optional fields depending on state
/// The number of participants intended to take part in this transaction
#[serde(default = "default_num_participants_2")]
#[serde(skip_serializing_if = "num_parts_is_2")]
pub num_parts: u8,
/// base amount (excluding fee)
#[serde(with = "secp_ser::string_or_u64")]
#[serde(skip_serializing_if = "u64_is_blank")]
#[serde(default = "default_u64_0")]
pub amt: u64,
/// fee
#[serde(skip_serializing_if = "fee_is_zero")]
#[serde(default = "default_fee")]
pub fee: FeeFields,
/// kernel features, if any
#[serde(skip_serializing_if = "u8_is_blank")]
#[serde(default = "default_u8_0")]
pub feat: u8,
/// TTL, the block height at which wallets
/// should refuse to process the transaction and unlock all
#[serde(with = "secp_ser::string_or_u64")]
#[serde(skip_serializing_if = "u64_is_blank")]
#[serde(default = "default_u64_0")]
pub ttl: u64,
// Structs always required
/// Participant data, each participant in the transaction will
/// insert their public data here. For now, 0 is sender and 1
/// is receiver, though this will change for multi-party
pub sigs: Vec<ParticipantDataV5>,
// Situational, but required at some point in the tx
/// Inputs/Output commits added to slate
#[serde(default = "default_coms_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub coms: Option<Vec<CommitsV5>>,
// Optional Structs
/// Payment Proof
#[serde(default = "default_payment_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub proof: Option<PaymentInfoV5>,
/// Kernel features arguments
#[serde(default = "default_kernel_features_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub feat_args: Option<KernelFeaturesArgsV5>,
}
fn default_payment_none() -> Option<PaymentInfoV5> {
None
}
fn default_offset_zero() -> BlindingFactor {
BlindingFactor::zero()
}
fn offset_is_zero(o: &BlindingFactor) -> bool {
*o == BlindingFactor::zero()
}
fn default_coms_none() -> Option<Vec<CommitsV5>> {
None
}
fn default_u64_0() -> u64 {
0
}
fn num_parts_is_2(n: &u8) -> bool {
*n == 2
}
fn default_num_participants_2() -> u8 {
2
}
fn default_kernel_features_none() -> Option<KernelFeaturesArgsV5> {
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
pub version: u16,
/// Version of grin block header this slate is compatible with
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<Signature>,
}
fn default_part_sig_none() -> Option<Signature> {
None
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct PaymentInfoV5 {
#[serde(with = "crate::slate::payment_proof_type_serde")]
pub ptype: PaymentProofType,
#[serde(default)]
#[serde(with = "ser::option_dalek_pubkey_serde")]
#[serde(skip_serializing_if = "Option::is_none")]
pub saddr: Option<DalekPublicKey>,
#[serde(with = "ser::dalek_pubkey_serde")]
pub raddr: DalekPublicKey,
#[serde_as(as = "Option<TimestampSeconds<i64>>")]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ts: Option<DateTime<Utc>>,
#[serde(default = "default_promise_signature_none")]
#[serde(with = "ser::option_dalek_sig_serde")]
#[serde(skip_serializing_if = "Option::is_none")]
pub psig: Option<DalekSignature>,
#[serde(skip_serializing_if = "Option::is_none")]
pub memo: Option<PaymentMemo>,
}
fn default_promise_signature_none() -> Option<DalekSignature> {
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<RangeProof>,
}
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<RangeProof> {
None
}
fn u64_is_blank(u: &u64) -> bool {
*u == 0
}
fn default_u8_0() -> u8 {
0
}
fn u8_is_blank(u: &u8) -> bool {
*u == 0
}
fn fee_is_zero(f: &FeeFields) -> bool {
f.is_zero()
}
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<Identifier>,
}
impl From<CbData> 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,
}