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
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use sha3::{Digest, Keccak256};
use thiserror::Error;

mod function;
mod parameter;

use function::Function;
pub use function::StateMutability;
pub use parameter::Parameter;
use parameter::ParameterType;

/// Parses a `.json` file containing ABI encoded Solidity functions.
///
/// It stores the functions in a `HashMap` with the function name being the key
/// and the parsed function the value.
pub struct Abi {
    pub functions: HashMap<String, Function>,
}

impl Default for Abi {
    fn default() -> Self {
        Self::new()
    }
}

impl Abi {
    /// Creates a new `Abi` instance with an empty `HashMap` within.
    #[inline]
    pub fn new() -> Self {
        Self {
            functions: HashMap::new(),
        }
    }

    /// Parses an ABI value into the `Abi` instance.
    pub fn parse_json(&mut self, abi: serde_json::Value) -> Result<(), AbiParserError> {
        let mut i: usize = 0;
        while abi[i] != serde_json::Value::Null {
            if abi[i]["type"] == "function" {
                if abi[i]["name"] != serde_json::Value::Null {
                    let name = abi[i]["name"].as_str().unwrap().to_owned();
                    self.functions.insert(name, Function::parse(&abi[i])?);
                } else {
                    return Err(AbiParserError::MissingData(
                        "Function name is missing from ABI.".to_owned(),
                    ));
                }
            }
            i += 1;
        }

        Ok(())
    }

    /// Parses an ABI `.json` file into the `Abi` instance.
    pub fn parse_file(&mut self, path_to_abi: &Path) -> Result<(), AbiParserError> {
        let file = File::open(path_to_abi)?;
        let reader = BufReader::new(file);
        let abi: serde_json::Value = serde_json::from_reader(reader)?;

        self.parse_json(abi)
    }

    pub fn get_state_mutability(&self, function_name: &str) -> Option<StateMutability> {
        if let Some(function) = self.functions.get(function_name) {
            return function.state_mutability;
        }

        None
    }

    /// Encodes a function call according to Solidity's contract [ABI
    /// specification](https://docs.soliditylang.org/en/v0.5.3/abi-spec.html#function-selector).
    ///
    /// If the given function is found in the parsed ABI file, the function
    /// encodes the function signature and the input data provided in the form
    /// of a [`Parameter`] vector.
    pub fn encode(
        &self,
        function_name: &str,
        parameters: Vec<Parameter>,
    ) -> Result<Vec<u8>, AbiParserError> {
        if let Some(function) = self.functions.get(function_name) {
            let mut abi_arguments = Vec::<String>::with_capacity(parameters.len());
            for (input, param) in function.inputs.iter().zip(parameters.iter()) {
                if input.parameter_type.type_check(param) {
                    abi_arguments.push(input.parameter_type.as_abi_string())
                } else {
                    return Err(AbiParserError::InvalidAbiEncoding(format!(
                        "Invalid parameter type supplied. Expected {:?}",
                        input.parameter_type
                    )));
                }
            }
            let signature = format!("{}({})", function_name, abi_arguments.join(","));
            let mut hasher = Keccak256::new();
            hasher.update(signature);
            // Take first 4 bytes of the Keccak hash
            let mut hash = hasher.finalize()[0..4].to_vec();
            // Append the encoded parameters to the hash
            parameter::encode_into(&mut hash, parameters);
            Ok(hash)
        } else {
            Err(AbiParserError::MissingData(
                "Function name not found in ABI".to_owned(),
            ))
        }
    }

    /// Decodes a hash into a [`Parameter`] vector.
    ///
    /// Based on the given ABI function name, the `Abi` parser iterates over that
    /// function's output parameter types and decodes the output hash accordingly.
    pub fn decode(
        &self,
        function_name: &str,
        hash: &[u8],
    ) -> Result<Vec<Parameter>, AbiParserError> {
        if let Some(function) = self.functions.get(function_name) {
            let mut start_index = 0;
            let mut parameters = Vec::<Parameter>::with_capacity(function.outputs.len());
            for output in &function.outputs {
                let (parameter, i) =
                    Parameter::decode(&output.parameter_type, &hash[start_index..]);
                start_index += i;
                parameters.push(parameter);
            }

            Ok(parameters)
        } else {
            Err(AbiParserError::MissingData(
                "Function name not found in ABI".to_owned(),
            ))
        }
    }
}

#[derive(Error, Debug)]
pub enum AbiParserError {
    #[error("Couldn't open ABI file: {0}")]
    FileIoError(#[from] std::io::Error),
    #[error("De-/Serialization error: {0}")]
    Serde(#[from] serde_json::Error),
    #[error("Missing data error: {0}")]
    MissingData(String),
    #[error("Invalid ABI encoding error: {0}")]
    InvalidAbiEncoding(String),
    #[error("Parameter type doesn't match the internal data type")]
    TypeError,
}