solidity_abi/
utils.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::{ABIDecodable, ABIDecodeError, ABIVariable};
6use cfx_bytes::Bytes;
7use cfx_types::U256;
8use std::{collections::LinkedList, slice::Iter};
9
10pub struct LinkedBytes {
11    length: usize,
12    data: LinkedList<Vec<u8>>,
13}
14
15pub fn padded_big_endian(length: usize) -> Vec<u8> {
16    U256::from(length).to_big_endian().to_vec()
17}
18
19impl LinkedBytes {
20    pub fn new() -> Self {
21        Self {
22            length: 0,
23            data: LinkedList::new(),
24        }
25    }
26
27    pub fn from_bytes(bytes: Vec<u8>) -> Self {
28        let mut answer = Self::new();
29        answer.length = bytes.len();
30        answer.data.push_back(bytes);
31        answer
32    }
33
34    pub fn append(&mut self, other: &mut Self) {
35        self.length += other.length;
36        self.data.append(&mut other.data);
37        other.length = 0;
38    }
39
40    pub fn to_vec(&self) -> Vec<u8> {
41        let mut answer = Vec::new();
42        for slice in &self.data {
43            answer.extend_from_slice(&slice)
44        }
45        answer
46    }
47
48    pub fn len(&self) -> usize { self.length }
49}
50
51pub fn read_abi_list<T: ABIVariable>(
52    data: &[u8], pointer: &mut Iter<u8>,
53) -> Result<T, ABIDecodeError> {
54    let res = if let Some(len) = T::STATIC_LENGTH {
55        pull_slice(pointer, len, "Incomplete static input parameter")?
56    } else {
57        let location = U256::from_big_endian(pull_slice(
58            pointer,
59            32,
60            "Incomplete location for dynamic input parameter",
61        )?);
62        abi_require(
63            location < U256::from(data.len()),
64            "Location out of bounds",
65        )?;
66        let loc = location.as_u64() as usize;
67        &data[loc..]
68    };
69    T::from_abi(res)
70}
71
72pub struct ABIListWriter {
73    heads_length: usize,
74    heads: LinkedBytes,
75    tails: LinkedBytes,
76}
77
78impl ABIListWriter {
79    pub fn with_heads_length(heads_length: usize) -> Self {
80        Self {
81            heads_length,
82            heads: LinkedBytes::new(),
83            tails: LinkedBytes::new(),
84        }
85    }
86
87    pub fn write_down<T: ABIVariable>(&mut self, input: &T) {
88        let mut encoded = input.to_abi();
89        if let Some(len) = T::STATIC_LENGTH {
90            assert_eq!(encoded.len(), len);
91            self.heads.append(&mut encoded);
92        } else {
93            let mut location = LinkedBytes::from_bytes(padded_big_endian(
94                self.tails.len() + self.heads_length,
95            ));
96            self.heads.append(&mut location);
97            self.tails.append(&mut encoded);
98        }
99    }
100
101    pub fn into_linked_bytes(mut self) -> LinkedBytes {
102        assert_eq!(self.heads.len(), self.heads_length);
103        self.heads.append(&mut self.tails);
104        self.heads
105    }
106}
107
108#[inline]
109pub fn abi_require(
110    claim: bool, desc: &'static str,
111) -> Result<(), ABIDecodeError> {
112    if !claim {
113        Err(ABIDecodeError(desc))
114    } else {
115        Ok(())
116    }
117}
118
119#[inline]
120pub fn pull_slice<'a>(
121    iter: &mut Iter<'a, u8>, n: usize, err_desc: &'static str,
122) -> Result<&'a [u8], ABIDecodeError> {
123    abi_require(iter.len() >= n, err_desc)?;
124
125    let slice = iter.as_slice();
126    let result = &slice[0..n];
127    *iter = slice[n..].iter();
128    Ok(result)
129}
130
131// abi decode string revert reason: Error(string)
132pub fn string_revert_reason_decode(output: &Bytes) -> String {
133    const MAX_LENGTH: usize = 50;
134    let decode_result = if output.len() < 4 {
135        Err(ABIDecodeError("Uncompleted Signature"))
136    } else {
137        let (sig, data) = output.split_at(4);
138        if sig != [8, 195, 121, 160] {
139            Err(ABIDecodeError("Unrecognized Signature"))
140        } else {
141            String::abi_decode(data)
142        }
143    };
144    match decode_result {
145        Ok(str) => {
146            if str.chars().count() <= MAX_LENGTH {
147                str
148            } else {
149                let truncated: String = str.chars().take(MAX_LENGTH).collect();
150                format!("{}...", truncated)
151            }
152        }
153        Err(_) => "".to_string(),
154    }
155}
156
157#[cfg(test)]
158mod test {
159    use super::string_revert_reason_decode;
160    use cfx_bytes::Bytes;
161    use rustc_hex::FromHex;
162
163    #[test]
164    fn test_decode_result() {
165        let input_hex = "08c379a0\
166            0000000000000000000000000000000000000000000000000000000000000020\
167            0000000000000000000000000000000000000000000000000000000000000018\
168            5468697320697320616e206572726f72206d6573736167650000000000000000\
169            ";
170        assert_eq!(
171            "This is an error message".to_string(),
172            string_revert_reason_decode(&input_hex.from_hex().unwrap())
173        );
174    }
175
176    #[test]
177    fn test_decode_result_with_utf8_multibyte() {
178        // Build ABI-encoded Error(string) with 60 Chinese chars
179        let long_str: String = "测".repeat(60);
180        let data_len = long_str.len();
181        let padded_len = (data_len + 31) / 32 * 32;
182        let mut data = vec![0u8; 32 + 32 + padded_len];
183        // Offset to string data (32)
184        data[31] = 0x20;
185        // String length as big-endian U256
186        data[63] = data_len as u8;
187        // String data
188        data[64..64 + data_len].copy_from_slice(long_str.as_bytes());
189        let mut output = Bytes::from(vec![8, 195, 121, 160]);
190        output.extend(data);
191        let result = string_revert_reason_decode(&output);
192        // Should have 50 Chinese chars + "..."
193        assert_eq!(result.len(), 153); // 50 * 3 + 3
194        assert!(result.ends_with("..."));
195        assert_eq!(result.chars().count(), 53); // 50 chars + 3 dots
196    }
197
198    #[test]
199    fn test_decode_result_short_utf8() {
200        // Build ABI-encoded Error(string) with short Chinese string
201        let short_str = "中文测试";
202        let data_len = short_str.len();
203        let padded_len = (data_len + 31) / 32 * 32;
204        let mut data = vec![0u8; 32 + 32 + padded_len];
205        // Offset = 32
206        data[31] = 0x20;
207        // Length
208        data[63] = data_len as u8;
209        // Data
210        data[64..64 + data_len].copy_from_slice(short_str.as_bytes());
211        let mut output = Bytes::from(vec![8, 195, 121, 160]);
212        output.extend(data);
213        let result = string_revert_reason_decode(&output);
214        assert_eq!(result, "中文测试");
215    }
216}