fix(solc): consistent serde for linked and unlinked bytecode (#948)

This commit is contained in:
Matthias Seitz 2022-02-22 15:23:23 +01:00 committed by GitHub
parent 1f822e47e6
commit 60515d9404
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 5 deletions

View File

@ -1592,6 +1592,7 @@ pub enum BytecodeObject {
#[serde(deserialize_with = "serde_helpers::deserialize_bytes")] #[serde(deserialize_with = "serde_helpers::deserialize_bytes")]
Bytecode(Bytes), Bytecode(Bytes),
/// Bytecode as hex string that's not fully linked yet and contains library placeholders /// Bytecode as hex string that's not fully linked yet and contains library placeholders
#[serde(with = "serde_helpers::string_bytes")]
Unlinked(String), Unlinked(String),
} }
@ -1612,6 +1613,13 @@ impl BytecodeObject {
BytecodeObject::Unlinked(_) => None, BytecodeObject::Unlinked(_) => None,
} }
} }
/// Returns a reference to the underlying `String` if the object is unlinked
pub fn as_str(&self) -> Option<&str> {
match self {
BytecodeObject::Bytecode(_) => None,
BytecodeObject::Unlinked(s) => Some(s.as_str()),
}
}
/// Returns the unlinked `String` if the object is unlinked or empty /// Returns the unlinked `String` if the object is unlinked or empty
pub fn into_unlinked(self) -> Option<String> { pub fn into_unlinked(self) -> Option<String> {
@ -1715,10 +1723,10 @@ impl BytecodeObject {
} }
} }
// Returns a not deployable bytecode by default as "0x" // Returns a not deployable bytecode by default as empty
impl Default for BytecodeObject { impl Default for BytecodeObject {
fn default() -> Self { fn default() -> Self {
BytecodeObject::Unlinked("0x".to_string()) BytecodeObject::Unlinked("".to_string())
} }
} }

View File

@ -77,6 +77,34 @@ pub mod json_string_opt {
} }
} }
/// serde support for string
pub mod string_bytes {
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(value: &String, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if value.starts_with("0x") {
serializer.serialize_str(value.as_str())
} else {
serializer.serialize_str(&format!("0x{}", value))
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
if let Some(rem) = value.strip_prefix("0x") {
Ok(rem.to_string())
} else {
Ok(value)
}
}
}
pub mod display_from_str_opt { pub mod display_from_str_opt {
use serde::{de, Deserialize, Deserializer, Serializer}; use serde::{de, Deserialize, Deserializer, Serializer};
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};

View File

@ -496,7 +496,7 @@ fn can_detect_type_error() {
fn can_compile_single_files() { fn can_compile_single_files() {
let tmp = TempProject::dapptools().unwrap(); let tmp = TempProject::dapptools().unwrap();
let foo = tmp let f = tmp
.add_contract( .add_contract(
"examples/Foo", "examples/Foo",
r#" r#"
@ -507,7 +507,7 @@ fn can_compile_single_files() {
) )
.unwrap(); .unwrap();
let compiled = tmp.project().compile_file(foo.clone()).unwrap(); let compiled = tmp.project().compile_file(f.clone()).unwrap();
assert!(!compiled.has_compiler_errors()); assert!(!compiled.has_compiler_errors());
assert!(compiled.find("Foo").is_some()); assert!(compiled.find("Foo").is_some());
@ -522,8 +522,43 @@ fn can_compile_single_files() {
) )
.unwrap(); .unwrap();
let compiled = tmp.project().compile_files(vec![foo, bar]).unwrap(); let compiled = tmp.project().compile_files(vec![f, bar]).unwrap();
assert!(!compiled.has_compiler_errors()); assert!(!compiled.has_compiler_errors());
assert!(compiled.find("Foo").is_some()); assert!(compiled.find("Foo").is_some());
assert!(compiled.find("Bar").is_some()); assert!(compiled.find("Bar").is_some());
} }
#[test]
fn consistent_bytecode() {
let tmp = TempProject::dapptools().unwrap();
tmp.add_source(
"LinkTest",
r#"
// SPDX-License-Identifier: MIT
library LibTest {
function foobar(uint256 a) public view returns (uint256) {
return a * 100;
}
}
contract LinkTest {
function foo() public returns (uint256) {
return LibTest.foobar(1);
}
}
"#,
)
.unwrap();
let compiled = tmp.compile().unwrap();
assert!(!compiled.has_compiler_errors());
let contract = compiled.find("LinkTest").unwrap();
let bytecode = &contract.bytecode.as_ref().unwrap().object;
assert!(bytecode.is_unlinked());
let s = bytecode.as_str().unwrap();
assert!(!s.starts_with("0x"));
let s = serde_json::to_string(&bytecode).unwrap();
assert_eq!(bytecode.clone(), serde_json::from_str(&s).unwrap());
}