/ node / tests / common / test_peer.rs
test_peer.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaOS library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use alphaos_account::Account;
 17  use alphaos_node_network::NodeType;
 18  use alphaos_node_router::{
 19      expect_message,
 20      messages::{ChallengeRequest, ChallengeResponse, Message, MessageCodec, MessageTrait},
 21  };
 22  use alphavm::{
 23      ledger::narwhal::Data,
 24      prelude::{Address, Field, FromBytes, MainnetV0 as CurrentNetwork, Network, TestRng, block::Block},
 25  };
 26  
 27  use std::{
 28      io,
 29      net::{IpAddr, Ipv4Addr, SocketAddr},
 30      str::FromStr,
 31  };
 32  
 33  use futures_util::{TryStreamExt, sink::SinkExt};
 34  use pea2pea::{
 35      Config,
 36      Connection,
 37      ConnectionSide,
 38      Node,
 39      Pea2Pea,
 40      protocols::{Handshake, OnDisconnect, Reading, Writing},
 41  };
 42  use rand::Rng;
 43  use tokio_util::codec::Framed;
 44  use tracing::*;
 45  
 46  const ALEO_MAXIMUM_FORK_DEPTH: u32 = 4096;
 47  
 48  /// Returns a fixed account.
 49  pub fn sample_account() -> Account<CurrentNetwork> {
 50      Account::<CurrentNetwork>::from_str("APrivateKey1zkp2oVPTci9kKcUprnbzMwq95Di1MQERpYBhEeqvkrDirK1").unwrap()
 51  }
 52  
 53  /// Loads the current network's genesis block.
 54  pub fn sample_genesis_block() -> Block<CurrentNetwork> {
 55      Block::<CurrentNetwork>::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap()
 56  }
 57  
 58  #[derive(Clone)]
 59  pub struct TestPeer {
 60      node: Node,
 61      node_type: NodeType,
 62      account: Account<CurrentNetwork>,
 63  }
 64  
 65  impl Pea2Pea for TestPeer {
 66      fn node(&self) -> &Node {
 67          &self.node
 68      }
 69  }
 70  
 71  impl TestPeer {
 72      pub async fn client() -> Self {
 73          Self::new(NodeType::Client, sample_account()).await
 74      }
 75  
 76      pub async fn prover() -> Self {
 77          Self::new(NodeType::Prover, sample_account()).await
 78      }
 79  
 80      pub async fn validator() -> Self {
 81          Self::new(NodeType::Validator, sample_account()).await
 82      }
 83  
 84      pub async fn new(node_type: NodeType, account: Account<CurrentNetwork>) -> Self {
 85          let peer = Self {
 86              node: Node::new(Config {
 87                  max_connections: 200,
 88                  listener_addr: Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0)),
 89                  ..Default::default()
 90              }),
 91              node_type,
 92              account,
 93          };
 94  
 95          peer.enable_handshake().await;
 96          peer.enable_reading().await;
 97          peer.enable_writing().await;
 98          peer.enable_disconnect().await;
 99  
100          peer.node().start_listening().await.unwrap();
101  
102          peer
103      }
104  
105      pub fn node_type(&self) -> NodeType {
106          self.node_type
107      }
108  
109      pub fn account(&self) -> &Account<CurrentNetwork> {
110          &self.account
111      }
112  
113      pub fn address(&self) -> Address<CurrentNetwork> {
114          self.account.address()
115      }
116  }
117  
118  impl Handshake for TestPeer {
119      async fn perform_handshake(&self, mut conn: Connection) -> io::Result<Connection> {
120          let rng = &mut TestRng::default();
121  
122          let local_ip = self.node().listening_addr().expect("listening address should be present");
123  
124          let peer_addr = conn.addr();
125          let node_side = !conn.side();
126          let stream = self.borrow_stream(&mut conn);
127          let mut framed = Framed::new(stream, MessageCodec::<CurrentNetwork>::default());
128  
129          // Retrieve the genesis block header.
130          let genesis_header = *sample_genesis_block().header();
131          // Retrieve the restrictions ID.
132          let restrictions_id = Field::<CurrentNetwork>::from_str(
133              "7562506206353711030068167991213732850758501012603348777370400520506564970105field",
134          )
135          .unwrap();
136  
137          // TODO(nkls): add assertions on the contents of messages.
138          match node_side {
139              ConnectionSide::Initiator => {
140                  // Send a challenge request to the peer.
141                  let our_request =
142                      ChallengeRequest::new(local_ip.port(), self.node_type(), self.address(), rng.r#gen(), None);
143                  framed.send(Message::ChallengeRequest(our_request)).await?;
144  
145                  // Receive the peer's challenge bundle.
146                  let _peer_response = expect_message!(Message::ChallengeResponse, framed, peer_addr);
147                  let peer_request = expect_message!(Message::ChallengeRequest, framed, peer_addr);
148  
149                  // Sign the nonce.
150                  let response_nonce: u64 = rng.r#gen();
151                  let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
152                  let signature = self.account().sign_bytes(&data, rng).unwrap();
153  
154                  // Send the challenge response.
155                  let our_response = ChallengeResponse {
156                      genesis_header,
157                      restrictions_id,
158                      signature: Data::Object(signature),
159                      nonce: response_nonce,
160                  };
161                  framed.send(Message::ChallengeResponse(our_response)).await?;
162              }
163              ConnectionSide::Responder => {
164                  // Listen for the challenge request.
165                  let peer_request = expect_message!(Message::ChallengeRequest, framed, peer_addr);
166  
167                  // Sign the nonce.
168                  let response_nonce: u64 = rng.r#gen();
169                  let data = [peer_request.nonce.to_le_bytes(), response_nonce.to_le_bytes()].concat();
170                  let signature = self.account().sign_bytes(&data, rng).unwrap();
171  
172                  // Send our challenge bundle.
173                  let our_response = ChallengeResponse {
174                      genesis_header,
175                      restrictions_id,
176                      signature: Data::Object(signature),
177                      nonce: response_nonce,
178                  };
179                  framed.send(Message::ChallengeResponse(our_response)).await?;
180                  let our_request =
181                      ChallengeRequest::new(local_ip.port(), self.node_type(), self.address(), rng.r#gen(), None);
182                  framed.send(Message::ChallengeRequest(our_request)).await?;
183  
184                  // Listen for the challenge response.
185                  let _peer_response = expect_message!(Message::ChallengeResponse, framed, peer_addr);
186              }
187          }
188  
189          Ok(conn)
190      }
191  }
192  
193  impl Writing for TestPeer {
194      type Codec = MessageCodec<CurrentNetwork>;
195      type Message = Message<CurrentNetwork>;
196  
197      fn codec(&self, _addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
198          Default::default()
199      }
200  }
201  
202  impl Reading for TestPeer {
203      type Codec = MessageCodec<CurrentNetwork>;
204      type Message = Message<CurrentNetwork>;
205  
206      fn codec(&self, _peer_addr: SocketAddr, _side: ConnectionSide) -> Self::Codec {
207          Default::default()
208      }
209  
210      async fn process_message(&self, _peer_ip: SocketAddr, _message: Self::Message) -> io::Result<()> {
211          Ok(())
212      }
213  }
214  
215  impl OnDisconnect for TestPeer {
216      async fn on_disconnect(&self, _peer_addr: SocketAddr) {}
217  }