Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
Emit events for inter contract calls (#12136)
Browse files Browse the repository at this point in the history
* Add topics to contract events

* Add `Call` events

* Fix compilation for no_std

* Added docs
  • Loading branch information
athei authored Sep 3, 2022
1 parent 1c0d008 commit 4b79116
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 84 deletions.
143 changes: 93 additions & 50 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use pallet_contracts_primitives::ExecReturnValue;
use smallvec::{Array, SmallVec};
use sp_core::{crypto::UncheckedFrom, ecdsa::Public as ECDSAPublic};
use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256};
use sp_runtime::traits::Convert;
use sp_runtime::traits::{Convert, Hash};
use sp_std::{marker::PhantomData, mem, prelude::*};

pub type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
Expand Down Expand Up @@ -784,16 +784,19 @@ where
///
/// This can be either a call or an instantiate.
fn run(&mut self, executable: E, input_data: Vec<u8>) -> Result<ExecReturnValue, ExecError> {
let entry_point = self.top_frame().entry_point;
let frame = self.top_frame();
let entry_point = frame.entry_point;
let delegated_code_hash =
if frame.delegate_caller.is_some() { Some(*executable.code_hash()) } else { None };
let do_transaction = || {
// We need to charge the storage deposit before the initial transfer so that
// it can create the account in case the initial transfer is < ed.
if entry_point == ExportedFunction::Constructor {
let top_frame = top_frame_mut!(self);
top_frame.nested_storage.charge_instantiate(
let frame = top_frame_mut!(self);
frame.nested_storage.charge_instantiate(
&self.origin,
&top_frame.account_id,
top_frame.contract_info.get(&top_frame.account_id),
&frame.account_id,
frame.contract_info.get(&frame.account_id),
)?;
}

Expand All @@ -805,23 +808,42 @@ where
.execute(self, &entry_point, input_data)
.map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?;

// Additional work needs to be performed in case of an instantiation.
if !output.did_revert() && entry_point == ExportedFunction::Constructor {
let frame = self.top_frame();

// It is not allowed to terminate a contract inside its constructor.
if matches!(frame.contract_info, CachedContract::Terminated) {
return Err(Error::<T>::TerminatedInConstructor.into())
}
// Avoid useless work that would be reverted anyways.
if output.did_revert() {
return Ok(output)
}

// Deposit an instantiation event.
deposit_event::<T>(
vec![],
Event::Instantiated {
deployer: self.caller().clone(),
contract: frame.account_id.clone(),
},
);
let frame = self.top_frame();
let account_id = &frame.account_id;
match (entry_point, delegated_code_hash) {
(ExportedFunction::Constructor, _) => {
// It is not allowed to terminate a contract inside its constructor.
if matches!(frame.contract_info, CachedContract::Terminated) {
return Err(Error::<T>::TerminatedInConstructor.into())
}

// Deposit an instantiation event.
Contracts::<T>::deposit_event(
vec![T::Hashing::hash_of(self.caller()), T::Hashing::hash_of(account_id)],
Event::Instantiated {
deployer: self.caller().clone(),
contract: account_id.clone(),
},
);
},
(ExportedFunction::Call, Some(code_hash)) => {
Contracts::<T>::deposit_event(
vec![T::Hashing::hash_of(account_id), T::Hashing::hash_of(&code_hash)],
Event::DelegateCalled { contract: account_id.clone(), code_hash },
);
},
(ExportedFunction::Call, None) => {
let caller = self.caller();
Contracts::<T>::deposit_event(
vec![T::Hashing::hash_of(caller), T::Hashing::hash_of(account_id)],
Event::Called { caller: caller.clone(), contract: account_id.clone() },
);
},
}

Ok(output)
Expand Down Expand Up @@ -850,6 +872,7 @@ where
// has changed.
Err(error) => (false, Err(error.into())),
};

self.pop_frame(success);
output
}
Expand Down Expand Up @@ -1134,10 +1157,13 @@ where
)?;
ContractInfoOf::<T>::remove(&frame.account_id);
E::remove_user(info.code_hash);
Contracts::<T>::deposit_event(Event::Terminated {
contract: frame.account_id.clone(),
beneficiary: beneficiary.clone(),
});
Contracts::<T>::deposit_event(
vec![T::Hashing::hash_of(&frame.account_id), T::Hashing::hash_of(&beneficiary)],
Event::Terminated {
contract: frame.account_id.clone(),
beneficiary: beneficiary.clone(),
},
);
Ok(())
}

Expand Down Expand Up @@ -1242,7 +1268,7 @@ where
}

fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
deposit_event::<Self::T>(
Contracts::<Self::T>::deposit_event(
topics,
Event::ContractEmitted { contract: self.top_frame().account_id.clone(), data },
);
Expand Down Expand Up @@ -1304,22 +1330,18 @@ where
let prev_hash = top_frame.contract_info().code_hash;
E::remove_user(prev_hash);
top_frame.contract_info().code_hash = hash;
Contracts::<Self::T>::deposit_event(Event::ContractCodeUpdated {
contract: top_frame.account_id.clone(),
new_code_hash: hash,
old_code_hash: prev_hash,
});
Contracts::<Self::T>::deposit_event(
vec![T::Hashing::hash_of(&top_frame.account_id), hash, prev_hash],
Event::ContractCodeUpdated {
contract: top_frame.account_id.clone(),
new_code_hash: hash,
old_code_hash: prev_hash,
},
);
Ok(())
}
}

