redgold_schema/
transaction.rs

1use crate::constants::{DECIMAL_MULTIPLIER, MAX_COIN_SUPPLY};
2use crate::helpers::with_metadata_hashable::{WithMetadataHashable, WithMetadataHashableFields};
3use crate::proto_serde::ProtoHashable;
4use crate::structs::TransactionType;
5use crate::structs::{Address, CurrencyAmount, ErrorInfo, ExternalTransactionId, FloatingUtxoId, Hash, HashType, Input, NetworkEnvironment, NodeMetadata, Observation, ObservationProof, Output, OutputType, PoWProof, PortfolioRequest, ProductId, Proof, PublicKey, StakeDeposit, StakeRequest, StakeWithdrawal, StandardContractType, StandardData, StandardRequest, StandardResponse, StructMetadata, SupportedCurrency, SwapFulfillment, SwapRequest, Transaction, TransactionOptions, TypedValue, UtxoEntry, UtxoId};
6use crate::{bytes_data, error_info, structs, ErrorInfoContext, HashClear, PeerMetadata, RgResult, SafeOption};
7use itertools::Itertools;
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, HashSet};
10use std::str::FromStr;
11
12pub const MAX_TRANSACTION_MESSAGE_SIZE: usize = 40;
13
14
15impl UtxoId {
16
17    pub fn new(hash: &Hash, output_index: i64) -> Self {
18        Self {
19            transaction_hash: Some(hash.clone()),
20            output_index,
21        }
22    }
23    pub fn format_str(&self) -> String {
24        format!("UtxoId: {} output: {}", self.transaction_hash.clone().expect("").hex(), self.output_index)
25    }
26
27    // This is questionable, should we do this long term for XOR distance?
28    pub fn utxo_id_vec(&self) -> Vec<u8> {
29        let mut merged: Vec<u8> = vec![];
30        merged.extend(self.transaction_hash.clone().expect("hash").vec());
31        merged.extend(self.output_index.to_le_bytes().to_vec());
32        merged
33    }
34
35    pub fn as_hash(&self) -> Hash {
36        let mut h = Hash::new_direct_transaction(&self.utxo_id_vec());
37        h.hash_type = HashType::UtxoId as i32;
38        h
39    }
40    pub fn utxo_id_hex(&self) -> String {
41        hex::encode(self.utxo_id_vec())
42    }
43}
44
45
46pub fn amount_to_raw_amount(redgold: u64) -> u64 {
47    assert!(redgold <= MAX_COIN_SUPPLY as u64);
48    return redgold * (DECIMAL_MULTIPLIER as u64);
49}
50
51trait BalanceConversion {
52    fn rounded_float(&self) -> f64;
53}
54
55impl BalanceConversion for i64 {
56    fn rounded_float(&self) -> f64 {
57        rounded_balance_i64(*self)
58    }
59}
60
61pub fn rounded_balance(redgold_amount: u64) -> f64 {
62    (redgold_amount as f64) / (DECIMAL_MULTIPLIER as f64)
63}
64
65pub fn rounded_balance_i64(redgold_amount: i64) -> f64 {
66    (redgold_amount as f64) / (DECIMAL_MULTIPLIER as f64)
67}
68
69impl WithMetadataHashableFields for Transaction {
70    fn struct_metadata_opt(&mut self) -> Option<&mut StructMetadata> {
71        self.struct_metadata.as_mut()
72    }
73
74    fn struct_metadata_opt_ref(&self) -> Option<&StructMetadata> {
75        self.struct_metadata.as_ref()
76    }
77}
78
79impl HashClear for Input {
80    fn hash_clear(&mut self) {
81        self.address = None;
82        self.output = None;
83    }
84}
85
86impl HashClear for Transaction {
87    fn hash_clear(&mut self) {
88        // TODO: Implement hashclear for inputs
89        for x in self.inputs.iter_mut() {
90            x.hash_clear();
91        }
92        if let Some(s) = self.struct_metadata_opt() {
93            s.hash_clear();
94        }
95    }
96}
97
98#[derive(PartialEq, PartialOrd, Debug, Clone, serde::Serialize, serde::Deserialize)]
99pub struct AddressBalance {
100    pub address: String,
101    pub rounded_balance: f64,
102}
103
104
105impl Transaction {
106
107    pub fn combine_multisig_proofs(&mut self, other: &Transaction, address: &Address) -> RgResult<Transaction> {
108        let mut updated = self.clone();
109        for (idx, input) in updated.inputs.iter_mut().enumerate() {
110            let other_input = other.inputs.get(idx).ok_msg("Missing input")?;
111            for proof in other_input.proof.iter() {
112                if input.proof.iter().any(|p| p.public_key == proof.public_key) {
113                    continue;
114                } else {
115                    input.proof.push(proof.clone());
116                }
117            }
118        }
119        Ok(updated)
120    }
121
122    pub fn with_pow(mut self) -> RgResult<Self> {
123        let hash = self.signable_hash();
124        let proof = PoWProof::from_hash_sha3_256(&hash, 1)?;
125        let opts = self.options.as_mut().expect("");
126        opts.pow_proof = Some(proof);
127        Ok(self)
128    }
129
130    pub fn signable_hash_or(&self) -> Hash {
131        self.struct_metadata_opt_ref()
132            .and_then(|s| s.signable_hash.clone()) // TODO: Change to as_ref() to prevent clone?
133            .unwrap_or(self.signable_hash())
134    }
135
136    pub fn sponsored_time(&self) -> RgResult<i64> {
137        let t = self.options()?.time_sponsor.safe_get_msg("Missing sponsored time")?.time;
138        Ok(t)
139    }
140
141    pub fn transaction_type(&self) -> RgResult<structs::TransactionType> {
142        let t = self.options()?.transaction_type;
143        TransactionType::from_i32(t).ok_msg("Invalid transaction type")
144    }
145
146    pub fn is_metadata_or_obs(&self) -> bool {
147        self.outputs.iter().all(|o| o.is_metadata() || o.observation().is_ok())
148    }
149    pub fn validate_network(&self, network: &NetworkEnvironment) -> RgResult<()> {
150        let opts = self.options()?;
151        let net = opts.network_type.safe_get_msg("Missing network type")?;
152        let net = NetworkEnvironment::from_i32(net.clone());
153        let net = net.safe_get_msg("Invalid network type")?;
154        if net != network {
155            Err(ErrorInfo::error_info("Invalid network type"))?
156        }
157        Ok(())
158    }
159    pub fn network(&self) -> RgResult<NetworkEnvironment> {
160        let opts = self.options()?;
161        let net = opts.network_type.safe_get_msg("Missing network type")?;
162        let net = NetworkEnvironment::from_i32(net.clone());
163        let net = net.safe_get_msg("Invalid network type")?;
164        Ok(net.clone())
165    }
166
167    pub fn is_test(&self) -> bool {
168        self.options.as_ref().and_then(|o| o.is_test).unwrap_or(false)
169    }
170    // TODO: Validator should ensure UtxoId only used once
171    // Lets rename all UtxoId just utxoid
172    pub fn input_of(&self, f: &UtxoId) -> Option<&Input> {
173        self.inputs.iter().find(|i| i.utxo_id.as_ref().filter(|&u| u == f).is_some())
174    }
175
176    pub fn is_swap(&self) -> bool {
177        self.outputs.iter().filter(|o| o.is_swap()).count() > 0
178    }
179
180    pub fn is_swap_fulfillment(&self) -> bool {
181        self.outputs.iter().filter(|o| o.is_swap_fulfillment()).count() > 0
182    }
183
184    pub fn swap_fulfillment(&self) -> Option<&SwapFulfillment> {
185        self.outputs.iter().filter_map(|o| o.swap_fulfillment()).next()
186    }
187
188    pub fn swap_fulfillment_amount_and_destination_and_origin(&self) -> Option<(&SwapFulfillment, &CurrencyAmount, &Address, Address)> {
189        self.first_input_address().and_then(|origin|
190            self.outputs.iter().filter_map(|o| {
191                o.swap_fulfillment().and_then(|f| {
192                    o.opt_amount_typed_ref().and_then(|a|
193                        o.address.as_ref().map(|addr| (f, a, addr, origin.clone()))
194                    )
195                })
196            }).next()
197        )
198    }
199
200    pub fn output_data(&self) -> impl Iterator<Item=&StandardData> {
201        self.outputs.iter().filter_map(|o| o.data.as_ref())
202    }
203
204    pub fn output_request(&self) -> impl Iterator<Item=&StandardRequest> {
205        self.output_data().map(|d| d.standard_request.as_ref()).flatten()
206    }
207
208    pub fn output_response(&self) -> impl Iterator<Item=&StandardResponse> {
209        self.output_data().map(|d| d.standard_response.as_ref()).flatten()
210    }
211
212    pub fn swap_request(&self) -> Option<&SwapRequest> {
213        self.output_request().filter_map(|d| d.swap_request.as_ref()).next()
214    }
215
216    pub fn swap_request_and_amount_and_party_address(&self) -> Option<(&SwapRequest, &CurrencyAmount, &Address)> {
217        self.outputs.iter().filter_map(|o| {
218            o.opt_amount_typed_ref()
219                .and_then(|a|
220                    o.address.as_ref()
221                        .and_then(|addr| o.swap_request()
222                            .map(|r| (r, a, addr))
223                ))
224        }).next()
225    }
226
227    pub fn swap_destination(&self) -> Option<&Address> {
228        self.swap_request().and_then(|r| r.destination.as_ref())
229    }
230
231    pub fn stake_request(&self) -> Option<&StakeRequest> {
232        self.output_request().filter_map(|d| d.stake_request.as_ref()).next()
233    }
234
235    pub fn stake_deposit_request(&self) -> Option<&StakeDeposit> {
236        self.stake_request().and_then(|d| d.deposit.as_ref())
237    }
238
239    pub fn stake_withdrawal_request(&self) -> Option<&StakeWithdrawal> {
240        self.stake_request().and_then(|d| d.withdrawal.as_ref())
241    }
242
243    pub fn stake_withdrawal_fulfillments(&self) -> impl Iterator<Item =&structs::StakeWithdrawalFulfillment> {
244        self.output_response().flat_map(|r| r.stake_withdrawal_fulfillment.as_ref())
245    }
246
247    pub fn stake_deposit_destination(&self) -> Option<&Address> {
248        self.stake_deposit_request()
249            .and_then(|d| d.deposit.as_ref())
250            .and_then(|d| d.address.as_ref())
251    }
252
253    pub fn stake_destination(&self) -> Option<&Address> {
254        self.stake_deposit_destination().or(self.stake_withdrawal_destination())
255    }
256
257    pub fn stake_withdrawal_destination(&self) -> Option<&Address> {
258        self.stake_withdrawal_request()
259            .and_then(|d| d.destination.as_ref())
260    }
261
262    pub fn swap_destination_currency(&self) -> Option<SupportedCurrency> {
263        self.swap_destination().map(|a| a.currency_or())
264    }
265
266    pub fn external_destination_currency(&self) -> Option<SupportedCurrency> {
267        self.swap_destination().or(self.stake_destination())
268            .map(|d| d.clone().mark_external().clone())
269            .map(|a| a.currency_or())
270    }
271
272    pub fn is_stake(&self) -> bool {
273        self.outputs.iter().filter(|o| o.is_stake()).count() > 0
274    }
275
276    pub fn has_portfolio_request(&self) -> bool {
277        self.portfolio_request().is_some()
278    }
279
280    pub fn portfolio_request(&self) -> Option<&PortfolioRequest> {
281        self.outputs.iter()
282            .filter_map(|o| o.request())
283            .filter_map(|r| r.portfolio_request.as_ref())
284            .next()
285    }
286
287
288    pub fn is_metadata(&self) -> bool {
289        self.outputs.iter().filter(|o| o.is_metadata()).count() > 0
290    }
291    pub fn is_request(&self) -> bool {
292        self.outputs.iter().filter(|o| o.is_request()).count() > 0
293    }
294    pub fn is_deploy(&self) -> bool {
295        self.outputs.iter().filter(|o| o.is_deploy()).count() > 0
296    }
297
298    pub fn observation_output_index(&self) -> RgResult<i64> {
299        self.outputs.iter().enumerate().find(|(_i, o)| o.observation().is_ok()
300        ).map(|o| o.0 as i64).ok_or(error_info("Missing observation output"))
301    }
302
303    pub fn observation_as_utxo_id(&self) -> RgResult<UtxoId> {
304        let o = self.observation_output_index();
305        Ok(UtxoId {
306            transaction_hash: Some(self.hash_or()),
307            output_index: o?,
308        })
309    }
310
311    pub fn observation_output(&self) -> RgResult<&Output> {
312        let o = self.observation_output_index()?;
313        let option = self.outputs.get(o as usize);
314        option.safe_get_msg("Missing observation output").cloned()
315    }
316
317    pub fn observation_output_as(&self) -> RgResult<UtxoEntry> {
318        let idx = self.observation_output_index()?;
319        let o = self.observation_output()?;
320        let u = o.utxo_entry(&self.hash_or(), idx as i64, self.time()?.clone());
321        Ok(u)
322    }
323
324    pub fn observation(&self) -> RgResult<&Observation> {
325        let mut map = self.outputs.iter().filter_map(|o| o.observation().ok());
326        let option = map.next();
327        option.ok_or(error_info("Missing observation"))
328    }
329
330    pub fn observation_proof(&self) -> RgResult<&Proof> {
331        let o = self.observation()?;
332        let p = o.parent_id.safe_get_msg("Missing parent id")?;
333        let input_opt = self.input_of(&p);
334        let input = input_opt.safe_get_msg("Missing input")?;
335        let proof = input.proof.get(0);
336        let proof_act = proof.safe_get_msg("Missing input proof")?;
337        Ok(*proof_act)
338    }
339    pub fn observation_public_key(&self) -> RgResult<&PublicKey> {
340        let proof_act = self.observation_proof()?;
341        let pk = proof_act.public_key.safe_get_msg("Missing public key")?;
342        Ok(pk)
343    }
344
345    pub fn build_observation_proofs(&self) -> RgResult<Vec<ObservationProof>> {
346        let h = self.hash_or();
347        let p = self.observation_proof()?;
348        let o = self.observation()?;
349        Ok(o.build_observation_proofs(&h, &p))
350    }
351
352    pub fn with_hashes(&mut self) -> &mut Self {
353        self.with_hash();
354        self.with_signable_hash().expect("signable hash");
355        self
356    }
357
358    pub fn with_signable_hash(&mut self) -> Result<&mut Self, ErrorInfo> {
359        self.struct_metadata()?.signable_hash = Some(self.signable_hash());
360        Ok(self)
361    }
362
363    pub fn add_proof_per_input(&mut self, proof: &Proof) -> &Transaction {
364        for i in self.inputs.iter_mut() {
365            i.proof.push(proof.clone());
366        }
367        self
368    }
369
370    pub fn utxo_inputs(&self) -> impl Iterator<Item = &UtxoId> {
371        self.inputs.iter().filter_map(|i| i.utxo_id.as_ref())
372    }
373
374    // This doesn't really need to return an error?
375    pub fn fixed_utxo_ids_of_inputs(&self) -> Result<Vec<UtxoId>, ErrorInfo> {
376        let mut utxo_ids = Vec::new();
377        for input in &self.inputs {
378            if let Some(f) = &input.utxo_id {
379                utxo_ids.push(f.clone());
380            }
381        }
382        Ok(utxo_ids)
383    }
384
385    // This doesn't really need to return an error?
386    pub fn input_utxo_ids(&self) -> impl Iterator<Item = &UtxoId> {
387        self.inputs.iter().flat_map(|i| i.utxo_id.as_ref())
388    }
389
390    pub fn output_amounts(&self) -> impl Iterator<Item=AddressBalance> + '_ {
391        self.outputs
392            .iter()
393            .flat_map(|o| o.address.as_ref()
394                .and_then(|a| a.render_string().ok())
395                .and_then(|a| o.opt_amount_typed().map(|aa|
396                    AddressBalance {
397                        address: a,
398                        rounded_balance: aa.to_fractional()
399                    })
400                )
401            )
402    }
403    pub fn output_amount_total(&self) -> CurrencyAmount {
404        self.output_amounts_opt().cloned().sum::<CurrencyAmount>()
405    }
406
407    pub fn output_amounts_opt(&self) -> impl Iterator<Item = &CurrencyAmount> {
408        self.outputs
409            .iter()
410            .flat_map(|o| o.data.as_ref().and_then(|d| d.amount.as_ref()))
411    }
412
413    pub fn output_address_amounts_opt(&self) -> impl Iterator<Item = (&Address, CurrencyAmount)> {
414        self.outputs
415            .iter()
416            .flat_map(|o| o.opt_amount_typed()
417                .and_then(|a| o.address.as_ref().map(|addr| (addr, a)))
418            )
419    }
420
421    pub fn output_amount_map<'a>(&'a self) -> HashMap<&'a Address, i64> {
422        self.outputs
423            .iter()
424            .filter_map(|o| {
425                // Now working entirely with references, avoiding cloning.
426                // This assumes `o.address` is an Option<&Address> and `o.opt_amount()`
427                // returns an Option<i64>.
428                o.address.as_ref().and_then(|ad| o.opt_amount().map(|a| (ad, a)))
429            })
430            .fold(HashMap::new(), |mut acc: HashMap<&'a Address, i64>, (ad, a)| {
431                *acc.entry(&ad).or_insert(0) += a;
432                acc
433            })
434    }
435
436    // TODO: Fix this, it's wrong
437    pub fn non_remainder_amount_rdg(&self) -> i64 {
438        let inputs = self.input_address_set();
439        self.outputs.iter().filter_map(|o| {
440            o.address.as_ref()
441                .filter(|&a| !inputs.contains(a))
442                .and_then(|_| o.opt_amount())}
443        ).sum::<i64>()
444    }
445    pub fn non_remainder_amount_rdg_typed(&self) -> CurrencyAmount {
446        let inputs = self.input_address_set();
447        self.outputs.iter().filter_map(|o| {
448            o.address.as_ref()
449                .filter(|&a| !inputs.contains(a))
450                .and_then(|_| o.opt_amount_typed())}
451        ).sum::<CurrencyAmount>()
452    }
453
454    // TODO: Return as currency amount with plus operators, also maybe make optional?
455    pub fn remainder_amount(&self) -> i64 {
456        let inputs = self.input_address_set();
457        self.outputs.iter().filter_map(|o| {
458            o.address.as_ref()
459                .filter(|&a| inputs.contains(a))
460                .and_then(|_| o.opt_amount())}
461        ).sum::<i64>()
462    }
463    pub fn fee_amount(&self) -> i64 {
464        self.outputs.iter()
465            .filter(|o| o.is_fee())
466            .flat_map(|o| o.opt_amount())
467            .sum::<i64>()
468    }
469
470    pub fn first_output_external_txid(&self) -> Option<&ExternalTransactionId> {
471        self.output_external_txids().next()
472    }
473    pub fn output_external_txids(&self) -> impl Iterator<Item = &ExternalTransactionId> {
474        self.output_response()
475            .filter_map(|r| r.swap_fulfillment.as_ref())
476            .filter_map(|d| d.external_transaction_id.as_ref())
477    }
478
479    pub fn output_rdg_amount_of(&self, address: &Address) -> i64 {
480        self.outputs
481            .iter()
482            .filter_map(|o| o.address.as_ref().filter(|&a| a == address)
483                .and_then(|_| o.opt_amount_typed()))
484            .filter(|a| a.currency_or() == SupportedCurrency::Redgold)
485            .map(|a| a.amount)
486            .sum::<i64>()
487    }
488
489    pub fn output_of(&self, address: &Address) -> Vec<&structs::Output> {
490        self.outputs
491            .iter()
492            .filter_map(|o| o.address.as_ref().filter(|&a| a == address).map(|_| o))
493            .collect_vec()
494    }
495
496    pub fn output_of_with_index(&self, address: &Address) -> Vec<(usize, &structs::Output)> {
497        self.outputs
498            .iter()
499            .enumerate()
500            .filter_map(|(index, o)| o.address.as_ref().filter(|&a| a == address).map(|_| (index, o)))
501            .collect_vec()
502    }
503
504    pub fn first_peer_utxo(&self) -> RgResult<UtxoEntry> {
505        let vec = self.utxo_outputs()?;
506        let option = vec.iter()
507            .filter(|f| f.output.as_ref().filter(|o| o.is_peer_data()).is_some())
508            .next();
509        let x = option.ok_or(error_info("Missing peer utxo"))?.clone();
510        Ok(x)
511    }
512
513    pub fn output_swap_amount_of(&self, address: &Address) -> i64 {
514        self.outputs
515            .iter()
516            .filter_map(|o| {
517                if o.is_swap() {
518                    o.address.as_ref()
519                        .filter(|&a| a == address)
520                        .and_then(|_| o.opt_amount())
521                } else {
522                    None
523                }
524            }).sum::<i64>()
525    }
526
527    pub fn has_swap_to(&self, address: &Address) -> bool {
528        self.output_swap_amount_of(address) > 0
529    }
530
531    pub fn output_bitcoin_address_of(&self, address: &Address) -> Option<&Address> {
532        address.render_string().ok().and_then(|s| {
533            self.outputs
534                .iter()
535                .filter(|o| {
536                    o.is_swap()
537                        .then(|| o.address.as_ref())
538                        .flatten()
539                        .and_then(|a| a.render_string().ok())
540                        .filter(|a| {
541                            a == &s
542                        })
543                        .is_some()
544                })
545                .filter_map(|o| o.data.as_ref().and_then(|d| d.address.as_ref()))
546                .next()
547        })
548    }
549
550    pub fn output_index(&self, output: &Output) -> RgResult<i64> {
551        let index = self.outputs.iter().position(|o| o == output);
552        index.safe_get_msg("Missing output index").map(|i| i.clone() as i64)
553    }
554
555    pub fn utxo_id_at(&self, index: usize) -> RgResult<UtxoId> {
556        let hash = self.hash_or();
557        let output = self.outputs.get(index);
558        output.safe_get_msg("Missing output")?;
559        let utxo_id = UtxoId::new(&hash, index as i64);
560        Ok(utxo_id)
561    }
562
563    pub fn liquidity_of(&self, a: &Address) -> Vec<(UtxoId, &StakeRequest)> {
564        self.output_of_with_index(a)
565            .iter()
566            .flat_map(|(u, o)|
567                self.utxo_id_at(*u).ok().and_then(|utxo_id|
568                    o.data.as_ref()
569                        .and_then(|d| d.standard_request.as_ref())
570                        .and_then(|d| d.stake_request.as_ref())
571                        .map(|l| (utxo_id, l))
572            ))
573            .collect_vec()
574    }
575    pub fn stake_requests(&self) -> Vec<(UtxoId, &StakeRequest)> {
576        self.outputs
577            .iter()
578            .enumerate()
579            .flat_map(|(u, o)|
580                self.utxo_id_at(u).ok().and_then(|utxo_id|
581                    o.stake_request()
582                        .map(|l| (utxo_id, l))
583            ))
584            .collect_vec()
585    }
586
587    pub fn total_output_amount(&self) -> i64 {
588        let mut total = 0;
589        for o in &self.outputs {
590            if let Some(a) = o.opt_amount() {
591                total += a
592            }
593        }
594        total
595    }
596
597    pub fn floating_inputs(&self) -> impl Iterator<Item = &FloatingUtxoId> {
598        self.inputs.iter().filter_map(|i| i.floating_utxo_id.as_ref())
599    }
600
601    pub fn total_input_amount(&self) -> i64 {
602        self.inputs.iter()
603            .filter_map(|i| i.output.as_ref())
604            .filter_map(|o| o.opt_amount())
605            .sum()
606    }
607
608    pub fn total_output_amount_float(&self) -> f64 {
609        CurrencyAmount::from(
610        self.total_output_amount()
611        ).to_fractional()
612    }
613
614    pub fn output_amounts_by_product(&self) -> HashMap<ProductId, CurrencyAmount> {
615        let mut map = HashMap::new();
616        for output in &self.outputs {
617            if let Some(product_id) = output.product_id.as_ref() {
618                if let Some(a) = output.opt_amount() {
619                    let aa = map.get(product_id).map(|x: &CurrencyAmount| x.amount + a).unwrap_or(a);
620                    map.insert(product_id.clone(), CurrencyAmount::from(aa));
621                }
622            }
623        }
624        map
625    }
626
627    #[allow(dead_code)]
628    fn clear_input_proofs(&mut self) {
629        for i in 0..self.inputs.len() {
630            self.inputs.get_mut(i).unwrap().proof.clear();
631        }
632    }
633
634    #[allow(dead_code)]
635    fn clear_counter_party_proofs(&mut self) {
636        for i in 0..self.outputs.len() {
637            self.outputs
638                .get_mut(i)
639                .unwrap()
640                .counter_party_proofs
641                .clear();
642        }
643    }
644    #[allow(dead_code)]
645    fn clear_confirmation_proofs(&mut self) {
646        for o in self.options.iter_mut() {
647            for c in o.contract.iter_mut() {
648                c.confirmation_proofs.clear()
649            }
650        }
651    }
652    #[allow(dead_code)]
653    pub fn signable_hash(&self) -> Hash {
654        let mut clone = self.clone();
655        clone.clear_input_proofs();
656        clone.clear_counter_party_proofs();
657        clone.clear_confirmation_proofs();
658        return clone.calculate_hash();
659    }
660
661    #[allow(dead_code)]
662    pub fn signed_hash(&self) -> Hash {
663        let mut clone = self.clone();
664        clone.clear_counter_party_proofs();
665        clone.clear_confirmation_proofs();
666        return clone.calculate_hash();
667    }
668
669
670    #[allow(dead_code)]
671    fn pre_counter_party_hash(&self) -> Hash {
672        self.signed_hash()
673    }
674
675    #[allow(dead_code)]
676    fn confirmation_hash(&self) -> Hash {
677        let mut clone = self.clone();
678        clone.clear_confirmation_proofs();
679        return clone.calculate_hash();
680    }
681
682    pub fn to_utxo_entries(&self, time: u64) -> Vec<UtxoEntry> {
683        return UtxoEntry::from_transaction(self, time as i64);
684    }
685
686    pub fn to_utxo_address(&self, address: &Address) -> Vec<UtxoEntry> {
687        let mut res = vec![];
688        let time = self.time();
689        if let Ok(time) = time {
690            for u in UtxoEntry::from_transaction(self, time.clone()) {
691                if u.address() == Ok(address) {
692                    res.push(u);
693                }
694            }
695        }
696        res
697    }
698
699    pub fn utxo_outputs(&self) -> RgResult<Vec<UtxoEntry>> {
700        let t = self.time()?;
701        return Ok(UtxoEntry::from_transaction(self, t.clone()));
702    }
703
704    pub fn output_utxo_ids(&self) -> impl Iterator<Item = &UtxoId>{
705        self.outputs.iter().flat_map(|o| o.utxo_id.as_ref())
706    }
707
708    pub fn head_utxo(&self) -> RgResult<UtxoEntry> {
709        let utxos = self.utxo_outputs()?;
710        let head_utxo = utxos.get(0);
711        let utxo = *head_utxo.safe_get_msg("Missing UTXO output for node metadata")?;
712        Ok(utxo.clone())
713    }
714
715    pub fn nmd_utxo(&self) -> RgResult<UtxoEntry> {
716        let utxos = self.utxo_outputs()?.iter().filter(|u|
717            u.output.as_ref().map(|o| o.is_node_metadata()).unwrap_or(false)
718        ).cloned().collect_vec();
719        let head_utxo = utxos.get(0);
720        let utxo = *head_utxo.safe_get_msg("Missing UTXO output for node metadata")?;
721        Ok(utxo.clone())
722    }
723
724    pub fn height(&self) -> RgResult<i64> {
725        let h = self.options.as_ref()
726            .and_then(|o| o.data.as_ref())
727            .and_then(|d| d.standard_data.as_ref())
728            .and_then(|s| s.height);
729        h.safe_get_msg("Missing height").cloned()
730    }
731
732    pub fn options(&self) -> RgResult<&TransactionOptions> {
733        self.options.safe_get_msg("Missing options")
734    }
735    pub fn salt(&self) -> RgResult<i64> {
736        let s = self.options()?.salt;
737        s.safe_get_msg("Missing salt").cloned()
738    }
739
740    // pub fn to_utxo_outputs_as_inputs(&self, time: u64) -> Vec<Input> {
741    //     return UtxoEntry::from_transaction(self, time as i64)
742    //         .iter()
743    //         .map(|u| u.to_input())
744    //         .collect::<Vec<Input>>();
745    // }
746    //
747    // pub fn to_utxo_input_ids(&self) -> Vec<Vec<u8>> {
748    //     return UtxoEntry::ids_from_transaction_inputs(self);
749    // }
750    //
751    // pub fn to_utxo_output_ids(&self) -> Vec<Vec<u8>> {
752    //     return UtxoEntry::ids_from_transaction_outputs(self);
753    // }
754
755    // pub fn currency_contract_hash() -> Vec<u8> {
756    //     dhash_str("Redgold_currency_contract").to_vec()
757    // }
758
759    // This function seems to halt with a bad amoubnt calll
760
761    pub fn peer_data(&self) -> Result<PeerMetadata, ErrorInfo> {
762        let mut res = vec![];
763        for o in &self.outputs {
764            if let Some(data) = &o.data {
765                if let Some(d) = &data.peer_data {
766                    res.push(d.clone());
767                }
768            }
769        }
770        if res.len() == 1 {
771            return Ok(res[0].clone());
772        } else {
773            return Err(ErrorInfo::error_info("Missing peer data in transaction"));
774        }
775    }
776
777    pub fn node_metadata(&self) -> Result<NodeMetadata, ErrorInfo> {
778        let mut res = vec![];
779        for o in &self.outputs {
780            if let Some(data) = &o.data {
781                if let Some(d) = &data.node_metadata {
782                    res.push(d.clone());
783                }
784            }
785        }
786        if res.len() == 1 {
787            return Ok(res[0].clone());
788        } else {
789            return Err(ErrorInfo::error_info("Missing node metadata in transaction"));
790        }
791    }
792
793    #[deprecated]
794    pub fn addresses(&self) -> Vec<Address> {
795        self.outputs.iter().filter_map(|o| o.address.clone()).collect_vec()
796    }
797
798    #[deprecated]
799    pub fn input_addresses(&self) -> Vec<Address> {
800        self.inputs.iter().filter_map(|o| o.address().ok()).collect_vec()
801    }
802
803    pub fn input_address_descriptor_address_or_public_key(&self) -> Vec<Address> {
804        self.inputs.iter().filter_map(|i| {
805            if let Some(d) = i.address_descriptor.as_ref() {
806                Some(d.to_address())
807            } else {
808                i.proof.get(0)
809                    .and_then(|p| p.public_key.as_ref())
810                    .and_then(|pk| pk.address().ok())
811            }
812        }).collect_vec()
813    }
814
815    #[deprecated]
816    pub fn input_address_set(&self) -> HashSet<Address> {
817        self.inputs.iter().filter_map(|o| o.address().ok()).collect()
818    }
819
820    #[deprecated]
821    pub fn first_input_address(&self) -> Option<Address> {
822        self.inputs.iter().flat_map(|o| o.address().ok()).next()
823    }
824
825    #[deprecated]
826    pub fn first_input_proof_public_key(&self) -> Option<&structs::PublicKey> {
827        self.inputs.first()
828            .and_then(|o| o.proof.get(0))
829            .and_then(|o| o.public_key.as_ref())
830    }
831
832    pub fn first_output_non_input_or_fee(&self) -> Option<&Output> {
833        let input_addrs = self.input_addresses();
834        self.outputs.iter()
835            .filter(|o| o.output_type != Some(OutputType::Fee as i32))
836            .filter(|o| o.address.as_ref().map(|a| !input_addrs.contains(a)).unwrap_or(true))
837            .next()
838    }
839
840    pub fn has_input_address_as_output_address(&self) -> bool {
841        let input_addrs = self.input_addresses();
842        self.outputs.iter()
843            .filter(|o| o.address.as_ref().map(|a| input_addrs.contains(a)).unwrap_or(false))
844            .next()
845            .is_some()
846    }
847
848    pub fn has_relation_to_address(&self, a: &Address) -> bool {
849        self.input_address_descriptor_address_or_public_key().contains(a) ||
850            self.output_address_amounts_opt().map(|(addr, _)| addr).any(|addr| addr == a)
851    }
852
853    pub fn is_outgoing(&self) -> bool {
854        self.has_input_address_as_output_address()
855    }
856
857    pub fn first_output_address_non_input_or_fee(&self) -> Option<Address> {
858        self.first_output_non_input_or_fee()
859            .and_then(|o| o.address.clone())
860    }
861
862    pub fn first_output_amount(&self) -> Option<f64> {
863        self.first_output_amount_typed().map(|o| o.to_fractional())
864    }
865
866    pub fn first_output_amount_i64(&self) -> Option<i64> {
867        self.first_output_non_input_or_fee().and_then(|o| o.opt_amount())
868    }
869
870    pub fn first_output_amount_typed(&self) -> Option<CurrencyAmount> {
871        self.first_output_non_input_or_fee().and_then(|o| o.opt_amount_typed())
872    }
873
874    pub fn first_contract_type(&self) -> Option<StandardContractType> {
875        self.outputs.iter()
876            .flat_map(|o| o.contract.as_ref())
877            .flat_map(|c| c.standard_contract_type.as_ref())
878            .flat_map(|c| StandardContractType::from_i32(c.clone()))
879            .next()
880    }
881
882}
883
884// #[derive(Clone)]
885// pub struct LiquidityInfo
886
887
888// TODO: ove into standard data
889pub fn amount_data(amount: u64) -> Option<StandardData> {
890    StandardData::amount_data(amount)
891}
892
893impl StandardData {
894
895    pub fn observation(o: Observation) -> Self {
896        let mut sd = Self::default();
897        sd.observation = Some(o);
898        sd
899    }
900
901    pub fn empty() -> Self {
902        Self::default()
903    }
904    pub fn peer_data(pd: PeerMetadata) -> Option<Self> {
905        let mut mt = Self::empty();
906        mt.peer_data = Some(pd);
907        Some(mt)
908    }
909    pub fn amount_data(amount: u64) -> Option<Self> {
910        let mut mt = Self::empty();
911        mt.amount = Some(CurrencyAmount::from(amount as i64));
912        Some(mt)
913    }
914
915
916}
917
918impl TypedValue {
919    pub fn bytes(bytes: &Vec<u8>) -> Self {
920        let mut s = Self::default();
921        s.bytes_value = bytes_data(bytes.clone());
922        s
923    }
924}
925
926
927#[derive(Serialize, Deserialize, Clone, Debug)]
928pub struct TransactionMaybeError {
929    pub transaction: Transaction,
930    pub error: Option<ErrorInfo>,
931}
932
933impl TransactionMaybeError {
934    pub fn new(transaction: Transaction) -> Self {
935        Self {
936            transaction,
937            error: None,
938        }
939    }
940    pub fn new_error(transaction: Transaction, error: ErrorInfo) -> Self {
941        Self {
942            transaction,
943            error: Some(error),
944        }
945    }
946}