Skip to content

Commit

Permalink
fix: prevent firewall permission popups on MacOS
Browse files Browse the repository at this point in the history
Fixes near#276.
  • Loading branch information
agostbiro committed Jun 27, 2023
1 parent c31a955 commit 411b91f
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 18 deletions.
1 change: 0 additions & 1 deletion workspaces/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ cargo-near = "0.3.1"
chrono = "0.4.19"
fs2 = "0.4"
hex = "0.4.2"
portpicker = "0.1.1"
rand = "0.8.4"
reqwest = { version = "0.11", features = ["json"] }
serde = "1.0"
Expand Down
1 change: 1 addition & 0 deletions workspaces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod result;
pub mod rpc;
pub mod types;

pub use network::pick_unused_port;
pub use network::variants::{DevNetwork, Network};
pub use result::Result;
pub use types::account::{Account, AccountDetails, Contract};
Expand Down
2 changes: 1 addition & 1 deletion workspaces/src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub use self::betanet::Betanet;
pub use self::info::Info;
pub use self::mainnet::Mainnet;
pub use self::sandbox::Sandbox;
pub use self::server::ValidatorKey;
pub use self::server::{pick_unused_port, ValidatorKey};
pub use self::testnet::Testnet;
pub use self::variants::{
AllowDevAccountCreation, NetworkClient, NetworkInfo, TopLevelAccountCreator,
Expand Down
62 changes: 51 additions & 11 deletions workspaces/src/network/server.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs::File;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::path::PathBuf;

use crate::error::{ErrorKind, SandboxErrorCode};
Expand All @@ -8,21 +9,41 @@ use crate::types::SecretKey;
use async_process::Child;
use fs2::FileExt;
use near_account_id::AccountId;
use portpicker::pick_unused_port;
use reqwest::Url;
use tempfile::TempDir;
use tracing::info;

use near_sandbox_utils as sandbox;
use tokio::net::TcpListener;

pub const DEFAULT_RPC_URL: &str = "http://localhost";
// Must be an IP address as `neard` expects socket address for network address.
const DEFAULT_RPC_HOST: &str = "127.0.0.1";

fn rpc_socket(port: u16) -> String {
format!("{DEFAULT_RPC_HOST}:{}", port)
}

/// Request an unused port from the OS.
pub async fn pick_unused_port() -> Result<u16> {
// Port 0 means the OS gives us an unused port
// Important to use localhost as using 0.0.0.0 leads to users getting brief firewall popups to
// allow inbound connections on MacOS.
let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
let listener = TcpListener::bind(addr)
.await
.map_err(|err| ErrorKind::Io.full("failed to bind to random port", err))?;
let port = listener
.local_addr()
.map_err(|err| ErrorKind::Io.full("failed to get local address for random port", err))?
.port();
Ok(port)
}

/// Acquire an unused port and lock it for the duration until the sandbox server has
/// been started.
fn acquire_unused_port() -> Result<(u16, File)> {
async fn acquire_unused_port() -> Result<(u16, File)> {
loop {
let port = pick_unused_port()
.ok_or_else(|| SandboxErrorCode::InitFailure.message("no ports free"))?;
let port = pick_unused_port().await?;
let lockpath = std::env::temp_dir().join(format!("near-sandbox-port{}.lock", port));
let lockfile = File::create(lockpath).map_err(|err| {
ErrorKind::Io.full(format!("failed to create lockfile for port {}", port), err)
Expand Down Expand Up @@ -90,18 +111,37 @@ impl SandboxServer {
crate::network::config::set_sandbox_configs(&home_dir)?;

// Try running the server with the follow provided rpc_ports and net_ports
let (rpc_port, rpc_port_lock) = acquire_unused_port()?;
let (net_port, net_port_lock) = acquire_unused_port()?;
let rpc_addr = format!("{}:{}", DEFAULT_RPC_URL, rpc_port);
// This is guaranteed to be a valid URL, since this is using the default URL.
let rpc_addr = Url::parse(&rpc_addr).unwrap();
let (rpc_port, rpc_port_lock) = acquire_unused_port().await?;
let (net_port, net_port_lock) = acquire_unused_port().await?;
// It's important that the address doesn't have a scheme, since the sandbox expects
// a valid socket address.
let rpc_addr = rpc_socket(rpc_port);
let net_addr = rpc_socket(net_port);

info!(target: "workspaces", "Starting up sandbox at localhost:{}", rpc_port);
let child = sandbox::run(&home_dir, rpc_port, net_port)

let options = &[
"--home",
home_dir
.as_os_str()
.to_str()
.expect("home_dir is valid utf8"),
"run",
"--rpc-addr",
&rpc_addr,
"--network-addr",
&net_addr,
];

let child = sandbox::run_with_options(options)
.map_err(|e| SandboxErrorCode::RunFailure.custom(e))?;

info!(target: "workspaces", "Started up sandbox at localhost:{} with pid={:?}", rpc_port, child.id());

let rpc_addr: Url = format!("http://{rpc_addr}")
.parse()
.expect("static scheme and host name with variable u16 port numbers form valid urls");

Ok(Self {
validator_key: ValidatorKey::HomeDir(home_dir),
rpc_addr,
Expand Down
8 changes: 3 additions & 5 deletions workspaces/tests/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use test_log::test;

use workspaces::network::{Sandbox, ValidatorKey};
use workspaces::Worker;
use workspaces::{pick_unused_port, Worker};

const NFT_WASM_FILEPATH: &str = "../examples/res/non_fungible_token.wasm";
const EXPECTED_NFT_METADATA: &str = r#"{
Expand Down Expand Up @@ -59,10 +59,8 @@ async fn test_dev_deploy() -> anyhow::Result<()> {

#[test(tokio::test)]
async fn test_manually_spawned_deploy() -> anyhow::Result<()> {
let rpc_port =
portpicker::pick_unused_port().ok_or_else(|| anyhow::anyhow!("no free ports"))?;
let net_port =
portpicker::pick_unused_port().ok_or_else(|| anyhow::anyhow!("no free ports"))?;
let rpc_port = pick_unused_port().await?;
let net_port = pick_unused_port().await?;
let mut home_dir = std::env::temp_dir();
home_dir.push(format!("test-sandbox-{}", rpc_port));

Expand Down

0 comments on commit 411b91f

Please sign in to comment.