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

incremental hash #4197

Merged
merged 16 commits into from
Jul 11, 2023
4 changes: 2 additions & 2 deletions massa-async-pool/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl Default for AsyncMessage {
destination: genesis_address,
validity_start: slot_zero,
validity_end: slot_zero,
hash: Hash::from_bytes(&[0; 32]),
hash: Hash::zero(),
..Default::default()
}
}
Expand Down Expand Up @@ -362,7 +362,7 @@ impl AsyncMessage {
can_be_executed: can_be_executed.unwrap_or(trigger.is_none()),
trigger,
// placeholder hash to serialize the message, replaced below
hash: Hash::from_bytes(&[0; 32]),
hash: Hash::zero(),
};
async_message_ser
.serialize(&message, &mut buffer)
Expand Down
3 changes: 1 addition & 2 deletions massa-db-exports/src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
// Commons
pub const LSMTREE_NODES_CF: &str = "lsmtree_nodes";
pub const LSMTREE_VALUES_CF: &str = "lsmtree_values";
pub const METADATA_CF: &str = "metadata";
pub const STATE_CF: &str = "state";
pub const VERSIONING_CF: &str = "versioning";
Expand Down Expand Up @@ -31,6 +29,7 @@ pub const EXECUTED_DENUNCIATIONS_PREFIX: &str = "executed_denunciations/";
pub const LEDGER_PREFIX: &str = "ledger/";
pub const MIP_STORE_PREFIX: &str = "versioning/";
pub const MIP_STORE_STATS_PREFIX: &str = "versioning_stats/";
pub const EXECUTION_TRAIL_HASH_PREFIX: &str = "execution_trail_hash/";

// Async Pool
pub const MESSAGE_DESER_ERROR: &str = "critical: message deserialization failed";
Expand Down
11 changes: 11 additions & 0 deletions massa-execution-worker/src/active_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,17 @@ impl ActiveHistory {
None
}

/// Gets the execution trail hash
pub fn get_execution_trail_hash(&self) -> HistorySearchResult<massa_hash::Hash> {
for history_element in self.0.iter().rev() {
if let SetOrKeep::Set(hash) = history_element.state_changes.execution_trail_hash_change
{
return HistorySearchResult::Present(hash);
}
}
HistorySearchResult::NoInfo
}

