solidity_abi/
basic.rs

1// Copyright 2020 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 super::{utils::abi_require, ABIDecodeError, ABIVariable, LinkedBytes};
6use cfx_types::{Address, H256, U256};
7
8impl ABIVariable for Address {
9    const BASIC_TYPE: bool = true;
10    const STATIC_LENGTH: Option<usize> = Some(32);
11
12    fn from_abi(data: &[u8]) -> Result<Self, ABIDecodeError> {
13        abi_require(data.len() == 32, "Invalid call data length")?;
14        Ok(Address::from_slice(&data[12..32]))
15    }
16
17    fn to_abi(&self) -> LinkedBytes {
18        let mut answer = vec![0u8; 12];
19        answer.extend_from_slice(self.as_bytes());
20        LinkedBytes::from_bytes(answer)
21    }
22
23    fn to_packed_abi(&self) -> LinkedBytes {
24        LinkedBytes::from_bytes(self.to_fixed_bytes().into())
25    }
26}
27
28impl ABIVariable for U256 {
29    const BASIC_TYPE: bool = true;
30    const STATIC_LENGTH: Option<usize> = Some(32);
31
32    fn from_abi(data: &[u8]) -> Result<Self, ABIDecodeError> {
33        abi_require(data.len() == 32, "Invalid call data length")?;
34        Ok(U256::from_big_endian(&data))
35    }
36
37    fn to_abi(&self) -> LinkedBytes {
38        LinkedBytes::from_bytes(self.to_big_endian().to_vec())
39    }
40
41    fn to_packed_abi(&self) -> LinkedBytes { self.to_abi() }
42}
43
44impl ABIVariable for H256 {
45    const BASIC_TYPE: bool = <[u8; 32]>::BASIC_TYPE;
46    const STATIC_LENGTH: Option<usize> = <[u8; 32]>::STATIC_LENGTH;
47
48    fn from_abi(data: &[u8]) -> Result<Self, ABIDecodeError> {
49        Ok(H256::from(<[u8; 32]>::from_abi(data)?))
50    }
51
52    fn to_abi(&self) -> LinkedBytes { self.0.to_abi() }
53
54    fn to_packed_abi(&self) -> LinkedBytes { self.0.to_packed_abi() }
55}
56
57impl ABIVariable for bool {
58    const BASIC_TYPE: bool = true;
59    const STATIC_LENGTH: Option<usize> = Some(32);
60
61    fn from_abi(data: &[u8]) -> Result<Self, ABIDecodeError> {
62        abi_require(data.len() == 32, "Invalid call data length")?;
63        Ok(data[31] != 0)
64    }
65
66    fn to_abi(&self) -> LinkedBytes {
67        let mut answer = vec![0u8; 32];
68        answer[31] = *self as u8;
69        LinkedBytes::from_bytes(answer)
70    }
71
72    fn to_packed_abi(&self) -> LinkedBytes {
73        LinkedBytes::from_bytes(vec![*self as u8])
74    }
75}
76
77macro_rules! impl_abi_variable_for_primitive {
78    () => {};
79    ($ty: ident) => {impl_abi_variable_for_primitive!($ty,);};
80    ($ty: ident, $($rest: ident),*) => {
81        impl ABIVariable for $ty {
82            const BASIC_TYPE: bool = true;
83            const STATIC_LENGTH: Option<usize> = Some(32);
84
85            /// Decode a fixed-size unsigned integer from a 32-byte ABI word.
86            ///
87            /// ⚠️ **Non-strict decoding (intentional, consensus-critical).**
88            ///
89            /// Only validates the 32-byte length and takes the low `BITS/8` bytes;
90            /// high-order bytes are **silently ignored even if non-zero**.
91            ///
92            /// This deviates from Solidity/EVM ABI semantics, which require
93            /// `value < 2^N` for `uintN` (`N < 256`) and revert otherwise. The same
94            /// calldata may therefore be interpreted differently:
95            ///   - Solidity: reverts on non-zero high bytes.
96            ///   - Conflux built-in: accepts the call with the truncated low value.
97            ///
98            /// **Caller obligations.** Built-in functions taking sub-256-bit integers
99            /// via this trait MUST perform their own range/overflow checks
100            /// (`checked_add`, `U256` widening, explicit bounds, etc.) and MUST NOT
101            /// assume the decoder has clamped the value. Treat the ABI-level type as
102            /// advisory only.
103            ///
104            /// See `tests_basic::test_*_truncates_high_bytes` for pinned behavior.
105            fn from_abi(data: &[u8]) -> Result<Self, ABIDecodeError> {
106                const BYTES: usize = ($ty::BITS/8) as usize;
107                abi_require(data.len() == 32, "Invalid call data length")?;
108                let mut bytes = [0u8; BYTES];
109                bytes.copy_from_slice(&data[32 - BYTES..]);
110                Ok($ty::from_be_bytes(bytes))
111            }
112
113            fn to_abi(&self) -> LinkedBytes {
114                const BYTES: usize = ($ty::BITS/8) as usize;
115                let mut answer = vec![0u8; 32];
116                answer[32 - BYTES..].copy_from_slice(&self.to_be_bytes());
117                LinkedBytes::from_bytes(answer)
118            }
119
120            fn to_packed_abi(&self) -> LinkedBytes {
121                LinkedBytes::from_bytes(self.to_be_bytes().to_vec())
122            }
123        }
124
125        impl_abi_variable_for_primitive!($($rest),*);
126    }
127}
128
129impl_abi_variable_for_primitive!(U8, u16, u32, u64, u128);
130
131#[allow(dead_code)]
132#[derive(Copy, Clone, Eq, PartialEq)]
133pub struct U8(u8);
134
135impl U8 {
136    #[allow(dead_code)]
137    const BITS: usize = 8;
138
139    #[allow(dead_code)]
140    fn to_be_bytes(self) -> [u8; 1] { [self.0] }
141
142    #[allow(dead_code)]
143    fn from_be_bytes(input: [u8; 1]) -> Self { U8(input[0]) }
144}
145
146#[cfg(test)]
147mod tests_basic {
148    use super::{U8, *};
149    use crate::ABIVariable;
150
151    #[test]
152    fn test_packed_encoding() {
153        let num = 0xDEADBEEFu32;
154        let packed = num.to_packed_abi().to_vec();
155        let expected = num.to_be_bytes().to_vec();
156        assert_eq!(packed, expected);
157    }
158
159    #[test]
160    fn test_u256_abi_basic() {
161        assert!(U256::BASIC_TYPE);
162        assert_eq!(U256::STATIC_LENGTH, Some(32));
163    }
164
165    #[test]
166    fn test_u256_packed_abi_consistency() {
167        let num = U256::max_value();
168        let abi = num.to_abi();
169        let packed_abi = num.to_packed_abi();
170        assert_eq!(abi.to_vec(), packed_abi.to_vec());
171    }
172
173    #[test]
174    fn test_u256_zero_value() {
175        let zero = U256::zero();
176        let encoded = zero.to_abi().to_vec();
177        assert_eq!(encoded, vec![0u8; 32]);
178    }
179
180    #[test]
181    fn test_h256_type_constants() {
182        assert_eq!(H256::BASIC_TYPE, <[u8; 32]>::BASIC_TYPE);
183        assert_eq!(H256::STATIC_LENGTH, <[u8; 32]>::STATIC_LENGTH);
184    }
185
186    #[test]
187    fn test_h256_from_abi_valid() {
188        let input = [42u8; 32];
189        let h256 = H256::from_abi(&input).unwrap();
190        assert_eq!(h256.0, input);
191    }
192
193    #[test]
194    fn test_h256_to_packed_abi() {
195        let h256 = H256([0xBB; 32]);
196        let packed_bytes = h256.to_packed_abi();
197        assert_eq!(packed_bytes.to_vec(), &[0xBB; 32]);
198    }
199
200    #[test]
201    fn test_u8_bits() {
202        assert_eq!(U8::BITS, 8);
203    }
204
205    #[test]
206    fn test_u8_to_be_bytes() {
207        let val = U8::from_be_bytes([123]);
208        assert_eq!(val.to_be_bytes(), [123]);
209    }
210
211    #[test]
212    fn test_u8_from_be_bytes() {
213        let u = U8::from_be_bytes([255]);
214        assert_eq!(u.to_be_bytes(), [255]);
215    }
216
217    #[test]
218    fn test_u8_eq() {
219        let a = U8::from_be_bytes([100]);
220        let b = U8::from_be_bytes([100]);
221        let c = U8::from_be_bytes([200]);
222        assert!(a == b);
223        assert!(a != c);
224    }
225
226    #[test]
227    fn test_u8_boundaries() {
228        let min = U8::from_be_bytes([0]);
229        let max = U8::from_be_bytes([255]);
230        assert_eq!(min.to_be_bytes(), [0]);
231        assert_eq!(max.to_be_bytes(), [255]);
232    }
233
234    #[test]
235    fn test_u8_byte_order_consistency() {
236        let input = [128];
237        let u = U8::from_be_bytes(input);
238        assert_eq!(u.to_be_bytes(), input);
239    }
240
241    // -------------------------------------------------------------------
242    // Pin-down tests for the non-strict `uintN` decoding behavior in
243    // `impl_abi_variable_for_primitive!`.
244    //
245    // These tests intentionally lock in the *current* "silently take the
246    // low BYTES bytes" semantics. The behavior is consensus-critical:
247    // changing it would alter how historical calldata to built-in
248    // contracts decodes and would require a Spec-gated transition (CIP).
249    //
250    // If a future change "fixes" `from_abi` to reject non-zero high bytes
251    // without a hard-fork gate, these tests should fail and force the
252    // change to go through the proper consensus-upgrade path. See the
253    // doc-comment on `from_abi` above for the full rationale.
254    // -------------------------------------------------------------------
255
256    /// Build a 32-byte ABI word whose low `n` bytes hold the big-endian
257    /// representation of `low` and whose high `32 - n` bytes are filled
258    /// with `high_fill`.
259    fn make_word_with_high_fill(low: &[u8], high_fill: u8) -> [u8; 32] {
260        assert!(low.len() <= 32, "low slice must fit in a 32-byte ABI word");
261        let mut word = [high_fill; 32];
262        let start = 32 - low.len();
263        word[start..].copy_from_slice(low);
264        word
265    }
266
267    #[test]
268    fn test_u8_truncates_high_bytes() {
269        // The local `U8` newtype also flows through the
270        // `impl_abi_variable_for_primitive!` macro and therefore inherits
271        // the same low-byte truncation. Lock this in so a future
272        // "fix" cannot quietly tighten U8 decoding.
273        let word = make_word_with_high_fill(&[0xAB], 0xFF);
274        let decoded = U8::from_abi(&word).unwrap();
275        assert_eq!(decoded.to_be_bytes(), [0xAB]);
276    }
277
278    #[test]
279    fn test_u16_truncates_high_bytes() {
280        // Low 2 bytes encode 0x1234; the remaining 30 high bytes are
281        // 0xFF. A Solidity uint16 dispatcher would revert; we instead
282        // silently keep only the low 2 bytes.
283        let word = make_word_with_high_fill(&0x1234u16.to_be_bytes(), 0xFF);
284        let decoded = u16::from_abi(&word).unwrap();
285        assert_eq!(decoded, 0x1234u16);
286    }
287
288    #[test]
289    fn test_u32_truncates_high_bytes() {
290        let word = make_word_with_high_fill(&0xDEADBEEFu32.to_be_bytes(), 0xAA);
291        let decoded = u32::from_abi(&word).unwrap();
292        assert_eq!(decoded, 0xDEADBEEFu32);
293    }
294
295    #[test]
296    fn test_u64_truncates_high_bytes() {
297        let word = make_word_with_high_fill(
298            &0x0123_4567_89AB_CDEFu64.to_be_bytes(),
299            0x01,
300        );
301        let decoded = u64::from_abi(&word).unwrap();
302        assert_eq!(decoded, 0x0123_4567_89AB_CDEFu64);
303    }
304
305    #[test]
306    fn test_u128_truncates_high_bytes() {
307        let low: u128 = 0x0011_2233_4455_6677_8899_AABB_CCDD_EEFFu128;
308        let word = make_word_with_high_fill(&low.to_be_bytes(), 0x77);
309        let decoded = u128::from_abi(&word).unwrap();
310        assert_eq!(decoded, low);
311    }
312
313    #[test]
314    fn test_uintn_accepts_max_low_with_dirty_high_bits() {
315        // u64::MAX in the low 8 bytes plus arbitrary garbage in the
316        // high 24 bytes should still decode to u64::MAX, not error.
317        let word = make_word_with_high_fill(&u64::MAX.to_be_bytes(), 0x5A);
318        let decoded = u64::from_abi(&word).unwrap();
319        assert_eq!(decoded, u64::MAX);
320    }
321
322    #[test]
323    fn test_uintn_decoding_is_non_canonical() {
324        // Two distinct 32-byte words that differ only in their high
325        // (ignored) bytes must decode to the same u32. This documents
326        // that built-in calldata is *not* canonical at the ABI layer.
327        let value: u32 = 0xCAFEBABE;
328        let word_clean = make_word_with_high_fill(&value.to_be_bytes(), 0x00);
329        let word_dirty = make_word_with_high_fill(&value.to_be_bytes(), 0xFF);
330        assert_ne!(word_clean, word_dirty);
331        assert_eq!(
332            u32::from_abi(&word_clean).unwrap(),
333            u32::from_abi(&word_dirty).unwrap(),
334        );
335    }
336
337    #[test]
338    fn test_uintn_rejects_wrong_word_length() {
339        // The length check is the only validation `from_abi` performs;
340        // make sure it still fires for non-32-byte inputs.
341        assert!(u16::from_abi(&[0u8; 31]).is_err());
342        assert!(u32::from_abi(&[0u8; 33]).is_err());
343        assert!(u64::from_abi(&[]).is_err());
344        assert!(u128::from_abi(&[0u8; 16]).is_err());
345    }
346
347    #[test]
348    fn test_uintn_roundtrip_zero_high_bytes() {
349        // Encoding via `to_abi` always zero-pads the high bytes; the
350        // round trip through `from_abi` must be the identity.
351        let cases_u16: &[u16] = &[0, 1, 0x1234, u16::MAX];
352        for &v in cases_u16 {
353            let bytes = v.to_abi().to_vec();
354            assert_eq!(bytes.len(), 32);
355            assert!(bytes[..30].iter().all(|&b| b == 0));
356            assert_eq!(u16::from_abi(&bytes).unwrap(), v);
357        }
358
359        let cases_u64: &[u64] = &[0, 1, 0x0123_4567_89AB_CDEF, u64::MAX];
360        for &v in cases_u64 {
361            let bytes = v.to_abi().to_vec();
362            assert_eq!(bytes.len(), 32);
363            assert!(bytes[..24].iter().all(|&b| b == 0));
364            assert_eq!(u64::from_abi(&bytes).unwrap(), v);
365        }
366    }
367}