Skip to content

Commit

Permalink
Merge pull request #17 from Ellipsis-Labs/jxiao/cleanup-and-unify-bas…
Browse files Browse the repository at this point in the history
…e-image

update to sha256 and print out hash after build
  • Loading branch information
jarry-xiao authored Jan 31, 2023
2 parents cd3a68c + 1087499 commit be2125a
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 39 deletions.
83 changes: 67 additions & 16 deletions Cargo.lock

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

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ anyhow = "1.0.68"
cmd_lib = "1.3.0"
solana-client = "1.14.10"
solana-sdk = "1.14.10"
sha1 = "0.10.5"
hex = "0.3.1"
sha256 = "1.1.1"
hex = "0.3.1"
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0.68"
toml = "0.7.1"
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ verifier-cli get-program-hash -p 2ZrriTQSVekoj414Ynysd48jyn4AX6ZF4TTJRqHfbJfn
Which will return the following hash:

```
627a5b29a06179d08ac5eab2c085703e59decbe6
08d91368d349c2b56c712422f6d274a1e8f1946ff2ecd1dc3efc3ebace52a760
```

By default, this command will strip any trailing zeros away from the program executable file and run the sha1 algorithm against it to compute the hash.


To manually verify this build, one could run the following from the root of this repository. _This command takes a long time because it is building the binary in a Docker container_

```
Expand All @@ -75,6 +74,6 @@ This will return the hash of the stripped executable

```
627a5b29a06179d08ac5eab2c085703e59decbe6
08d91368d349c2b56c712422f6d274a1e8f1946ff2ecd1dc3efc3ebace52a760
```
57 changes: 39 additions & 18 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::io::Read;
use std::{io::Read, path::Path};

use clap::{Parser, Subcommand};
use cmd_lib::{init_builtin_logger, run_cmd, run_fun};
use sha1::{Digest, Sha1};
use serde::Deserialize;
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
Expand All @@ -16,12 +16,22 @@ struct Arguments {
subcommand: SubCommand,
}

#[derive(Deserialize, Debug)]
struct Config {
package: Package,
}

#[derive(Deserialize, Debug)]
struct Package {
name: String,
}

#[derive(Subcommand, Debug)]
enum SubCommand {
/// Deterministically build the program in an Docker container
Build {
/// Path to mount to the docker image
filepath: Option<String>,
build_dir: Option<String>,
#[clap(short, long)]
base_image: Option<String>,
},
Expand Down Expand Up @@ -61,7 +71,7 @@ fn main() -> anyhow::Result<()> {
let args = Arguments::parse();
match args.subcommand {
SubCommand::Build {
filepath,
build_dir: filepath,
base_image,
} => build(filepath, base_image),
SubCommand::VerifyFromImage {
Expand Down Expand Up @@ -104,17 +114,16 @@ fn main() -> anyhow::Result<()> {
}
}

pub fn get_binary_hash(bytes: Vec<u8>) -> String {
let mut hasher = Sha1::new();
let mut buffer = bytes
fn get_binary_hash(program_data: Vec<u8>) -> String {
let buffer = program_data
.into_iter()
.rev()
.skip_while(|&x| x == 0)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect::<Vec<_>>();
buffer = buffer.iter().map(|x| *x).rev().collect::<Vec<_>>();
hasher.update(&buffer);
let program_hash = hasher.finalize();
hex::encode(program_hash)
sha256::digest(&buffer[..])
}

pub fn build(filepath: Option<String>, base_image: Option<String>) -> anyhow::Result<()> {
Expand All @@ -131,11 +140,25 @@ pub fn build(filepath: Option<String>, base_image: Option<String>) -> anyhow::Re
let container_id = run_fun!(
docker run
--rm
-v $path:/work
-v $path:/build
-dit $image
sh -c "cargo build-sbf -- --locked --frozen"
)?;
run_cmd!(docker logs --follow $container_id)?;
let build_path = Path::new(&path);
let toml_path = build_path.join("Cargo.toml");
let toml: Config = toml::from_str(&std::fs::read_to_string(&toml_path)?)?;
let package_name = toml.package.name;
let executable_path = Path::new(&path)
.join("target")
.join("deploy")
.join(format!("{}.so", package_name));
let mut f = std::fs::File::open(&executable_path)?;
let metadata = std::fs::metadata(&executable_path)?;
let mut buffer = vec![0; metadata.len() as usize];
f.read(&mut buffer)?;
let program_hash = get_binary_hash(buffer);
println!("Executable hash: {}", program_hash);
Ok(())
}

Expand All @@ -154,7 +177,7 @@ pub fn verify_from_image(
let output = run_fun!(
docker run --rm
-it $image sh -c
"(wc -c $executable_path && shasum $executable_path) | tr '\n' ' '"
"(wc -c $executable_path && shasum -a 256 $executable_path) | tr '\n' ' '"
| tail -n 1
| awk "{print $1, $3}"
)?;
Expand All @@ -168,13 +191,11 @@ pub fn verify_from_image(

let offset = UpgradeableLoaderState::size_of_programdata_metadata();
let account_data = &client.get_account_data(&program_buffer)?[offset..offset + executable_size];
let mut hasher = Sha1::new();
hasher.update(account_data);
let program_hash = hasher.finalize();
let program_hash = sha256::digest(account_data);
println!("Executable hash (un-stripped): {}", executable_hash);
println!("Program hash (un-stripped): {}", hex::encode(program_hash));
println!("Program hash (un-stripped): {}", program_hash);

if hex::encode(program_hash) != executable_hash {
if program_hash != executable_hash {
println!("Executable hash mismatch");
return Err(anyhow::Error::msg("Executable hash mismatch"));
} else {
Expand Down

0 comments on commit be2125a

Please sign in to comment.