102 lines
3.1 KiB
Rust
102 lines
3.1 KiB
Rust
use {
|
|
crate::api::ChainId,
|
|
anyhow::{
|
|
ensure,
|
|
Result,
|
|
},
|
|
ethers::types::Address,
|
|
sha3::{
|
|
Digest,
|
|
Keccak256,
|
|
},
|
|
};
|
|
|
|
/// A HashChain.
|
|
#[derive(Clone)]
|
|
pub struct PebbleHashChain {
|
|
hash: Vec<[u8; 32]>,
|
|
next: usize,
|
|
}
|
|
|
|
impl PebbleHashChain {
|
|
// Given a secret, we hash it with Keccak256 len times to get the final hash, this is an S/KEY
|
|
// like protocol in which revealing the hashes in reverse proves knowledge.
|
|
pub fn new(secret: [u8; 32], length: usize) -> Self {
|
|
let mut hash = Vec::<[u8; 32]>::with_capacity(length);
|
|
hash.push(Keccak256::digest(secret).into());
|
|
for _ in 1..length {
|
|
hash.push(Keccak256::digest(&hash[hash.len() - 1]).into());
|
|
}
|
|
|
|
hash.reverse();
|
|
|
|
Self { hash, next: 0 }
|
|
}
|
|
|
|
pub fn from_config(
|
|
secret: &str,
|
|
chain_id: &ChainId,
|
|
provider_address: &Address,
|
|
contract_address: &Address,
|
|
random: &[u8; 32],
|
|
chain_length: u64,
|
|
) -> Result<Self> {
|
|
let mut input: Vec<u8> = vec![];
|
|
input.extend_from_slice(&hex::decode(secret)?);
|
|
input.extend_from_slice(&chain_id.as_bytes());
|
|
input.extend_from_slice(&provider_address.as_bytes());
|
|
input.extend_from_slice(&contract_address.as_bytes());
|
|
input.extend_from_slice(random);
|
|
|
|
let secret: [u8; 32] = Keccak256::digest(input).into();
|
|
Ok(Self::new(secret, chain_length.try_into()?))
|
|
}
|
|
|
|
/// Reveal the next hash in the chain using the previous proof.
|
|
pub fn reveal(&mut self) -> Result<[u8; 32]> {
|
|
ensure!(self.next < self.len(), "no more hashes in the chain");
|
|
let next = self.hash[self.next].clone();
|
|
self.next += 1;
|
|
Ok(next)
|
|
}
|
|
|
|
pub fn reveal_ith(&self, i: usize) -> Result<[u8; 32]> {
|
|
ensure!(i < self.len(), "index not in range");
|
|
Ok(self.hash[i].clone())
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.hash.len()
|
|
}
|
|
}
|
|
|
|
/// `HashChainState` tracks the mapping between on-chain sequence numbers to hash chains.
|
|
/// This struct is required to handle the case where the provider rotates their commitment,
|
|
/// which requires tracking multiple hash chains here.
|
|
pub struct HashChainState {
|
|
// The sequence number where the hash chain starts. Must be stored in sorted order.
|
|
pub offsets: Vec<usize>,
|
|
pub hash_chains: Vec<PebbleHashChain>,
|
|
}
|
|
|
|
impl HashChainState {
|
|
pub fn from_chain_at_offset(offset: usize, chain: PebbleHashChain) -> HashChainState {
|
|
HashChainState {
|
|
offsets: vec![offset],
|
|
hash_chains: vec![chain],
|
|
}
|
|
}
|
|
|
|
pub fn reveal(&self, sequence_number: u64) -> Result<[u8; 32]> {
|
|
let sequence_number: usize = sequence_number.try_into()?;
|
|
let chain_index = self
|
|
.offsets
|
|
.partition_point(|x| x <= &sequence_number)
|
|
.checked_sub(1)
|
|
.ok_or(anyhow::anyhow!(
|
|
"Hash chain for the requested sequence number is not available."
|
|
))?;
|
|
self.hash_chains[chain_index].reveal_ith(sequence_number - self.offsets[chain_index])
|
|
}
|
|
}
|