hashiverse_lib/protocol/rpc/
rpc_response.rs1use crate::protocol::payload::payload::PayloadResponseKind;
18use crate::tools::server_id::ServerId;
19use crate::tools::time::{TimeMillis, TimeMillisBytes, TIME_MILLIS_BYTES};
20use 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};
21use crate::tools::{compression, config, signing, BytesGatherer};
22use bitflags::bitflags;
23use bytes::{Buf, Bytes};
24
25bitflags! {
26 pub struct RpcResponsePacketTxFlags: u8 {
27 const COMPRESSED = 1 << 0;
28 }
29}
30
31pub struct RpcResponsePacketTx;
43
44impl RpcResponsePacketTx {
45 #[allow(clippy::too_many_arguments)] pub fn encode(
47 server_id_signature_key: &SignatureKey,
48 server_id_verification_key_bytes: &VerificationKeyBytes,
49 server_id_pq_commitment_bytes: &PQCommitmentBytes,
50 server_id_verification_sponsor_id: &Id,
51 server_id_timestamp: &TimeMillis,
52 server_id_hash: &Hash,
53 server_id_salt: &Salt,
54 pow_content_hash: &Hash,
55 flags: RpcResponsePacketTxFlags,
56 payload_response_kind: PayloadResponseKind,
57 payload_uncompressed: BytesGatherer,
58 ) -> anyhow::Result<BytesGatherer> {
59 let payload_compressed: BytesGatherer = match flags.contains(RpcResponsePacketTxFlags::COMPRESSED) {
61 true => compression::compress_for_speed(&payload_uncompressed.to_bytes())?,
62 false => payload_uncompressed,
63 };
64
65 let payload_compressed_len = payload_compressed.len();
66
67 if payload_compressed_len > config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE {
69 anyhow::bail!("response payload size exceeds maximum allowed size: {} > {}", payload_compressed_len, config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE);
70 }
71
72 let pow_content_hash_signature = signing::sign(server_id_signature_key, pow_content_hash.as_ref());
73
74 let mut result = BytesGatherer::default();
76 result.put_u8(1); result.put_u8(flags.bits());
78 result.put_u16_le(payload_response_kind as u16);
79 result.put_slice(server_id_verification_key_bytes.as_ref());
80 result.put_slice(server_id_pq_commitment_bytes.as_ref());
81 result.put_slice(server_id_verification_sponsor_id.as_ref());
82 result.put_slice(server_id_timestamp.encode_be().as_ref());
83 result.put_slice(server_id_hash.as_ref());
84 result.put_slice(server_id_salt.as_ref());
85 result.put_slice(pow_content_hash_signature.as_ref());
86 result.put_u32_le(payload_compressed_len as u32);
87 result.put_bytes_gatherer(payload_compressed);
88
89 Ok(result)
90 }
91}
92
93pub struct RpcResponsePacketRx {
104 pub response_request_kind: PayloadResponseKind,
105 pub bytes: Bytes,
106}
107
108impl RpcResponsePacketRx {
109 pub fn decode(destination_id: &Id, pow_content_hash: &Hash, pow_min: Pow, mut response_bytes: Bytes) -> anyhow::Result<Self> {
110 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 + size_of::<u32>() {
112 anyhow::bail!("RpcResponsePacket is too short for header");
113 }
114
115 let version = response_bytes.get_u8();
116 if 1 != version {
117 anyhow::bail!("Unsupported RpcRequestPacket version: {}", version);
118 }
119
120 let flags = RpcResponsePacketTxFlags::from_bits(response_bytes.get_u8()).ok_or_else(|| anyhow::anyhow!("Invalid RpcResponsePacket flags"))?;
121 let response_request_kind = PayloadResponseKind::from_u16(response_bytes.get_u16_le())?;
122 let server_id_verification_key = VerificationKeyBytes(response_bytes.slice(..VERIFICATION_KEY_BYTES).as_ref().try_into()?);
123 response_bytes.advance(VERIFICATION_KEY_BYTES);
124 let server_id_pq_commitment_bytes = PQCommitmentBytes(response_bytes.slice(..PQ_COMMITMENT_BYTES).as_ref().try_into()?);
125 response_bytes.advance(PQ_COMMITMENT_BYTES);
126 let server_id_verification_sponsor_id = Id(response_bytes.slice(..ID_BYTES).as_ref().try_into()?);
127 response_bytes.advance(ID_BYTES);
128 let server_id_verification_timestamp_bytes: TimeMillisBytes = TimeMillisBytes(response_bytes.slice(..TIME_MILLIS_BYTES).as_ref().try_into()?);
129 response_bytes.advance(TIME_MILLIS_BYTES);
130 let server_id_verification_hash = Hash(response_bytes.slice(..HASH_BYTES).as_ref().try_into()?);
131 response_bytes.advance(HASH_BYTES);
132 let server_id_verification_salt = Salt(response_bytes.slice(..SALT_BYTES).as_ref().try_into()?);
133 response_bytes.advance(SALT_BYTES);
134
135 let pow_content_hash_signature: Signature = Signature(response_bytes.slice(..SIGNATURE_BYTES).as_ref().try_into()?);
136 response_bytes.advance(SIGNATURE_BYTES);
137
138 let response_payload_len = response_bytes.get_u32_le() as usize;
139
140 if response_payload_len > config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE {
141 anyhow::bail!("RpcResponsePacket payload too large: {} > {}", response_payload_len, config::PROTOCOL_MAX_BLOB_SIZE_RESPONSE);
142 }
143
144 if response_bytes.len() < response_payload_len {
146 anyhow::bail!("RpcResponsePacket is too short for payload");
147 }
148
149 let response_payload = response_bytes.slice(..response_payload_len);
150 response_bytes.advance(response_payload_len);
151
152 if !response_bytes.is_empty() {
154 anyhow::bail!("RpcResponsePacket is too long");
155 }
156
157 let (pow, pow_hash) = ServerId::pow_measure(
159 &server_id_verification_sponsor_id,
160 &server_id_verification_key,
161 &server_id_pq_commitment_bytes,
162 &server_id_verification_timestamp_bytes,
163 &server_id_verification_hash,
164 &server_id_verification_salt,
165 )?;
166 if pow < pow_min {
167 anyhow::bail!(format!("Server ID pow is not sufficient: {} < {}", pow, pow_min));
168 }
169
170 let id = ServerId::server_pow_hash_to_id(pow_hash)?;
172 if id != *destination_id {
173 if !destination_id.is_zero() {
174 anyhow::bail!("Server ID verification failed");
175 }
176 }
177
178 let verification_key = server_id_verification_key.to_verification_key()?;
180 signing::verify(&verification_key, &pow_content_hash_signature, pow_content_hash.as_ref())?;
181
182 let response_payload_decompressed = match flags.contains(RpcResponsePacketTxFlags::COMPRESSED) {
184 true => compression::decompress(response_payload.as_ref())?.to_bytes(),
185 false => response_payload,
186 };
187
188 Ok(Self {
189 response_request_kind,
190 bytes: response_payload_decompressed,
191 })
192 }
193}