hashiverse_lib/protocol/rpc/
rpc_response.rs1use crate::protocol::payload::payload::PayloadResponseKind;
24use crate::tools::server_id::ServerId;
25use crate::tools::time::{TimeMillis, TimeMillisBytes, TIME_MILLIS_BYTES};
26use crate::tools::types::{Hash, Id, PQCommitmentBytes, Pow, Salt, Signature, SignatureKey, VerificationKeyBytes, HASH_BYTES, ID_BYTES, PQ_COMMITMENT_BYTES, SALT_BYTES, SIGNATURE_BYTES, VERIFICATION_KEY_BYTES};
27use crate::tools::{compression, config, hashing, signing, BytesGatherer};
28use bitflags::bitflags;
29use bytes::{Buf, Bytes};
30
31pub const RPC_RESPONSE_PACKET_VERSION: u8 = 2;
34
35bitflags! {
36 pub struct RpcResponsePacketTxFlags: u8 {
37 const COMPRESSED = 1 << 0;
38 }
39}
40
41pub struct RpcResponsePacketTx;
55
56impl RpcResponsePacketTx {
57 #[allow(clippy::too_many_arguments)] pub fn encode(
59 server_id_signature_key: &SignatureKey,
60 server_id_verification_key_bytes: &VerificationKeyBytes,
61 server_id_pq_commitment_bytes: &PQCommitmentBytes,
62 server_id_verification_sponsor_id: &Id,
63 server_id_timestamp: &TimeMillis,
64 server_id_hash: &Hash,
65 server_id_salt: &Salt,
66 pow_content_hash: &Hash,
67 flags: RpcResponsePacketTxFlags,
68 payload_response_kind: PayloadResponseKind,
69 payload_uncompressed: BytesGatherer,
70 ) -> anyhow::Result<BytesGatherer> {
71 let payload_compressed: BytesGatherer = match flags.contains(RpcResponsePacketTxFlags::COMPRESSED) {
73 true => compression::compress_for_speed(&payload_uncompressed.to_bytes())?,
74 false => payload_uncompressed,
75 };
76
77 let payload_compressed_len = payload_compressed.len();
78
79 if payload_compressed_len > config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE {
81 anyhow::bail!("response payload size exceeds maximum allowed size: {} > {}", payload_compressed_len, config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE);
82 }
83
84 let pow_content_hash_salt = Salt::random();
85 let pow_content_hash_salted = hashing::hash_two(pow_content_hash_salt.as_ref(), pow_content_hash.as_ref());
86 let pow_content_hash_signature = signing::sign(server_id_signature_key, pow_content_hash_salted.as_ref());
87
88 let mut result = BytesGatherer::default();
90 result.put_u8(RPC_RESPONSE_PACKET_VERSION);
91 result.put_u8(flags.bits());
92 result.put_u16_le(payload_response_kind as u16);
93 result.put_slice(server_id_verification_key_bytes.as_ref());
94 result.put_slice(server_id_pq_commitment_bytes.as_ref());
95 result.put_slice(server_id_verification_sponsor_id.as_ref());
96 result.put_slice(server_id_timestamp.encode_be().as_ref());
97 result.put_slice(server_id_hash.as_ref());
98 result.put_slice(server_id_salt.as_ref());
99 result.put_slice(pow_content_hash_signature.as_ref());
100 result.put_slice(pow_content_hash_salt.as_ref());
101 result.put_u32_le(payload_compressed_len as u32);
102 result.put_bytes_gatherer(payload_compressed);
103
104 Ok(result)
105 }
106}
107
108pub struct RpcResponsePacketRx {
119 pub response_request_kind: PayloadResponseKind,
120 pub bytes: Bytes,
121}
122
123impl RpcResponsePacketRx {
124 pub fn decode(destination_id: &Id, pow_content_hash: &Hash, pow_min: Pow, mut response_bytes: Bytes) -> anyhow::Result<Self> {
125 if response_bytes.len() < size_of::<u8>() + size_of::<u8>() + size_of::<u16>() + VERIFICATION_KEY_BYTES + PQ_COMMITMENT_BYTES + ID_BYTES + TIME_MILLIS_BYTES + HASH_BYTES + SALT_BYTES + SIGNATURE_BYTES + SALT_BYTES + size_of::<u32>() {
127 anyhow::bail!("RpcResponsePacket is too short for header");
128 }
129
130 let version = response_bytes.get_u8();
131 if RPC_RESPONSE_PACKET_VERSION != version {
132 anyhow::bail!("Unsupported RpcResponsePacket version: {}", version);
133 }
134
135 let flags = RpcResponsePacketTxFlags::from_bits(response_bytes.get_u8()).ok_or_else(|| anyhow::anyhow!("Invalid RpcResponsePacket flags"))?;
136 let response_request_kind = PayloadResponseKind::from_u16(response_bytes.get_u16_le())?;
137 let server_id_verification_key = VerificationKeyBytes(response_bytes.slice(..VERIFICATION_KEY_BYTES).as_ref().try_into()?);
138 response_bytes.advance(VERIFICATION_KEY_BYTES);
139 let server_id_pq_commitment_bytes = PQCommitmentBytes(response_bytes.slice(..PQ_COMMITMENT_BYTES).as_ref().try_into()?);
140 response_bytes.advance(PQ_COMMITMENT_BYTES);
141 let server_id_verification_sponsor_id = Id(response_bytes.slice(..ID_BYTES).as_ref().try_into()?);
142 response_bytes.advance(ID_BYTES);
143 let server_id_verification_timestamp_bytes: TimeMillisBytes = TimeMillisBytes(response_bytes.slice(..TIME_MILLIS_BYTES).as_ref().try_into()?);
144 response_bytes.advance(TIME_MILLIS_BYTES);
145 let server_id_verification_hash = Hash(response_bytes.slice(..HASH_BYTES).as_ref().try_into()?);
146 response_bytes.advance(HASH_BYTES);
147 let server_id_verification_salt = Salt(response_bytes.slice(..SALT_BYTES).as_ref().try_into()?);
148 response_bytes.advance(SALT_BYTES);
149
150 let pow_content_hash_signature: Signature = Signature(response_bytes.slice(..SIGNATURE_BYTES).as_ref().try_into()?);
151 response_bytes.advance(SIGNATURE_BYTES);
152 let pow_content_hash_salt = Salt(response_bytes.slice(..SALT_BYTES).as_ref().try_into()?);
153 response_bytes.advance(SALT_BYTES);
154
155 let response_payload_len = response_bytes.get_u32_le() as usize;
156
157 if response_payload_len > config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE {
158 anyhow::bail!("RpcResponsePacket payload too large: {} > {}", response_payload_len, config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE);
159 }
160
161 if response_bytes.len() < response_payload_len {
163 anyhow::bail!("RpcResponsePacket is too short for payload");
164 }
165
166 let response_payload = response_bytes.slice(..response_payload_len);
167 response_bytes.advance(response_payload_len);
168
169 if !response_bytes.is_empty() {
171 anyhow::bail!("RpcResponsePacket is too long");
172 }
173
174 let (pow, pow_hash) = ServerId::pow_measure(
176 &server_id_verification_sponsor_id,
177 &server_id_verification_key,
178 &server_id_pq_commitment_bytes,
179 &server_id_verification_timestamp_bytes,
180 &server_id_verification_hash,
181 &server_id_verification_salt,
182 )?;
183 if pow < pow_min {
184 anyhow::bail!(format!("Server ID pow is not sufficient: {} < {}", pow, pow_min));
185 }
186
187 let id = ServerId::server_pow_hash_to_id(pow_hash)?;
189 if id != *destination_id {
190 if !destination_id.is_zero() {
191 anyhow::bail!("Server ID verification failed");
192 }
193 }
194
195 let verification_key = server_id_verification_key.to_verification_key()?;
197 let pow_content_hash_salted = hashing::hash_two(pow_content_hash_salt.as_ref(), pow_content_hash.as_ref());
198 signing::verify(&verification_key, &pow_content_hash_signature, pow_content_hash_salted.as_ref())?;
199
200 let response_payload_decompressed = match flags.contains(RpcResponsePacketTxFlags::COMPRESSED) {
202 true => compression::decompress(response_payload.as_ref())?.to_bytes(),
203 false => response_payload,
204 };
205
206 Ok(Self {
207 response_request_kind,
208 bytes: response_payload_decompressed,
209 })
210 }
211}