Skip to content

Commit

Permalink
runtime-sdk: introduce Context trait
Browse files Browse the repository at this point in the history
  • Loading branch information
ptrus committed Apr 14, 2021
1 parent 73221ef commit afa2283
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 182 deletions.
273 changes: 153 additions & 120 deletions runtime-sdk/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,47 +73,82 @@ impl<'a> DispatchContext<'a> {
}
}

/// Last runtime block header.
pub fn runtime_header(&self) -> &roothash::Header {
self.runtime_header
/// Runtime state store.
pub fn runtime_state(&mut self) -> storage::MKVSStore<&mut dyn mkvs::MKVS> {
storage::MKVSStore::new(self.io_ctx.clone(), &mut self.runtime_storage)
}

/// Results of executing the last successful runtime round.
pub fn runtime_round_results(&self) -> &roothash::RoundResults {
self.runtime_round_results
/// Executes a function with the transaction-specific context set.
pub fn with_tx<F, R>(&mut self, tx: transaction::Transaction, f: F) -> R
where
F: FnOnce(TxContext<'_, '_>, transaction::Call) -> R,
{
// Create a store wrapped by an overlay store so we can either rollback or commit.
let store = storage::MKVSStore::new(self.io_ctx.clone(), &mut self.runtime_storage);
let store = storage::OverlayStore::new(store);

let tx_ctx = TxContext {
mode: self.mode,
runtime_header: self.runtime_header,
runtime_round_results: self.runtime_round_results,
store,
tx_auth_info: tx.auth_info,
tags: Tags::new(),
// NOTE: Since a limit is enforced (which is a u32) this cast is always safe.
max_messages: self.max_messages.saturating_sub(self.messages.len() as u32),
messages: Vec::new(),
values: &mut self.values,
};
f(tx_ctx, tx.call)
}
}

/// Runtime state store.
pub fn runtime_state(&mut self) -> storage::MKVSStore<&mut dyn mkvs::MKVS> {
storage::MKVSStore::new(self.io_ctx.clone(), &mut self.runtime_storage)
impl<'a> Context for DispatchContext<'a> {
type S = storage::MKVSStore<&'a mut dyn mkvs::MKVS>;

fn mode(&self) -> Mode {
self.mode
}

fn runtime_header(&self) -> &roothash::Header {
&self.runtime_header
}

fn runtime_round_results(&self) -> &roothash::RoundResults {
&self.runtime_round_results
}

fn runtime_state(&mut self) -> &mut Self::S {
todo!()
}

fn tx_auth_info(&self) -> Option<&transaction::AuthInfo> {
None
}

fn emit_event<E: Event>(&mut self, event: E) {
self.block_tags.push(event.to_tag());
}

/// Emits runtime messages
pub fn emit_messages(
fn emit_message(
&mut self,
mut msgs: Vec<(roothash::Message, MessageEventHookInvocation)>,
msg: roothash::Message,
hook: MessageEventHookInvocation,
) -> Result<(), Error> {
// Check against maximum number of messages that can be emitted per round.
if self.messages.len() > self.max_messages as usize {
return Err(Error::TooManyMessages);
}

self.messages.append(&mut msgs);
self.messages.push((msg, hook));
Ok(())
}

/// Emits tags not tied to a specific transaction.
pub fn emit_tags(&mut self, mut tags: Tags) {
self.block_tags.append(&mut tags);
}

/// Finalize the context and return the emitted runtime messages, consuming the context.
pub fn commit(self) -> (Tags, Vec<(roothash::Message, MessageEventHookInvocation)>) {
fn commit(self) -> (Tags, Vec<(roothash::Message, MessageEventHookInvocation)>) {
(self.block_tags, self.messages)
}

/// Fetches or sets a value associated with the context.
pub fn value<V>(&mut self, key: &'static str) -> &mut V
fn value<V>(&mut self, key: &'static str) -> &mut V
where
V: Any + Default,
{
Expand All @@ -124,10 +159,7 @@ impl<'a> DispatchContext<'a> {
.expect("type should stay the same")
}

/// Takes a value associated with the context.
///
/// The previous value is removed so subsequent fetches will return the default value.
pub fn take_value<V>(&mut self, key: &'static str) -> Box<V>
fn take_value<V>(&mut self, key: &'static str) -> Box<V>
where
V: Any + Default,
{
Expand All @@ -136,58 +168,93 @@ impl<'a> DispatchContext<'a> {
.map(|x| x.downcast().expect("type should stay the same"))
.unwrap_or_default()
}
}

/// Executes a function with the transaction-specific context set.
pub fn with_tx<F, R>(&mut self, tx: transaction::Transaction, f: F) -> R
where
F: FnOnce(Context<'_, '_>, transaction::Call) -> R,
{
// Create a store wrapped by an overlay store so we can either rollback or commit.
let store = storage::MKVSStore::new(self.io_ctx.clone(), &mut self.runtime_storage);
let store = storage::OverlayStore::new(store);
/// Runtime sdk context.
pub trait Context
where
Self::S: storage::Store,
{
type S;

let tx_ctx = Context {
mode: self.mode,
runtime_header: self.runtime_header,
runtime_round_results: self.runtime_round_results,
store,
tx_auth_info: Some(tx.auth_info),
tags: Tags::new(),
// NOTE: Since a limit is enforced (which is a u32) this cast is always safe.
max_messages: self.max_messages.saturating_sub(self.messages.len() as u32),
messages: Vec::new(),
values: &mut self.values,
};
f(tx_ctx, tx.call)
/// Context mode.
fn mode(&self) -> Mode;

/// Whether the transaction is just being checked for validity.
fn is_check_only(&self) -> bool {
self.mode() == Mode::CheckTx
}

/// Executes a function with function specific sub-context set.
pub fn with_ctx<F, R>(&mut self, f: F) -> R
where
F: FnOnce(Context<'_, '_>) -> R,
{
// Create a store wrapped by an overlay store so we can either rollback or commit.
let store = storage::MKVSStore::new(self.io_ctx.clone(), &mut self.runtime_storage);
let store = storage::OverlayStore::new(store);
/// Whether the transaction is just being simulated.
fn is_simulation(&self) -> bool {
self.mode() == Mode::SimulateTx
}

let ctx = Context {
mode: self.mode,
runtime_header: self.runtime_header,
runtime_round_results: self.runtime_round_results,
store,
tx_auth_info: None,
tags: Tags::new(),
// NOTE: Since a limit is enforced (which is a u32) this cast is always safe.
max_messages: self.max_messages.saturating_sub(self.messages.len() as u32),
messages: Vec::new(),
values: &mut self.values,
};
f(ctx)
/// Last runtime block header.
fn runtime_header(&self) -> &roothash::Header;

/// Results of executing the last successful runtime round.
fn runtime_round_results(&self) -> &roothash::RoundResults;

/// Runtime state store.
fn runtime_state(&mut self) -> &mut Self::S;

/// Transaction authentication information.
///
/// Only present if this is a transaction processing context.
fn tx_auth_info(&self) -> Option<&transaction::AuthInfo>;

/// Authenticated address of the caller.
///
/// In case there are multiple signers of a transaction, this will return the address
/// corresponding to the first signer.
///
/// Only present if this is a transaction processing context.
fn tx_caller_address(&self) -> Option<Address> {
self.tx_auth_info()
.map(|info| Address::from_pk(&info.signer_info[0].public_key))
}

/// Emits an event.
fn emit_event<E: Event>(&mut self, event: E);

/// Attempts to emit consensus runtime message.
fn emit_message(
&mut self,
msg: roothash::Message,
hook: MessageEventHookInvocation,
) -> Result<(), Error>;

fn emit_messages(
&mut self,
msgs: Vec<(roothash::Message, MessageEventHookInvocation)>,
) -> Result<(), Error> {
for m in msgs {
self.emit_message(m.0, m.1)?;
}

Ok(())
}

/// Commit any changes made to storage, return any emitted tags and runtime messages. It
/// consumes the transaction context.
fn commit(self) -> (Tags, Vec<(roothash::Message, MessageEventHookInvocation)>);

/// Fetches or sets a value associated with the context.
fn value<V>(&mut self, key: &'static str) -> &mut V
where
V: Any + Default;

/// Takes a value associated with the context.
///
/// The previous value is removed so subsequent fetches will return the default value.
fn take_value<V>(&mut self, key: &'static str) -> Box<V>
where
V: Any + Default;
}

/// Per-transaction/method dispatch sub-context.
pub struct Context<'a, 'b> {
pub struct TxContext<'a, 'b> {
mode: Mode,

runtime_header: &'a roothash::Header,
Expand All @@ -197,7 +264,7 @@ pub struct Context<'a, 'b> {
store: storage::OverlayStore<storage::MKVSStore<&'b mut &'a mut dyn mkvs::MKVS>>,

/// Transaction authentication info.
tx_auth_info: Option<transaction::AuthInfo>,
tx_auth_info: transaction::AuthInfo,

/// Emitted tags.
tags: Tags,
Expand All @@ -211,62 +278,34 @@ pub struct Context<'a, 'b> {
values: &'b mut BTreeMap<&'static str, Box<dyn Any>>,
}

impl<'a, 'b> Context<'a, 'b> {
/// Whether the transaction is just being checked for validity.
pub fn is_check_only(&self) -> bool {
self.mode == Mode::CheckTx
impl<'a, 'b> Context for TxContext<'a, 'b> {
type S = storage::OverlayStore<storage::MKVSStore<&'b mut &'a mut dyn mkvs::MKVS>>;

fn runtime_state(&mut self) -> &mut Self::S {
&mut self.store
}

/// Whether the transaction is just being simulated.
pub fn is_simulation(&self) -> bool {
self.mode == Mode::SimulateTx
fn mode(&self) -> Mode {
self.mode
}

/// Last runtime block header.
pub fn runtime_header(&self) -> &roothash::Header {
fn runtime_header(&self) -> &roothash::Header {
self.runtime_header
}

/// Results of executing the last successful runtime round.
pub fn runtime_round_results(&self) -> &roothash::RoundResults {
fn runtime_round_results(&self) -> &roothash::RoundResults {
self.runtime_round_results
}

/// Runtime state store.
pub fn runtime_state(
&mut self,
) -> &mut storage::OverlayStore<storage::MKVSStore<&'b mut &'a mut dyn mkvs::MKVS>> {
&mut self.store
}

/// Transaction authentication information.
///
/// Only present if this is a transaction processing context.
pub fn tx_auth_info(&self) -> Option<&transaction::AuthInfo> {
self.tx_auth_info.as_ref()
}

/// Authenticated address of the caller.
///
/// In case there are multiple signers of a transaction, this will return the address
/// corresponding to the first signer.
///
/// Only present if this is a transaction processing context.
pub fn tx_caller_address(&self) -> Option<Address> {
self.tx_auth_info()
.map(|info| Address::from_pk(&info.signer_info[0].public_key))
fn tx_auth_info(&self) -> Option<&transaction::AuthInfo> {
Some(&self.tx_auth_info)
}

/// Emits an event.
pub fn emit_event<E: Event>(&mut self, event: E) {
fn emit_event<E: Event>(&mut self, event: E) {
self.tags.push(event.to_tag());
}

/// Attempts to emit a runtime message.
///
/// Returns an index of the emitted message that can be used to correlate the corresponding
/// result after the message has been processed (in the next round).
pub fn emit_message(
fn emit_message(
&mut self,
msg: roothash::Message,
hook: MessageEventHookInvocation,
Expand All @@ -281,15 +320,12 @@ impl<'a, 'b> Context<'a, 'b> {
Ok(())
}

/// Commit any changes made to storage, return any emitted tags and runtime messages. It
/// consumes the transaction context.
pub fn commit(self) -> (Tags, Vec<(roothash::Message, MessageEventHookInvocation)>) {
fn commit(self) -> (Tags, Vec<(roothash::Message, MessageEventHookInvocation)>) {
self.store.commit();
(self.tags, self.messages)
}

/// Fetches or sets a value associated with the context.
pub fn value<V>(&mut self, key: &'static str) -> &mut V
fn value<V>(&mut self, key: &'static str) -> &mut V
where
V: Any + Default,
{
Expand All @@ -300,10 +336,7 @@ impl<'a, 'b> Context<'a, 'b> {
.expect("type should stay the same")
}

/// Takes a value associated with the context.
///
/// The previous value is removed so subsequent fetches will return the default value.
pub fn take_value<V>(&mut self, key: &'static str) -> Box<V>
fn take_value<V>(&mut self, key: &'static str) -> Box<V>
where
V: Any + Default,
{
Expand Down
Loading

0 comments on commit afa2283

Please sign in to comment.