use snarkos_account::Account;
use snarkos_node_network::NodeType;
use snarkos_node_router::{
expect_message,
messages::{ChallengeRequest, ChallengeResponse, Message, MessageCodec, MessageTrait},
};
use snarkos_node_tcp::ConnectError;
use snarkvm::{
console::network::{MainnetV0 as CurrentNetwork, Network},
ledger::{block::Block, narwhal::Data},
prelude::{Address, Field, FromBytes, TestRng},
utilities::into_io_error,
};
use std::{
io,
net::{IpAddr, Ipv4Addr, SocketAddr},
str::FromStr,
};
use futures_util::{TryStreamExt, sink::SinkExt};
use pea2pea::{
Config,
Connection,
ConnectionSide,
Node,
Pea2Pea,
protocols::{Handshake, OnDisconnect, Reading, Writing},
};
use rand::Rng;
use tokio_util::codec::Framed;
use tracing::*;
const ALEO_MAXIMUM_FORK_DEPTH: u32 = 4096;
pub fn sample_account() -> Account<CurrentNetwork> {
Account::<CurrentNetwork>::from_str("APrivateKey1zkp2oVPTci9kKcUprnbzMwq95Di1MQERpYBhEeqvkrDirK1").unwrap()
}
pub fn sample_genesis_block() -> Block<CurrentNetwork> {
Block::<CurrentNetwork>::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap()
}
#[derive(Clone)]
pub struct TestPeer {
node: Node,
node_type: NodeType,
account: Account<CurrentNetwork>,
}
impl Pea2Pea for TestPeer {
fn node(&self) -> &Node {
&self.node
}
}
impl TestPeer {
pub async fn client() -> Self {
Self::new(NodeType::Client, sample_account()).await
}
pub async fn prover() -> Self {
Self::new(NodeType::Prover, sample_account()).await
}
pub async fn validator() -> Self {
Self::new(NodeType::Validator, sample_account()).await
}
pub async fn new(node_type: NodeType, account: Account<CurrentNetwork>) -> Self {
let peer = Self {
node: Node::new(Config {
max_connections: 200,
listener_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
..Default::default()
}),
node_type,
account,
};
peer.enable_handshake().await;
peer.enable_reading().await;
peer.enable_writing().await;
peer.enable_disconnect().await;
peer.node().start_listening().await.unwrap();
peer
}
pub fn node_type(&self) -> NodeType {
self.node_type
}
pub fn account(&self) -> &Account<CurrentNetwork> {
&self.account
}
pub fn address(&self) -> Address<CurrentNetwork> {
self.account.address()
}
}
impl Handshake for TestPeer {
async fn perform_handshake(&self, conn: Connection) -> io::Result<Connection> {
self.perform_handshake_inner(conn).await.map_err(into_io_error)
}
}
impl TestPeer {
async fn perform_handshake_inner(&self, mut conn: Connection) -> Result<Connection, ConnectError> {
let rng = &mut TestRng::default();
let local_ip = self.node().listening_addr().expect("listening address should be present");
let peer_addr = conn.addr();
let node_side = !conn.side();
let stream = self.borrow_stream(&mut conn);
let mut framed = Framed::new(stream, MessageCodec::<CurrentNetwork>::default());
let genesis_header = *sample_genesis_block().header();
let restrictions_id = Field::<CurrentNetwork>::from_str(
"7562506206353711030068167991213732850758501012603348777370400520506564970105field",
)
.unwrap();
match node_side {
ConnectionSide::Initiator => {
let our_request =
ChallengeRequest::new(local_ip.port(), self.node_type(), self.address(), rng.r#gen(), None);
framed.send(Message::ChallengeRequest(our_request)).await?;
let _peer_response = expect_message!(Message::ChallengeResponse, framed, peer_addr);
let peer_request = expect_message!(Message::ChallengeRequest, framed, peer_addr);
let response_nonce: u64 = rng.r#gen();
let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
let signature = self.account().sign_bytes(&data, rng).unwrap();
let our_response = ChallengeResponse {
genesis_header,
restrictions_id,
signature: Data::Object(signature),
nonce: response_nonce,
};
framed.send(Message::ChallengeResponse(our_response)).await?;
}
ConnectionSide::Responder => {
let peer_request = expect_message!(Message::ChallengeRequest, framed, peer_addr);
let response_nonce: u64 = rng.r#gen();
let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
let signature = self.account().sign_bytes(&data, rng).unwrap();
let our_response = ChallengeResponse {
genesis_header,
restrictions_id,
signature: Data::Object(signature),
nonce: response_nonce,
};
framed.send(Message::ChallengeResponse(our_response)).await?;
let our_request =
ChallengeRequest::new(local_ip.port(), self.node_type(), self.address(), rng.r#gen(), None);
framed.send(Message::ChallengeRequest(our_request)).await?;
let _peer_response = expect_message!(Message::ChallengeResponse, framed, peer_addr);
}
}
Ok(conn)
}
}
impl Writing for TestPeer {
type Codec = MessageCodec<CurrentNetwork>;
type Message = Message<CurrentNetwork>;
fn codec(&self, _addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
Default::default()
}
}
impl Reading for TestPeer {
type Codec = MessageCodec<CurrentNetwork>;
type Message = Message<CurrentNetwork>;
fn codec(&self, _peer_addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
Default::default()
}
async fn process_message(&self, _peer_ip: SocketAddr, _message: Self::Message) -> io::Result<()> {
Ok(())
}
}
impl OnDisconnect for TestPeer {
async fn on_disconnect(&self, _peer_addr: SocketAddr) {}
}