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

Adds mainnet archival rpc and fixes ref-finance example #57

Merged
merged 6 commits into from
Jan 24, 2022
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
14 changes: 10 additions & 4 deletions examples/src/ref_finance.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
use std::{collections::HashMap, convert::TryInto};

use near_units::{parse_gas, parse_near};
use workspaces::{prelude::*, DevNetwork};
use workspaces::{prelude::*, BlockHeight, DevNetwork};
use workspaces::{Account, AccountId, Contract, Network, Worker};

const FT_CONTRACT_FILEPATH: &str = "./examples/res/fungible_token.wasm";

/// Contract id of ref-finance on mainnet.
const REF_FINANCE_ACCOUNT_ID: &str = "v2.ref-finance.near";

/// BlockId referencing back to a specific time just in case the contract has
/// changed or has been updated at a later time.
const BLOCK_HEIGHT: BlockHeight = 50_000_000;

/// Pull down the ref-finance contract and deploy it to the sandbox network,
/// initializing it with all data required to run the tests.
async fn create_ref(
owner: &Account,
worker: &Worker<impl Network + StatePatcher>,
) -> anyhow::Result<Contract> {
let mainnet = workspaces::mainnet();
let mainnet = workspaces::mainnet_archival();
let ref_finance_id: AccountId = REF_FINANCE_ACCOUNT_ID.parse()?;

// This will pull down the relevant ref-finance contract from mainnet. We're going
// to be overriding the initial balance with 1000N instead of what's on mainnet.
let ref_finance = worker
.import_contract(&ref_finance_id, &mainnet)
.with_initial_balance(parse_near!("1000 N"))
.initial_balance(parse_near!("1000 N"))
.block_height(BLOCK_HEIGHT)
.transact()
.await?;

Expand Down Expand Up @@ -54,10 +59,11 @@ async fn create_wnear(
owner: &Account,
worker: &Worker<impl Network + StatePatcher>,
) -> anyhow::Result<Contract> {
let mainnet = workspaces::mainnet();
let mainnet = workspaces::mainnet_archival();
let wnear_id: AccountId = "wrap.near".to_string().try_into()?;
let wnear = worker
.import_contract(&wnear_id, &mainnet)
.block_height(BLOCK_HEIGHT)
.transact()
.await?;

Expand Down
6 changes: 4 additions & 2 deletions workspaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ mod worker;
pub mod prelude;

pub use network::{Account, Contract, DevNetwork, Network};
pub use types::{AccountId, InMemorySigner};
pub use worker::{mainnet, sandbox, testnet, with_mainnet, with_sandbox, with_testnet, Worker};
pub use types::{AccountId, BlockHeight, CryptoHash, InMemorySigner};
pub use worker::{
mainnet, mainnet_archival, sandbox, testnet, with_mainnet, with_sandbox, with_testnet, Worker,
};
13 changes: 13 additions & 0 deletions workspaces/src/network/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::types::{AccountId, SecretKey};
use crate::Contract;

const RPC_URL: &str = "https://rpc.mainnet.near.org";
const ARCHIVAL_URL: &str = "https://archival-rpc.mainnet.near.org";

pub struct Mainnet {
client: Client,
Expand All @@ -27,6 +28,18 @@ impl Mainnet {
},
}
}

pub(crate) fn archival() -> Self {
Self {
client: Client::new(ARCHIVAL_URL.into()),
info: Info {
name: "mainnet-archival".into(),
root_id: "near".parse().unwrap(),
keystore_path: PathBuf::from(".near-credentials/mainnet/"),
rpc_url: ARCHIVAL_URL.into(),
},
}
}
}

#[async_trait]
Expand Down
47 changes: 31 additions & 16 deletions workspaces/src/rpc/patch.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use near_crypto::KeyType;
use near_jsonrpc_client::methods::sandbox_patch_state::RpcSandboxPatchStateRequest;
use near_primitives::{
account::AccessKey, hash::CryptoHash, state_record::StateRecord, types::Balance,
};
use near_primitives::types::BlockId;
use near_primitives::{account::AccessKey, state_record::StateRecord, types::Balance};

use crate::network::DEV_ACCOUNT_SEED;
use crate::rpc::client::Client;
use crate::types::SecretKey;
use crate::{AccountId, Contract, InMemorySigner};
use crate::types::{BlockHeight, SecretKey};
use crate::{AccountId, Contract, CryptoHash, InMemorySigner};

