sn_transfers/cashnotes/
signed_spend.rs1use 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#[derive(Debug, Clone, PartialOrd, Ord, Serialize, Deserialize)]
28pub struct SignedSpend {
29 pub spend: Spend,
31 #[debug(skip)]
33 pub derived_key_sig: Signature,
34}
35
36impl SignedSpend {
37 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 pub fn unique_pubkey(&self) -> &UniquePubkey {
48 &self.spend.unique_pubkey
49 }
50
51 pub fn address(&self) -> SpendAddress {
53 SpendAddress::from_unique_pubkey(&self.spend.unique_pubkey)
54 }
55
56 pub fn amount(&self) -> NanoTokens {
58 self.spend.amount()
59 }
60
61 pub fn reason(&self) -> &SpendReason {
63 &self.spend.reason
64 }
65
66 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 pub fn verify(&self) -> Result<()> {
84 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 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 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 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 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 #[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
182impl 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#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
206pub struct Spend {
207 pub unique_pubkey: UniquePubkey,
209 pub reason: SpendReason,
211 pub ancestors: BTreeSet<UniquePubkey>,
213 pub descendants: BTreeMap<UniquePubkey, NanoTokens>,
215 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 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 pub fn hash(&self) -> Hash {
250 Hash::hash(&self.to_bytes_for_signing())
251 }
252
253 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 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 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}