Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to the versioning scheme outlined in the [README.md](RE
- Fixed an issue where `event.committed` was always equal to `true` in the block replay RPC endpoint
- Added `result_hex` and `post_condition_aborted` to the block replay RPC endpoint
- Added `--epoch <epoch_number>` flag to `clarity-cli` commands to specify the epoch context for evaluation.
- Added the `events` array to readonly endpoints output.

### Fixed

Expand Down
61 changes: 54 additions & 7 deletions clarity/src/vm/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,14 +1123,27 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
&self.global_context.epoch_id
}

pub fn execute_contract_with_events(
&mut self,
contract: &QualifiedContractIdentifier,
tx_name: &str,
args: &[SymbolicExpression],
read_only: bool,
) -> Result<(Value, Vec<StacksTransactionEvent>), VmExecutionError> {
self.inner_execute_contract(contract, tx_name, args, read_only, false)
}

pub fn execute_contract(
&mut self,
contract: &QualifiedContractIdentifier,
tx_name: &str,
args: &[SymbolicExpression],
read_only: bool,
) -> Result<Value, VmExecutionError> {
self.inner_execute_contract(contract, tx_name, args, read_only, false)
match self.inner_execute_contract(contract, tx_name, args, read_only, false) {
Ok((value, _events)) => Ok(value),
Err(e) => Err(e),
}
}

/// This method is exposed for callers that need to invoke a private method directly.
Expand All @@ -1143,7 +1156,10 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
args: &[SymbolicExpression],
read_only: bool,
) -> Result<Value, VmExecutionError> {
self.inner_execute_contract(contract, tx_name, args, read_only, true)
match self.inner_execute_contract(contract, tx_name, args, read_only, true) {
Ok((value, _events)) => Ok(value),
Err(e) => Err(e),
}
}

/// This method handles actual execution of contract-calls on a contract.
Expand All @@ -1159,7 +1175,7 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
args: &[SymbolicExpression],
read_only: bool,
allow_private: bool,
) -> Result<Value, VmExecutionError> {
) -> Result<(Value, Vec<StacksTransactionEvent>), VmExecutionError> {
let contract_size = self
.global_context
.database
Expand Down Expand Up @@ -1207,11 +1223,11 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
return Err(CheckErrorKind::CircularReference(vec![func_identifier.to_string()]).into())
}
self.call_stack.insert(&func_identifier, true);
let res = self.execute_function_as_transaction(&func, &args, Some(&contract.contract_context), allow_private);
let res = self.execute_function_as_transaction_and_events(&func, &args, Some(&contract.contract_context), allow_private);
self.call_stack.remove(&func_identifier, true)?;

match res {
Ok(value) => {
Ok((value, events)) => {
if let Some(handler) = self.global_context.database.get_cc_special_cases_handler() {
handler(
self.global_context,
Expand All @@ -1223,7 +1239,7 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
&value
)?;
}
Ok(value)
Ok((value, events))
},
Err(e) => Err(e)
}
Expand All @@ -1237,6 +1253,23 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
next_contract_context: Option<&ContractContext>,
allow_private: bool,
) -> Result<Value, VmExecutionError> {
let (value, _events) = self.execute_function_as_transaction_and_events(
function,
args,
next_contract_context,
allow_private,
)?;

Ok(value)
}

pub fn execute_function_as_transaction_and_events(
&mut self,
function: &DefinedFunction,
args: &[Value],
next_contract_context: Option<&ContractContext>,
allow_private: bool,
) -> Result<(Value, Vec<StacksTransactionEvent>), VmExecutionError> {
let make_read_only = function.is_read_only();

if make_read_only {
Expand All @@ -1260,11 +1293,25 @@ impl<'a, 'b, 'hooks> Environment<'a, 'b, 'hooks> {
function.execute_apply(args, &mut nested_env)
};

if make_read_only {
// retrieve all the events
let mut events = vec![];
self.global_context
.event_batches
.iter()
.for_each(|event_batch| {
events.extend(event_batch.events.clone());
});

let result = if make_read_only {
self.global_context.roll_back()?;
result
} else {
self.global_context.handle_tx_result(result, allow_private)
};

match result {
Ok(result) => Ok((result, events)),
Err(e) => Err(e),
}
}

Expand Down
15 changes: 15 additions & 0 deletions docs/rpc/components/schemas/read-only-function-result.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,18 @@ oneOf:
cause:
type: string
description: A string representing the cause of the error.
events:
type: array
description: Contract events generated by the call
items:
type: object
properties:
sender:
type: string
description: The contract address generating the event.
key:
type: string
description: Who generated the event (generally "print").
value:
type: string
description: Hex-encoded Clarity value of the event.
Loading