Support OutputHasher for all platform crate (#205)

This commit is contained in:
Han
2025-11-05 20:05:59 +08:00
committed by GitHub
parent 1de217d43a
commit 6547820fc8
16 changed files with 136 additions and 69 deletions

View File

@@ -6,11 +6,10 @@ rust-version.workspace = true
license.workspace = true
[dependencies]
digest = { workspace = true, optional = true }
digest.workspace = true
[features]
default = []
output-hasher = ["dep:digest"]
[lints]
workspace = true

View File

@@ -4,7 +4,6 @@ extern crate alloc;
use alloc::vec::Vec;
#[cfg(feature = "output-hasher")]
pub mod output_hasher;
/// Platform dependent methods.

View File

@@ -1,23 +1,50 @@
use core::marker::PhantomData;
use digest::{Digest, Output, OutputSizeUser, generic_array::ArrayLength};
use core::{marker::PhantomData, ops::Deref};
use digest::{
Digest, Output, OutputSizeUser,
generic_array::{ArrayLength, GenericArray},
};
pub use digest;
/// A hasher that given the output, returns a fixed size hash of it.
pub trait OutputHasher: OutputSizeUser {
fn output_hash(output: &[u8]) -> Output<Self>;
/// A hasher that given the output, returns a hash of it.
pub trait OutputHasher {
type Hash<'a>: Deref<Target = [u8]>;
fn output_hash(output: &[u8]) -> Self::Hash<'_>;
}
/// [`OutputHasher`] implementation that expects the output size to be equal to
/// the fixed size, and returns it as is.
pub struct IdentityOutput<S>(PhantomData<S>);
/// A hasher that given the output, returns a fixed-size hash of it.
pub trait FixedOutputHasher: OutputHasher + OutputSizeUser {}
impl<S: ArrayLength<u8> + 'static> OutputSizeUser for IdentityOutput<S> {
type OutputSize = S;
impl<T: OutputHasher + OutputSizeUser> FixedOutputHasher for T {}
/// A marker used to mark [`IdentityOutput`] to accept unsized output.
pub struct Unsized;
/// [`OutputHasher`] implementation that returns output as is.
///
/// By setting generic `U = Unsized` it takes output with any size.
///
/// By setting generic `U = typenum::U{SIZE}` it expects the output to match
/// the `SIZE`.
pub struct IdentityOutput<U = Unsized>(PhantomData<U>);
impl OutputHasher for IdentityOutput<Unsized> {
type Hash<'a> = &'a [u8];
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
output
}
}
impl<S: ArrayLength<u8> + 'static> OutputHasher for IdentityOutput<S> {
fn output_hash(output: &[u8]) -> Output<Self> {
impl<U: ArrayLength<u8> + 'static> OutputSizeUser for IdentityOutput<U> {
type OutputSize = U;
}
impl<U: ArrayLength<u8> + 'static> OutputHasher for IdentityOutput<U> {
type Hash<'a> = GenericArray<u8, U>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
assert!(
output.len() == Self::output_size(),
"output length should be equal to {}",
@@ -29,16 +56,20 @@ impl<S: ArrayLength<u8> + 'static> OutputHasher for IdentityOutput<S> {
}
}
/// [`OutputHasher`] implementation that expects the output size to be less than
/// or equal to the fixed size, and returns it with 0s padding.
pub struct PaddedOutput<S>(PhantomData<S>);
/// [`OutputHasher`] implementation that returns output with 0s padding.
///
/// By setting generic `U = typenum::U{SIZE}` it expects the output to be less
/// than or equal to the `SIZE`.
pub struct PaddedOutput<U>(PhantomData<U>);
impl<S: ArrayLength<u8> + 'static> OutputSizeUser for PaddedOutput<S> {
type OutputSize = S;
impl<U: ArrayLength<u8> + 'static> OutputSizeUser for PaddedOutput<U> {
type OutputSize = U;
}
impl<S: ArrayLength<u8> + 'static> OutputHasher for PaddedOutput<S> {
fn output_hash(output: &[u8]) -> Output<Self> {
impl<U: ArrayLength<u8> + 'static> OutputHasher for PaddedOutput<U> {
type Hash<'a> = GenericArray<u8, U>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
assert!(
output.len() <= Self::output_size(),
"output length should be less than or equal to {}",
@@ -51,7 +82,9 @@ impl<S: ArrayLength<u8> + 'static> OutputHasher for PaddedOutput<S> {
}
impl<D: Digest> OutputHasher for D {
fn output_hash(output: &[u8]) -> Output<Self> {
type Hash<'a> = Output<D>;
fn output_hash(output: &[u8]) -> Self::Hash<'_> {
D::digest(output)
}
}

View File

@@ -10,7 +10,7 @@ license.workspace = true
airbender_riscv_common.workspace = true
# Local dependencies
ere-platform-trait = { workspace = true, features = ["output-hasher"] }
ere-platform-trait.workspace = true
[features]
default = []

View File

@@ -4,17 +4,17 @@ extern crate alloc;
use alloc::vec::Vec;
use core::{array, iter::repeat_with, marker::PhantomData};
use ere_platform_trait::output_hasher::{OutputHasher, digest::typenum::U32};
use ere_platform_trait::output_hasher::FixedOutputHasher;
pub use airbender_riscv_common as riscv_common;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput},
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum::U32},
};
pub struct AirbenderPlatform<D>(PhantomData<D>);
pub struct AirbenderPlatform<H>(PhantomData<H>);
impl<D: OutputHasher<OutputSize = U32>> Platform for AirbenderPlatform<D> {
impl<H: FixedOutputHasher<OutputSize = U32>> Platform for AirbenderPlatform<H> {
fn read_whole_input() -> Vec<u8> {
let len = riscv_common::csr_read_word() as usize;
repeat_with(riscv_common::csr_read_word)
@@ -25,7 +25,7 @@ impl<D: OutputHasher<OutputSize = U32>> Platform for AirbenderPlatform<D> {
}
fn write_whole_output(output: &[u8]) {
let hash = D::output_hash(output);
let hash = H::output_hash(output);
let words = array::from_fn(|i| u32::from_le_bytes(array::from_fn(|j| hash[4 * i + j])));
riscv_common::zksync_os_finish_success(&words);
}

View File

@@ -4,8 +4,12 @@ extern crate alloc;
use alloc::vec::Vec;
use core::{marker::PhantomData, slice};
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use jolt_sdk as jolt;
// FIXME: Because the crate `jolt-common` is not `no_std` compatible, so we have
@@ -65,9 +69,9 @@ impl JoltMemoryConfig for DefaulJoltMemoryConfig {
const MEMORY_SIZE: u64 = DEFAULT_MEMORY_SIZE;
}
pub struct JoltPlatform<C = DefaulJoltMemoryConfig>(PhantomData<C>);
pub struct JoltPlatform<C = DefaulJoltMemoryConfig, H = IdentityOutput>(PhantomData<(C, H)>);
impl<C: JoltMemoryConfig> Platform for JoltPlatform<C> {
impl<C: JoltMemoryConfig, H: OutputHasher> Platform for JoltPlatform<C, H> {
fn read_whole_input() -> Vec<u8> {
let memory_layout = C::memory_layout();
let input_ptr = memory_layout.input_start as *const u8;
@@ -78,10 +82,11 @@ impl<C: JoltMemoryConfig> Platform for JoltPlatform<C> {
}
fn write_whole_output(output: &[u8]) {
let hash = H::output_hash(output);
let memory_layout = C::memory_layout();
let output_ptr = memory_layout.output_start as *mut u8;
let max_output_len = memory_layout.max_output_size as usize;
let output_slice = unsafe { core::slice::from_raw_parts_mut(output_ptr, max_output_len) };
jolt::postcard::to_slice(output, output_slice).unwrap();
jolt::postcard::to_slice(&*hash, output_slice).unwrap();
}
}

View File

@@ -3,18 +3,24 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use nexus_rt;
pub struct NexusPlatform;
pub struct NexusPlatform<H = IdentityOutput>(PhantomData<H>);
impl Platform for NexusPlatform {
impl<H: OutputHasher> Platform for NexusPlatform<H> {
fn read_whole_input() -> Vec<u8> {
nexus_rt::read_private_input().unwrap()
}
fn write_whole_output(output: &[u8]) {
nexus_rt::write_public_output(&output).unwrap()
let hash = H::output_hash(output);
nexus_rt::write_public_output(&*hash).unwrap()
}
}

View File

@@ -10,7 +10,7 @@ license.workspace = true
openvm.workspace = true
# Local dependencies
ere-platform-trait = { workspace = true, features = ["output-hasher"] }
ere-platform-trait.workspace = true
[features]
default = ["getrandom-unsupported"]

View File

@@ -3,24 +3,24 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::{OutputHasher, digest::typenum::U32};
use core::{marker::PhantomData, ops::Deref};
use ere_platform_trait::output_hasher::FixedOutputHasher;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput},
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum::U32},
};
pub use openvm;
pub struct OpenVMPlatform<D>(PhantomData<D>);
pub struct OpenVMPlatform<H>(PhantomData<H>);
impl<D: OutputHasher<OutputSize = U32>> Platform for OpenVMPlatform<D> {
impl<H: FixedOutputHasher<OutputSize = U32>> Platform for OpenVMPlatform<H> {
fn read_whole_input() -> Vec<u8> {
openvm::io::read_vec()
}
fn write_whole_output(output: &[u8]) {
let hash = D::output_hash(output);
openvm::io::reveal_bytes32(hash.into());
let hash = H::output_hash(output).deref().try_into().unwrap();
openvm::io::reveal_bytes32(hash);
}
}

View File

@@ -3,18 +3,24 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use pico_sdk;
pub struct PicoPlatform;
pub struct PicoPlatform<H = IdentityOutput>(PhantomData<H>);
impl Platform for PicoPlatform {
impl<H: OutputHasher> Platform for PicoPlatform<H> {
fn read_whole_input() -> Vec<u8> {
pico_sdk::io::read_vec()
}
fn write_whole_output(output: &[u8]) {
pico_sdk::io::commit_bytes(output);
let hash = H::output_hash(output);
pico_sdk::io::commit_bytes(&*hash);
}
}

View File

@@ -3,13 +3,18 @@
extern crate alloc;
use alloc::{vec, vec::Vec};
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use risc0_zkvm;
pub struct Risc0Platform;
pub struct Risc0Platform<H = IdentityOutput>(PhantomData<H>);
impl Platform for Risc0Platform {
impl<H: OutputHasher> Platform for Risc0Platform<H> {
fn read_whole_input() -> Vec<u8> {
let len = {
let mut bytes = [0; 4];
@@ -22,6 +27,7 @@ impl Platform for Risc0Platform {
}
fn write_whole_output(output: &[u8]) {
risc0_zkvm::guest::env::commit_slice(output);
let hash = H::output_hash(output);
risc0_zkvm::guest::env::commit_slice(&*hash);
}
}

View File

@@ -3,18 +3,24 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use sp1_zkvm;
pub struct SP1Platform;
pub struct SP1Platform<H = IdentityOutput>(PhantomData<H>);
impl Platform for SP1Platform {
impl<H: OutputHasher> Platform for SP1Platform<H> {
fn read_whole_input() -> Vec<u8> {
sp1_zkvm::io::read_vec()
}
fn write_whole_output(output: &[u8]) {
sp1_zkvm::io::commit_slice(output);
let hash = H::output_hash(output);
sp1_zkvm::io::commit_slice(&*hash);
}
}

View File

@@ -3,18 +3,24 @@
extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::OutputHasher;
pub use ere_platform_trait::Platform;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum},
};
pub use zkm_zkvm;
pub struct ZirenPlatform;
pub struct ZirenPlatform<H = IdentityOutput>(PhantomData<H>);
impl Platform for ZirenPlatform {
impl<H: OutputHasher> Platform for ZirenPlatform<H> {
fn read_whole_input() -> Vec<u8> {
zkm_zkvm::io::read_vec()
}
fn write_whole_output(output: &[u8]) {
zkm_zkvm::io::commit_slice(output);
let hash = H::output_hash(output);
zkm_zkvm::io::commit_slice(&*hash);
}
}

View File

@@ -10,7 +10,7 @@ license.workspace = true
ziskos.workspace = true
# Local dependencies
ere-platform-trait = { workspace = true, features = ["output-hasher"] }
ere-platform-trait.workspace = true
[features]
default = []

View File

@@ -4,23 +4,23 @@ extern crate alloc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use ere_platform_trait::output_hasher::{OutputHasher, digest::typenum::U32};
use ere_platform_trait::output_hasher::FixedOutputHasher;
pub use ere_platform_trait::{
Platform,
output_hasher::{IdentityOutput, PaddedOutput},
output_hasher::{IdentityOutput, PaddedOutput, digest::typenum::U32},
};
pub use ziskos;
pub struct ZiskPlatform<D>(PhantomData<D>);
pub struct ZiskPlatform<H>(PhantomData<H>);
impl<D: OutputHasher<OutputSize = U32>> Platform for ZiskPlatform<D> {
impl<H: FixedOutputHasher<OutputSize = U32>> Platform for ZiskPlatform<H> {
fn read_whole_input() -> Vec<u8> {
ziskos::read_input()
}
fn write_whole_output(output: &[u8]) {
let hash = D::output_hash(output);
let hash = H::output_hash(output);
hash.chunks_exact(4).enumerate().for_each(|(idx, bytes)| {
ziskos::set_output(idx, u32::from_le_bytes(bytes.try_into().unwrap()))
});

View File

@@ -1,8 +1,9 @@
use ere_platform_risc0::{Platform, Risc0Platform};
type P = Risc0Platform;
fn main() {
let alignment =
u32::from_le_bytes(Risc0Platform::read_whole_input().try_into().unwrap()) as usize;
let alignment = u32::from_le_bytes(P::read_whole_input().try_into().unwrap()) as usize;
let layout = std::alloc::Layout::from_size_align(1, alignment).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };