ethcontract_common/
bytecode.rs

1//! This module implements `solc` and Truffle bytecode output parsing and
2//! linking. `Bytecode` is represented as a hex string with special placeholders
3//! for libraries that require linking.
4
5use crate::errors::{BytecodeError, LinkError};
6use serde::de::{Error as DeError, Visitor};
7use serde::{Deserialize, Deserializer, Serialize};
8use std::collections::HashSet;
9use std::fmt::{Formatter, Result as FmtResult};
10use std::mem;
11use web3::types::{Address, Bytes};
12
13/// The string representation of the byte code. Note that this must be a
14/// `String` since `solc` linking requires string manipulation of the
15/// bytecode string representation.
16#[derive(Clone, Debug, Default, Serialize)]
17pub struct Bytecode(String);
18
19impl Bytecode {
20    /// Reads hex bytecode representation from a string slice.
21    pub fn from_hex_str(s: &str) -> Result<Self, BytecodeError> {
22        if s.is_empty() {
23            // special case where we have an empty string byte code.
24            return Ok(Bytecode::default());
25        }
26
27        // Verify that the length is even
28        if s.len() % 2 != 0 {
29            return Err(BytecodeError::InvalidLength);
30        }
31
32        // account for optional 0x prefix
33        let s = s.strip_prefix("0x").unwrap_or(s);
34
35        // verify that each code block is valid hex
36        for block in CodeIter(s) {
37            let block = block?;
38
39            if let Some(pos) = block.bytes().position(|b| !b.is_ascii_hexdigit()) {
40                return Err(BytecodeError::InvalidHexDigit(
41                    block.chars().nth(pos).expect("valid pos"),
42                ));
43            }
44        }
45
46        Ok(Bytecode(s.to_string()))
47    }
48
49    /// Links a library into the current bytecode.
50    ///
51    /// # Panics
52    ///
53    /// Panics if an invalid library name is used (for example if it is more
54    /// than 38 characters long).
55    pub fn link<S>(&mut self, name: S, address: Address) -> Result<(), LinkError>
56    where
57        S: AsRef<str>,
58    {
59        let name = name.as_ref();
60        assert!(name.len() <= 38, "invalid library name for linking");
61
62        // NOTE(nlordell): solc linking works by string search and replace of
63        //   '__$name__..__' with the library address; see generated bytecode for
64        //   `LinkedContract` contract for and example of how it looks like
65        let placeholder = format!("__{:_<38}", name);
66        let address = to_fixed_hex(&address);
67        if !self.0.contains(&placeholder) {
68            return Err(LinkError::NotFound(name.to_string()));
69        }
70        self.0 = self.0.replace(&placeholder, &address);
71
72        Ok(())
73    }
74
75    /// Converts a bytecode into its byte representation.
76    pub fn to_bytes(&self) -> Result<Bytes, LinkError> {
77        match self.undefined_libraries().next() {
78            Some(library) => Err(LinkError::UndefinedLibrary(library.to_string())),
79            None => Ok(Bytes(hex::decode(&self.0).expect("valid hex"))),
80        }
81    }
82
83    /// Returns an iterator over all libraries remaining in the bytecode.
84    pub fn undefined_libraries(&self) -> LibIter<'_> {
85        LibIter {
86            cursor: &self.0,
87            seen: HashSet::new(),
88        }
89    }
90
91    /// Returns true if bytecode requires linking.
92    pub fn requires_linking(&self) -> bool {
93        self.undefined_libraries().next().is_some()
94    }
95
96    /// Returns true if the bytecode is an empty bytecode.
97    pub fn is_empty(&self) -> bool {
98        self.0.is_empty()
99    }
100}
101
102/// Internal type for iterating though a bytecode's string code blocks skipping
103/// the `solc` linker placeholders.
104struct CodeIter<'a>(&'a str);
105
106impl<'a> Iterator for CodeIter<'a> {
107    type Item = Result<&'a str, BytecodeError>;
108
109    fn next(&mut self) -> Option<Self::Item> {
110        if self.0.is_empty() {
111            return None;
112        }
113
114        match self.0.find("__") {
115            Some(pos) => {
116                let (block, tail) = self.0.split_at(pos);
117                if tail.len() < 40 {
118                    Some(Err(BytecodeError::PlaceholderTooShort))
119                } else {
120                    self.0 = &tail[40..];
121                    Some(Ok(block))
122                }
123            }
124            None => Some(Ok(mem::take(&mut self.0))),
125        }
126    }
127}
128
129/// An iterator over link placeholders in the bytecode.
130pub struct LibIter<'a> {
131    cursor: &'a str,
132    seen: HashSet<&'a str>,
133}
134
135impl<'a> Iterator for LibIter<'a> {
136    type Item = &'a str;
137
138    fn next(&mut self) -> Option<Self::Item> {
139        while let Some(pos) = self.cursor.find("__") {
140            // NOTE(nlordell): this won't panic since we only construct this iterator
141            //   on valid Bytecode instances where this has been verified
142            let (placeholder, tail) = self.cursor[pos..].split_at(40);
143            let lib = placeholder.trim_matches('_');
144
145            self.cursor = tail;
146            if self.seen.insert(lib) {
147                return Some(lib);
148            }
149        }
150        None
151    }
152}
153
154impl<'de> Deserialize<'de> for Bytecode {
155    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
156    where
157        D: Deserializer<'de>,
158    {
159        deserializer.deserialize_str(BytecodeVisitor)
160    }
161}
162
163/// A serde visitor for deserializing bytecode.
164struct BytecodeVisitor;
165
166impl Visitor<'_> for BytecodeVisitor {
167    type Value = Bytecode;
168
169    fn expecting(&self, f: &mut Formatter) -> FmtResult {
170        write!(f, "valid EVM bytecode string representation")
171    }
172
173    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
174    where
175        E: DeError,
176    {
177        Bytecode::from_hex_str(v).map_err(E::custom)
178    }
179}
180
181fn to_fixed_hex(address: &Address) -> String {
182    format!("{:040x}", address)
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn default_bytecode_is_empty() {
191        assert!(Bytecode::default().is_empty());
192    }
193
194    #[test]
195    fn empty_hex_bytecode_is_empty() {
196        assert!(Bytecode::from_hex_str("0x").unwrap().is_empty());
197    }
198
199    #[test]
200    fn unprefixed_hex_bytecode_is_not_empty() {
201        assert!(!Bytecode::from_hex_str("feedface").unwrap().is_empty());
202    }
203
204    #[test]
205    fn to_fixed_hex_() {
206        for (value, expected) in &[
207            (
208                "0x0000000000000000000000000000000000000000",
209                "0000000000000000000000000000000000000000",
210            ),
211            (
212                "0x0102030405060708091020304050607080900001",
213                "0102030405060708091020304050607080900001",
214            ),
215            (
216                "0x9fac3b52be975567103c4695a2835bba40076da1",
217                "9fac3b52be975567103c4695a2835bba40076da1",
218            ),
219        ] {
220            let value: Address = value[2..].parse().unwrap();
221            assert_eq!(to_fixed_hex(&value), *expected);
222        }
223    }
224
225    #[test]
226    fn bytecode_link_success() {
227        let address = Address::zero();
228        let address_encoded = [0u8; 20];
229        let name = "name";
230        let placeholder = format!("__{:_<38}", name);
231        let mut bytecode = Bytecode::from_hex_str(&format!(
232            "0x61{}{}61{}",
233            placeholder, placeholder, placeholder
234        ))
235        .unwrap();
236        bytecode.link(name, address).unwrap();
237        let bytes = bytecode.to_bytes().unwrap();
238        let mut expected = Vec::<u8>::new();
239        expected.extend([0x61]);
240        expected.extend(address_encoded);
241        expected.extend(address_encoded);
242        expected.extend([0x61]);
243        expected.extend(address_encoded);
244        assert_eq!(bytes.0, expected);
245    }
246
247    #[test]
248    fn bytecode_link_fail() {
249        let address = Address::zero();
250        let placeholder = format!("__{:_<38}", "name0");
251        let mut bytecode = Bytecode::from_hex_str(&format!(
252            "0x61{}{}61{}",
253            placeholder, placeholder, placeholder
254        ))
255        .unwrap();
256        // name does not match
257        match bytecode.link("name1", address) {
258            Err(LinkError::NotFound(_)) => (),
259            _ => panic!("should fail with not found error"),
260        }
261    }
262}