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: api key and custom network object #306

Merged
merged 8 commits into from Oct 4, 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Added

- RPC API Keys used to interact with services such as Pagoda Console.
- [Import a couple functions over from near_crypto for PublicKey](https://github.com/near/workspaces-rs/pull/265)
- Impl `Ord`, `PartialOrd`, `Hash`, `BorshSerialize`, `BorshDeserialize`, `Display`, and `FromStr` for `PublicKey`
- NOTE: Borsh bytes format is the same as near-sdk, where it is in the form of [bytes_len, key_type, key_data..]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,4 +346,5 @@ These environment variables will be useful if there was ever a snag hit:
- `NEAR_SANDBOX_BIN_PATH`: Set this to our own prebuilt `neard-sandbox` bin path if we want to use a non-default version of the sandbox or configure nearcore with our own custom features that we want to test in workspaces.
- `NEAR_SANDBOX_MAX_PAYLOAD_SIZE`: Sets the max payload size for sending transaction commits to sandbox. The default is 1gb and is necessary for patching large states.
- `NEAR_SANDBOX_MAX_FILES`: Set the max amount of files that can be opened at a time in the sandbox. If none is specified, the default size of 4096 will be used. The actual near chain will use over 10,000 in practice, but for testing this should be much lower since we do not have a constantly running blockchain unless our tests take up that much time.
- `NEAR_RPC_API_KEY`: This is the API key necessary for communicating with RPC nodes. This is useful when interacting with services such as Pagoda Console or a service that can access RPC metrics. This is not a **hard** requirement, but it is recommended to running the Pagoda example in the examples folder.
- `NEAR_ENABLE_SANDBOX_LOG`: Set this to `1` to enable sandbox logging. This is useful for debugging issues with the `neard-sandbox` binary.
7 changes: 5 additions & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ serde_json = { version = "1.0" }
tokio = { version = "1.10.0", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3.5", features = ["env-filter"] }
workspaces = { path = "../workspaces", features = ["experimental"] }

workspaces = { path = "../workspaces", features = ["experimental", "unstable"] }

[[example]]
name = "async_transaction"
Expand Down Expand Up @@ -84,3 +83,7 @@ path = "src/tx_status.rs"
[[example]]
name = "noop"
path = "src/noop.rs"

[[example]]
name = "custom_network"
path = "src/custom_network.rs"
21 changes: 21 additions & 0 deletions examples/src/custom_network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// URL to the Pagoda API to use for testnet.
pub const PAGODA_TESTNET_RPC_URL: &str = "https://near-testnet.api.pagoda.co/rpc/v1/";

#[tokio::main]
async fn main() -> anyhow::Result<()> {
// `NEAR_RPC_API_KEY="xxx" cargo run --package examples --example custom_network`
if let Ok(val) = std::env::var("NEAR_RPC_API_KEY") {
// Reference to what can be called by this network: https://docs.pagoda.co/endpoints
let worker = workspaces::custom(PAGODA_TESTNET_RPC_URL)
.api_key(&val)
.await?;
let res = worker.view_block().await?;

assert!(res.height() > 0);
return Ok(());
}

// skip the test
println!("NEAR_RPC_API_KEY is not set, skipping the example");
return Ok(());
}
3 changes: 3 additions & 0 deletions workspaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ pub use worker::{
betanet, mainnet, mainnet_archival, sandbox, testnet, testnet_archival, with_betanet,
with_mainnet, with_mainnet_archival, with_sandbox, with_testnet, with_testnet_archival, Worker,
};

#[cfg(feature = "unstable")]
pub use worker::{custom, with_custom};
2 changes: 1 addition & 1 deletion workspaces/src/network/betanet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct Betanet {
impl FromNetworkBuilder for Betanet {
async fn from_builder<'a>(build: NetworkBuilder<'a, Self>) -> crate::result::Result<Self> {
let rpc_url = build.rpc_addr.unwrap_or_else(|| RPC_URL.into());
let client = Client::new(&rpc_url);
let client = Client::new(&rpc_url, build.api_key)?;
client.wait_for_rpc().await?;

Ok(Self {
Expand Down
12 changes: 12 additions & 0 deletions workspaces/src/network/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub struct NetworkBuilder<'a, T> {
pub(crate) name: &'a str,
pub(crate) rpc_addr: Option<String>,
pub(crate) validator_key: Option<ValidatorKey>,
pub(crate) api_key: Option<String>,
_network: PhantomData<T>,
}

Expand All @@ -54,6 +55,7 @@ impl<'a, T> NetworkBuilder<'a, T> {
name,
rpc_addr: None,
validator_key: None,
api_key: None,
_network: PhantomData,
}
}
Expand All @@ -69,6 +71,16 @@ impl<'a, T> NetworkBuilder<'a, T> {
self.rpc_addr = Some(addr.into());
self
}

/// Sets the API key for this network. Useful for setting the API key to an RPC
/// server that requires it.
///
/// Note that if you're using a custom network, the burden is on you to ensure that
/// the methods you're calling are supported by the RPC server you're connecting to.
pub fn api_key(mut self, api_key: &str) -> Self {
self.api_key = Some(api_key.into());
self
}
}

// So far, only Sandbox makes use of validator_key.
Expand Down
54 changes: 54 additions & 0 deletions workspaces/src/network/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::network::{Info, NetworkClient, NetworkInfo};
use crate::result::Result;
use crate::rpc::client::Client;
use std::path::PathBuf;

use super::builder::{FromNetworkBuilder, NetworkBuilder};

/// Holds information about a custom network.
pub struct Custom {
client: Client,
info: Info,
}

#[async_trait::async_trait]
impl FromNetworkBuilder for Custom {
async fn from_builder<'a>(build: NetworkBuilder<'a, Self>) -> Result<Self> {
let rpc_url = build
.rpc_addr
.expect("rpc address should be provided for custom network");
let client = Client::new(&rpc_url, build.api_key)?;
client.wait_for_rpc().await?;

Ok(Self {
client,
info: Info {
name: build.name.into(),
root_id: "near".parse().unwrap(),
keystore_path: PathBuf::from(".near-credentials/mainnet/"),
rpc_url: url::Url::parse(&rpc_url).expect("custom provided url should be valid"),
},
})
}
}

impl std::fmt::Debug for Custom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Custom")
.field("root_id", &self.info.root_id)
.field("rpc_url", &self.info.rpc_url)
.finish()
}
}

