ergo_lib_wasm/
address.rs

1//! Address types
2
3use ergo_lib::ergo_chain_types::EcPoint;
4use ergo_lib::ergotree_ir::serialization::SigmaSerializable;
5use ergo_lib::ergotree_ir::sigma_protocol::sigma_boolean::ProveDlog;
6use wasm_bindgen::prelude::*;
7
8use crate::{ergo_tree::ErgoTree, error_conversion::to_js};
9
10extern crate derive_more;
11use derive_more::{From, Into};
12
13/// Network type
14#[wasm_bindgen]
15#[repr(u8)]
16#[derive(PartialEq, Eq, Debug, Clone, Copy)]
17pub enum NetworkPrefix {
18    /// Mainnet
19    Mainnet = 0,
20    /// Testnet
21    Testnet = 16,
22}
23
24impl From<NetworkPrefix> for ergo_lib::ergotree_ir::chain::address::NetworkPrefix {
25    fn from(v: NetworkPrefix) -> Self {
26        use ergo_lib::ergotree_ir::chain::address::NetworkPrefix::*;
27        match v {
28            NetworkPrefix::Mainnet => Mainnet,
29            NetworkPrefix::Testnet => Testnet,
30        }
31    }
32}
33
34impl From<ergo_lib::ergotree_ir::chain::address::NetworkPrefix> for NetworkPrefix {
35    fn from(v: ergo_lib::ergotree_ir::chain::address::NetworkPrefix) -> Self {
36        use NetworkPrefix::*;
37        match v {
38            ergo_lib::ergotree_ir::chain::address::NetworkPrefix::Mainnet => Mainnet,
39            ergo_lib::ergotree_ir::chain::address::NetworkPrefix::Testnet => Testnet,
40        }
41    }
42}
43
44/// Address types
45#[wasm_bindgen]
46#[repr(u8)]
47pub enum AddressTypePrefix {
48    /// 0x01 - Pay-to-PublicKey(P2PK) address
49    P2Pk = 1,
50    /// 0x02 - Pay-to-Script-Hash(P2SH)
51    Pay2Sh = 2,
52    /// 0x03 - Pay-to-Script(P2S)
53    Pay2S = 3,
54}
55
56impl From<AddressTypePrefix> for ergo_lib::ergotree_ir::chain::address::AddressTypePrefix {
57    fn from(v: AddressTypePrefix) -> Self {
58        use ergo_lib::ergotree_ir::chain::address::AddressTypePrefix::*;
59        match v {
60            AddressTypePrefix::P2Pk => P2Pk,
61            AddressTypePrefix::Pay2Sh => Pay2Sh,
62            AddressTypePrefix::Pay2S => Pay2S,
63        }
64    }
65}
66
67impl From<ergo_lib::ergotree_ir::chain::address::AddressTypePrefix> for AddressTypePrefix {
68    fn from(v: ergo_lib::ergotree_ir::chain::address::AddressTypePrefix) -> Self {
69        use AddressTypePrefix::*;
70        match v {
71            ergo_lib::ergotree_ir::chain::address::AddressTypePrefix::P2Pk => P2Pk,
72            ergo_lib::ergotree_ir::chain::address::AddressTypePrefix::Pay2Sh => Pay2Sh,
73            ergo_lib::ergotree_ir::chain::address::AddressTypePrefix::Pay2S => Pay2S,
74        }
75    }
76}
77
78/**
79 * An address is a short string corresponding to some script used to protect a box. Unlike (string-encoded) binary
80 * representation of a script, an address has some useful characteristics:
81 *
82 * - Integrity of an address could be checked., as it is incorporating a checksum.
83 * - A prefix of address is showing network and an address type.
84 * - An address is using an encoding (namely, Base58) which is avoiding similarly l0Oking characters, friendly to
85 * double-clicking and line-breaking in emails.
86 *
87 *
88 *
89 * An address is encoding network type, address type, checksum, and enough information to watch for a particular scripts.
90 *
91 * Possible network types are:
92 * Mainnet - 0x00
93 * Testnet - 0x10
94 *
95 * For an address type, we form content bytes as follows:
96 *
97 * P2PK - serialized (compressed) public key
98 * P2SH - first 192 bits of the Blake2b256 hash of serialized script bytes
99 * P2S  - serialized script
100 *
101 * Address examples for testnet:
102 *
103 * 3   - P2PK (3WvsT2Gm4EpsM9Pg18PdY6XyhNNMqXDsvJTbbf6ihLvAmSb7u5RN)
104 * ?   - P2SH (rbcrmKEYduUvADj9Ts3dSVSG27h54pgrq5fPuwB)
105 * ?   - P2S (Ms7smJwLGbUAjuWQ)
106 *
107 * for mainnet:
108 *
109 * 9  - P2PK (9fRAWhdxEsTcdb8PhGNrZfwqa65zfkuYHAMmkQLcic1gdLSV5vA)
110 * ?  - P2SH (8UApt8czfFVuTgQmMwtsRBZ4nfWquNiSwCWUjMg)
111 * ?  - P2S (4MQyML64GnzMxZgm, BxKBaHkvrTvLZrDcZjcsxsF7aSsrN73ijeFZXtbj4CXZHHcvBtqSxQ)
112 *
113 *
114 * Prefix byte = network type + address type
115 *
116 * checksum = blake2b256(prefix byte ++ content bytes)
117 *
118 * address = prefix byte ++ content bytes ++ checksum
119 *
120 */
121#[wasm_bindgen]
122#[derive(PartialEq, Eq, Debug, Clone, From, Into)]
123pub struct Address(pub(crate) ergo_lib::ergotree_ir::chain::address::Address);
124
125#[wasm_bindgen]
126impl Address {
127    /// Re-create the address from ErgoTree that was built from the address
128    ///
129    /// At some point in the past a user entered an address from which the ErgoTree was built.
130    /// Re-create the address from this ErgoTree.
131    /// `tree` - ErgoTree that was created from an Address
132    pub fn recreate_from_ergo_tree(ergo_tree: &ErgoTree) -> Result<Address, JsValue> {
133        ergo_lib::ergotree_ir::chain::address::Address::recreate_from_ergo_tree(
134            &ergo_tree.clone().into(),
135        )
136        .map(Address)
137        .map_err(to_js)
138    }
139
140    /// Create a P2PK address from serialized PK bytes(EcPoint/GroupElement)
141    pub fn p2pk_from_pk_bytes(bytes: &[u8]) -> Result<Address, JsValue> {
142        ergo_lib::ergotree_ir::chain::address::Address::p2pk_from_pk_bytes(bytes)
143            .map(Address)
144            .map_err(to_js)
145    }
146
147    /// Decode (base58) testnet address from string, checking that address is from the testnet
148    pub fn from_testnet_str(s: &str) -> Result<Address, JsValue> {
149        ergo_lib::ergotree_ir::chain::address::AddressEncoder::new(
150            ergo_lib::ergotree_ir::chain::address::NetworkPrefix::Testnet,
151        )
152        .parse_address_from_str(s)
153        .map(Address)
154        .map_err(to_js)
155    }
156
157    /// Decode (base58) mainnet address from string, checking that address is from the mainnet
158    pub fn from_mainnet_str(s: &str) -> Result<Address, JsValue> {
159        ergo_lib::ergotree_ir::chain::address::AddressEncoder::new(
160            ergo_lib::ergotree_ir::chain::address::NetworkPrefix::Mainnet,
161        )
162        .parse_address_from_str(s)
163        .map(Address)
164        .map_err(to_js)
165    }
166
167    /// Decode (base58) address from string without checking the network prefix
168    #[allow(clippy::should_implement_trait)]
169    pub fn from_base58(s: &str) -> Result<Address, JsValue> {
170        ergo_lib::ergotree_ir::chain::address::AddressEncoder::unchecked_parse_address_from_str(s)
171            .map(Address)
172            .map_err(to_js)
173    }
174
175    /// Encode (base58) address
176    pub fn to_base58(&self, network_prefix: NetworkPrefix) -> String {
177        ergo_lib::ergotree_ir::chain::address::AddressEncoder::encode_address_as_string(
178            network_prefix.into(),
179            &self.0,
180        )
181    }
182
183    /// Decode from a serialized address (that includes the network prefix)
184    pub fn from_bytes(data: Vec<u8>) -> Result<Address, JsValue> {
185        ergo_lib::ergotree_ir::chain::address::AddressEncoder::unchecked_parse_address_from_bytes(
186            &data,
187        )
188        .map(Address)
189        .map_err(to_js)
190    }
191
192    /// Encode address as serialized bytes (that includes the network prefix)
193    pub fn to_bytes(&self, network_prefix: NetworkPrefix) -> Vec<u8> {
194        ergo_lib::ergotree_ir::chain::address::AddressEncoder::encode_address_as_bytes(
195            network_prefix.into(),
196            &self.0,
197        )
198    }
199
200    /// Returns underlying value for each address type
201    /// (serialized EcPoint for P2PK, stored bytes for P2SH and P2S)
202    pub fn content_bytes(&self) -> Vec<u8> {
203        self.0.content_bytes()
204    }
205
206    /// Get the type of the address
207    pub fn address_type_prefix(&self) -> AddressTypePrefix {
208        self.0.address_type_prefix().into()
209    }
210
211    /// Create an address from a public key
212    pub fn from_public_key(bytes: &[u8]) -> Result<Address, JsValue> {
213        EcPoint::sigma_parse_bytes(bytes)
214            .map(|point| {
215                ergo_lib::ergotree_ir::chain::address::Address::P2Pk(ProveDlog::new(point))
216            })
217            .map(Address)
218            .map_err(to_js)
219    }
220
221    /// Creates an ErgoTree script from the address
222    pub fn to_ergo_tree(&self) -> Result<ErgoTree, JsValue> {
223        self.0.script().map(|script| script.into()).map_err(to_js)
224    }
225}
226
227/// Combination of an Address with a network
228/// These two combined together form a base58 encoding
229#[wasm_bindgen]
230#[derive(PartialEq, Eq, Debug, Clone)]
231pub struct NetworkAddress(ergo_lib::ergotree_ir::chain::address::NetworkAddress);
232
233#[wasm_bindgen]
234impl NetworkAddress {
235    /// create a new NetworkAddress(address + network prefix) for a given network type
236    pub fn new(network: NetworkPrefix, address: &Address) -> NetworkAddress {
237        NetworkAddress(ergo_lib::ergotree_ir::chain::address::NetworkAddress::new(
238            network.into(),
239            &address.clone().into(),
240        ))
241    }
242
243    /// Decode (base58) a NetworkAddress (address + network prefix) from string
244    pub fn from_base58(s: &str) -> Result<NetworkAddress, JsValue> {
245        ergo_lib::ergotree_ir::chain::address::AddressEncoder::unchecked_parse_network_address_from_str(s)
246            .map(NetworkAddress)
247            .map_err(to_js)
248    }
249
250    /// Encode (base58) address
251    pub fn to_base58(&self) -> String {
252        self.0.to_base58()
253    }
254
255    /// Decode from a serialized address
256    pub fn from_bytes(data: Vec<u8>) -> Result<NetworkAddress, JsValue> {
257        ergo_lib::ergotree_ir::chain::address::AddressEncoder::unchecked_parse_network_address_from_bytes(
258            &data,
259        )
260        .map(NetworkAddress)
261        .map_err(to_js)
262    }
263
264    /// Encode address as serialized bytes
265    pub fn to_bytes(&self) -> Vec<u8> {
266        ergo_lib::ergotree_ir::chain::address::AddressEncoder::encode_address_as_bytes(
267            self.network().into(),
268            &self.address().into(),
269        )
270    }
271
272    /// Network for the address
273    pub fn network(&self) -> NetworkPrefix {
274        self.0.network().into()
275    }
276
277    /// Get address without network information
278    pub fn address(&self) -> Address {
279        self.0.address().into()
280    }
281}