1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2021 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/
//
// Modification based on https://github.com/hlb8122/rust-bitcoincash-addr in MIT License.
// A copy of the original license is included in LICENSE.rust-bitcoincash-addr.

use crate::types::DecodingError;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

// The polymod function is used to calculate the checksum of the address.
pub fn polymod(v: &[u8]) -> u64 {
    let mut c = 1;
    for d in v {
        let c0 = (c >> 35) as u8;
        c = ((c & 0x07ffffffff) << 5) ^ u64::from(*d);
        if c0 & 0x01 != 0 {
            c ^= 0x98f2bc8e61;
        }
        if c0 & 0x02 != 0 {
            c ^= 0x79b76d99e2;
        }
        if c0 & 0x04 != 0 {
            c ^= 0xf33e5fb3c4;
        }
        if c0 & 0x08 != 0 {
            c ^= 0xae2eabe2a8;
        }
        if c0 & 0x10 != 0 {
            c ^= 0x1e4f43e470;
        }
    }
    c ^ 1
}

/// The checksum calculation includes the lower 5 bits of each character of the
/// prefix.
/// - e.g. "bit..." becomes 2,9,20,...
// Expand the address prefix for the checksum operation.
pub fn expand_prefix(prefix: &str) -> Vec<u8> {
    let mut ret: Vec<u8> = prefix.chars().map(|c| (c as u8) & 0x1f).collect();
    ret.push(0);
    ret
}

// This method assume that data is valid string of inbits.
// When pad is true, any remaining bits are padded and encoded into a new byte;
// when pad is false, any remaining bits are checked to be zero and discarded.
pub fn convert_bits(
    data: &[u8], inbits: u8, outbits: u8, pad: bool,
) -> Result<Vec<u8>, DecodingError> {
    assert!(inbits <= 8 && outbits <= 8);
    let num_bytes = (data.len() * inbits as usize + outbits as usize - 1)
        / outbits as usize;
    let mut ret = Vec::with_capacity(num_bytes);
    let mut acc: u16 = 0; // accumulator of bits
    let mut num: u8 = 0; // num bits in acc
    let groupmask = (1 << outbits) - 1;
    for d in data.iter() {
        // We push each input chunk into a 16-bit accumulator
        acc = (acc << inbits) | u16::from(*d);
        num += inbits;
        // Then we extract all the output groups we can
        while num >= outbits {
            // Store only the highest outbits.
            ret.push((acc >> (num - outbits)) as u8);
            // Clear the highest outbits.
            acc &= !(groupmask << (num - outbits));
            num -= outbits;
        }
    }
    if pad {
        // If there's some bits left, pad and add it
        if num > 0 {
            ret.push((acc << (outbits - num)) as u8);
        }
    } else {
        // FIXME: add unit tests for it.
        // If there's some bits left, figure out if we need to remove padding
        // and add it
        let padding = ((data.len() * inbits as usize) % outbits as usize) as u8;
        if num >= inbits || acc != 0 {
            return Err(DecodingError::InvalidPadding {
                from_bits: inbits,
                padding_bits: padding,
                padding: acc,
            });
        }
    }
    Ok(ret)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[cfg(not(feature = "std"))]
    extern crate alloc;
    #[cfg(not(feature = "std"))]
    use alloc::vec;

    #[test]
    fn test_expand_prefix() {
        assert_eq!(expand_prefix("cfx"), vec![0x03, 0x06, 0x18, 0x00]);

        assert_eq!(
            expand_prefix("cfxtest"),
            vec![0x03, 0x06, 0x18, 0x14, 0x05, 0x13, 0x14, 0x00]
        );

        assert_eq!(
            expand_prefix("net17"),
            vec![0x0e, 0x05, 0x14, 0x11, 0x17, 0x00]
        );
    }

    #[test]
    fn test_convert_bits() {
        // 00000000 --> 0, 0, 0, 0, 0, 0, 0, 0
        assert_eq!(convert_bits(&[0], 8, 1, false), Ok(vec![0; 8]));

        // 00000000 --> 000, 000, 00_
        assert_eq!(convert_bits(&[0], 8, 3, false), Ok(vec![0, 0])); // 00_ is dropped
        assert_eq!(convert_bits(&[0], 8, 3, true), Ok(vec![0, 0, 0])); // 00_ becomes 000

        // 00000001 --> 000, 000, 01_
        assert!(convert_bits(&[1], 8, 3, false).is_err()); // 01_ != 0 (ignored incomplete chunk must be 0)
        assert_eq!(convert_bits(&[1], 8, 3, true), Ok(vec![0, 0, 2])); // 01_ becomes 010

        // 00000001 --> 0000000, 1______
        assert_eq!(convert_bits(&[1], 8, 7, true), Ok(vec![0, 64])); // 1______ becomes 1000000

        // 0, 0, 0, 0, 0, 0, 0, 0 --> 00000000
        assert_eq!(convert_bits(&[0; 8], 1, 8, false), Ok(vec![0]));

        // 000, 000, 010 -> 00000001, 0_______
        assert_eq!(convert_bits(&[0, 0, 2], 3, 8, false), Ok(vec![1])); // 0_______ is dropped
        assert_eq!(convert_bits(&[0, 0, 2], 3, 8, true), Ok(vec![1, 0])); // 0_______ becomes 00000000

        // 000, 000, 011 -> 00000001, 1_______
        assert!(convert_bits(&[0, 0, 3], 3, 8, false).is_err()); // 1_______ != 0 (ignored incomplete chunk must be 0)

        // 00000000, 00000001, 00000010, 00000011, 00000100 -->
        // 00000, 00000, 00000, 10000, 00100, 00000, 11000, 00100
        assert_eq!(
            convert_bits(&[0, 1, 2, 3, 4], 8, 5, false),
            Ok(vec![0, 0, 0, 16, 4, 0, 24, 4])
        );

        // 00000000, 00000001, 00000010 -->
        // 00000, 00000, 00000, 10000, 0010_
        assert!(convert_bits(&[0, 1, 2], 8, 5, false).is_err()); // 0010_ != 0 (ignored incomplete chunk must be 0)

        assert_eq!(
            convert_bits(&[0, 1, 2], 8, 5, true),
            Ok(vec![0, 0, 0, 16, 4])
        ); // 0010_ becomes 00100

        // 00000, 00000, 00000, 10000, 00100, 00000, 11000, 00100 -->
        // 00000000, 00000001, 00000010, 00000011, 00000100
        assert_eq!(
            convert_bits(&[0, 0, 0, 16, 4, 0, 24, 4], 5, 8, false),
            Ok(vec![0, 1, 2, 3, 4])
        );

        // 00000, 00000, 00000, 10000, 00100 -->
        // 00000000, 00000001, 00000010, 0_______
        assert_eq!(
            convert_bits(&[0, 0, 0, 16, 4], 5, 8, false),
            Ok(vec![0, 1, 2])
        ); // 0_______ is dropped

        assert_eq!(
            convert_bits(&[0, 0, 0, 16, 4], 5, 8, true),
            Ok(vec![0, 1, 2, 0])
        ); // 0_______ becomes 00000000
    }
}