cfx_rpc_cfx_types/
address.rs

1// Copyright 2021 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use cfx_addr::{cfx_addr_decode, cfx_addr_encode, EncodingOptions, Network};
6use cfx_types::H160;
7use once_cell::sync::OnceCell;
8use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
9use std::fmt;
10
11/// Global flag to indicate whether to use simple mode for RpcAddress
12/// encoding/decoding.
13pub static USE_SIMPLE_RPC_ADDRESS: OnceCell<bool> = OnceCell::new();
14
15/// This is the address type used in Rpc. It deserializes user's Rpc input, or
16/// it prepares the base32 address for Rpc output.
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18pub struct RpcAddress {
19    /// It's user's input or encoded output address.
20    pub base32_address: String,
21    pub hex_address: H160,
22    pub network: Network,
23}
24
25impl RpcAddress {
26    pub fn try_from_h160(
27        hex_address: H160, network: Network,
28    ) -> Result<Self, String> {
29        let simple_mode = *USE_SIMPLE_RPC_ADDRESS.get().unwrap_or(&false);
30        let mode = if simple_mode {
31            EncodingOptions::Simple
32        } else {
33            EncodingOptions::QrCode
34        };
35        let base32_address = cfx_addr_encode(&hex_address.0, network, mode)
36            .map_err(|e| e.to_string())?;
37        Ok(Self {
38            base32_address,
39            hex_address,
40            network,
41        })
42    }
43
44    pub fn null(network: Network) -> Result<Self, String> {
45        Self::try_from_h160(H160::default(), network)
46    }
47}
48
49impl From<RpcAddress> for H160 {
50    fn from(x: RpcAddress) -> Self { x.hex_address }
51}
52
53impl<'a> Deserialize<'a> for RpcAddress {
54    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55    where D: Deserializer<'a> {
56        let s: String = Deserialize::deserialize(deserializer)?;
57
58        let parsed_address = cfx_addr_decode(&s).map_err(|e| {
59            de::Error::custom(format!(
60                "Invalid base32 address: input {} error {}",
61                s, e
62            ))
63        })?;
64        match parsed_address.hex_address {
65            None => Err(de::Error::custom(format!(
66                "Invalid base32 address: input {} not a SIZE_160 address.",
67                s
68            ))),
69            Some(hex_address) => Ok(Self {
70                base32_address: parsed_address.input_base32_address,
71                hex_address,
72                network: parsed_address.network,
73            }),
74        }
75    }
76}
77
78impl Serialize for RpcAddress {
79    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80    where S: Serializer {
81        serializer.serialize_str(&self.base32_address)
82    }
83}
84
85#[derive(Debug)]
86pub struct RcpAddressNetworkInconsistent {
87    pub from_network: Network,
88    pub to_network: Network,
89}
90
91impl fmt::Display for RcpAddressNetworkInconsistent {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        write!(
94            f,
95            "network prefix inconsistent in from({}) and to({})",
96            self.from_network, self.to_network
97        )
98    }
99}
100
101pub fn check_rpc_address_network(
102    rpc_request_network: Option<Network>, expected: &Network,
103) -> Result<(), UnexpectedRpcAddressNetwork> {
104    if let Some(rpc_network) = rpc_request_network {
105        if rpc_network != *expected {
106            return Err(UnexpectedRpcAddressNetwork {
107                expected: *expected,
108                got: rpc_network,
109            });
110        }
111    }
112    Ok(())
113}
114
115pub fn check_two_rpc_address_network_match(
116    from: Option<&RpcAddress>, to: Option<&RpcAddress>,
117) -> Result<Option<Network>, RcpAddressNetworkInconsistent> {
118    match (from, to) {
119        (None, None) => Ok(None),
120        (None, Some(b)) => Ok(Some(b.network)),
121        (Some(a), None) => Ok(Some(a.network)),
122        (Some(a), Some(b)) => {
123            if a.network != b.network {
124                return Err(RcpAddressNetworkInconsistent {
125                    from_network: a.network,
126                    to_network: b.network,
127                });
128            }
129            Ok(Some(a.network))
130        }
131    }
132}
133
134#[derive(Debug)]
135pub struct UnexpectedRpcAddressNetwork {
136    pub expected: Network,
137    pub got: Network,
138}
139
140impl fmt::Display for UnexpectedRpcAddressNetwork {
141    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142        write!(
143            f,
144            "network prefix unexpected: ours {}, got {}",
145            self.expected, self.got
146        )
147    }
148}
149#[cfg(test)]
150mod tests {
151    use super::RpcAddress;
152    use cfx_addr::{cfx_addr_encode, EncodingOptions, Network};
153    use cfx_types::H160;
154    use log::debug;
155    use serde_json;
156
157    fn check_deserialize(base32_address: &str, hex: &str, network: Network) {
158        let addr_hex: H160 = hex.trim_start_matches("0x").parse().unwrap();
159        let parsed_result = serde_json::from_str::<RpcAddress>(base32_address);
160        debug!(
161            "parsed: {:?}, expected hex addr {:?}, expected base32 addr {:?}",
162            parsed_result,
163            addr_hex,
164            cfx_addr_encode(
165                addr_hex.as_bytes(),
166                network,
167                EncodingOptions::Simple
168            )
169        );
170        let parsed = parsed_result.unwrap();
171        assert_eq!(parsed.network, network);
172        assert_eq!(parsed.hex_address, addr_hex);
173    }
174
175    #[test]
176    fn test_deserialize_address() {
177        check_deserialize(
178            "\"cfx:acc7uawf5ubtnmezvhu9dhc6sghea0403y2dgpyfjp\"",
179            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
180            Network::Main,
181        );
182
183        check_deserialize(
184            "\"cfxtest:acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg\"",
185            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
186            Network::Test,
187        );
188
189        check_deserialize(
190            "\"cfxtest:type.contract:acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg\"",
191            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
192            Network::Test,
193        );
194    }
195
196    #[test]
197    #[should_panic]
198    fn test_deserialize_incorrect_network_prefix() {
199        check_deserialize(
200            "\"cfy:acc7uawf5ubtnmezvhu9dhc6sghea0403y2dgpyfjp\"",
201            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
202            Network::Main,
203        );
204    }
205
206    #[test]
207    #[should_panic]
208    fn test_deserialize_no_network_prefix() {
209        check_deserialize(
210            "\"acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg\"",
211            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
212            Network::Main,
213        );
214    }
215
216    #[test]
217    #[should_panic]
218    fn test_deserialize_incorrect_type() {
219        check_deserialize(
220            "\"cfx:type.user:acc7uawf5ubtnmezvhu9dhc6sghea0403y2dgpyfjp\"",
221            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
222            Network::Main,
223        );
224    }
225
226    #[test]
227    #[should_panic]
228    fn test_deserialize_incorrect_checksum() {
229        check_deserialize(
230            "\"cfx:acc7uawf5ubtnmezvhu9dhc6sghea0403ywjz6wtpg\"",
231            "0x85d80245dc02f5a89589e1f19c5c718e405b56cd",
232            Network::Main,
233        );
234    }
235}