Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add voyager verification interface #1

Open
wants to merge 2 commits into
base: contract_verify
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
scarb 2.5.4
scarb 2.6.5
98 changes: 59 additions & 39 deletions crates/sncast/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,19 +155,31 @@ fn main() -> Result<()> {

let runtime = Runtime::new().expect("Failed to instantiate Runtime");

if let Commands::Script(script) = &cli.command {
run_script_command(&cli, runtime, script, numbers_format, &output_format)
} else {
let mut config = load_global_config::<CastConfig>(&None, &cli.profile)?;
update_cast_config(&mut config, &cli);
let provider = get_provider(&config.url)?;
runtime.block_on(run_async_command(
cli,
config,
provider,
numbers_format,
output_format,
))
match &cli.command {
Commands::Script(script) => {
run_script_command(&cli, runtime, script, numbers_format, &output_format)
}
Commands::Verify(verify) => {
// run_verify_command(&cli, runtime, verify, numbers_format, &output_format)
runtime.block_on(run_verify_command(
&cli,
verify,
numbers_format,
&output_format,
))
}
_ => {
let mut config = load_global_config::<CastConfig>(&None, &cli.profile)?;
update_cast_config(&mut config, &cli);
let provider = get_provider(&config.url)?;
runtime.block_on(run_async_command(
cli,
config,
provider,
numbers_format,
output_format,
))
}
}
}

Expand Down Expand Up @@ -430,32 +442,7 @@ async fn run_async_command(
print_command_result("tx-status", &mut result, numbers_format, &output_format)?;
Ok(())
}
Commands::Verify(verify) => {
let manifest_path = assert_manifest_path_exists()?;
let package_metadata = get_package_metadata(&manifest_path, &verify.package)?;
let artifacts = build_and_load_artifacts(
&package_metadata,
&BuildConfig {
scarb_toml_path: manifest_path.clone(),
json: cli.json,
profile: cli.profile.unwrap_or("dev".to_string()),
},
)
.expect("Failed to build contract");
let mut result = starknet_commands::verify::verify(
verify.contract_address,
verify.contract_name,
verify.verifier,
verify.network,
verify.confirm_verification,
&package_metadata.manifest_path,
&artifacts,
)
.await;

print_command_result("verify", &mut result, numbers_format, &output_format)?;
Ok(())
}
Commands::Verify(_) => unreachable!(),
Commands::Script(_) => unreachable!(),
}
}
Expand Down Expand Up @@ -536,6 +523,39 @@ fn run_script_command(
Ok(())
}

async fn run_verify_command(
cli: &Cli,
verify: &Verify,
numbers_format: NumbersFormat,
output_format: &OutputFormat,
) -> Result<()> {
let manifest_path = assert_manifest_path_exists()?;
let package_metadata = get_package_metadata(&manifest_path, &verify.package)?;

let artifacts = build_and_load_artifacts(
&package_metadata,
&BuildConfig {
scarb_toml_path: manifest_path.clone(),
json: cli.json,
profile: cli.profile.clone().unwrap_or("dev".to_string()),
},
)
.expect("Failed to build contract");
let mut result = starknet_commands::verify::verify(
verify.contract_address,
verify.contract_name.clone(),
verify.verifier.clone(),
verify.network.clone(),
verify.confirm_verification,
&package_metadata.manifest_path,
&artifacts,
)
.await;

print_command_result("verify", &mut result, numbers_format, &output_format)?;
Ok(())
}

