redgold_schema/
output.rs

1use crate::structs::{ContentionKey, CurrencyAmount, ErrorInfo, Hash, Observation, Output, OutputType, StakeRequest, StandardContractType, StandardData, StandardRequest, StandardResponse, StateSelector, SwapFulfillment, SwapRequest, UtxoEntry};
2use crate::transaction::amount_data;
3use crate::{Address, HashClear, RgResult, SafeOption};
4
5pub fn output_data(address: Vec<u8>, amount: u64) -> Output {
6    Output::new(&Address::address_data(address).expect(""), amount as i64)
7}
8
9pub fn tx_output_data(address: Address, amount: u64) -> Output {
10    Output::new(&address, amount as i64)
11}
12
13impl HashClear for Output {
14    fn hash_clear(&mut self) {}
15}
16
17impl Output {
18
19    pub fn is_request(&self) -> bool {
20        self.output_type == Some(OutputType::RequestCall as i32)
21    }
22
23    pub fn is_multisig(&self) -> bool {
24        self.contract.as_ref().map(|c| c.threshold.is_some()).unwrap_or(false)
25    }
26
27    pub fn multisig_threshold_naive(&self) -> Option<i64> {
28        self.contract.as_ref().and_then(|c| c.threshold.as_ref())
29            .map(|x| x.value)
30    }
31
32    pub fn is_deploy(&self) -> bool {
33        self.output_type == Some(OutputType::Deploy as i32)
34    }
35
36    pub fn code(&self) -> Option<Vec<u8>> {
37        self.contract.as_ref()
38            .and_then(|d| d.code_execution_contract.as_ref())
39            .and_then(|d| d.code.as_ref())
40            .map(|d| d.value.clone())
41    }
42
43    pub fn validate_deploy_code(&self) -> RgResult<Vec<u8>> {
44        // Validate deploy
45        if self.is_deploy() {
46            if let Some(d) = self.contract.as_ref()
47                .and_then(|d| d.code_execution_contract.as_ref())
48                .and_then(|d| d.code.as_ref())
49                .map(|d| d.value.clone())
50                .filter(|d| !d.is_empty())
51                .filter(|d| Address::script_hash(d).ok() == self.address)
52            {
53                return Ok(d);
54            }
55        }
56        Err(ErrorInfo::error_info("Not a deploy"))
57    }
58
59    pub fn pay_update_descendents(&self) -> bool {
60        self.contract.as_ref().map(|c| c.pay_update_descendents).unwrap_or(false)
61    }
62
63    pub fn request_data(&self) -> RgResult<&Vec<u8>> {
64        if self.is_request() {
65            if let Some(d) = self.data.as_ref().and_then(|d| d.request.as_ref()) {
66                return Ok(&d.value);
67            }
68        }
69        Err(ErrorInfo::error_info("Not a request"))
70    }
71
72    pub fn request_contention_key(&self) -> RgResult<ContentionKey> {
73        let option = self.address.safe_get_msg("Missing address")?;
74        let sel = self.request_selector()?;
75        Ok(ContentionKey::contract_request(option, sel))
76    }
77
78    pub fn request_selector(&self) -> RgResult<Option<&StateSelector>> {
79        if self.is_request() {
80            return Ok(self.data.as_ref().and_then(|d|
81                d.standard_request.as_ref().and_then(|r| r.selector.as_ref())));
82        }
83        Err(ErrorInfo::error_info("Not a request"))
84    }
85
86    pub fn new(address: &Address, amount: i64) -> Output {
87        Output {
88            address: Some(address.clone()),
89            product_id: None,
90            counter_party_proofs: vec![],
91            data: amount_data(amount as u64),
92            contract: None,
93            output_type: None,
94            utxo_id: None
95        }
96    }
97    pub fn from_data(data: StandardData) -> Self {
98        let mut o = Output::default();
99        o.data = Some(data);
100        o
101    }
102
103    pub fn is_swap(&self) -> bool {
104        self.contract.as_ref().and_then(|c| c.standard_contract_type)
105            .filter(|&c| c == StandardContractType::Swap as i32).is_some()
106    }
107
108    pub fn is_swap_fulfillment(&self) -> bool {
109        self.swap_fulfillment().is_some()
110    }
111
112    pub fn stake_request(&self) -> Option<&StakeRequest> {
113        self.request().and_then(|c| c.stake_request.as_ref())
114    }
115
116    pub fn is_peer_data(&self) -> bool {
117        self.data.as_ref().and_then(|c| c.peer_data.as_ref()).is_some()
118    }
119
120    pub fn is_node_metadata(&self) -> bool {
121        self.data.as_ref().and_then(|c| c.node_metadata.as_ref()).is_some()
122    }
123
124    pub fn is_metadata(&self) -> bool {
125        self.is_node_metadata() || self.is_peer_data()
126    }
127
128    pub fn request(&self) -> Option<&StandardRequest> {
129        self.data.as_ref().and_then(|d| d.standard_request.as_ref())
130    }
131
132    pub fn response(&self) -> Option<&StandardResponse> {
133        self.data.as_ref().and_then(|d| d.standard_response.as_ref())
134    }
135
136    pub fn swap_fulfillment(&self) -> Option<&SwapFulfillment> {
137        self.response().and_then(|r| r.swap_fulfillment.as_ref())
138    }
139
140    pub fn swap_request(&self) -> Option<&SwapRequest> {
141        self.request().and_then(|r| r.swap_request.as_ref())
142    }
143
144    pub fn is_stake(&self) -> bool {
145        self.request().and_then(|c| c.stake_request.as_ref()).is_some()
146    }
147
148    pub fn is_fee(&self) -> bool {
149        self.output_type == Some(OutputType::Fee as i32)
150    }
151
152    pub fn utxo_entry(
153        &self,
154        transaction_hash: &Hash,
155        output_index: i64,
156        time: i64,
157    ) -> UtxoEntry {
158        return UtxoEntry::from_output_new(
159            self, &transaction_hash,
160            output_index, time
161        );
162    }
163
164    pub fn amount(&self) -> u64 {
165        self.amount_i64() as u64
166    }
167
168    pub fn amount_i64(&self) -> i64 {
169        self.data.as_ref().unwrap().amount.clone().unwrap().amount
170    }
171
172    pub fn safe_ensure_amount(&self) -> Result<&i64, ErrorInfo> {
173        Ok(&self.data.safe_get_msg("Missing data field on output")?
174            .amount.safe_get_msg("Missing amount field on output")?.amount)
175    }
176
177    pub fn observation(&self) -> RgResult<&Observation> {
178        self.data.safe_get_msg("Missing data field on output")?
179            .observation.safe_get_msg("Missing observation field on output")
180    }
181
182    pub fn opt_amount(&self) -> Option<i64> {
183        self.data.safe_get_msg("Missing data field on output").ok()
184            .and_then(|data| data.amount.as_ref())
185            .map(|data| data.amount)
186    }
187
188    pub fn opt_amount_typed(&self) -> Option<CurrencyAmount> {
189        self.data.safe_get_msg("Missing data field on output").ok().and_then(|data| data.amount.clone())
190    }
191
192    pub fn opt_amount_typed_ref(&self) -> Option<&CurrencyAmount> {
193        self.data.as_ref().and_then(|data| data.amount.as_ref())
194    }
195
196    pub fn rounded_amount(&self) -> f64 {
197        crate::transaction::rounded_balance(self.amount())
198    }
199}