Skip to content
72 changes: 71 additions & 1 deletion crates/mina-verify-capture/src/rpc_net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ use libp2p::swarm::{
use libp2p::{Multiaddr, PeerId};
use void::Void;

use crate::rpc::RpcConn;
use crate::transport::ed25519::{Keypair as EdKeypair, SecretKey};
use mina_p2p_messages::v2::MinaBlockBlockStableV2;
use mina_p2p_messages::rpc::AnswerSyncLedgerQueryV2;
use mina_p2p_messages::v2::{
LedgerHash, MinaBlockBlockStableV2, MinaLedgerSyncLedgerAnswerStableV2,
MinaLedgerSyncLedgerQueryStableV1,
};

const RPC_PROTOCOL: StreamProtocol = StreamProtocol::new("coda/rpcs/0.0.1");

Expand Down Expand Up @@ -183,3 +188,68 @@ pub async fn fetch_best_tip(
_ = tokio::time::sleep(deadline) => Err("deadline reached before fetching best tip".into()),
}
}

/// Connect to a peer and issue a batch of `answer_sync_ledger_query` queries against one
/// ledger root, returning the answers in query order. The transport for trustless account
/// reads on-device: pair with `mina_verify::sync_ledger_queries` /
/// `mina_verify::verify_account_at_root`. Runs the queries over one persistent [`RpcConn`]
/// while keeping the swarm polled, exactly as [`fetch_best_tip`].
pub async fn fetch_sync_ledger_answers(
chain_id: &str,
peers: &[&str],
ledger_hash: LedgerHash,
queries: &[MinaLedgerSyncLedgerQueryStableV1],
deadline: Duration,
) -> Result<Vec<MinaLedgerSyncLedgerAnswerStableV2>, String> {
let addrs: Vec<Multiaddr> = peers
.iter()
.map(|s| s.parse().expect("multiaddr"))
.collect();
let local_key: libp2p::identity::Keypair = EdKeypair::from(SecretKey::generate()).into();
let pnet_input = format!("/coda/0.0.1/{chain_id}");

let mut swarm = crate::transport::swarm(
local_key,
pnet_input.as_bytes(),
Vec::<Multiaddr>::new(),
addrs.iter().cloned(),
RpcBehaviour::default(),
);

let root = ledger_hash.0.clone();

let run = async {
let stream = loop {
match swarm.next().await {
Some(SwarmEvent::Behaviour(stream)) => break stream,
_ => {}
}
};
let walk = async {
let mut conn = RpcConn::open(stream).await?;
let mut answers = Vec::with_capacity(queries.len());
for query in queries {
let resp = conn
.call::<AnswerSyncLedgerQueryV2>(&(root.clone(), query.clone()))
.await?;
let answer = resp
.0
.map_err(|e| format!("sync-ledger rpc error: {e:?}"))?;
answers.push(answer);
}
Ok::<_, String>(answers)
};
let mut walk = std::pin::pin!(walk);
loop {
tokio::select! {
_ = swarm.next() => {}
r = &mut walk => return r,
}
}
};

tokio::select! {
r = run => r,
_ = tokio::time::sleep(deadline) => Err("deadline reached before sync-ledger answers".into()),
}
}
17 changes: 13 additions & 4 deletions crates/mina-verify-capture/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,26 @@ where
.with_tokio()
.with_other_transport(|local_key| {
let pnet = pnet::PnetConfig::new(pnet::PreSharedKey::new(pnet_key));
tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
let base = tcp::tokio::Transport::new(tcp::Config::default().nodelay(true))
.and_then(move |socket, _| pnet.handshake(socket))
.upgrade(upgrade::Version::V1)
.authenticate(noise::Config::new(local_key).expect("libp2p-noise static keypair"))
.multiplex(yamux)
.timeout(std::time::Duration::from_secs(20))
.boxed()
.boxed();
// Resolve `/dns4/…` seed multiaddrs with an explicit resolver config rather
// than the OS one. libp2p's `.with_dns()` reads `/etc/resolv.conf`, which does
// not exist on Android (libp2p-dns docs note it "fails (panics even!)" there) —
// so on a phone the seeds never resolve and we hang forever on "Connecting".
// Cloudflare's 1.1.1.1 is hard-coded here so the same path works on every OS.
libp2p::dns::tokio::Transport::custom(
base,
libp2p::dns::ResolverConfig::cloudflare(),
libp2p::dns::ResolverOpts::default(),
)
.boxed()
})
.unwrap()
.with_dns()
.unwrap()
.with_behaviour(|_| behaviour)
.unwrap()
.with_swarm_config(|c| c.with_idle_connection_timeout(std::time::Duration::from_secs(300)))
Expand Down
Loading
Loading