Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion crates/iroha/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,6 @@ impl Client {

let mut tx_builder = match instructions.into() {
Executable::Instructions(instructions) => tx_builder.with_instructions(instructions),
Executable::Wasm(wasm) => tx_builder.with_wasm(wasm),
};

if let Some(transaction_ttl) = self.transaction_ttl {
Expand Down
10 changes: 6 additions & 4 deletions crates/iroha_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,17 @@ trait RunContext {
/// Submit instructions or dump them to stdout depending on the flag
fn finish(&mut self, instructions: impl Into<Executable>) -> Result<()> {
let mut instructions = match instructions.into() {
Executable::Wasm(wasm) => {
if self.input_instructions() || self.output_instructions() {
Executable::Instructions(instructions) => {
let has_wasm = instructions
.iter()
.any(|i| matches!(i, InstructionBox::ExecuteWasm(_)));
if has_wasm && (self.input_instructions() || self.output_instructions()) {
eyre::bail!(
"Incompatible `--input` `--output` flags with `iroha transaction wasm`"
)
}
return self.submit(wasm);
instructions.into_vec()
}
Executable::Instructions(instructions) => instructions.into_vec(),
};
if self.input_instructions() {
let mut acc: Vec<InstructionBox> = parse_json5_stdin_unchecked()?;
Expand Down
2 changes: 1 addition & 1 deletion crates/iroha_core/benches/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ fn validate_transaction(criterion: &mut Criterion) {
let _ = criterion.bench_function("validate", |b| {
b.iter(|| {
match state_block
.validate_transaction(transaction.clone(), &mut wasm_cache)
.validate_transaction(&transaction, &mut wasm_cache)
.1
{
Ok(_) => success_count += 1,
Expand Down
7 changes: 3 additions & 4 deletions crates/iroha_core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ mod valid {
let accepted_tx = AcceptedTransaction::new_unchecked(tx.clone());

let (hash, result) =
state_block.validate_transaction(accepted_tx, &mut wasm_cache);
state_block.validate_transaction(&accepted_tx, &mut wasm_cache);

match &result {
Err(reason) => {
Expand Down Expand Up @@ -961,9 +961,8 @@ mod valid {
if transaction.authority() != genesis_account {
return Err(InvalidGenesisError::UnexpectedAuthority);
}
let Executable::Instructions(isi) = transaction.instructions() else {
return Err(InvalidGenesisError::NotInstructions);
};

let Executable::Instructions(isi) = transaction.instructions();
if i == 0 {
let [InstructionBox::Upgrade(_)] = isi.as_ref() else {
return Err(InvalidGenesisError::MustUpgrade);
Expand Down
7 changes: 1 addition & 6 deletions crates/iroha_core/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,7 @@ impl Executor {

match self {
Self::Initial => {
let (_authority, Executable::Instructions(instructions)) = transaction.into()
else {
return Err(ValidationFail::NotPermitted(
"Genesis transaction must not be a smart contract".to_owned(),
));
};
let (_authority, Executable::Instructions(instructions)) = transaction.into();

for isi in instructions {
isi.execute(authority, state_transaction)?
Expand Down
1 change: 1 addition & 0 deletions crates/iroha_core/src/smartcontracts/isi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl Execute for InstructionBox {
Self::Grant(isi) => isi.execute(authority, state_transaction),
Self::Revoke(isi) => isi.execute(authority, state_transaction),
Self::ExecuteTrigger(isi) => isi.execute(authority, state_transaction),
Self::ExecuteWasm(isi) => isi.execute(authority, state_transaction),
Self::SetParameter(isi) => isi.execute(authority, state_transaction),
Self::Upgrade(isi) => isi.execute(authority, state_transaction),
Self::Log(isi) => isi.execute(authority, state_transaction),
Expand Down
58 changes: 58 additions & 0 deletions crates/iroha_core/src/smartcontracts/isi/triggers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,64 @@ pub mod isi {
Ok(())
}
}

use crate::smartcontracts::wasm;
impl Execute for WasmExecutable<WasmSmartContract> {
#[metrics(+"execute_wasm")]
fn execute(
self,
authority: &AccountId,
state_transaction: &mut StateTransaction<'_, '_>,
) -> Result<(), Error> {
let mut wasm_runtime = wasm::RuntimeBuilder::<wasm::state::SmartContract>::new()
.with_config(state_transaction.world().parameters().smart_contract)
.with_engine(state_transaction.engine().clone()) // Cloning engine is cheap
.build()
.expect("failed to create wasm runtime");
wasm_runtime
.execute(state_transaction, authority.clone(), self.object())
.map_err(|error| {
Error::WasmExecution(crate::smartcontracts::isi::error::WasmExecutionError {
reason: format!("{:?}", eyre::Report::from(error)),
})
})
}
}

impl Execute for WasmExecutable<TriggerModule> {
#[metrics(+"execute_wasm_trigger")]
fn execute(
self,
authority: &AccountId,
state_transaction: &mut StateTransaction<'_, '_>,
) -> Result<(), Error> {
let module = state_transaction
.world()
.triggers()
.get_compiled_contract(self.object().hash())
.expect("INTERNAL BUG: contract is not present")
.clone();
wasm::RuntimeBuilder::<wasm::state::Trigger>::new()
.with_config(state_transaction.world().parameters().smart_contract)
.with_engine(state_transaction.engine.clone()) // Cloning engine is cheap
.build()
.and_then(|mut wasm_runtime| {
wasm_runtime.execute_trigger_module(
state_transaction,
self.object().id(),
authority.clone(),
&module,
self.object().event().clone(),
)
})
.map_err(|error| {
Error::WasmExecution(crate::smartcontracts::isi::error::WasmExecutionError {
reason: format!("{:?}", eyre::Report::from(error)),
})
})
.map(|_| ())
}
}
}

pub mod query {
Expand Down
121 changes: 84 additions & 37 deletions crates/iroha_core/src/smartcontracts/isi/triggers/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ use std::{fmt, marker::PhantomData, num::NonZeroU64};
use iroha_crypto::HashOf;
use iroha_data_model::{
events::EventFilter,
isi::error::{InstructionExecutionError, MathError},
isi::{
error::{InstructionExecutionError, MathError},
InstructionBoxHashed,
},
prelude::*,
query::error::FindError,
transaction::WasmSmartContract,
Expand Down Expand Up @@ -274,6 +277,32 @@ impl<'de> DeserializeSeed<'de> for WasmSeed<'_, WasmSmartContractEntry> {
deserializer.deserialize_map(WasmSmartContractEntryVisitor { loader: self })
}
}

/// Converts [`InstructionBoxHashed`] to [`InstructionBox`].
/// `wasm_hash_resolver` is required to obtain wasm bytes from hash
pub fn hashed_to_isi<F>(isi_hashed: InstructionBoxHashed, wasm_hash_resolver: F) -> InstructionBox
where
F: FnOnce(HashOf<WasmSmartContract>) -> InstructionBox,
{
match isi_hashed {
InstructionBoxHashed::Register(o) => InstructionBox::Register(o),
InstructionBoxHashed::Unregister(o) => InstructionBox::Unregister(o),
InstructionBoxHashed::Mint(o) => InstructionBox::Mint(o),
InstructionBoxHashed::Burn(o) => InstructionBox::Burn(o),
InstructionBoxHashed::Transfer(o) => InstructionBox::Transfer(o),
InstructionBoxHashed::SetKeyValue(o) => InstructionBox::SetKeyValue(o),
InstructionBoxHashed::RemoveKeyValue(o) => InstructionBox::RemoveKeyValue(o),
InstructionBoxHashed::Grant(o) => InstructionBox::Grant(o),
InstructionBoxHashed::Revoke(o) => InstructionBox::Revoke(o),
InstructionBoxHashed::ExecuteTrigger(o) => InstructionBox::ExecuteTrigger(o),
InstructionBoxHashed::SetParameter(o) => InstructionBox::SetParameter(o),
InstructionBoxHashed::Upgrade(o) => InstructionBox::Upgrade(o),
InstructionBoxHashed::Log(o) => InstructionBox::Log(o),
InstructionBoxHashed::Custom(o) => InstructionBox::Custom(o),
InstructionBoxHashed::ExecuteWasm(o) => wasm_hash_resolver(o),
}
}

/// Trait to perform read-only operations on [`SetBlock`], [`SetTransaction`] and [`SetView`]
#[allow(missing_docs)]
pub trait SetReadOnly {
Expand All @@ -289,9 +318,9 @@ pub trait SetReadOnly {
fn contracts(&self)
-> &impl StorageReadOnly<HashOf<WasmSmartContract>, WasmSmartContractEntry>;

/// Get original [`WasmSmartContract`] for [`TriggerId`].
/// Returns `None` if there's no [`Trigger`]
/// with specified `id` that has WASM executable
/// Get original [`WasmSmartContract`] for [`HashOf<WasmSmartContract>`].
/// Returns `None` if there's no [`WasmSmartContract`]
/// with specified `hash`
#[inline]
fn get_original_contract(
&self,
Expand Down Expand Up @@ -324,14 +353,21 @@ pub trait SetReadOnly {
} = action;

let original_executable = match executable {
ExecutableRef::Wasm(ref blob_hash) => {
let original_wasm = self
.get_original_contract(blob_hash)
.cloned()
.expect("No original smartcontract saved for trigger. This is a bug.");
Executable::Wasm(original_wasm)
}
ExecutableRef::Instructions(isi) => Executable::Instructions(isi),
ExecutableRef::Instructions(isi) => Executable::Instructions(
isi.into_iter()
.map(|isi| -> InstructionBox {
hashed_to_isi(isi, |hash| -> InstructionBox {
WasmExecutable::binary(
self.get_original_contract(&hash).cloned().expect(
"No original smartcontract saved for trigger. This is a bug.",
),
)
.into()
})
})
.collect::<Vec<_>>()
.into(),
),
};

SpecializedAction {
Expand Down Expand Up @@ -688,30 +724,44 @@ impl<'block, 'set> SetTransaction<'block, 'set> {
}

let loaded_executable = match executable {
Executable::Wasm(bytes) => {
let hash = HashOf::new(&bytes);
// Store original executable representation to respond to queries with.
if let Some(WasmSmartContractEntry { count, .. }) = self.contracts.get_mut(&hash) {
// Considering 1 trigger registration takes 1 second,
// it would take 584 942 417 355 years to overflow.
*count = count.checked_add(1).expect(
Executable::Instructions(instructions) => {
ExecutableRef::Instructions(
instructions
.into_iter()
.map(|isi| -> Result<InstructionBoxHashed> {
match isi {
InstructionBox::ExecuteWasm(ref wasm) => {
let hash = HashOf::new(wasm.object());
// Store original executable representation to respond to queries with.
if let Some(WasmSmartContractEntry { count, .. }) =
self.contracts.get_mut(&hash)
{
// Considering 1 trigger registration takes 1 second,
// it would take 584 942 417 355 years to overflow.
*count = count.checked_add(1).expect(
"There is no way someone could register 2^64 amount of same triggers",
);
// Cloning module is cheap, under Arc inside
} else {
let module = wasm::load_module(engine, &bytes)?;
self.contracts.insert(
hash,
WasmSmartContractEntry {
original_contract: bytes,
compiled_contract: module,
count: NonZeroU64::MIN,
},
);
}
ExecutableRef::Wasm(hash)
// Cloning module is cheap, under Arc inside
} else {
let module = wasm::load_module(engine, wasm.object())?;
self.contracts.insert(
hash,
WasmSmartContractEntry {
original_contract: wasm.object().clone(),
compiled_contract: module,
count: NonZeroU64::MIN,
},
);
}
Ok(isi.into())
}
_ => Ok(isi.into()),
}
})
.collect::<Result<Vec<_>, _>>()?
.into(),
)
}
Executable::Instructions(instructions) => ExecutableRef::Instructions(instructions),
};
map(self).insert(
trigger_id.clone(),
Expand Down Expand Up @@ -923,16 +973,13 @@ impl<'block, 'set> SetTransaction<'block, 'set> {
/// Which can be used to obtain compiled by `wasmtime` module
#[derive(Clone, Serialize, Deserialize)]
pub enum ExecutableRef {
/// Loaded WASM
Wasm(HashOf<WasmSmartContract>),
/// Vector of ISI
Instructions(ConstVec<InstructionBox>),
Instructions(ConstVec<InstructionBoxHashed>),
}

impl core::fmt::Debug for ExecutableRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Wasm(hash) => f.debug_tuple("Wasm").field(hash).finish(),
Self::Instructions(instructions) => {
f.debug_tuple("Instructions").field(instructions).finish()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ pub struct LoadedAction<F> {
impl<F> LoadedAction<F> {
pub(super) fn extract_blob_hash(&self) -> Option<HashOf<WasmSmartContract>> {
match self.executable {
ExecutableRef::Wasm(blob_hash) => Some(blob_hash),
ExecutableRef::Instructions(_) => None,
}
}
Expand Down
Loading
Loading