-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
## Description This PR continues the effort of merging #705 in multiple pieces. This PR introduces the core hashchain logic as a library crate. The reasons to factor the code in this way are: 1. Allows this PR to be safely merged because it has no impact on existing engine code 2. The same core logic will be reusable between components that will perform the hashchain computation in the future, namely: Aurora Engine smart contract, standalone engine, Borealis Refiner. In the next PR I'll pull in the changes from #705 which actually introduce the hashchain calculation into the Aurora Engine smart contract and standalone engine. ## Performance / NEAR gas cost considerations N/A ## Testing New hashchain tests --------- Co-authored-by: Oleksandr Anyshchenko <[email protected]>
- Loading branch information
Showing
9 changed files
with
998 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "aurora-engine-hashchain" | ||
version = "1.0.0" | ||
authors.workspace = true | ||
edition.workspace = true | ||
readme.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
license.workspace = true | ||
publish.workspace = true | ||
autobenches = false | ||
|
||
[dependencies] | ||
aurora-engine-types.workspace = true | ||
aurora-engine-sdk.workspace = true | ||
fixed-hash.workspace = true | ||
impl-serde.workspace = true | ||
|
||
[features] | ||
default = ["std"] | ||
std = ["aurora-engine-types/std", "aurora-engine-sdk/std", "fixed-hash/std", "impl-serde/std"] | ||
borsh-compat = ["aurora-engine-types/borsh-compat", "aurora-engine-sdk/borsh-compat"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
//! Based on Parity Common Eth Bloom implementation | ||
//! Link: <https://github.com/paritytech/parity-common/blob/master/ethbloom/src/lib.rs> | ||
//! | ||
//! Reimplemented here since there is a large mismatch in types and dependencies. | ||
#![allow(clippy::expl_impl_clone_on_copy)] | ||
|
||
use aurora_engine_sdk::keccak; | ||
use aurora_engine_types::borsh::{self, BorshDeserialize, BorshSerialize}; | ||
use aurora_engine_types::parameters::engine::ResultLog; | ||
use fixed_hash::construct_fixed_hash; | ||
use impl_serde::impl_fixed_hash_serde; | ||
|
||
const BLOOM_SIZE: usize = 256; | ||
const BLOOM_BITS: u32 = 3; | ||
|
||
construct_fixed_hash! { | ||
/// Bloom hash type with 256 bytes (2048 bits) size. | ||
#[derive(BorshSerialize, BorshDeserialize)] | ||
pub struct Bloom(BLOOM_SIZE); | ||
} | ||
|
||
impl_fixed_hash_serde!(Bloom, BLOOM_SIZE); | ||
|
||
/// Returns log2. | ||
const fn log2(x: usize) -> u32 { | ||
if x <= 1 { | ||
return 0; | ||
} | ||
|
||
let n = x.leading_zeros(); | ||
usize::BITS - n | ||
} | ||
|
||
impl Bloom { | ||
/// Add a new element to the bloom filter | ||
#[allow(clippy::as_conversions)] | ||
pub fn accrue(&mut self, input: &[u8]) { | ||
let m = self.0.len(); | ||
let bloom_bits = m * 8; | ||
let mask = bloom_bits - 1; | ||
let bloom_bytes = (log2(bloom_bits) + 7) / 8; | ||
let hash = keccak(input); | ||
let mut ptr = 0; | ||
|
||
for _ in 0..BLOOM_BITS { | ||
let mut index = 0; | ||
for _ in 0..bloom_bytes { | ||
index = (index << 8) | hash[ptr] as usize; | ||
ptr += 1; | ||
} | ||
index &= mask; | ||
self.0[m - 1 - index / 8] |= 1 << (index % 8); | ||
} | ||
} | ||
|
||
/// Merge two bloom filters | ||
pub fn accrue_bloom(&mut self, bloom: &Self) { | ||
for i in 0..BLOOM_SIZE { | ||
self.0[i] |= bloom.0[i]; | ||
} | ||
} | ||
} | ||
|
||
#[must_use] | ||
pub fn get_logs_bloom(logs: &[ResultLog]) -> Bloom { | ||
let mut logs_bloom = Bloom::default(); | ||
|
||
for log in logs { | ||
logs_bloom.accrue_bloom(&get_log_bloom(log)); | ||
} | ||
|
||
logs_bloom | ||
} | ||
|
||
#[must_use] | ||
pub fn get_log_bloom(log: &ResultLog) -> Bloom { | ||
let mut log_bloom = Bloom::default(); | ||
|
||
log_bloom.accrue(log.address.as_bytes()); | ||
for topic in &log.topics { | ||
log_bloom.accrue(&topic[..]); | ||
} | ||
|
||
log_bloom | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
pub const ERR_STATE_NOT_FOUND: &[u8; 19] = b"ERR_STATE_NOT_FOUND"; | ||
pub const ERR_STATE_SERIALIZATION_FAILED: &[u8; 26] = b"ERR_STATE_SERIALIZE_FAILED"; | ||
pub const ERR_STATE_CORRUPTED: &[u8; 19] = b"ERR_STATE_CORRUPTED"; | ||
pub const ERR_BLOCK_HEIGHT_INCORRECT: &[u8; 26] = b"ERR_BLOCK_HEIGHT_INCORRECT"; | ||
pub const ERR_REQUIRES_FEATURE_INTEGRATION_TEST: &[u8; 37] = | ||
b"ERR_REQUIRES_FEATURE_INTEGRATION_TEST"; | ||
|
||
#[derive(Debug)] | ||
/// Blockchain Hashchain Error | ||
pub enum BlockchainHashchainError { | ||
/// The state is missing from storage, need to initialize with contract `new` method. | ||
NotFound, | ||
/// The state serialized had failed. | ||
SerializationFailed, | ||
/// The state is corrupted, possibly due to failed state migration. | ||
DeserializationFailed, | ||
/// The block height is incorrect regarding the current block height. | ||
BlockHeightIncorrect, | ||
/// Some functionality requires integration-test feature. | ||
RequiresFeatureIntegrationTest, | ||
} | ||
|
||
impl AsRef<[u8]> for BlockchainHashchainError { | ||
fn as_ref(&self) -> &[u8] { | ||
match self { | ||
Self::NotFound => ERR_STATE_NOT_FOUND, | ||
Self::SerializationFailed => ERR_STATE_SERIALIZATION_FAILED, | ||
Self::DeserializationFailed => ERR_STATE_CORRUPTED, | ||
Self::BlockHeightIncorrect => ERR_BLOCK_HEIGHT_INCORRECT, | ||
Self::RequiresFeatureIntegrationTest => ERR_REQUIRES_FEATURE_INTEGRATION_TEST, | ||
} | ||
} | ||
} |
Oops, something went wrong.