fn deposit_event<T: Config>(topics: Vec<T::Hash>, event: Event<T>) {
<frame_system::Pallet<T>>::deposit_event_indexed(
&topics,
<T as Config>::Event::from(event).into(),
)
}

mod sealing {
use super::*;

Expand Down Expand Up @@ -1347,7 +1369,7 @@ mod tests {
gas::GasMeter,
storage::Storage,
tests::{
test_utils::{get_balance, place_contract, set_balance},
test_utils::{get_balance, hash, place_contract, set_balance},
Call, Event as MetaEvent, ExtBuilder, Test, TestFilter, ALICE, BOB, CHARLIE, GAS_LIMIT,
},
Error,
Expand Down Expand Up @@ -2237,7 +2259,10 @@ mod tests {
);
assert_eq!(
&events(),
&[Event::Instantiated { deployer: BOB, contract: instantiated_contract_address }]
&[
Event::Instantiated { deployer: BOB, contract: instantiated_contract_address },
Event::Called { caller: ALICE, contract: BOB },
]
);
});
}
Expand Down Expand Up @@ -2289,7 +2314,7 @@ mod tests {

// The contract wasn't instantiated so we don't expect to see an instantiation
// event here.
assert_eq!(&events(), &[]);
assert_eq!(&events(), &[Event::Called { caller: ALICE, contract: BOB },]);
});
}

Expand Down Expand Up @@ -2591,14 +2616,24 @@ mod tests {
let remark_hash = <Test as frame_system::Config>::Hashing::hash(b"Hello World");
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: MetaEvent::System(frame_system::Event::Remarked {
sender: BOB,
hash: remark_hash
}),
topics: vec![],
},]
vec![
EventRecord {
phase: Phase::Initialization,
event: MetaEvent::System(frame_system::Event::Remarked {
sender: BOB,
hash: remark_hash
}),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: MetaEvent::Contracts(crate::Event::Called {
caller: ALICE,
contract: BOB,
}),
topics: vec![hash(&ALICE), hash(&BOB)],
},
]
);
});
}
Expand Down Expand Up @@ -2684,6 +2719,14 @@ mod tests {
},),
topics: vec![],
},
EventRecord {
phase: Phase::Initialization,
event: MetaEvent::Contracts(crate::Event::Called {
caller: ALICE,
contract: BOB,
}),
topics: vec![hash(&ALICE), hash(&BOB)],
},
]
);
});
Expand Down
50 changes: 44 additions & 6 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,19 +631,21 @@ pub mod pallet {
};
<PrefabWasmModule<T>>::add_user(code_hash)?;
<PrefabWasmModule<T>>::remove_user(contract.code_hash);
Self::deposit_event(Event::ContractCodeUpdated {
contract: dest.clone(),
new_code_hash: code_hash,
old_code_hash: contract.code_hash,
});
Self::deposit_event(
vec![T::Hashing::hash_of(&dest), code_hash, contract.code_hash],
Event::ContractCodeUpdated {
contract: dest.clone(),
new_code_hash: code_hash,
old_code_hash: contract.code_hash,
},
);
contract.code_hash = code_hash;
Ok(())
})
}
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Contract deployed by address at the specified address.
Instantiated { deployer: T::AccountId, contract: T::AccountId },
Expand Down Expand Up @@ -685,6 +687,35 @@ pub mod pallet {
/// Previous code hash of the contract.
old_code_hash: T::Hash,
},

/// A contract was called either by a plain account or another contract.
///
/// # Note
///
/// Please keep in mind that like all events this is only emitted for successful
/// calls. This is because on failure all storage changes including events are
/// rolled back.
Called {
/// The account that called the `contract`.
caller: T::AccountId,
/// The contract that was called.
contract: T::AccountId,
},

/// A contract delegate called a code hash.
///
/// # Note
///
/// Please keep in mind that like all events this is only emitted for successful
/// calls. This is because on failure all storage changes including events are
/// rolled back.
DelegateCalled {
/// The contract that performed the delegate call and hence in whose context
/// the `code_hash` is executed.
contract: T::AccountId,
/// The code hash that was delegate called.
code_hash: CodeHash<T>,
},
}

#[pallet::error]
Expand Down Expand Up @@ -1084,4 +1115,11 @@ where
};
InternalInstantiateOutput { result: try_exec(), gas_meter, storage_deposit }
}

fn deposit_event(topics: Vec<T::Hash>, event: Event<T>) {
<frame_system::Pallet<T>>::deposit_event_indexed(
&topics,
<T as Config>::Event::from(event).into(),
)
}
}
Loading

0 comments on commit 4b79116

Please sign in to comment.