feat: expand solc capabilities / chore: update ethabi (#445)

* chore: use ethabi with bumped ethereum-types

* fix: do not use internal type in human readable abi

* fix: do not use internal type in abigen

* feat(solc): save the runtime bytecode

* feat: implement serde for CompiledContract

* feat: allow overriding solc binary path

* feat: expose providing raw file paths

* feat: do not set evm versions on old solc

* chore: use upstream ethabi
This commit is contained in:
Georgios Konstantopoulos 2021-09-13 15:35:50 +03:00 committed by GitHub
parent 90df511704
commit 77bc5aa30f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 20 deletions

11
Cargo.lock generated
View File

@ -805,8 +805,7 @@ dependencies = [
[[package]] [[package]]
name = "ethabi" name = "ethabi"
version = "14.1.0" version = "14.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/rust-ethereum/ethabi/?branch=master#506f6cc364cdb458ac09f2be4f300779da256b08"
checksum = "a01317735d563b3bad2d5f90d2e1799f414165408251abb762510f40e790e69a"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ethereum-types", "ethereum-types",
@ -833,9 +832,9 @@ dependencies = [
[[package]] [[package]]
name = "ethereum-types" name = "ethereum-types"
version = "0.11.0" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f64b5df66a228d85e4b17e5d6c6aa43b0310898ffe8a85988c4c032357aaabfd" checksum = "0dd6bde671199089e601e8d47e153368b893ef885f11f365a3261ec58153c211"
dependencies = [ dependencies = [
"ethbloom", "ethbloom",
"fixed-hash", "fixed-hash",
@ -1946,9 +1945,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]] [[package]]
name = "primitive-types" name = "primitive-types"
version = "0.9.1" version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06345ee39fbccfb06ab45f3a1a5798d9dafa04cb8921a76d227040003a234b0e" checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373"
dependencies = [ dependencies = [
"fixed-hash", "fixed-hash",
"impl-codec", "impl-codec",

View File

@ -86,3 +86,4 @@ rand = "0.8.4"
serde = { version = "1.0.124", features = ["derive"] } serde = { version = "1.0.124", features = ["derive"] }
serde_json = "1.0.64" serde_json = "1.0.64"
tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] }

View File

@ -169,6 +169,7 @@ impl Parse for Method {
Ok(Param { Ok(Param {
name: "".into(), name: "".into(),
kind, kind,
internal_type: None,
}) })
}) })
.collect::<ParseResult<Vec<_>>>()?; .collect::<ParseResult<Vec<_>>>()?;

View File

@ -11,7 +11,8 @@ keywords = ["ethereum", "web3", "celo", "ethers"]
[dependencies] [dependencies]
rlp = { version = "0.5.0", default-features = false } rlp = { version = "0.5.0", default-features = false }
ethabi = { version = "14.1.0", default-features = false } # ethabi = { version = "14.1.0", default-features = false }
ethabi = { git = "https://github.com/rust-ethereum/ethabi/", branch = "master" }
arrayvec = { version = "0.7.1", default-features = false } arrayvec = { version = "0.7.1", default-features = false }
rlp-derive = { version = "0.1.0", default-features = false } rlp-derive = { version = "0.1.0", default-features = false }

View File

@ -364,6 +364,7 @@ impl AbiParser {
Ok(Param { Ok(Param {
name: name.to_string(), name: name.to_string(),
kind: self.parse_type(type_str)?, kind: self.parse_type(type_str)?,
internal_type: None,
}) })
} }
} }

View File

