diff --git a/workspaces/src/lib.rs b/workspaces/src/lib.rs index 8fd58b99..fc4032e3 100644 --- a/workspaces/src/lib.rs +++ b/workspaces/src/lib.rs @@ -5,7 +5,7 @@ mod worker; pub mod prelude; -pub use network::{Account, Contract, DevNetwork, Network}; +pub use network::{Account, AccountDetails, Block, Contract, DevNetwork, Network}; pub use types::{AccountId, BlockHeight, CryptoHash, InMemorySigner}; pub use worker::{ mainnet, mainnet_archival, sandbox, testnet, with_mainnet, with_sandbox, with_testnet, Worker, diff --git a/workspaces/src/network/account.rs b/workspaces/src/network/account.rs index 8704814c..c7372cb2 100644 --- a/workspaces/src/network/account.rs +++ b/workspaces/src/network/account.rs @@ -1,10 +1,11 @@ use std::convert::TryInto; use near_crypto::KeyType; +use near_primitives::views::AccountView; use crate::rpc::client::{DEFAULT_CALL_DEPOSIT, DEFAULT_CALL_FN_GAS}; use crate::types::{AccountId, Balance, Gas, InMemorySigner, SecretKey}; -use crate::{Network, Worker}; +use crate::{CryptoHash, Network, Worker}; use super::{CallExecution, CallExecutionDetails, ViewResultDetails}; @@ -75,6 +76,14 @@ impl Account { .await } + /// Views the current account's details such as balance and storage usage. + pub async fn view_account( + &self, + worker: &Worker, + ) -> anyhow::Result { + worker.view_account(&self.id).await + } + /// Create a new sub account. Returns a CreateAccountBuilder object that /// we can make use of to fill out the rest of the details. The sub account /// id will be in the form of: "{new_account_id}.{parent_account_id}" @@ -165,6 +174,19 @@ impl Contract { worker.view(self.id(), function, args).await } + /// View the WASM code bytes of this contract. + pub async fn view_code(&self, worker: &Worker) -> anyhow::Result> { + worker.view_code(self.id()).await + } + + /// Views the current contract's details such as balance and storage usage. + pub async fn view_account( + &self, + worker: &Worker, + ) -> anyhow::Result { + worker.view_account(self.id()).await + } + /// Deletes the current contract, and returns the execution details of this /// transaction. The beneciary will receive the funds of the account deleted pub async fn delete_contract( @@ -314,3 +336,25 @@ where }) } } + +/// Details of an Account or Contract. This is an non-exhaustive list of items +/// that the account stores in the blockchain state. +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub struct AccountDetails { + pub balance: Balance, + pub locked: Balance, + pub code_hash: CryptoHash, + pub storage_usage: u64, +} + +impl From for AccountDetails { + fn from(account: AccountView) -> Self { + Self { + balance: account.amount, + locked: account.locked, + code_hash: CryptoHash(account.code_hash.0), + storage_usage: account.storage_usage, + } + } +} diff --git a/workspaces/src/network/block.rs b/workspaces/src/network/block.rs new file mode 100644 index 00000000..d619a107 --- /dev/null +++ b/workspaces/src/network/block.rs @@ -0,0 +1,60 @@ +use near_primitives::views::{BlockHeaderView, BlockView}; + +use crate::{BlockHeight, CryptoHash}; + +/// Struct containing information on block coming from the network +#[derive(Debug, PartialEq)] +pub struct Block { + header: BlockHeader, +} + +impl Block { + /// The block timestamp in nanoseconds. + pub fn timestamp(&self) -> u64 { + self.header.timestamp_nanosec + } + + /// Current height of this block. + pub fn height(&self) -> BlockHeight { + self.header.height + } + + /// The hash of the block itself. + pub fn hash(&self) -> &CryptoHash { + &self.header.hash + } + + /// The id of an epoch this block belongs to. + pub fn epoch_id(&self) -> &CryptoHash { + &self.header.epoch_id + } +} + +/// The block header info. This is a non-exhaustive list of items that +/// could be present in a block header. More can be added in the future. +#[derive(Debug, PartialEq)] +struct BlockHeader { + height: BlockHeight, + epoch_id: CryptoHash, + hash: CryptoHash, + timestamp_nanosec: u64, +} + +impl From for Block { + fn from(block_view: BlockView) -> Self { + Self { + header: block_view.header.into(), + } + } +} + +impl From for BlockHeader { + fn from(header_view: BlockHeaderView) -> Self { + Self { + height: header_view.height, + epoch_id: CryptoHash(header_view.epoch_id.0), + hash: CryptoHash(header_view.hash.0), + timestamp_nanosec: header_view.timestamp_nanosec, + } + } +} diff --git a/workspaces/src/network/mod.rs b/workspaces/src/network/mod.rs index a84d6e4f..07ed6d05 100644 --- a/workspaces/src/network/mod.rs +++ b/workspaces/src/network/mod.rs @@ -1,4 +1,5 @@ mod account; +mod block; mod info; mod mainnet; mod result; @@ -17,7 +18,8 @@ use crate::rpc::patch::ImportContractBuilder; use crate::types::{AccountId, KeyType, SecretKey}; use crate::Worker; -pub use crate::network::account::{Account, Contract}; +pub use crate::network::account::{Account, AccountDetails, Contract}; +pub use crate::network::block::Block; pub use crate::network::mainnet::Mainnet; pub use crate::network::result::{CallExecution, CallExecutionDetails, ViewResultDetails}; pub use crate::network::sandbox::Sandbox; diff --git a/workspaces/src/rpc/client.rs b/workspaces/src/rpc/client.rs index 14594c61..65c1d229 100644 --- a/workspaces/src/rpc/client.rs +++ b/workspaces/src/rpc/client.rs @@ -15,7 +15,8 @@ use near_primitives::transaction::{ }; use near_primitives::types::{Balance, BlockId, Finality, Gas, StoreKey}; use near_primitives::views::{ - AccessKeyView, AccountView, ContractCodeView, FinalExecutionOutcomeView, QueryRequest, + AccessKeyView, AccountView, BlockView, ContractCodeView, FinalExecutionOutcomeView, + QueryRequest, }; use crate::network::ViewResultDetails; @@ -238,6 +239,18 @@ impl Client { } } + pub(crate) async fn view_block(&self, block_id: Option) -> anyhow::Result { + let block_reference = block_id + .map(Into::into) + .unwrap_or_else(|| Finality::None.into()); + + let block_view = self + .query(&methods::block::RpcBlockRequest { block_reference }) + .await?; + + Ok(block_view) + } + pub(crate) async fn deploy( &self, signer: &InMemorySigner, diff --git a/workspaces/src/types.rs b/workspaces/src/types.rs index bfbec6b5..e5a98dcd 100644 --- a/workspaces/src/types.rs +++ b/workspaces/src/types.rs @@ -2,11 +2,13 @@ /// and internal libraries like near-jsonrpc-client requires specific versions /// of these types which shouldn't be exposed either. use std::convert::TryFrom; +use std::fmt; use std::path::Path; pub use near_account_id::AccountId; pub(crate) use near_crypto::{KeyType, Signer}; -use near_primitives::serialize::from_base; +use near_primitives::logging::pretty_hash; +use near_primitives::serialize::{from_base, to_base}; use serde::{Deserialize, Serialize}; pub type Gas = u64; @@ -62,6 +64,7 @@ impl InMemorySigner { // type taken from near_primitives::hash::CryptoHash. /// CryptoHash is type for storing the hash of a specific block. +#[derive(Hash, PartialEq)] pub struct CryptoHash(pub [u8; 32]); impl std::str::FromStr for CryptoHash { @@ -93,3 +96,15 @@ impl TryFrom> for CryptoHash { >::try_from(v.as_ref()) } } + +impl fmt::Debug for CryptoHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", pretty_hash(&self.to_string())) + } +} + +impl fmt::Display for CryptoHash { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&to_base(&self.0), f) + } +} diff --git a/workspaces/src/worker/impls.rs b/workspaces/src/worker/impls.rs index 68dab131..b1fd596d 100644 --- a/workspaces/src/worker/impls.rs +++ b/workspaces/src/worker/impls.rs @@ -4,15 +4,15 @@ use async_trait::async_trait; use near_primitives::types::{Balance, StoreKey}; use crate::network::{ - Account, AllowDevAccountCreation, CallExecution, CallExecutionDetails, Contract, NetworkClient, - NetworkInfo, StatePatcher, TopLevelAccountCreator, ViewResultDetails, + Account, AllowDevAccountCreation, Block, CallExecution, CallExecutionDetails, Contract, + NetworkClient, NetworkInfo, StatePatcher, TopLevelAccountCreator, ViewResultDetails, }; use crate::network::{Info, Sandbox}; use crate::rpc::client::{Client, DEFAULT_CALL_DEPOSIT, DEFAULT_CALL_FN_GAS}; use crate::rpc::patch::ImportContractBuilder; use crate::types::{AccountId, Gas, InMemorySigner, SecretKey}; use crate::worker::Worker; -use crate::Network; +use crate::{AccountDetails, Network}; impl Clone for Worker { fn clone(&self) -> Self { @@ -87,6 +87,7 @@ where self.workspace.client() } + /// Call into a contract's change function. pub async fn call( &self, contract: &Contract, @@ -108,6 +109,7 @@ where .and_then(CallExecutionDetails::from_outcome) } + /// Call into a contract's view function. pub async fn view( &self, contract_id: &AccountId, @@ -119,6 +121,15 @@ where .await } + /// View the WASM code bytes of a contract on the network. + pub async fn view_code(&self, contract_id: &AccountId) -> anyhow::Result> { + let code_view = self.client().view_code(contract_id.clone(), None).await?; + Ok(code_view.code) + } + + /// View the state of a account/contract on the network. This will return the internal + /// state of the account in the form of a map of key-value pairs; where STATE contains + /// info on a contract's internal data. pub async fn view_state( &self, contract_id: &AccountId, @@ -127,6 +138,13 @@ where self.client().view_state(contract_id.clone(), prefix).await } + /// View the latest block from the network + pub async fn view_latest_block(&self) -> anyhow::Result { + self.client().view_block(None).await.map(Into::into) + } + + /// Transfer tokens from one account to another. The signer is the account + /// that will be used to to send from. pub async fn transfer_near( &self, signer: &InMemorySigner, @@ -139,6 +157,8 @@ where .and_then(CallExecutionDetails::from_outcome) } + /// Deletes an account from the network. The beneficiary will receive the balance + /// of the account deleted. pub async fn delete_account( &self, account_id: &AccountId, @@ -150,6 +170,14 @@ where .await .and_then(CallExecutionDetails::from_outcome) } + + /// View account details of a specific account on the network. + pub async fn view_account(&self, account_id: &AccountId) -> anyhow::Result { + self.client() + .view_account(account_id.clone(), None) + .await + .map(Into::into) + } } impl Worker {