saito_core/core/msg/
handshake.rs1use std::io::{Error, ErrorKind};
2
3use log::{trace, warn};
4
5use crate::core::consensus::peers::peer_service::PeerService;
6use crate::core::defs::{SaitoHash, SaitoPublicKey, SaitoSignature};
7use crate::core::process::version::Version;
8use crate::core::util::serialize::Serialize;
9
10#[derive(Debug)]
11pub struct HandshakeChallenge {
12 pub challenge: SaitoHash,
13}
14
15#[derive(Debug)]
16pub struct HandshakeResponse {
17 pub public_key: SaitoPublicKey,
18 pub signature: SaitoSignature,
19 pub is_lite: bool,
20 pub block_fetch_url: String,
21 pub challenge: SaitoHash,
22 pub services: Vec<PeerService>,
23 pub wallet_version: Version,
24 pub core_version: Version,
25}
26
27impl Serialize<Self> for HandshakeChallenge {
28 fn serialize(&self) -> Vec<u8> {
29 let buffer = [self.challenge.to_vec()].concat();
30 return buffer;
31 }
32 fn deserialize(buffer: &Vec<u8>) -> Result<Self, Error> {
33 if buffer.len() < 32 {
34 warn!(
35 "Deserializing Handshake Challenge, buffer size is :{:?}",
36 buffer.len()
37 );
38 return Err(Error::from(ErrorKind::InvalidData));
39 }
40
41 let mut challenge = HandshakeChallenge { challenge: [0; 32] };
42 challenge.challenge = buffer[0..32]
43 .to_vec()
44 .try_into()
45 .or(Err(Error::from(ErrorKind::InvalidInput)))?;
46
47 Ok(challenge)
48 }
49}
50
51impl Serialize<Self> for HandshakeResponse {
52 fn serialize(&self) -> Vec<u8> {
53 [
54 self.core_version.serialize(),
55 self.wallet_version.serialize(),
56 self.public_key.to_vec(),
57 self.signature.to_vec(),
58 self.challenge.to_vec(),
59 (self.is_lite as u8).to_be_bytes().to_vec(),
60 (self.block_fetch_url.len() as u32).to_be_bytes().to_vec(),
61 self.block_fetch_url.as_bytes().to_vec(),
62 PeerService::serialize_services(&self.services),
63 ]
64 .concat()
65 }
66 fn deserialize(buffer: &Vec<u8>) -> Result<Self, Error> {
67 trace!("deserializing handshake buffer : {:?}", buffer.len());
68
69 const MIN_LEN: usize = 142;
70
71 if buffer.len() < MIN_LEN {
72 warn!(
73 "Deserializing failed for handshake response, buffer size is :{:?}",
74 buffer.len()
75 );
76 return Err(Error::from(ErrorKind::InvalidData));
77 }
78
79 let mut response = HandshakeResponse {
80 core_version: Version::deserialize(&buffer[0..4].to_vec())?,
81 wallet_version: Version::deserialize(&buffer[4..8].to_vec())?,
82 public_key: buffer[8..41]
83 .to_vec()
84 .try_into()
85 .or(Err(Error::from(ErrorKind::InvalidInput)))?,
86 signature: buffer[41..105]
87 .to_vec()
88 .try_into()
89 .or(Err(Error::from(ErrorKind::InvalidInput)))?,
90 challenge: buffer[105..137]
91 .to_vec()
92 .try_into()
93 .or(Err(Error::from(ErrorKind::InvalidInput)))?,
94 is_lite: buffer[137] != 0,
95 block_fetch_url: "".to_string(),
96 services: vec![],
97 };
98 let url_length = u32::from_be_bytes(
99 buffer[138..142]
100 .try_into()
101 .or(Err(Error::from(ErrorKind::InvalidInput)))?,
102 ) as usize;
103
104 if url_length > 0 {
106 if buffer.len() < MIN_LEN + url_length {
107 warn!(
108 "cannot read block fetch url of size : {:?} from buffer size : {:?}",
109 url_length,
110 buffer.len()
111 );
112 return Err(Error::from(ErrorKind::InvalidData));
113 }
114 trace!("reading URL with length : {:?}", url_length);
115 let result = String::from_utf8(buffer[MIN_LEN..(MIN_LEN + url_length)].to_vec());
116 if result.is_err() {
117 warn!(
118 "failed decoding block fetch url. {:?}",
119 result.err().unwrap()
120 );
121 return Err(Error::from(ErrorKind::InvalidData));
122 }
123
124 response.block_fetch_url = result.unwrap();
125 trace!("block fetch url read as : {:?}", response.block_fetch_url);
126 }
127
128 if buffer.len() > (MIN_LEN + url_length) {
130 trace!("reading peer services");
131 let service_buffer = buffer[(MIN_LEN + url_length)..].to_vec();
132
133 let services = PeerService::deserialize_services(service_buffer);
134 if services.is_err() {
135 let len = buffer.len() - (MIN_LEN + url_length);
136 warn!(
137 "Deserializing failed for handshake response, remaining buffer of size :{:?} cannot be parsed for peer services", len);
138 return Err(Error::from(ErrorKind::InvalidData));
139 }
140 let services = services.unwrap();
141 trace!("{:?} services read from handshake response", services.len());
142 response.services = services;
143 }
144
145 Ok(response)
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use crate::core::msg::handshake::{HandshakeChallenge, HandshakeResponse};
152 use crate::core::process::version::Version;
153 use crate::core::util::serialize::Serialize;
154
155 #[test]
156 fn test_handshake() {
157 let crypto = secp256k1::Secp256k1::new();
158
159 let (_secret_key_1, _public_key_1) =
160 crypto.generate_keypair(&mut secp256k1::rand::thread_rng());
161 let (secret_key_2, public_key_2) =
162 crypto.generate_keypair(&mut secp256k1::rand::thread_rng());
163 let challenge = HandshakeChallenge {
164 challenge: rand::random(),
165 };
166 let buffer = challenge.serialize();
167 assert_eq!(buffer.len(), 32);
168 let challenge2 = HandshakeChallenge::deserialize(&buffer).expect("deserialization failed");
169 assert_eq!(challenge.challenge, challenge2.challenge);
170
171 let signature = crypto.sign_ecdsa(
172 &secp256k1::Message::from_slice(&challenge.challenge).unwrap(),
173 &secret_key_2,
174 );
175 let response = HandshakeResponse {
176 public_key: public_key_2.serialize(),
177 signature: signature.serialize_compact(),
178 challenge: rand::random(),
179 is_lite: false,
180 block_fetch_url: "http://url/test2".to_string(),
181 services: vec![],
182 wallet_version: Version {
183 major: 1,
184 minor: 2,
185 patch: 3,
186 },
187 core_version: Version::new(10, 20, 30),
188 };
189 let buffer = response.serialize();
190 assert_eq!(buffer.len(), 158);
191 let response2 = HandshakeResponse::deserialize(&buffer).expect("deserialization failed");
192 assert_eq!(response.challenge, response2.challenge);
193 assert_eq!(response.public_key, response2.public_key);
194 assert_eq!(response.block_fetch_url, response2.block_fetch_url);
195
196 assert_eq!(response.signature, response2.signature);
197 assert_eq!(response.wallet_version, response2.wallet_version);
198 assert_eq!(response.core_version, response2.core_version);
199 }
200}