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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
use crate::errors::{BytecodeError, LinkError};
use crate::str::{AddressHexExt, StringReplaceExt};
use hex;
use serde::de::{Error as DeError, Visitor};
use serde::{Deserialize, Deserializer};
use std::collections::HashSet;
use std::fmt::{Formatter, Result as FmtResult};
use std::mem;
use web3::types::{Address, Bytes};

/// The string representation of the byte code. Note that this must be a
/// `String` since `solc` linking requires string manipulation of the
/// bytecode string representation.
#[derive(Clone, Debug, Default)]
pub struct Bytecode(String);

impl Bytecode {
    /// Read hex bytecode representation from a string slice.
    pub fn from_hex_str<S>(s: S) -> Result<Bytecode, BytecodeError>
    where
        S: AsRef<str>,
    {
        let s = s.as_ref();
        if s.is_empty() {
            // special case where we have an empty string byte code.
            return Ok(Bytecode::default());
        }

        // check that we start with 0x
        if !s.starts_with("0x") {
            return Err(BytecodeError::MissingHexPrefix);
        }
        // and that the length is even
        if s.len() % 2 != 0 {
            return Err(BytecodeError::InvalidLength);
        }

        // verify that each code block is valid hex
        for block in CodeIter(&s[2..]) {
            let block = block?;

            if let Some(pos) = block.bytes().position(|b| match b {
                b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => false,
                _ => true,
            }) {
                return Err(BytecodeError::InvalidHexDigit(
                    block.chars().nth(pos).expect("valid pos"),
                ));
            }
        }

        Ok(Bytecode(s[2..].to_string()))
    }

    /// Link a library into the current bytecode.
    ///
    /// # Panics
    ///
    /// Panics if an invalid library name is used (for example if it is more
    /// than 38 characters long).
    pub fn link<S>(&mut self, name: S, address: Address) -> Result<(), LinkError>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref();
        if name.len() > 38 {
            panic!("invalid library name for linking");
        }

        // NOTE(nlordell): solc linking works by string search and replace of
        //   '__$name__..__' with the library address; see generated bytecode for
        //   `LinkedContract` contract for and example of how it looks like
        let placeholder = format!("__{:_<38}", name);
        let address = address.to_fixed_hex();
        let matched = self.0.replace_all_in_place(&placeholder, &address);
        if !matched {
            return Err(LinkError::NotFound(name.to_string()));
        }

        Ok(())
    }

    /// Convert a bytecode into its byte representation.
    pub fn into_bytes(self) -> Result<Bytes, LinkError> {
        match self.undefined_libraries().next() {
            Some(library) => Err(LinkError::UndefinedLibrary(library.to_string())),
            None => Ok(Bytes(hex::decode(&self.0).expect("valid hex"))),
        }
    }

    /// Iterator over all libraries remaining in the bytecode.
    pub fn undefined_libraries(&self) -> LibIter<'_> {
        LibIter {
            cursor: &self.0,
            seen: HashSet::new(),
        }
    }

    /// Returns true if bytecode requires linking.
    pub fn requires_linking(&self) -> bool {
        self.undefined_libraries().next().is_some()
    }

    /// Returns true if the bytecode is an empty bytecode.
    pub fn is_empty(&self) -> bool {
        self.0 == ""
    }
}

/// internal type for iterating though a bytecode's string code blocks skipping
/// the `solc` linker placeholders.
struct CodeIter<'a>(&'a str);

impl<'a> Iterator for CodeIter<'a> {
    type Item = Result<&'a str, BytecodeError>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.0.is_empty() {
            return None;
        }

        match self.0.find("__") {
            Some(pos) => {
                let (block, tail) = self.0.split_at(pos);
                if tail.len() < 40 {
                    Some(Err(BytecodeError::PlaceholderTooShort))
                } else {
                    self.0 = &tail[40..];
                    Some(Ok(block))
                }
            }
            None => Some(Ok(mem::replace(&mut self.0, ""))),
        }
    }
}

/// An iterator over link placeholders in the bytecode.
pub struct LibIter<'a> {
    cursor: &'a str,
    seen: HashSet<&'a str>,
}

impl<'a> Iterator for LibIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        while let Some(pos) = self.cursor.find("__") {
            // NOTE(nlordell): this won't panic since we only construct this iterator
            //   on valid Bytecode instances where this has been verified
            let (placeholder, tail) = self.cursor[pos..].split_at(40);
            let lib = placeholder.trim_matches('_');

            self.cursor = tail;
            if self.seen.insert(lib) {
                return Some(lib);
            }
        }
        None
    }
}

impl<'de> Deserialize<'de> for Bytecode {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_str(BytecodeVisitor)
    }
}

/// A serde visitor for deserializing bytecode.
struct BytecodeVisitor;

impl<'de> Visitor<'de> for BytecodeVisitor {
    type Value = Bytecode;

    fn expecting(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "valid EVM bytecode string representation")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        Bytecode::from_hex_str(v).map_err(E::custom)
    }

    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    where
        E: DeError,
    {
        // TODO(nlordell): try to reuse this allocation
        self.visit_str(&v)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_bytecode_is_empty() {
        assert!(Bytecode::default().is_empty());
    }

    #[test]
    fn empty_hex_bytecode_is_empty() {
        assert!(Bytecode::from_hex_str("0x").unwrap().is_empty());
    }
}