nula_core/limits.rs
1//! Hard limits enforced by the Nostr protocol primitives.
2//!
3//! Centralising every spec-mandated byte length, length cap, and
4//! tunable cost ceiling in a single namespace makes it easy to:
5//!
6//! - bound caller-side validation against the same numbers the
7//! parsers and validators use internally;
8//! - audit the surface for drift relative to the cited NIPs;
9//! - cite the value in user-facing documentation without chasing the
10//! constant down through nested modules.
11//!
12//! Each entry below is either a re-export of the canonical constant
13//! that lives next to its consumer, or a `pub const` defined here when
14//! the source value is currently `pub(crate)` / private but still
15//! protocol-binding.
16//!
17//! # Stability
18//!
19//! Removing a constant from this module is a breaking change. Adding
20//! new constants is additive. Renaming requires a deprecation cycle.
21
22/// Length, in bytes, of the SHA-256 event id mandated by NIP-01.
23pub use crate::event::id::EVENT_ID_SIZE as EVENT_ID_BYTES;
24/// Length, in bytes, of a serialised BIP-340 Schnorr signature.
25pub use crate::key::keys::SIGNATURE_SIZE as SIGNATURE_BYTES;
26/// Length, in bytes, of an x-only BIP-340 public key.
27pub use crate::key::public_key::PUBLIC_KEY_SIZE as PUBLIC_KEY_BYTES;
28/// Length, in bytes, of a BIP-340 secret key.
29pub use crate::key::secret_key::SECRET_KEY_SIZE as SECRET_KEY_BYTES;
30/// Maximum length of a [`crate::message::SubscriptionId`], measured
31/// in Unicode scalar values.
32pub use crate::message::subscription_id::MAX_LENGTH as SUBSCRIPTION_ID_MAX_CHARS;
33/// Maximum length, in characters, of a NIP-19 bech32 string the
34/// decoder will accept before rejecting the input outright.
35pub use crate::nips::nip19::MAX_NIP19_LENGTH as NIP19_MAX_LENGTH;
36
37/// Smallest plaintext length the NIP-44 v2 encryptor accepts.
38///
39/// Spec: <https://github.com/nostr-protocol/nips/blob/master/44.md#encryption>.
40pub const NIP44_MIN_PLAINTEXT_BYTES: usize = 1;
41
42/// Largest plaintext length the NIP-44 v2 encryptor accepts.
43///
44/// Spec: <https://github.com/nostr-protocol/nips/blob/master/44.md#encryption>.
45pub const NIP44_MAX_PLAINTEXT_BYTES: usize = 65_535;
46
47/// Largest *raw* payload length (header + nonce + ciphertext + MAC)
48/// the NIP-44 v2 decoder will accept before base64 encoding.
49///
50/// Spec: <https://github.com/nostr-protocol/nips/blob/master/44.md#decoding>.
51pub const NIP44_MAX_PAYLOAD_BYTES: usize = 65_603;
52
53/// Largest `log_n` (scrypt cost factor) the encoder will accept.
54///
55/// `log_n = 30` corresponds to ~1 GiB of working memory and many
56/// minutes of CPU on commodity hardware — far above any sane
57/// production setting. The cap exists to keep accidentally-pathological
58/// values from wedging a decryptor.
59#[cfg(feature = "nip49")]
60#[cfg_attr(docsrs, doc(cfg(feature = "nip49")))]
61pub use crate::nips::nip49::MAX_LOG_N as NIP49_MAX_LOG_N;
62/// Length, in bytes, of the XChaCha20-Poly1305 nonce embedded in
63/// an `ncryptsec`.
64#[cfg(feature = "nip49")]
65#[cfg_attr(docsrs, doc(cfg(feature = "nip49")))]
66pub use crate::nips::nip49::NONCE_BYTES as NIP49_NONCE_BYTES;
67/// Length, in bytes, of the scrypt salt embedded in an `ncryptsec`.
68#[cfg(feature = "nip49")]
69#[cfg_attr(docsrs, doc(cfg(feature = "nip49")))]
70pub use crate::nips::nip49::SALT_BYTES as NIP49_SALT_BYTES;
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 /// Drift detector: every spec value is repeated here so a future
77 /// refactor that silently changes a constant (e.g. through a
78 /// rebase mishap) trips this test before it reaches a release.
79 #[test]
80 fn pinned_values_match_spec() {
81 assert_eq!(EVENT_ID_BYTES, 32);
82 assert_eq!(SIGNATURE_BYTES, 64);
83 assert_eq!(PUBLIC_KEY_BYTES, 32);
84 assert_eq!(SECRET_KEY_BYTES, 32);
85 assert_eq!(SUBSCRIPTION_ID_MAX_CHARS, 64);
86 assert_eq!(NIP19_MAX_LENGTH, 5_000);
87 assert_eq!(NIP44_MIN_PLAINTEXT_BYTES, 1);
88 assert_eq!(NIP44_MAX_PLAINTEXT_BYTES, 65_535);
89 assert_eq!(NIP44_MAX_PAYLOAD_BYTES, 65_603);
90 #[cfg(feature = "nip49")]
91 {
92 assert_eq!(NIP49_SALT_BYTES, 16);
93 assert_eq!(NIP49_NONCE_BYTES, 24);
94 assert_eq!(NIP49_MAX_LOG_N, 30);
95 }
96 }
97}