pub struct ImportContractBuilder<'a, 'b> {
account_id: AccountId,
Expand All @@ -20,6 +19,8 @@ pub struct ImportContractBuilder<'a, 'b> {
/// Initial balance of the account. If None, uses what is specified
/// from the other account instead.
initial_balance: Option<Balance>,

block_id: Option<BlockId>,
}

impl<'a, 'b> ImportContractBuilder<'a, 'b> {
Expand All @@ -34,63 +35,77 @@ impl<'a, 'b> ImportContractBuilder<'a, 'b> {
into_network,
import_data: false,
initial_balance: None,
block_id: None,
}
}

pub fn block_height(mut self, block_height: BlockHeight) -> Self {
self.block_id = Some(BlockId::Height(block_height));
self
}

pub fn block_hash(mut self, block_hash: CryptoHash) -> Self {
self.block_id = Some(BlockId::Hash(near_primitives::hash::CryptoHash(
block_hash.0,
)));
self
}

pub fn with_data(mut self) -> Self {
self.import_data = true;
self
}

pub fn with_initial_balance(mut self, initial_balance: Balance) -> Self {
pub fn initial_balance(mut self, initial_balance: Balance) -> Self {
self.initial_balance = Some(initial_balance);
self
}

pub async fn transact(self) -> anyhow::Result<Contract> {
let account_id = self.account_id;
let sk = SecretKey::from_seed(KeyType::ED25519, DEV_ACCOUNT_SEED);
let pk = sk.public_key();
let signer = InMemorySigner::from_secret_key(self.account_id.clone(), sk);
let signer = InMemorySigner::from_secret_key(account_id.clone(), sk);

let mut account_view = self
.from_network
.view_account(self.account_id.clone(), None)
.view_account(account_id.clone(), self.block_id.clone())
.await?;
if let Some(initial_balance) = self.initial_balance {
account_view.amount = initial_balance;
}

let mut records = vec![
StateRecord::Account {
account_id: self.account_id.clone(),
account_id: account_id.clone(),
account: account_view.clone().into(),
},
StateRecord::AccessKey {
account_id: self.account_id.clone(),
account_id: account_id.clone(),
public_key: pk.clone().into(),
access_key: AccessKey::full_access(),
},
];

if account_view.code_hash != CryptoHash::default() {
if account_view.code_hash != near_primitives::hash::CryptoHash::default() {
let code_view = self
.from_network
.view_code(self.account_id.clone(), None)
.view_code(account_id.clone(), self.block_id.clone())
.await?;
records.push(StateRecord::Contract {
account_id: self.account_id.clone(),
account_id: account_id.clone(),
code: code_view.code,
});
}

if self.import_data {
records.extend(
self.from_network
.view_state_raw(self.account_id.clone(), None, None)
.view_state_raw(account_id.clone(), None, self.block_id)
.await?
.into_iter()
.map(|(key, value)| StateRecord::Data {
account_id: self.account_id.clone(),
account_id: account_id.clone(),
data_key: key,
value,
}),
Expand All @@ -111,6 +126,6 @@ impl<'a, 'b> ImportContractBuilder<'a, 'b> {
.await
.map_err(|err| anyhow::anyhow!("Failed to patch state: {:?}", err))?;

Ok(Contract::new(self.account_id, signer))
Ok(Contract::new(account_id, signer))
}
}
39 changes: 39 additions & 0 deletions workspaces/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/// Types copied over from near_primitives since those APIs are not yet stable.
/// 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::path::Path;

pub use near_account_id::AccountId;
pub(crate) use near_crypto::{KeyType, Signer};
use near_primitives::serialize::from_base;
use serde::{Deserialize, Serialize};

pub type Gas = u64;
Expand All @@ -13,6 +15,9 @@ pub type Gas = u64;
/// in yoctoNear (1e-24).
pub type Balance = u128;

/// Height of a specific block
pub type BlockHeight = u64;

impl From<PublicKey> for near_crypto::PublicKey {
fn from(pk: PublicKey) -> Self {
pk.0
Expand Down Expand Up @@ -54,3 +59,37 @@ impl InMemorySigner {
&self.0
}
}

// type taken from near_primitives::hash::CryptoHash.
/// CryptoHash is type for storing the hash of a specific block.
pub struct CryptoHash(pub [u8; 32]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why use newtype for this over just an alias of [u8; 32] for simplicity?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just took whatever was in near_primitives. It's a newtype there, so stuck with that. I don't know if changing it to an alias of [u8; 32] would make it harder to transition later when near_primitives is 1.0

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would it be hard to transition? Just wrapping the bytes passed in with CryptoHash internally? Am I missing something with what you mean here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just thinking whether or not if we end up just exposing near_primitives::hash::CryptoHash later and its still just a newtype which would make it a breaking change to transition over to. But maybe not a huge concern if we force everyone to just rely on our CryptoHash from workspaces

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, the primitives type prob shouldn't be tied to our API. I'm not sure if I'm misunderstanding what you're saying here though. Why would we ever need to expose the primitives type to be a part of our API?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just thinking of this case, because there was a conversation we had about potentially exposing the near_primitives/near_account_id types once they released 1.0. But regardless of that, I think the newtype here would be better suited since it might be harder for users to create CryptoHashes if it was just an alias of [u8; 32]. The FromStr impl should allow us to create it easily


impl std::str::FromStr for CryptoHash {
type Err = Box<dyn std::error::Error>;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let bytes = from_base(s).map_err::<Self::Err, _>(|e| e.to_string().into())?;
Self::try_from(bytes)
}
}

impl TryFrom<&[u8]> for CryptoHash {
type Error = Box<dyn std::error::Error>;

fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if bytes.len() != 32 {
return Err("incorrect length for hash".into());
}
let mut buf = [0; 32];
buf.copy_from_slice(bytes);
Ok(CryptoHash(buf))
}
}

impl TryFrom<Vec<u8>> for CryptoHash {
type Error = Box<dyn std::error::Error>;

fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
<Self as TryFrom<&[u8]>>::try_from(v.as_ref())
}
}
4 changes: 4 additions & 0 deletions workspaces/src/worker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ pub fn mainnet() -> Worker<Mainnet> {
Worker::new(Mainnet::new())
}

pub fn mainnet_archival() -> Worker<Mainnet> {
Worker::new(Mainnet::archival())
}

pub async fn with_sandbox<F, T>(task: F) -> T::Output
where
F: Fn(Worker<Sandbox>) -> T,
Expand Down
3 changes: 3 additions & 0 deletions workspaces/tests/patch_state.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Required since `test_log` adds more recursion than the standard recursion limit of 128
#![recursion_limit = "256"]

use borsh::{self, BorshDeserialize, BorshSerialize};
use serde_json::json;
use test_log::test;
Expand Down