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

feat: gas hook on ExecutionFinalResult #284

Merged
merged 27 commits into from Oct 5, 2023
Merged
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
2 changes: 1 addition & 1 deletion examples/src/custom_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ async fn main() -> anyhow::Result<()> {

// skip the test
println!("NEAR_RPC_API_KEY is not set, skipping the example");
return Ok(());
Ok(())
}
1 change: 0 additions & 1 deletion examples/src/ref_finance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ async fn create_custom_ft(
.transact()
.await?
.into_result()?;
();

Ok(ft)
}
Expand Down
8 changes: 7 additions & 1 deletion workspaces/src/error/impls.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::borrow::Cow;
use std::fmt;
use std::{borrow::Cow, sync::PoisonError};

use crate::result::ExecutionFailure;

Expand Down Expand Up @@ -135,6 +135,12 @@ impl std::error::Error for Error {
}
}

impl<T> From<PoisonError<T>> for Error {
fn from(value: PoisonError<T>) -> Self {
Error::custom(ErrorKind::Other, value.to_string())
}
}

impl SandboxErrorCode {
pub(crate) fn message<T>(self, msg: T) -> Error
where
Expand Down
22 changes: 18 additions & 4 deletions workspaces/src/network/variants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,16 @@ where
T: DevNetwork + TopLevelAccountCreator + 'static,
{
pub async fn create_tla(&self, id: AccountId, sk: SecretKey) -> Result<Execution<Account>> {
self.workspace
let res = self
.workspace
.create_tla(self.clone().coerce(), id, sk)
.await
.await?;

self.tx_callbacks.iter().for_each(|meter| {
meter(res.details.total_gas_burnt).unwrap();
});

Ok(res)
}

pub async fn create_tla_and_deploy(
Expand All @@ -53,9 +60,16 @@ where
sk: SecretKey,
wasm: &[u8],
) -> Result<Execution<Contract>> {
self.workspace
let res = self
.workspace
.create_tla_and_deploy(self.clone().coerce(), id, sk, wasm)
.await
.await?;

self.tx_callbacks.iter().for_each(|meter| {
meter(res.details.total_gas_burnt).unwrap();
});

Ok(res)
}

pub async fn dev_generate(&self) -> (AccountId, SecretKey) {
Expand Down
36 changes: 31 additions & 5 deletions workspaces/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,13 +236,28 @@ impl Transaction {
}

async fn transact_raw(self) -> Result<FinalExecutionOutcomeView> {
send_batch_tx_and_retry(
let view = send_batch_tx_and_retry(
self.worker.client(),
&self.signer,
&self.receiver_id,
self.actions?,
)
.await
.await?;

if !self.worker.tx_callbacks.is_empty() {
let total_gas_burnt = view.transaction_outcome.outcome.gas_burnt
+ view
.receipts_outcome
.iter()
.map(|t| t.outcome.gas_burnt)
.sum::<u64>();

for callback in self.worker.tx_callbacks {
callback(Gas::from_gas(total_gas_burnt))?;
}
}

Ok(view)
}

/// Process the transaction, and return the result of the execution.
Expand Down Expand Up @@ -335,7 +350,8 @@ impl CallTransaction {
/// object and return us the execution details, along with any errors if the transaction
/// failed in any process along the way.
pub async fn transact(self) -> Result<ExecutionFinalResult> {
self.worker
let txn = self
.worker
.client()
.call(
&self.signer,
Expand All @@ -347,7 +363,12 @@ impl CallTransaction {
)
.await
.map(ExecutionFinalResult::from_view)
.map_err(crate::error::Error::from)
.map_err(crate::error::Error::from)?;

for callback in self.worker.tx_callbacks.iter() {
callback(txn.total_gas_burnt)?;
}
Ok(txn)
}

/// Send the transaction to the network to be processed. This will be done asynchronously
Expand Down Expand Up @@ -446,10 +467,15 @@ impl<'a, 'b> CreateAccountTransaction<'a, 'b> {

let signer = InMemorySigner::from_secret_key(id, sk);
let account = Account::new(signer, self.worker.clone());
let details = ExecutionFinalResult::from_view(outcome);

for callback in self.worker.tx_callbacks.iter() {
callback(details.total_gas_burnt)?;
}

Ok(Execution {
result: account,
details: ExecutionFinalResult::from_view(outcome),
details,
})
}
}
Expand Down
69 changes: 69 additions & 0 deletions workspaces/src/types/gas_meter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::sync::{Arc, Mutex};

use super::Gas;
use crate::result::Result;
use crate::Worker;

/// A hook that is called on every transaction that is sent to the network.
/// This is useful for debugging purposes, or for tracking the amount of gas
/// that is being used.
pub type GasHook = Arc<dyn Fn(Gas) -> Result<()> + Send + Sync>;

/// Allows you to meter the amount of gas consumed by transaction(s).
/// Note: This only works with transactions that resolve to [`crate::result::ExecutionFinalResult`]
/// Example
/// ```rust, ignore, no_run
/// let mut worker = near_workspaces::sandbox().await?;
/// let meter = GasMeter::now(&mut worker);
///
/// let wasm = std::fs::read(STATUS_MSG_WASM_FILEPATH)?;
/// let contract = worker.dev_deploy(&wasm).await?;
///
/// contract
/// .call("set_status")
/// .args_json(json!({
/// "message": "hello_world",
/// }))
/// .transact()
/// .await?;
///
/// println!("Total Gas consumed: {}", meter.elapsed()?);
/// ```
pub struct GasMeter {
ChaoticTempest marked this conversation as resolved.
Show resolved Hide resolved
gas: Arc<Mutex<Gas>>,
}

impl GasMeter {
/// Create a new gas meter with 0 gas consumed.
pub fn now<T: ?Sized>(worker: &mut Worker<T>) -> Self {
let meter = Self {
gas: Arc::new(Mutex::new(Gas::from_gas(0))),
};

let gas_consumed = Arc::downgrade(&Arc::clone(&meter.gas));
worker.tx_callbacks.push(Arc::new(move |gas: Gas| {
// upgrades if meter is still alive, else noop.
if let Some(consumed) = gas_consumed.upgrade() {
let mut consumed = consumed.lock()?;
*consumed = Gas::from_gas(consumed.as_gas() + gas.as_gas());
}

Ok(())
}));

meter
}

/// Get the total amount of gas consumed.
pub fn elapsed(&self) -> Result<Gas> {
let meter = self.gas.lock()?;
Ok(*meter)
}

/// Reset the gas consumed to 0.
pub fn reset(&self) -> Result<()> {
let mut meter = self.gas.lock()?;
*meter = Gas::from_gas(0);
Ok(())
}
}
3 changes: 3 additions & 0 deletions workspaces/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
pub(crate) mod account;
pub(crate) mod block;
pub(crate) mod chunk;
pub(crate) mod gas_meter;

#[cfg(feature = "interop_sdk")]
mod sdk;
Expand All @@ -27,6 +28,8 @@ use crate::result::Result;
pub use self::account::{AccountDetails, AccountDetailsPatch};
pub use self::chunk::{Chunk, ChunkHeader};

pub use self::gas_meter::{GasHook, GasMeter};

/// Nonce is a unit used to determine the order of transactions in the pool.
pub type Nonce = u64;

Expand Down
1 change: 1 addition & 0 deletions workspaces/src/worker/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl<T: ?Sized> Clone for Worker<T> {
fn clone(&self) -> Self {
Self {
workspace: self.workspace.clone(),
tx_callbacks: self.tx_callbacks.clone(),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions workspaces/src/worker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::sync::Arc;

use crate::network::builder::NetworkBuilder;
use crate::network::{Betanet, Custom, Mainnet, Sandbox, Testnet};
use crate::types::GasHook;
use crate::{Network, Result};

/// The `Worker` type allows us to interact with any NEAR related networks, such
Expand All @@ -13,6 +14,7 @@ use crate::{Network, Result};
/// deploying a contract, or interacting with transactions.
pub struct Worker<T: ?Sized> {
pub(crate) workspace: Arc<T>,
pub(crate) tx_callbacks: Vec<GasHook>,
}

impl<T> Worker<T>
Expand All @@ -22,6 +24,7 @@ where
pub(crate) fn new(network: T) -> Self {
Self {
workspace: Arc::new(network),
tx_callbacks: vec![],
}
}
}
Expand All @@ -30,6 +33,7 @@ impl<T: Network + 'static> Worker<T> {
pub(crate) fn coerce(self) -> Worker<dyn Network> {
Worker {
workspace: self.workspace,
tx_callbacks: self.tx_callbacks,
}
}
}
Expand Down
Loading
Loading