redgold_schema/
address.rs

1use itertools::Itertools;
2use crate::structs::{Address, AddressDescriptor, AddressInfo, AddressType, ErrorInfo, Hash, OutputContract, PublicKey, SupportedCurrency, UtxoEntry, Weighting};
3use crate::{bytes_data, error_info, from_hex, ErrorInfoContext, RgResult, SafeOption};
4use crate::structs;
5use sha3::Sha3_224;
6
7use crate::proto_serde::ProtoSerde;
8use crate::structs::SupportedCurrency::Redgold;
9use sha3::Digest;
10
11// impl fromstr for address etc. impl tostring
12impl Into<Address> for structs::PublicKey {
13    fn into(self) -> Address {
14        Address::from_struct_public(&self).expect("some")
15    }
16}
17
18impl Into<Address> for Vec<u8> {
19    fn into(self) -> Address {
20        Address::address_data(self).expect("some")
21    }
22}
23
24
25impl AddressDescriptor {
26    pub fn from_multisig_public_keys_and_threshold(public_keys: &Vec<PublicKey>, threshold: i64) -> AddressDescriptor {
27        let mut pks = public_keys.clone();
28        pks.sort_by_key(|k| k.vec());
29        let mut descriptor = AddressDescriptor::default();
30        descriptor.public_keys = pks.clone();
31        let mut contract = OutputContract::default();
32        contract.threshold = Some(Weighting::from_int_basis(threshold, pks.len() as i64));
33        descriptor.contract = Some(contract);
34        descriptor
35    }
36
37    pub fn to_address(&self) -> Address {
38        Address::from_multisig_public_keys_and_threshold(
39            &self.public_keys, self.contract.as_ref().unwrap().threshold.as_ref().unwrap().value
40        )
41    }
42}
43
44
45impl Address {
46
47    pub fn from_multisig_public_keys_and_threshold(public_keys: &Vec<PublicKey>, threshold: i64) -> Address {
48        let descriptor = AddressDescriptor::from_multisig_public_keys_and_threshold(public_keys, threshold);
49        let bytes = descriptor.to_hashed().bytes;
50
51        Self {
52            address: bytes,
53            address_type: AddressType::MultisigContract as i32,
54            currency: Redgold as i32,
55        }
56    }
57
58    pub fn mark_bitcoin_external(&mut self) -> &mut Self {
59        self.currency = SupportedCurrency::Bitcoin as i32;
60        self
61    }
62
63    pub fn mark_ethereum_external(&mut self) -> &mut Self {
64        self.currency = SupportedCurrency::Ethereum as i32;
65        self
66    }
67
68    pub fn mark_external(&mut self) -> &mut Self {
69        match self.address_type() {
70            AddressType::BitcoinExternalString => self.mark_bitcoin_external(),
71            AddressType::EthereumExternalString => self.mark_ethereum_external(),
72            AddressType::SolanaExternalString => {
73                self.set_currency(SupportedCurrency::Solana);
74                self
75            },
76            AddressType::MoneroExternalString => {
77                self.set_currency(SupportedCurrency::Monero);
78                self
79            },
80            _ => self
81        }
82    }
83
84    pub fn as_external(&self) -> Address {
85        let mut address = self.clone();
86        address.mark_external();
87        address.clone()
88    }
89
90    pub fn as_internal(&self) -> Address {
91        let mut address = self.clone();
92        address.set_currency(Redgold);
93        address
94    }
95
96    pub fn script_hash(input: impl AsRef<[u8]>) -> RgResult<Self> {
97        let mut new = Self::from_bytes(Self::hash(input.as_ref()))?;
98        new.address_type = AddressType::ScriptHash as i32;
99        Ok(new)
100    }
101
102    pub fn from_bitcoin_external(address: &String) -> Address {
103        let mut ret = Self::from_bitcoin(address);
104        ret.currency = SupportedCurrency::Bitcoin as i32;
105        ret
106    }
107
108        // Maybe consider checking here to see if the address is valid?
109    // Or before this actually, we should potentially not do 'into bytes'
110    // but instead change this to a vec and decode it.
111    pub fn from_bitcoin(address: &String) -> Address {
112        Self {
113            address: bytes_data(address.clone().into_bytes()),
114            address_type: AddressType::BitcoinExternalString as i32,
115            // currency: SupportedCurrency::Bitcoin as i32,
116            currency: Redgold as i32,
117        }
118    }
119    pub fn from_eth_direct(address: impl Into<String>) -> Address {
120        let address = address.into();
121        Self {
122            address: bytes_data(address.clone().into_bytes()),
123            address_type: AddressType::EthereumExternalString as i32,
124            currency: Redgold as i32,
125        }
126    }
127
128    pub fn from_monero(address: &String) -> Address {
129        Self {
130            address: bytes_data(address.clone().into_bytes()),
131            address_type: AddressType::MoneroExternalString as i32,
132            currency: Redgold as i32,
133        }
134    }
135    pub fn from_solana(address: &String) -> Address {
136        Self {
137            address: bytes_data(address.clone().into_bytes()),
138            address_type: AddressType::SolanaExternalString as i32,
139            currency: Redgold as i32,
140        }
141    }
142
143    pub fn from_type(address: &String, t: AddressType) -> Address {
144        Self {
145            address: bytes_data(address.clone().into_bytes()),
146            address_type: t as i32,
147            currency: Redgold as i32,
148        }
149    }
150
151    pub fn from_monero_external(address: impl AsRef<str>) -> Address {
152        let address = address.as_ref().to_string();
153        let mut ret = Self::from_monero(&address);
154        ret.currency = SupportedCurrency::Monero as i32;
155        ret
156    }
157
158    pub fn from_solana_external(address: &String) -> Address {
159        let mut ret = Self::from_monero(address);
160        ret.currency = SupportedCurrency::Solana as i32;
161        ret
162    }
163
164    pub fn from_eth_external_exact(address: impl Into<String>) -> Address {
165        let address = address.into();
166        let mut ret = Self::from_eth_direct(address);
167        ret.currency = SupportedCurrency::Ethereum as i32;
168        ret
169    }
170
171    pub fn is_bitcoin(&self) -> bool {
172        self.address_type == AddressType::BitcoinExternalString as i32
173    }
174
175    pub fn address_typed(&self) -> RgResult<AddressType> {
176        AddressType::from_i32(self.address_type).ok_msg("Invalid address type")
177    }
178
179    pub fn raw_bytes(&self) -> RgResult<Vec<u8>> {
180        Ok(self.address.safe_get()?.value.clone())
181    }
182
183    pub fn external_string_address(&self) -> RgResult<String> {
184        String::from_utf8(self.raw_bytes()?)
185            .error_info("Unable to convert bitcoin address bytes to utf8 string")
186    }
187
188    pub fn render_string(&self) -> Result<String, ErrorInfo> {
189
190        let address_string = match self.address_typed()? {
191            AddressType::BitcoinExternalString => {
192                self.external_string_address()?
193            }
194            AddressType::EthereumExternalString => {
195                self.external_string_address()?
196            }
197            AddressType::MoneroExternalString => {
198                self.external_string_address()?
199            }
200            AddressType::SolanaExternalString => {
201                self.external_string_address()?
202            }
203            _ => {
204                self.hex()
205            }
206        };
207        Ok(address_string)
208    }
209    pub fn from_bytes(bytes: Vec<u8>) -> Result<Address, ErrorInfo> {
210        let addr = Self::new_raw(bytes);
211        addr.verify_checksum()?;
212        Ok(addr)
213    }
214
215    pub fn from_byte_calculate(vec: &Vec<u8>) -> Result<Address, ErrorInfo> {
216        Self::from_bytes(Self::hash(&vec))
217    }
218
219
220    pub fn from_struct_public(pk: &structs::PublicKey) -> Result<Address, ErrorInfo> {
221        Self::from_byte_calculate(&pk.vec())
222    }
223
224    pub fn with_checksum(bytes: Vec<u8>) -> Vec<u8> {
225        let checksum_bytes = Hash::digest(bytes.clone()).raw_bytes().unwrap();
226        let mut res: Vec<u8> = Vec::new();
227        res.extend_from_slice(&bytes);
228        res.extend_from_slice(&checksum_bytes[0..4]);
229        res
230    }
231
232    fn hash(buf: &[u8]) -> Vec<u8> {
233        let bytes = Sha3_224::digest(buf).to_vec();
234        Self::with_checksum(bytes)
235    }
236
237    pub fn verify_length(&self) -> Result<(), ErrorInfo> {
238        // let i = self.address.safe_bytes()?.len();
239        // if i != 32 {
240        //     Err(error_info(format!("Invalid address length: {:?}", i)))?;
241        // }
242        Ok(())
243    }
244
245    pub fn verify_checksum(&self) -> Result<(), ErrorInfo> {
246        self.verify_length()?;
247        let bytes = self.raw_bytes()?;
248        if Self::with_checksum(bytes[0..28].to_vec()) != bytes {
249            Err(error_info("Invalid address checksum bytes"))?;
250        }
251        Ok(())
252    }
253
254    pub fn validated(self) -> RgResult<Self> {
255        self.verify_length()?;
256        self.verify_checksum()?;
257        Ok(self)
258    }
259
260    pub fn str_to_address(s: String) -> Vec<u8> {
261        hex::decode(s).expect("hex")
262        // return base58::from_check(&s[3..]).unwrap();
263    }
264
265    pub fn address_to_str(a: &Vec<u8>) -> String {
266        // let mut b = base58::check_encode_slice(&*a);
267        // b.insert_str(0, "rg1");
268        // return b;
269        hex::encode(a)
270    }
271
272    pub fn address_data(address: Vec<u8>) -> Option<Address> {
273        Some(Self::new_raw(address))
274    }
275
276    pub fn new_raw(address: Vec<u8>) -> Address {
277        Address {
278            address: bytes_data(address),
279            address_type: AddressType::Sha3224ChecksumPublic as i32,
280            currency: SupportedCurrency::Redgold as i32,
281        }
282    }
283
284    pub fn raw_from_hex(hex: impl Into<String>) -> RgResult<Address> {
285        Self::new_raw(from_hex(hex.into())?).validated()
286    }
287
288    pub fn currency_or(&self) -> SupportedCurrency {
289        self.currency()
290    }
291}
292
293
294
295impl AddressInfo {
296    pub fn from_utxo_entries(address: Address, entries: Vec<UtxoEntry>) -> Self {
297        let mut bal: i64 = 0;
298        for r in &entries {
299            if let Some(o) = &r.output {
300                if let Some(d) = &o.data {
301                    if let Some(a) = &d.amount {
302                        bal += a.amount;
303                    }
304                }
305            }
306        }
307        AddressInfo {
308            address: Some(address.clone()),
309            utxo_entries: entries,
310            balance: bal,
311            recent_transactions: vec![],
312            balances: vec![]
313        }
314    }
315}