@ -21,13 +21,15 @@ pub enum SolcError {
SerdeJson(#[from] serde_json::Error), SerdeJson(#[from] serde_json::Error),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
/// The result of a solc compilation /// The result of a solc compilation
pub struct CompiledContract { pub struct CompiledContract {
/// The contract's ABI /// The contract's ABI
pub abi: Abi, pub abi: Abi,
/// The contract's bytecode /// The contract's bytecode
pub bytecode: Bytes, pub bytecode: Bytes,
/// The contract's runtime bytecode
pub runtime_bytecode: Bytes,
} }
/// Solidity Compiler Bindings /// Solidity Compiler Bindings
@ -55,6 +57,9 @@ pub struct CompiledContract {
/// # } /// # }
/// ``` /// ```
pub struct Solc { pub struct Solc {
/// The path to the Solc binary
pub solc_path: Option<PathBuf>,
/// The path where contracts will be read from /// The path where contracts will be read from
pub paths: Vec<String>, pub paths: Vec<String>,
@ -72,7 +77,7 @@ pub struct Solc {
} }
impl Solc { impl Solc {
/// Instantiates the Solc builder for the provided paths /// Instantiates The Solc builder with the provided glob of Solidity files
pub fn new(path: &str) -> Self { pub fn new(path: &str) -> Self {
// Convert the glob to a vector of string paths // Convert the glob to a vector of string paths
// TODO: This might not be the most robust way to do this // TODO: This might not be the most robust way to do this
@ -80,9 +85,14 @@ impl Solc {
.expect("could not get glob") .expect("could not get glob")
.map(|path| path.expect("path not found").to_string_lossy().to_string()) .map(|path| path.expect("path not found").to_string_lossy().to_string())
.collect::<Vec<String>>(); .collect::<Vec<String>>();
Self::new_with_paths(paths)
}
/// Instantiates the Solc builder for the provided paths
pub fn new_with_paths(paths: Vec<String>) -> Self {
Self { Self {
paths, paths,
solc_path: None,
optimizer: Some(200), // default optimizer runs = 200 optimizer: Some(200), // default optimizer runs = 200
evm_version: EvmVersion::Istanbul, evm_version: EvmVersion::Istanbul,
allowed_paths: Vec::new(), allowed_paths: Vec::new(),
@ -92,13 +102,19 @@ impl Solc {
/// Gets the ABI for the contracts /// Gets the ABI for the contracts
pub fn build_raw(self) -> Result<HashMap<String, CompiledContractStr>> { pub fn build_raw(self) -> Result<HashMap<String, CompiledContractStr>> {
let mut command = Command::new(SOLC); let path = self.solc_path.unwrap_or_else(|| PathBuf::from(SOLC));
let mut command = Command::new(&path);
let version = Solc::version(Some(path));
command command.arg("--combined-json").arg("abi,bin,bin-runtime");
.arg("--evm-version")
.arg(self.evm_version.to_string()) if (version.starts_with("0.5") && self.evm_version < EvmVersion::Istanbul)
.arg("--combined-json") || !version.starts_with("0.4")
.arg("abi,bin"); {
command
.arg("--evm-version")
.arg(self.evm_version.to_string());
}
if let Some(runs) = self.optimizer { if let Some(runs) = self.optimizer {
command command
@ -148,7 +164,21 @@ impl Solc {
))) )))
} }
}; };
contracts.insert(name, CompiledContractStr { abi, bin });
let runtime_bin =
if let serde_json::Value::String(bin) = contract["bin-runtime"].take() {
bin
} else {
panic!("no runtime bytecode found")
};
contracts.insert(
name,
CompiledContractStr {
abi,
bin,
runtime_bin,
},
);
} else { } else {
return Err(SolcError::SolcError( return Err(SolcError::SolcError(
"could not find `bin` in solc output".to_string(), "could not find `bin` in solc output".to_string(),
@ -174,7 +204,19 @@ impl Solc {
let bytecode = hex::decode(contract.bin) let bytecode = hex::decode(contract.bin)
.expect("solc did not produce valid bytecode") .expect("solc did not produce valid bytecode")
.into(); .into();
(name, CompiledContract { abi, bytecode })
// parse the runtime bytecode
let runtime_bytecode = hex::decode(contract.runtime_bin)
.expect("solc did not produce valid runtime-bytecode")
.into();
(
name,
CompiledContract {
abi,
bytecode,
runtime_bytecode,
},
)
}) })
.collect::<HashMap<String, CompiledContract>>(); .collect::<HashMap<String, CompiledContract>>();
@ -186,8 +228,8 @@ impl Solc {
/// # Panics /// # Panics
/// ///
/// If `solc` is not in the user's $PATH /// If `solc` is not in the user's $PATH
pub fn version() -> String { pub fn version(solc_path: Option<PathBuf>) -> String {
let command_output = Command::new(SOLC) let command_output = Command::new(solc_path.unwrap_or_else(|| PathBuf::from(SOLC)))
.arg("--version") .arg("--version")
.output() .output()
.unwrap_or_else(|_| panic!("`{}` not in user's $PATH", SOLC)); .unwrap_or_else(|_| panic!("`{}` not in user's $PATH", SOLC));
@ -209,6 +251,12 @@ impl Solc {
self self
} }
/// Sets the path to the solc binary
pub fn solc_path(mut self, path: PathBuf) -> Self {
self.solc_path = Some(std::fs::canonicalize(path).unwrap());
self
}
/// Sets the optimizer runs (default = 200). None indicates no optimization /// Sets the optimizer runs (default = 200). None indicates no optimization
/// ///
/// ```rust,no_run /// ```rust,no_run
@ -257,7 +305,7 @@ impl Solc {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum EvmVersion { pub enum EvmVersion {
Homestead, Homestead,
TangerineWhistle, TangerineWhistle,
@ -297,4 +345,6 @@ pub struct CompiledContractStr {
pub abi: String, pub abi: String,
/// The contract's bytecode in hex /// The contract's bytecode in hex
pub bin: String, pub bin: String,
/// The contract's runtime bytecode in hex
pub runtime_bin: String,
} }