fn update_cast_config(config: &mut CastConfig, cli: &Cli) {
macro_rules! clone_or_else {
($field:expr, $config_field:expr) => {
Expand Down
102 changes: 102 additions & 0 deletions crates/sncast/src/starknet_commands/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ use std::ffi::OsStr;
use std::{env, fmt};
use walkdir::WalkDir;

struct VoyagerVerificationInterface {
network: Network,
workspace_dir: Utf8PathBuf,
}

struct WalnutVerificationInterface {
network: Network,
workspace_dir: Utf8PathBuf,
Expand All @@ -30,6 +35,92 @@ trait VerificationInterface {
fn gen_explorer_url(&self) -> Result<String>;
}

#[async_trait::async_trait]
impl VerificationInterface for VoyagerVerificationInterface {
fn new(network: Network, workspace_dir: Utf8PathBuf) -> Self {
VoyagerVerificationInterface {
network,
workspace_dir,
}
}

async fn verify(
&self,
contract_address: FieldElement,
contract_name: String,
) -> Result<VerifyResponse> {
// Read all files name along with their contents in a JSON format
// in the workspace dir recursively
// key is the file name and value is the file content
let mut file_data = serde_json::Map::new();

// Recursively read files and their contents in workspace directory
for entry in WalkDir::new(self.workspace_dir.clone()).follow_links(true) {
let entry = entry?;
let path = entry.path();
if path.is_file() {
if let Some(extension) = path.extension() {
if extension == OsStr::new("cairo") || extension == OsStr::new("toml") {
let relative_path = path.strip_prefix(self.workspace_dir.clone())?;
let file_content = std::fs::read_to_string(path)?;
file_data.insert(
relative_path.to_string_lossy().into_owned(),
serde_json::Value::String(file_content),
);
}
}
}
}

// Serialize the JSON object to a JSON string
let source_code = serde_json::Value::Object(file_data);

// Create the JSON payload with "contract name," "address," and "source_code" fields
let payload = VerificationPayload {
contract_name: contract_name.to_string(),
contract_address: contract_address.to_string(),
source_code,
};

// Serialize the payload to a JSON string for the POST request
let json_payload = serde_json::to_string(&payload)?;

// Send the POST request to the explorer
let client = reqwest::Client::new();
let api_res = client
.post(self.gen_explorer_url()?)
.header("Content-Type", "application/json")
.body(json_payload)
.send()
.await
.context("Failed to send request to verifier API")?;

match api_res.status() {
StatusCode::OK => {
let message = api_res
.text()
.await
.context("Failed to read verifier API response")?;
Ok(VerifyResponse { message })
}
_ => {
let message = api_res.text().await.context("Failed to verify contract")?;
Err(anyhow!(message))
}
}
}

fn gen_explorer_url(&self) -> Result<String> {
let api_base_url = env::var("VOYAGER_API_URL")
.unwrap_or_else(|_| "https://api.voyager.online/beta".to_string());
let path = match self.network {
Network::Mainnet => "/v1/sn_main/verify",
Network::Sepolia => "/v1/sn_sepolia/verify",
};
Ok(format!("{}{}", api_base_url, path))
}
}

#[async_trait::async_trait]
impl VerificationInterface for WalnutVerificationInterface {
fn new(network: Network, workspace_dir: Utf8PathBuf) -> Self {
Expand All @@ -44,11 +135,16 @@ impl VerificationInterface for WalnutVerificationInterface {
contract_address: FieldElement,
contract_name: String,
) -> Result<VerifyResponse> {

println!("Verifying contract {contract_name} at address {contract_address}");

// Read all files name along with their contents in a JSON format
// in the workspace dir recursively
// key is the file name and value is the file content
let mut file_data = serde_json::Map::new();

println!("directory: {0}", self.workspace_dir);

// Recursively read files and their contents in workspace directory
for entry in WalkDir::new(self.workspace_dir.clone()).follow_links(true) {
let entry = entry?;
Expand Down Expand Up @@ -144,12 +240,14 @@ pub struct Verify {
#[derive(ValueEnum, Clone, Debug)]
pub enum Verifier {
Walnut,
Voyager
}

impl fmt::Display for Verifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Verifier::Walnut => write!(f, "walnut"),
Verifier::Voyager => write!(f, "voyager"),
}
}
}
Expand Down Expand Up @@ -196,5 +294,9 @@ pub async fn verify(
let walnut = WalnutVerificationInterface::new(network, workspace_dir.to_path_buf());
walnut.verify(contract_address, contract_name).await
}
Verifier::Voyager => {
let voyager = VoyagerVerificationInterface::new(network, workspace_dir.to_path_buf());
voyager.verify(contract_address, contract_name).await
}
}
}
Loading
Loading