impl NetworkClient for Custom {
fn client(&self) -> &Client {
&self.client
}
}

impl NetworkInfo for Custom {
fn info(&self) -> &Info {
&self.info
}
}
2 changes: 1 addition & 1 deletion workspaces/src/network/mainnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct Mainnet {
impl FromNetworkBuilder for Mainnet {
async fn from_builder<'a>(build: NetworkBuilder<'a, Self>) -> Result<Self> {
let rpc_url = build.rpc_addr.unwrap_or_else(|| RPC_URL.into());
let client = Client::new(&rpc_url);
let client = Client::new(&rpc_url, build.api_key)?;
client.wait_for_rpc().await?;

Ok(Self {
Expand Down
2 changes: 2 additions & 0 deletions workspaces/src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ pub(crate) mod builder;
pub(crate) mod variants;

pub mod betanet;
pub mod custom;
pub mod mainnet;
pub mod testnet;

pub(crate) use variants::DEV_ACCOUNT_SEED;

pub use self::betanet::Betanet;
pub use self::custom::Custom;
pub use self::info::Info;
pub use self::mainnet::Mainnet;
pub use self::sandbox::Sandbox;
Expand Down
2 changes: 1 addition & 1 deletion workspaces/src/network/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ impl FromNetworkBuilder for Sandbox {
}
};

let client = Client::new(&server.rpc_addr());
let client = Client::new(&server.rpc_addr(), build.api_key)?;
client.wait_for_rpc().await?;

// Server locks some ports on startup due to potential port collision, so we need
Expand Down
2 changes: 1 addition & 1 deletion workspaces/src/network/testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct Testnet {
impl FromNetworkBuilder for Testnet {
async fn from_builder<'a>(build: NetworkBuilder<'a, Self>) -> Result<Self> {
let rpc_url = build.rpc_addr.unwrap_or_else(|| RPC_URL.into());
let client = Client::new(&rpc_url);
let client = Client::new(&rpc_url, build.api_key)?;
client.wait_for_rpc().await?;

Ok(Self {
Expand Down
13 changes: 9 additions & 4 deletions workspaces/src/rpc/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,20 @@ pub struct Client {
}

impl Client {
pub(crate) fn new(rpc_addr: &str) -> Self {
pub(crate) fn new(rpc_addr: &str, api_key: Option<String>) -> Result<Self> {
let connector = JsonRpcClient::new_client();
let rpc_client = connector.connect(rpc_addr);
let mut rpc_client = connector.connect(rpc_addr);
if let Some(api_key) = api_key {
let api_key = near_jsonrpc_client::auth::ApiKey::new(api_key)
.map_err(|e| ErrorKind::DataConversion.custom(e))?;
rpc_client = rpc_client.header(api_key);
}

Self {
Ok(Self {
rpc_client,
rpc_addr: rpc_addr.into(),
access_key_nonces: RwLock::new(HashMap::new()),
}
})
}

pub(crate) async fn query_broadcast_tx(
Expand Down
18 changes: 17 additions & 1 deletion workspaces/src/worker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt;
use std::sync::Arc;

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

/// The `Worker` type allows us to interact with any NEAR related networks, such
Expand Down Expand Up @@ -76,6 +76,13 @@ pub fn betanet<'a>() -> NetworkBuilder<'a, Betanet> {
NetworkBuilder::new("betanet")
}

/// Connect to a custom network, and grab a [`Worker`] that can interact with it.
///
/// Note: the burden of ensuring the methods that are able to be called are left up to the user.
pub fn custom<'a>(rpc_url: &str) -> NetworkBuilder<'a, Custom> {
NetworkBuilder::new("custom").rpc_addr(rpc_url)
}

/// Run a locally scoped task where a [`sandbox`] instanced [`Worker`] is supplied.
pub async fn with_sandbox<F, T>(task: F) -> Result<T::Output>
where
Expand Down Expand Up @@ -129,3 +136,12 @@ where
{
Ok(task(betanet().await?).await)
}

#[allow(dead_code)]
pub async fn with_custom<F, T>(task: F, rpc_url: &str) -> Result<T::Output>
where
F: Fn(Worker<Custom>) -> T,
T: core::future::Future,
{
Ok(task(custom(rpc_url).await?).await)
}
Loading