/// Gets the index of a slot in history
pub fn get_slot_index(&self, slot: &Slot, thread_count: u8) -> SlotIndexPosition {
let first_slot = match self.0.front() {
Expand Down
108 changes: 73 additions & 35 deletions massa-execution-worker/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! More generally, the context acts only on its own state
//! and does not write anything persistent to the consensus state.

use crate::active_history::HistorySearchResult;
use crate::speculative_async_pool::SpeculativeAsyncPool;
use crate::speculative_executed_denunciations::SpeculativeExecutedDenunciations;
use crate::speculative_executed_ops::SpeculativeExecutedOps;
Expand All @@ -21,7 +22,7 @@ use massa_execution_exports::{
};
use massa_final_state::{FinalState, StateChanges};
use massa_hash::Hash;
use massa_ledger_exports::LedgerChanges;
use massa_ledger_exports::{LedgerChanges, SetOrKeep};
use massa_models::address::ExecutionAddressCycleInfo;
use massa_models::bytecode::Bytecode;
use massa_models::denunciation::DenunciationIndex;
Expand Down Expand Up @@ -155,13 +156,16 @@ pub struct ExecutionContext {
/// operation id that originally caused this execution (if any)
pub origin_operation_id: Option<OperationId>,

// cache of compiled runtime modules
/// Execution trail hash
pub execution_trail_hash: Hash,

/// cache of compiled runtime modules
pub module_cache: Arc<RwLock<ModuleCache>>,

// Vesting Manager
/// Vesting Manager
pub vesting_manager: Arc<VestingManager>,

// Address factory
/// Address factory
pub address_factory: AddressFactory,
}

Expand All @@ -183,6 +187,7 @@ impl ExecutionContext {
module_cache: Arc<RwLock<ModuleCache>>,
vesting_manager: Arc<VestingManager>,
mip_store: MipStore,
execution_trail_hash: massa_hash::Hash,
) -> Self {
ExecutionContext {
speculative_ledger: SpeculativeLedger::new(
Expand Down Expand Up @@ -219,13 +224,14 @@ impl ExecutionContext {
stack: Default::default(),
read_only: Default::default(),
events: Default::default(),
unsafe_rng: Xoshiro256PlusPlus::from_seed([0u8; 32]),
unsafe_rng: init_prng(&execution_trail_hash),
creator_address: Default::default(),
origin_operation_id: Default::default(),
module_cache,
config,
vesting_manager,
address_factory: AddressFactory { mip_store },
execution_trail_hash,
}
}

Expand Down Expand Up @@ -306,35 +312,29 @@ impl ExecutionContext {
vesting_manager: Arc<VestingManager>,
mip_store: MipStore,
) -> Self {
// Deterministically seed the unsafe RNG to allow the bytecode to use it.
// Note that consecutive read-only calls for the same slot will get the same random seed.

// Add the current slot to the seed to ensure different draws at every slot
let mut seed: Vec<u8> = slot.to_bytes_key().to_vec();
// Add a marker to the seed indicating that we are in read-only mode
// to prevent random draw collisions with active executions
seed.push(0u8); // 0u8 = read-only
let seed = massa_hash::Hash::compute_from(&seed).into_bytes();
// We use Xoshiro256PlusPlus because it is very fast,
// has a period long enough to ensure no repetitions will ever happen,
// of decent quality (given the unsafe constraints)
// but not cryptographically secure (and that's ok because the internal state is exposed anyways)
let unsafe_rng = Xoshiro256PlusPlus::from_seed(seed);
// Get the execution hash trail
let prev_execution_trail_hash = active_history.read().get_execution_trail_hash();
let prev_execution_trail_hash = match prev_execution_trail_hash {
HistorySearchResult::Present(h) => h,
_ => final_state.read().get_execution_trail_hash(),
};
let execution_trail_hash =
generate_execution_trail_hash(&prev_execution_trail_hash, &slot, None, true);

// return readonly context
ExecutionContext {
max_gas,
slot,
stack: call_stack,
read_only: true,
unsafe_rng,
..ExecutionContext::new(
config,
final_state,
active_history,
module_cache,
vesting_manager,
mip_store,
execution_trail_hash,
)
}
}
Expand Down Expand Up @@ -380,33 +380,31 @@ impl ExecutionContext {
vesting_manager: Arc<VestingManager>,
mip_store: MipStore,
) -> Self {
// Deterministically seed the unsafe RNG to allow the bytecode to use it.

// Add the current slot to the seed to ensure different draws at every slot
let mut seed: Vec<u8> = slot.to_bytes_key().to_vec();
// Add a marker to the seed indicating that we are in active mode
// to prevent random draw collisions with read-only executions
seed.push(1u8); // 1u8 = active

// For more deterministic entropy, seed with the block ID if any
if let Some(block_id) = &opt_block_id {
seed.extend(block_id.to_bytes()); // append block ID
}
let seed = massa_hash::Hash::compute_from(&seed).into_bytes();
let unsafe_rng = Xoshiro256PlusPlus::from_seed(seed);
// Get the execution hash trail
let prev_execution_trail_hash = active_history.read().get_execution_trail_hash();
let prev_execution_trail_hash = match prev_execution_trail_hash {
HistorySearchResult::Present(h) => h,
_ => final_state.read().get_execution_trail_hash(),
};
let execution_trail_hash = generate_execution_trail_hash(
&prev_execution_trail_hash,
&slot,
opt_block_id.as_ref(),
false,
);

// return active slot execution context
ExecutionContext {
slot,
opt_block_id,
unsafe_rng,
..ExecutionContext::new(
config,
final_state,
active_history,
module_cache,
vesting_manager,
mip_store,
execution_trail_hash,
)
}
}
Expand Down Expand Up @@ -936,6 +934,7 @@ impl ExecutionContext {
pos_changes: self.speculative_roll_state.take(),
executed_ops_changes: self.speculative_executed_ops.take(),
executed_denunciations_changes: self.speculative_executed_denunciations.take(),
execution_trail_hash_change: SetOrKeep::Set(self.execution_trail_hash),
};

std::mem::take(&mut self.opt_block_id);
Expand Down Expand Up @@ -1074,3 +1073,42 @@ impl ExecutionContext {
.get_address_deferred_credits(address, min_slot)
}
}

/// Generate the execution trail hash
fn generate_execution_trail_hash(
previous_execution_trail_hash: &massa_hash::Hash,
slot: &Slot,
opt_block_id: Option<&BlockId>,
read_only: bool,
) -> massa_hash::Hash {
match opt_block_id {
None => massa_hash::Hash::compute_from_tuple(&[
previous_execution_trail_hash.to_bytes(),
&slot.to_bytes_key(),
&[if read_only { 1u8 } else { 0u8 }, 0u8],
]),
Some(block_id) => massa_hash::Hash::compute_from_tuple(&[
previous_execution_trail_hash.to_bytes(),
&slot.to_bytes_key(),
&[if read_only { 1u8 } else { 0u8 }, 1u8],
block_id.to_bytes(),
]),
}
}

/// Initializes and seeds the PRNG with the given execution trail hash.
fn init_prng(execution_trail_hash: &massa_hash::Hash) -> Xoshiro256PlusPlus {
// Deterministically seed the unsafe RNG to allow the bytecode to use it.
// Note that consecutive read-only calls for the same slot will get the same random seed.
let seed = massa_hash::Hash::compute_from_tuple(&[
"PRNG_SEED".as_bytes(),
execution_trail_hash.to_bytes(),
])
.into_bytes();

// We use Xoshiro256PlusPlus because it is very fast,
// has a period long enough to ensure no repetitions will ever happen,
// of decent quality (given the unsafe constraints)
// but not cryptographically secure (and that's ok because the internal state is exposed anyways)
Xoshiro256PlusPlus::from_seed(seed)
}
14 changes: 8 additions & 6 deletions massa-execution-worker/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,13 @@ impl ExecutionState {
) -> ExecutionState {
// Get the slot at the output of which the final state is attached.
// This should be among the latest final slots.
let last_final_slot = final_state
.read()
.db
.read()
.get_change_id()
.expect("Critical error: Final state has no slot attached");
let last_final_slot;
let execution_trail_hash;
{
let final_state_read = final_state.read();
last_final_slot = final_state_read.get_slot();
execution_trail_hash = final_state_read.get_execution_trail_hash();
}

// Create default active history
let active_history: Arc<RwLock<ActiveHistory>> = Default::default();
Expand Down Expand Up @@ -160,6 +161,7 @@ impl ExecutionState {
module_cache.clone(),
vesting_manager.clone(),
mip_store.clone(),
execution_trail_hash,
)));

// Instantiate the interface providing ABI access to the VM, share the execution context with it
Expand Down
Loading
Loading