Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ once_cell = "1.21"
num = "0.4.3"
sha1 = "0.10"
sha3 = "0.10"
sha2 = "0.10"
thiserror = "2.0"
which = "8.0"
path-slash = "0.2"
Expand Down
4 changes: 2 additions & 2 deletions crates/llvm-context/src/polkavm/context/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct Build {
/// The PolkaVM text assembly.
pub assembly_text: Option<String>,
/// The metadata hash.
pub metadata_hash: Option<[u8; BYTE_LENGTH_WORD]>,
pub metadata_hash: Option<Vec<u8>>,
/// The PolkaVM binary bytecode.
pub bytecode: Vec<u8>,
/// The PolkaVM bytecode hash. Unlinked builds don't have a hash yet.
Expand All @@ -19,7 +19,7 @@ pub struct Build {

impl Build {
/// A shortcut constructor.
pub fn new(metadata_hash: Option<[u8; BYTE_LENGTH_WORD]>, bytecode: Vec<u8>) -> Self {
pub fn new(metadata_hash: Option<Vec<u8>>, bytecode: Vec<u8>) -> Self {
Self {
assembly_text: None,
metadata_hash,
Expand Down
9 changes: 2 additions & 7 deletions crates/llvm-context/src/polkavm/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl<'ctx> Context<'ctx> {
pub fn build(
self,
contract_path: &str,
metadata_hash: Option<revive_common::Keccak256>,
metadata_hash: Option<Vec<u8>>,
) -> anyhow::Result<Build> {
self.link_polkavm_exports(contract_path)?;
self.link_immutable_data(contract_path)?;
Expand Down Expand Up @@ -325,12 +325,7 @@ impl<'ctx> Context<'ctx> {

self.debug_config.dump_object(contract_path, &object)?;

crate::polkavm::build(
&object,
metadata_hash
.as_ref()
.map(|hash| hash.as_bytes().try_into().unwrap()),
)
crate::polkavm::build(&object, metadata_hash)
}

/// Verifies the current LLVM IR module.
Expand Down
5 changes: 1 addition & 4 deletions crates/llvm-context/src/polkavm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ pub mod context;
pub mod evm;

/// Get a [Build] from contract bytecode and its auxilliary data.
pub fn build(
bytecode: &[u8],
metadata_hash: Option<[u8; BYTE_LENGTH_WORD]>,
) -> anyhow::Result<Build> {
pub fn build(bytecode: &[u8], metadata_hash: Option<Vec<u8>>) -> anyhow::Result<Build> {
Ok(Build::new(metadata_hash, bytecode.to_owned()))
}

Expand Down
1 change: 1 addition & 0 deletions crates/resolc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
which = { workspace = true }
normpath = { workspace = true }
sha2 = { workspace = true }

revive-common = { workspace = true }
revive-llvm-context = { workspace = true }
Expand Down
15 changes: 13 additions & 2 deletions crates/resolc/src/project/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use revive_llvm_context::PolkaVMContextYulData;
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
use serde::Deserialize;
use serde::Serialize;
use sha2::Digest;

use revive_llvm_context::PolkaVMWriteLLVM;

Expand Down Expand Up @@ -84,8 +85,18 @@ impl Contract {
let metadata_json = serde_json::to_value(&metadata).expect("Always valid");
let metadata_json_bytes = serde_json::to_vec(&metadata_json).expect("Always valid");
let metadata_bytes = match metadata_hash {
MetadataHash::Keccak256 => Keccak256::from_slice(&metadata_json_bytes).into(),
MetadataHash::IPFS => todo!("IPFS hash isn't supported yet"),
MetadataHash::Keccak256 => {
let digest = Keccak256::from_slice(&metadata_json_bytes);
Some(digest.to_vec())
}
MetadataHash::IPFS => {
// IPFS multihash: 0x12 (sha2-256) 0x20 (32 bytes) + sha2-256 digest
let digest = sha2::Sha256::digest(&metadata_json_bytes);
let mut mh = Vec::with_capacity(34);
mh.extend_from_slice(&[0x12, 0x20]);
mh.extend_from_slice(&digest);
Some(mh)
}
MetadataHash::None => None,
};
debug_config.set_contract_path(&self.identifier.full_path);
Expand Down
9 changes: 0 additions & 9 deletions crates/resolc/src/resolc/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use std::path::PathBuf;

use clap::Parser;
use path_slash::PathExt;
use revive_common::MetadataHash;
use revive_solc_json_interface::SolcStandardJsonOutputError;

/// Compiles the provided Solidity input files (or use the standard input if no files
Expand Down Expand Up @@ -226,14 +225,6 @@ impl Arguments {
));
}

if self.metadata_hash == Some(MetadataHash::IPFS.to_string()) {
messages.push(SolcStandardJsonOutputError::new_error(
"`IPFS` metadata hash type is not supported. Please use `keccak256` instead.",
None,
None,
));
}

let modes = [
self.yul,
self.combined_json.is_some(),
Expand Down
68 changes: 68 additions & 0 deletions crates/resolc/src/tests/unit/ipfs_metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::path::PathBuf;

use revive_common::MetadataHash;
use revive_llvm_context::{initialize_llvm, DebugConfig, OptimizerSettings, PolkaVMTarget};
use revive_solc_json_interface::SolcStandardJsonInputSettingsLibraries;

use crate::process::native_process::EXECUTABLE;
use crate::project::Project;
use crate::DEFAULT_EXECUTABLE_NAME;

#[test]
fn compiles_with_ipfs_metadata_hash_and_emits_multihash() {
let debug = DebugConfig::new(None, true);

let resolc_path = std::env::var("CARGO_BIN_EXE_resolc")
.ok()
.map(PathBuf::from)
.unwrap_or_else(|| PathBuf::from(DEFAULT_EXECUTABLE_NAME));
let _ = EXECUTABLE.set(resolc_path);

initialize_llvm(PolkaVMTarget::PVM, DEFAULT_EXECUTABLE_NAME, &[]);
let project = Project::try_from_yul_paths(
&[PathBuf::from("src/tests/data/yul/Test.yul")],
None,
SolcStandardJsonInputSettingsLibraries::default(),
&debug,
)
.expect("project from yul");

let mut messages = Vec::new();
let build = project
.compile(
&mut messages,
OptimizerSettings::none(),
MetadataHash::IPFS,
&debug,
&[],
Default::default(),
)
.expect("compile should succeed");

assert!(
messages.is_empty(),
"No errors expected, got: {:?}",
messages
);

let (.., result) = build
.results
.into_iter()
.next()
.expect("one contract result");
let contract = result.expect("contract built successfully");

let bytes = contract
.build
.metadata_hash
.as_ref()
.expect("metadata hash should be present");
let slice: &[u8] = &bytes[..];

assert_eq!(slice.len(), 34, "multihash length must be 34 bytes");
assert_eq!(
&slice[0..2],
&[0x12, 0x20],
"multihash prefix must be sha2-256 (0x12 0x20)"
);
}
1 change: 1 addition & 0 deletions crates/resolc/src/tests/unit/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! The Solidity compiler unit tests.

mod factory_dependency;
mod ipfs_metadata;
mod ir_artifacts;
mod libraries;
mod messages;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,3 @@
//! The metadata hash mode.

use std::str::FromStr;

use serde::Deserialize;
use serde::Serialize;

/// The metadata hash mode.
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MetadataHash {
/// Do not include bytecode hash.
#[serde(rename = "none")]
None,
/// The default keccak256 hash.
#[serde(rename = "keccak256")]
Keccak256,
}

impl FromStr for MetadataHash {
type Err = anyhow::Error;

fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
"none" => Ok(Self::None),
"keccak256" => Ok(Self::Keccak256),
_ => anyhow::bail!("Unknown bytecode hash mode: `{}`", string),
}
}
}
pub use revive_common::MetadataHash;
8 changes: 8 additions & 0 deletions crates/solc-json-interface/tests/metadata_ipfs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use revive_solc_json_interface::SolcStandardJsonInputSettingsMetadataHash;

#[test]
fn accepts_ipfs_metadata_hash_in_standard_json() {
let parsed: SolcStandardJsonInputSettingsMetadataHash =
serde_json::from_str("\"ipfs\"").expect("should deserialize 'ipfs'");
assert_eq!(parsed, SolcStandardJsonInputSettingsMetadataHash::IPFS);
}
5 changes: 0 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.