Skip to main content

sn_transfers/cashnotes/
signed_spend.rs

1// Copyright 2024 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3.
4// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed
5// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
6// KIND, either express or implied. Please review the Licences for the specific language governing
7// permissions and limitations relating to use of the SAFE Network Software.
8
9use super::spend_reason::SpendReason;
10use super::{Hash, NanoTokens, UniquePubkey};
11use crate::{
12    DerivationIndex, DerivedSecretKey, Result, Signature, SpendAddress, TransferError,
13    NETWORK_ROYALTIES_PK,
14};
15
16use custom_debug::Debug;
17use serde::{Deserialize, Serialize};
18use std::{
19    cmp::Ordering,
20    collections::{BTreeMap, BTreeSet},
21};
22
23/// `SignedSpend`s are the core of the Network's transaction system.
24/// They are the data type on the Network used to commit to a transfer of value. Analogous to a transaction in Bitcoin.
25/// They are signed piece of data proving the owner's commitment to transfer value.
26/// `Spend`s refer to their ancestors and descendants, forming a directed acyclic graph that starts from Genesis.
27#[derive(Debug, Clone, PartialOrd, Ord, Serialize, Deserialize)]
28pub struct SignedSpend {
29    /// The Spend, together with the owner's signature over it, constitutes the SignedSpend.
30    pub spend: Spend,
31    /// The DerivedSecretKey's signature over the Spend, proving the owner's commitment to the Spend.
32    #[debug(skip)]
33    pub derived_key_sig: Signature,
34}
35
36impl SignedSpend {
37    /// Create a new SignedSpend
38    pub fn sign(spend: Spend, sk: &DerivedSecretKey) -> Self {
39        let derived_key_sig = sk.sign(&spend.to_bytes_for_signing());
40        Self {
41            spend,
42            derived_key_sig,
43        }
44    }
45
46    /// Get public key of input CashNote.
47    pub fn unique_pubkey(&self) -> &UniquePubkey {
48        &self.spend.unique_pubkey
49    }
50
51    /// Get the SpendAddress where this Spend shoud be
52    pub fn address(&self) -> SpendAddress {
53        SpendAddress::from_unique_pubkey(&self.spend.unique_pubkey)
54    }
55
56    /// Get Nano
57    pub fn amount(&self) -> NanoTokens {
58        self.spend.amount()
59    }
60
61    /// Get reason.
62    pub fn reason(&self) -> &SpendReason {
63        &self.spend.reason
64    }
65
66    /// Represent this SignedSpend as bytes.
67    pub fn to_bytes(&self) -> Vec<u8> {
68        let mut bytes: Vec<u8> = Default::default();
69        bytes.extend(self.spend.to_bytes_for_signing());
70        bytes.extend(self.derived_key_sig.to_bytes());
71        bytes
72    }
73
74    /// Verify a SignedSpend
75    ///
76    /// Checks that:
77    /// - it was signed by the DerivedSecretKey that owns the CashNote for this Spend
78    /// - the signature is valid
79    ///
80    /// It does NOT check:
81    /// - if the spend exists on the Network
82    /// - the spend's parents and if they exist on the Network
83    pub fn verify(&self) -> Result<()> {
84        // check signature
85        // the spend is signed by the DerivedSecretKey
86        // corresponding to the UniquePubkey of the CashNote being spent.
87        if self
88            .spend
89            .unique_pubkey
90            .verify(&self.derived_key_sig, self.spend.to_bytes_for_signing())
91        {
92            Ok(())
93        } else {
94            Err(TransferError::InvalidSpendSignature(*self.unique_pubkey()))
95        }
96    }
97
98    /// Verify the parents of this Spend, making sure the input parent_spends are ancestors of self.
99    /// - Also handles the case of parent double spends.
100    /// - verifies that the parent_spends contains self as an output
101    /// - verifies the sum of total inputs equals to the sum of outputs
102    pub fn verify_parent_spends(&self, parent_spends: &BTreeSet<SignedSpend>) -> Result<()> {
103        let unique_key = self.unique_pubkey();
104        trace!("Verifying parent_spends for {self:?}");
105
106        // sort parents by key (identify double spent parents)
107        let mut parents_by_key = BTreeMap::new();
108        for s in parent_spends {
109            parents_by_key
110                .entry(s.unique_pubkey())
111                .or_insert_with(Vec::new)
112                .push(s);
113        }
114
115        let mut total_inputs: u64 = 0;
116        for (_, spends) in parents_by_key {
117            // check for double spend parents
118            if spends.len() > 1 {
119                error!("While verifying parents of {unique_key}, found a double spend parent: {spends:?}");
120                return Err(TransferError::DoubleSpentParent);
121            }
122
123            // check that the parent refers to self
124            if let Some(parent) = spends.first() {
125                match parent.spend.get_output_amount(unique_key) {
126                    Some(amount) => {
127                        total_inputs += amount.as_nano();
128                    }
129                    None => {
130                        return Err(TransferError::InvalidParentSpend(format!(
131                            "Parent spend {:?} doesn't contain self spend {unique_key:?} as one of its output",
132                            parent.unique_pubkey()
133                        )));
134                    }
135                }
136            }
137        }
138
139        let total_outputs = self.amount().as_nano();
140        if total_outputs != total_inputs {
141            return Err(TransferError::InvalidParentSpend(format!(
142                "Parents total input value {total_inputs:?} doesn't match Spend's value {total_outputs:?}"
143            )));
144        }
145
146        trace!("Validated parent_spends for {unique_key}");
147        Ok(())
148    }
149
150    /// Create a random Spend for testing
151    #[cfg(test)]
152    pub(crate) fn random_spend_to(
153        rng: &mut rand::prelude::ThreadRng,
154        output: UniquePubkey,
155        value: u64,
156    ) -> Self {
157        use crate::MainSecretKey;
158
159        let sk = MainSecretKey::random();
160        let index = DerivationIndex::random(rng);
161        let derived_sk = sk.derive_key(&index);
162        let unique_pubkey = derived_sk.unique_pubkey();
163        let reason = SpendReason::default();
164        let ancestor = MainSecretKey::random()
165            .derive_key(&DerivationIndex::random(rng))
166            .unique_pubkey();
167        let spend = Spend {
168            unique_pubkey,
169            reason,
170            ancestors: BTreeSet::from_iter(vec![ancestor]),
171            descendants: BTreeMap::from_iter(vec![(output, (NanoTokens::from(value)))]),
172            royalties: vec![],
173        };
174        let derived_key_sig = derived_sk.sign(&spend.to_bytes_for_signing());
175        Self {
176            spend,
177            derived_key_sig,
178        }
179    }
180}
181
182// Impl manually to avoid clippy complaint about Hash conflict.
183impl PartialEq for SignedSpend {
184    fn eq(&self, other: &Self) -> bool {
185        self.spend == other.spend && self.derived_key_sig == other.derived_key_sig
186    }
187}
188
189impl Eq for SignedSpend {}
190
191impl std::hash::Hash for SignedSpend {
192    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
193        let bytes = self.to_bytes();
194        bytes.hash(state);
195    }
196}
197
198/// Represents a spent UniquePubkey on the Network.
199/// When a CashNote is spent, a Spend is created with the UniquePubkey of the CashNote.
200/// It is then sent to the Network along with the signature of the owner using the DerivedSecretKey matching its UniquePubkey.
201/// A Spend can have multiple ancestors (other spends) which will refer to it as a descendant.
202/// A Spend's value is equal to the total value given by its ancestors, which one can fetch on the Network to check.
203/// A Spend can have multiple descendants (other spends) which will refer to it as an ancestor.
204/// A Spend's value is equal to the total value of given to its descendants.
205#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
206pub struct Spend {
207    /// UniquePubkey of input CashNote that this SignedSpend is proving to be spent.
208    pub unique_pubkey: UniquePubkey,
209    /// Reason why this CashNote was spent.
210    pub reason: SpendReason,
211    /// parent spends of this spend
212    pub ancestors: BTreeSet<UniquePubkey>,
213    /// spends we are parents of along with the amount we commited to give them
214    pub descendants: BTreeMap<UniquePubkey, NanoTokens>,
215    /// royalties outputs' derivation indexes
216    pub royalties: Vec<DerivationIndex>,
217}
218
219impl core::fmt::Debug for Spend {
220    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221        write!(f, "Spend({:?}({:?}))", self.unique_pubkey, self.hash())
222    }
223}
224
225impl Spend {
226    /// Represent this Spend as bytes.
227    /// There is no from_bytes, because this function is not symetric as it uses hashes
228    pub fn to_bytes_for_signing(&self) -> Vec<u8> {
229        let mut bytes: Vec<u8> = Default::default();
230        bytes.extend(self.unique_pubkey.to_bytes());
231        bytes.extend(self.reason.hash().as_ref());
232        bytes.extend("ancestors".as_bytes());
233        for ancestor in self.ancestors.iter() {
234            bytes.extend(&ancestor.to_bytes());
235        }
236        bytes.extend("descendants".as_bytes());
237        for (descendant, amount) in self.descendants.iter() {
238            bytes.extend(&descendant.to_bytes());
239            bytes.extend(amount.to_bytes());
240        }
241        bytes.extend("royalties".as_bytes());
242        for royalty in self.royalties.iter() {
243            bytes.extend(royalty.as_bytes());
244        }
245        bytes
246    }
247
248    /// represent this Spend as a Hash
249    pub fn hash(&self) -> Hash {
250        Hash::hash(&self.to_bytes_for_signing())
251    }
252
253    /// Returns the amount to be spent in this Spend
254    pub fn amount(&self) -> NanoTokens {
255        let amount: u64 = self
256            .descendants
257            .values()
258            .map(|amount| amount.as_nano())
259            .sum();
260        NanoTokens::from(amount)
261    }
262
263    /// Returns the royalties descendants of this Spend
264    pub fn network_royalties(&self) -> BTreeSet<(UniquePubkey, NanoTokens, DerivationIndex)> {
265        let roy_pks: BTreeMap<UniquePubkey, DerivationIndex> = self
266            .royalties
267            .iter()
268            .map(|di| (NETWORK_ROYALTIES_PK.new_unique_pubkey(di), *di))
269            .collect();
270        self.descendants
271            .iter()
272            .filter_map(|(pk, amount)| roy_pks.get(pk).map(|di| (*pk, *amount, *di)))
273            .collect()
274    }
275
276    /// Returns the amount of a particual output target.
277    /// None if the target is not one of the outputs
278    pub fn get_output_amount(&self, target: &UniquePubkey) -> Option<NanoTokens> {
279        self.descendants.get(target).copied()
280    }
281}
282
283impl PartialOrd for Spend {
284    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
285        Some(self.cmp(other))
286    }
287}
288
289impl Ord for Spend {
290    fn cmp(&self, other: &Self) -> Ordering {
291        self.unique_pubkey.cmp(&other.unique_pubkey)
292    }
293}