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 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}