handshake_types/
compact.rs

1use extended_primitives::Uint256;
2
3#[derive(Debug, PartialEq, Clone, Copy)]
4pub struct Compact(u32);
5
6impl From<u32> for Compact {
7    fn from(u: u32) -> Self {
8        Compact(u)
9    }
10}
11
12impl From<Compact> for u32 {
13    fn from(c: Compact) -> Self {
14        c.0
15    }
16}
17
18impl From<Uint256> for Compact {
19    fn from(u: Uint256) -> Self {
20        Compact::from_u256(u)
21    }
22}
23
24impl From<Compact> for Uint256 {
25    fn from(c: Compact) -> Self {
26        // ignore overflows and negative values
27        c.to_u256().unwrap_or_else(|x| x)
28    }
29}
30
31//@todo to_target -> Returns a hash.
32//@todo from_target -> takes in a hash
33//@todo to_difficulty -> Returns f64
34//@todo from_difficulty -> from f64
35impl Compact {
36    pub fn new(u: u32) -> Self {
37        Compact(u)
38    }
39
40    pub fn max_value() -> Self {
41        Uint256::max_value().into()
42    }
43
44    /// Computes the target [0, T] that a blockhash must land in to be valid
45    /// Returns value in error, if there is an overflow or its negative value
46    pub fn to_u256(&self) -> Result<Uint256, Uint256> {
47        if self.0 == 0 {
48            return Ok(Uint256::from_u64(0).unwrap());
49        }
50        let exponent = self.0 >> 24;
51        let negative = (self.0 >> 23) & 1;
52
53        let mut mantissa = self.0 & 0x_7ff_fff;
54
55        let result = if exponent <= 3 {
56            mantissa >>= 8 * (3 - exponent as usize);
57            Uint256::from(mantissa)
58        } else {
59            Uint256::from(mantissa) << (8 * (exponent as usize - 3))
60        };
61
62        let overflow = (mantissa != 0 && exponent > 34)
63            || (mantissa > 0xff && exponent > 33)
64            || (mantissa > 0xffff && exponent > 32);
65
66        if negative != 0 || overflow {
67            Err(result)
68        } else {
69            Ok(result)
70        }
71    }
72
73    pub fn from_u256(val: Uint256) -> Self {
74        let mut size = (val.bits() + 7) / 8;
75        let mut compact = if size <= 3 {
76            (val.low_u64() << (8 * (3 - size))) as u32
77        } else {
78            let bn = val >> (8 * (size - 3));
79            bn.low_u32()
80        };
81
82        if (compact & 0x00800000) != 0 {
83            compact >>= 8;
84            size += 1;
85        }
86
87        assert!((compact & !0x_7ff_fff) == 0);
88        assert!(size < 256);
89        Compact(compact | (size << 24) as u32)
90    }
91
92    pub fn to_f64(&self) -> f64 {
93        let mut shift = (self.0 >> 24) & 0xff;
94        let mut diff = f64::from(0x0000ffffu32) / f64::from(self.0 & 0x00ffffffu32);
95        while shift < 29 {
96            diff *= f64::from(256);
97            shift += 1;
98        }
99        while shift > 29 {
100            diff /= f64::from(256.0);
101            shift -= 1;
102        }
103        diff
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::Compact;
110    use super::*;
111
112    #[test]
113    fn test_compact_to_u256() {
114        assert_eq!(Compact::new(0x01003456).to_u256(), Ok(0u64.into()));
115        assert_eq!(Compact::new(0x01123456).to_u256(), Ok(0x12u64.into()));
116        assert_eq!(Compact::new(0x02008000).to_u256(), Ok(0x80u64.into()));
117        assert_eq!(Compact::new(0x05009234).to_u256(), Ok(0x92340000u64.into()));
118        // negative -0x12345600
119        assert!(Compact::new(0x04923456).to_u256().is_err());
120        assert_eq!(Compact::new(0x04123456).to_u256(), Ok(0x12345600u64.into()));
121    }
122
123    #[test]
124    fn test_from_u256() {
125        let test1 = Uint256::from(1000u64);
126        assert_eq!(Compact::new(0x0203e800), Compact::from_u256(test1));
127
128        // let test2 = Uint256::from(2).pow(Uint256::from(256 - 32)) - Uint256::from(1);
129        // assert_eq!(Compact::new(0x1d00ffff), Compact::from_u256(test2));
130    }
131
132    #[test]
133    fn test_compact_to_from_u256() {
134        // TODO: it does not work both ways for small values... check why
135        let compact = Compact::new(0x1d00ffff);
136        let compact2 = Compact::from_u256(compact.to_u256().unwrap());
137        assert_eq!(compact, compact2);
138
139        let compact = Compact::new(0x05009234);
140        let compact2 = Compact::from_u256(compact.to_u256().unwrap());
141        assert_eq!(compact, compact2);
142    }
143
144    #[test]
145    fn difficulty() {
146        fn compare_f64(v1: f64, v2: f64) -> bool {
147            (v1 - v2).abs() < 0.00001
148        }
149
150        assert!(compare_f64(Compact::new(0x1b0404cb).to_f64(), 16307.42094));
151
152        // tests from original bitcoin client:
153        // https://github.com/bitcoin/bitcoin/blob/1e8f88e071019907785b260477bd359bef6f9a8f/src/test/blockchain_tests.cpp
154
155        assert!(compare_f64(Compact::new(0x1f111111).to_f64(), 0.000001));
156        assert!(compare_f64(Compact::new(0x1ef88f6f).to_f64(), 0.000016));
157        assert!(compare_f64(Compact::new(0x1df88f6f).to_f64(), 0.004023));
158        assert!(compare_f64(Compact::new(0x1cf88f6f).to_f64(), 1.029916));
159        assert!(compare_f64(
160            Compact::new(0x12345678).to_f64(),
161            5913134931067755359633408.0
162        ));
163    }
164}