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    let mut bytes = [0u8; 32];
17    U256::from(length).to_big_endian(&mut bytes);
18    bytes.to_vec()
19}
20
21impl LinkedBytes {
22    pub fn new() -> Self {
23        Self {
24            length: 0,
25            data: LinkedList::new(),
26        }
27    }
28
29    pub fn from_bytes(bytes: Vec<u8>) -> Self {
30        let mut answer = Self::new();
31        answer.length = bytes.len();
32        answer.data.push_back(bytes);
33        answer
34    }
35
36    pub fn append(&mut self, other: &mut Self) {
37        self.length += other.length;
38        self.data.append(&mut other.data);
39        other.length = 0;
40    }
41
42    pub fn to_vec(&self) -> Vec<u8> {
43        let mut answer = Vec::new();
44        for slice in &self.data {
45            answer.extend_from_slice(&slice)
46        }
47        answer
48    }
49
50    pub fn len(&self) -> usize { self.length }
51}
52
53pub fn read_abi_list<T: ABIVariable>(
54    data: &[u8], pointer: &mut Iter<u8>,
55) -> Result<T, ABIDecodeError> {
56    let res = if let Some(len) = T::STATIC_LENGTH {
57        pull_slice(pointer, len, "Incomplete static input parameter")?
58    } else {
59        let location = U256::from_big_endian(pull_slice(
60            pointer,
61            32,
62            "Incomplete location for dynamic input parameter",
63        )?);
64        abi_require(
65            location < U256::from(data.len()),
66            "Location out of bounds",
67        )?;
68        let loc = location.as_u64() as usize;
69        &data[loc..]
70    };
71    T::from_abi(res)
72}
73
74pub struct ABIListWriter {
75    heads_length: usize,
76    heads: LinkedBytes,
77    tails: LinkedBytes,
78}
79
80impl ABIListWriter {
81    pub fn with_heads_length(heads_length: usize) -> Self {
82        Self {
83            heads_length,
84            heads: LinkedBytes::new(),
85            tails: LinkedBytes::new(),
86        }
87    }
88
89    pub fn write_down<T: ABIVariable>(&mut self, input: &T) {
90        let mut encoded = input.to_abi();
91        if let Some(len) = T::STATIC_LENGTH {
92            assert_eq!(encoded.len(), len);
93            self.heads.append(&mut encoded);
94        } else {
95            let mut location = LinkedBytes::from_bytes(padded_big_endian(
96                self.tails.len() + self.heads_length,
97            ));
98            self.heads.append(&mut location);
99            self.tails.append(&mut encoded);
100        }
101    }
102
103    pub fn into_linked_bytes(mut self) -> LinkedBytes {
104        assert_eq!(self.heads.len(), self.heads_length);
105        self.heads.append(&mut self.tails);
106        self.heads
107    }
108}
109
110#[inline]
111pub fn abi_require(
112    claim: bool, desc: &'static str,
113) -> Result<(), ABIDecodeError> {
114    if !claim {
115        Err(ABIDecodeError(desc))
116    } else {
117        Ok(())
118    }
119}
120
121#[inline]
122pub fn pull_slice<'a>(
123    iter: &mut Iter<'a, u8>, n: usize, err_desc: &'static str,
124) -> Result<&'a [u8], ABIDecodeError> {
125    abi_require(iter.len() >= n, err_desc)?;
126
127    let slice = iter.as_slice();
128    let result = &slice[0..n];
129    *iter = slice[n..].iter();
130    Ok(result)
131}
132
133// abi decode string revert reason: Error(string)
134pub fn string_revert_reason_decode(output: &Bytes) -> String {
135    const MAX_LENGTH: usize = 50;
136    let decode_result = if output.len() < 4 {
137        Err(ABIDecodeError("Uncompleted Signature"))
138    } else {
139        let (sig, data) = output.split_at(4);
140        if sig != [8, 195, 121, 160] {
141            Err(ABIDecodeError("Unrecognized Signature"))
142        } else {
143            String::abi_decode(data)
144        }
145    };
146    match decode_result {
147        Ok(str) => {
148            if str.len() < MAX_LENGTH {
149                str
150            } else {
151                format!("{}...", str[..MAX_LENGTH].to_string())
152            }
153        }
154        Err(_) => "".to_string(),
155    }
156}
157
158#[cfg(test)]
159mod test {
160    use super::string_revert_reason_decode;
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}