Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lok52 committed Nov 10, 2023
1 parent 8cf11b1 commit 80031ff
Show file tree
Hide file tree
Showing 11 changed files with 492 additions and 92 deletions.
376 changes: 299 additions & 77 deletions smart-contract-verifier/Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ blockscout-service-launcher = { version = "0.9.0" }
bytes = "1.3"
config = "0.13"
cron = "0.11"
ethers-solc = "2.0.6"
ethers-solc = "2.0.10"
ethers-core = "2.0.10"
futures = "0.3"
lazy_static = "1"
Expand All @@ -40,6 +40,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
url = "2.3"

[dev-dependencies]
ethers-solc = { version = "2.0.10", features = ["svm-solc"] }
blockscout-service-launcher = { version = "0.9.0" , features = ["test-server"]}
ethabi = "18.0"
pretty_assertions = "1.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use anyhow::anyhow;
use blockscout_service_launcher::launcher::ConfigSettings;
use blockscout_service_launcher::{
launcher::{MetricsSettings, ServerSettings},
launcher::{ConfigSettings, MetricsSettings, ServerSettings},
tracing::{JaegerSettings, TracingSettings},
};
use cron::Schedule;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use blockscout_service_launcher::{
launcher::ConfigSettings,
test_server::{get_test_server_settings, init_server, send_post_request},
};
use ethers_solc::{CompilerInput, CompilerOutput, EvmVersion, Solc};
use rstest::rstest;
use serde::Deserialize;
use smart_contract_verifier_proto::blockscout::smart_contract_verifier::v2::{
LookupMethodsRequest, LookupMethodsResponse,
};
use smart_contract_verifier_server::Settings;
use std::{
collections::{BTreeMap, BTreeSet},
path::PathBuf,
};

const ROUTE: &str = "/api/v2/verifier/solidity/methods:lookup";

fn process_compiler_output(
output: &CompilerOutput,
contract_name: &str,
) -> anyhow::Result<(LookupMethodsRequest, BTreeMap<String, String>)> {
let (_, contract) = output
.contracts_iter()
.find(|(name, _)| *name == contract_name)
.ok_or_else(|| anyhow::anyhow!("contract not found"))?;
let evm = contract.evm.as_ref().expect("evm included");
let deployed_bytecode = evm
.deployed_bytecode
.as_ref()
.expect("deployed bytecode included")
.bytecode
.as_ref()
.expect("bytecode included");
let methods = evm.method_identifiers.clone();

let bytecode = deployed_bytecode
.object
.clone()
.into_bytes()
.unwrap()
.to_string();
let abi = serde_json::to_string(&contract.abi.clone().expect("abi included"))?;
let source_map = deployed_bytecode
.source_map
.as_ref()
.expect("srcmap included")
.clone();
let file_ids = output
.sources
.iter()
.map(|(name, file)| (file.id, name.clone()))
.collect();

let request = LookupMethodsRequest {
abi,
bytecode,
file_ids,
source_map,
};
Ok((request, methods))
}

#[derive(Deserialize)]
struct TestCase {
version: String,
contract_name: String,
}

#[rstest]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_lookup_methods(#[files("tests/test_cases_lookup_methods/*")] test_dir: PathBuf) {
let mut settings = Settings::build().expect("Failed to build settings");
let (server_settings, base) = get_test_server_settings();
settings.server = server_settings;
settings.vyper.enabled = false;
settings.solidity.enabled = true;
settings.sourcify.enabled = false;
settings.jaeger.enabled = false;
settings.tracing.enabled = false;

init_server(|| smart_contract_verifier_server::run(settings), &base).await;

let test_case: TestCase = serde_json::from_str(
std::fs::read_to_string(test_dir.join("config.json"))
.unwrap()
.as_str(),
)
.expect("Failed to parse test case");

let solc =
Solc::find_or_install_svm_version(test_case.version).expect("failed to install version");

let inputs = CompilerInput::new(test_dir).expect("failed to read dir");
let input = inputs[0].clone().evm_version(EvmVersion::London);
let output = solc.compile(&input).expect("failed to compile");

let (request, methods) = process_compiler_output(&output, &test_case.contract_name).unwrap();
let res: LookupMethodsResponse = send_post_request(&base, ROUTE, &request).await;

// Make sure we extracted all methods
assert_eq!(
methods.values().collect::<BTreeSet<&String>>(),
res.methods.keys().collect::<BTreeSet<&String>>()
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.8.0;

abstract contract AbstractContract {
// Declaring functions
function getStr(
string memory _strIn) public view virtual returns(
string memory);
function setValue(uint _in1, uint _in2) public virtual;
function add() public virtual returns(uint);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
pragma solidity ^0.8.0;

import "./AbstractContract.sol";

contract DerivedContract is AbstractContract{
uint private num1;
uint private num2;

function getStr(string memory _strIn) public pure override returns(string memory) {
return _strIn;
}

function setValue(uint _in1, uint _in2) public override {
num1 = _in1;
num2 = _in2;
}

function add() public view override returns(uint) {
return (num2 + num1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"version": "0.8.17",
"contract_name": "DerivedContract"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

contract Greeter {
string private greeting;

constructor(string memory _greeting) {
greeting = _greeting;
}

function greet() public view returns (string memory) {
return greeting;
}

function setGreeting(string memory _greeting) public {
greeting = _greeting;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"version": "0.8.17",
"contract_name": "Greeter"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::opcodes::{opcode, Opcode};
use bytes::Bytes;
use std::fmt::{Debug, Display};
use std::fmt::Debug;

pub struct DisassembledOpcode {
pub operation: Opcode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,6 @@ pub fn find_methods(request: LookupMethodsRequest) -> LookupMethodsResponse {
LookupMethodsResponse { methods }
}

fn prepend_selector(partial_selector: &Vec<u8>) -> anyhow::Result<Vec<u8>> {
if partial_selector.len() > 4 {
return Err(anyhow::anyhow!("selector is too long"));
};

// prepend selector with 0s if it's shorter than 4 bytes
let mut selector = partial_selector.clone();
selector.splice(..0, repeat(0).take(4 - partial_selector.len()));
Ok(selector)
}

fn find_src_map_index(selector: &[u8; 4], opcodes: &[DisassembledOpcode]) -> Option<usize> {
for window in opcodes.windows(5) {
if window[0].operation.name.starts_with("PUSH")
Expand Down Expand Up @@ -122,3 +111,29 @@ fn parse_selectors(abi: Abi) -> BTreeMap<String, [u8; 4]> {
.map(|f| (f.signature(), f.short_signature()))
.collect()
}

fn prepend_selector(partial_selector: &Vec<u8>) -> anyhow::Result<Vec<u8>> {
if partial_selector.len() > 4 {
return Err(anyhow::anyhow!("selector is too long"));
};

// prepend selector with 0s if it's shorter than 4 bytes
let mut selector = partial_selector.clone();
selector.splice(..0, repeat(0).take(4 - partial_selector.len()));
Ok(selector)
}

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

#[test]
fn test_prepend_selector() {
assert_eq!(
prepend_selector(&vec![1, 2, 3, 4]).unwrap(),
vec![1, 2, 3, 4]
);
assert_eq!(prepend_selector(&vec![1, 2]).unwrap(), vec![0, 0, 1, 2]);
assert!(prepend_selector(&vec![1, 2, 3, 4, 5]).is_err());
}
}

0 comments on commit 80031ff

Please sign in to comment.