nash_protocol/types/blockchain/
mod.rs

1//! Types used across protocol requests for constructing and validating
2//! blockchain payload data. Any types specific to an individual protocol
3//! request will live in the respective module.
4
5pub mod btc;
6pub mod eth;
7pub mod neo;
8
9use crate::errors::{ProtocolError, Result};
10use crate::types::{Asset, Blockchain};
11use bigdecimal::{BigDecimal, ToPrimitive};
12use std::convert::TryFrom;
13
14/// Convert a bigdecimal `num` to `u64` for serialization in the protocol using the
15/// precision scheme defined by the Nash ME
16pub fn bigdecimal_to_nash_u64(num: &BigDecimal, precision: u32) -> Result<u64> {
17    let num = bigdecimal_to_nash_prec(num, precision);
18    let multiplier = BigDecimal::from(u64::pow(10, precision));
19    (num * multiplier)
20        .with_scale(0)
21        .to_u64()
22        .ok_or(ProtocolError("Result does not fit into u64."))
23}
24
25pub fn nash_u64_to_bigdecimal(num: u64, precision: u32) -> BigDecimal {
26    let num = BigDecimal::from(num);
27    let divider = BigDecimal::from(u64::pow(10, precision));
28    num / divider
29}
30
31/// Convert a bigdecimal to precision expected by the Nash ME
32/// Nash ME defines precision as only digits right of decimal point
33pub fn bigdecimal_to_nash_prec(num: &BigDecimal, precision: u32) -> BigDecimal {
34    let scale = BigDecimal::from(u64::pow(10, precision));
35    let scaled = (num * &scale).with_scale(0);
36    &scaled / &scale
37}
38
39/// The type prefix indicates what operation this data represents. This is
40/// encoded by 1 byte in the protocol. For example, a payload representing
41/// the fill order operation has `0x01` at the start of the data. These
42/// prefixes are the same for both NEO and Ethereum payloads.
43
44#[derive(Clone, Debug, PartialEq)]
45pub enum Prefix {
46    SyncState,
47    FillOrder,
48    Deposit,
49    Withdrawal,
50}
51
52impl Prefix {
53    pub fn to_bytes(&self) -> [u8; 1] {
54        match self {
55            Self::SyncState => [0x00],
56            Self::FillOrder => [0x01],
57            Self::Deposit => [0x02],
58            Self::Withdrawal => [0x03],
59        }
60    }
61    pub fn from_bytes(bytes: [u8; 1]) -> Result<Self> {
62        match bytes {
63            [0x00] => Ok(Self::SyncState),
64            [0x01] => Ok(Self::FillOrder),
65            [0x02] => Ok(Self::Deposit),
66            [0x03] => Ok(Self::Withdrawal),
67            _ => Err(ProtocolError("Invalid prefix byte")),
68        }
69    }
70}
71
72#[derive(Clone, Debug, PartialEq)]
73pub enum Address {
74    Ethereum(eth::Address),
75    Bitcoin(btc::Address),
76    NEO(neo::Address),
77}
78
79impl Address {
80    pub fn new(chain: Blockchain, hex_str: &str) -> Result<Self> {
81        match chain {
82            Blockchain::Bitcoin => Ok(Self::Bitcoin(btc::Address::new(hex_str)?)),
83            Blockchain::Ethereum => Ok(Self::Ethereum(eth::Address::new(hex_str)?)),
84            Blockchain::NEO => Ok(Self::NEO(neo::Address::new(hex_str)?)),
85        }
86    }
87}
88
89impl TryFrom<Address> for eth::Address {
90    type Error = ProtocolError;
91
92    fn try_from(address: Address) -> Result<Self> {
93        match address {
94            Address::Ethereum(address) => Ok(address),
95            _ => Err(ProtocolError(
96                "Tried to convert from something that is not an ETH address",
97            )),
98        }
99    }
100}
101
102impl TryFrom<Address> for neo::Address {
103    type Error = ProtocolError;
104
105    fn try_from(address: Address) -> Result<Self> {
106        match address {
107            Address::NEO(address) => Ok(address),
108            _ => Err(ProtocolError(
109                "Tried to convert from something that is not an NEO address",
110            )),
111        }
112    }
113}
114
115impl TryFrom<Address> for btc::Address {
116    type Error = ProtocolError;
117
118    fn try_from(address: Address) -> Result<Self> {
119        match address {
120            Address::Bitcoin(address) => Ok(address),
121            _ => Err(ProtocolError(
122                "Tried to convert from something that is not an ETH address",
123            )),
124        }
125    }
126}
127
128/// An asset in the Ethereum and NEO smart contract protocols is represented
129/// either by an asset id or an indicator that the trade is occurring across
130/// blockchains.
131#[derive(Clone, Debug, PartialEq)]
132pub enum AssetOrCrosschain {
133    Asset(Asset),
134    Crosschain,
135}
136
137#[derive(Clone, Debug, PartialEq)]
138pub enum PublicKey {
139    Bitcoin(btc::PublicKey),
140    Ethereum(eth::PublicKey),
141    NEO(neo::PublicKey),
142}
143
144impl PublicKey {
145    pub fn new(chain: Blockchain, hex_str: &str) -> Result<Self> {
146        Ok(match chain {
147            Blockchain::Bitcoin => Self::Bitcoin(btc::PublicKey::new(hex_str)?),
148            Blockchain::Ethereum => Self::Ethereum(eth::PublicKey::new(hex_str)?),
149            Blockchain::NEO => Self::NEO(neo::PublicKey::new(hex_str)?),
150        })
151    }
152
153    pub fn to_hex_str(&self) -> String {
154        match self {
155            Self::Bitcoin(key) => key.to_hex(),
156            Self::Ethereum(key) => key.to_hex(),
157            Self::NEO(key) => key.to_hex(),
158        }
159    }
160
161    pub fn to_address(&self) -> Result<Address> {
162        Ok(match self {
163            Self::Bitcoin(key) => Address::Bitcoin(key.to_address()?),
164            Self::Ethereum(key) => Address::Ethereum(key.to_address()),
165            Self::NEO(key) => Address::NEO(key.to_address()),
166        })
167    }
168}
169
170impl TryFrom<PublicKey> for eth::PublicKey {
171    type Error = ProtocolError;
172
173    fn try_from(address: PublicKey) -> Result<Self> {
174        match address {
175            PublicKey::Ethereum(pub_key) => Ok(pub_key),
176            _ => Err(ProtocolError(
177                "Tried to convert from something that is not an ETH public key",
178            )),
179        }
180    }
181}
182
183impl TryFrom<PublicKey> for neo::PublicKey {
184    type Error = ProtocolError;
185
186    fn try_from(address: PublicKey) -> Result<Self> {
187        match address {
188            PublicKey::NEO(pub_key) => Ok(pub_key),
189            _ => Err(ProtocolError(
190                "Tried to convert from something that is not an NEO public key",
191            )),
192        }
193    }
194}
195
196impl TryFrom<PublicKey> for btc::PublicKey {
197    type Error = ProtocolError;
198
199    fn try_from(address: PublicKey) -> Result<Self> {
200        match address {
201            PublicKey::Bitcoin(pub_key) => Ok(pub_key),
202            _ => Err(ProtocolError(
203                "Tried to convert from something that is not an BTC public key",
204            )),
205        }
206    }
207}
208
209/// Wrapper type for the two kinds of movments: deposit and withdrawal
210#[derive(PartialEq)]
211pub enum MovementType {
212    Deposit,
213    Withdrawal,
214}
215
216impl MovementType {
217    /// Return appropriate prefix for the movement on ETH
218    pub fn to_prefix(&self) -> Prefix {
219        match self {
220            Self::Deposit => Prefix::Deposit,
221            Self::Withdrawal => Prefix::Withdrawal,
222        }
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use super::{bigdecimal_to_nash_prec, bigdecimal_to_nash_u64};
229    use bigdecimal::BigDecimal;
230    use std::str::FromStr;
231
232    #[test]
233    fn nash_precision() {
234        let num = BigDecimal::from_str("1.55555").unwrap();
235        let prec_num = bigdecimal_to_nash_prec(&num, 4);
236        assert_eq!(prec_num.to_string(), "1.5555");
237    }
238
239    #[test]
240    fn bd_test() {
241        let bd_1 = BigDecimal::from_str("0.03451").unwrap();
242        let bd_2 = BigDecimal::from_str("0.03454").unwrap();
243        println!("Good: {}", bd_1.inverse());
244        println!("Bad: {}", bd_2.inverse());
245        let step1_1 = bigdecimal_to_nash_prec(&bd_1.inverse(), 8);
246        let step1_2 = bigdecimal_to_nash_prec(&bd_2.inverse(), 8);
247        println!("Good: {}", step1_1);
248        println!("Bad: {}", step1_2);
249        let converted_1 = bigdecimal_to_nash_u64(&bd_1.inverse(), 8).unwrap();
250        let converted_2 = bigdecimal_to_nash_u64(&bd_2.inverse(), 8).unwrap();
251        println!("Good: {}", converted_1);
252        println!("Bad: {}", converted_2);
253    }
254}