sumchain_primitives/node_registry.rs
1//! Node Registry types for SUM Chain.
2//!
3//! Defines the on-chain data structures for the node registry,
4//! which tracks network nodes beyond validators (e.g. Archive/Storage nodes).
5
6use serde::{Deserialize, Serialize};
7
8use crate::Address;
9
10/// Role a node can play in the network
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12#[repr(u8)]
13pub enum NodeRole {
14 /// Block-producing validator
15 Validator = 0,
16 /// Full archive/storage node
17 ArchiveNode = 1,
18}
19
20impl NodeRole {
21 pub fn from_byte(b: u8) -> Option<Self> {
22 match b {
23 0 => Some(NodeRole::Validator),
24 1 => Some(NodeRole::ArchiveNode),
25 _ => None,
26 }
27 }
28}
29
30/// Status of a registered node
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32#[repr(u8)]
33pub enum NodeStatus {
34 /// Node is active and in good standing
35 Active = 0,
36 /// Node has been slashed for misbehaviour
37 Slashed = 1,
38}
39
40impl NodeStatus {
41 pub fn from_byte(b: u8) -> Option<Self> {
42 match b {
43 0 => Some(NodeStatus::Active),
44 1 => Some(NodeStatus::Slashed),
45 _ => None,
46 }
47 }
48}
49
50/// On-chain record for a registered node
51#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52pub struct NodeRecord {
53 /// Node operator address
54 pub address: Address,
55 /// Role this node fulfils
56 pub role: NodeRole,
57 /// Staked balance in native Koppa base units
58 pub staked_balance: u64,
59 /// Current status
60 pub status: NodeStatus,
61 /// Block height at which the node was registered
62 pub registered_at: u64,
63}
64
65/// Operations that can be performed on the node registry
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub enum NodeRegistryOperation {
68 /// Register a new node with a role and initial stake
69 Register {
70 role: NodeRole,
71 stake: u64,
72 },
73 /// Update a node's status (e.g. slash)
74 UpdateStatus {
75 target: Address,
76 new_status: NodeStatus,
77 },
78}
79
80/// Transaction data for node registry operations
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub struct NodeRegistryTxData {
83 pub operation: NodeRegistryOperation,
84}
85
86// ─── V2 Operations ───────────────────────────────────────────────────────────
87
88/// V2 node registry operations. Additive — V1 `NodeRegistryOperation` is unchanged.
89///
90/// Currently carries account-level X25519 encryption-key registration for
91/// SNIP V2 Ask 3. May grow to cover other V2 account-level keying ops over time.
92#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
93pub enum NodeRegistryOperationV2 {
94 /// Register (or rotate) the X25519 encryption pubkey for the signer's address.
95 ///
96 /// One key per account; subsequent calls overwrite. The pubkey is a 32-byte
97 /// Montgomery U-coordinate (RFC 7748). Clamping is on the private scalar,
98 /// not the public, so no clamping check applies. **The executor does**
99 /// reject the seven libsodium-blocklisted low/small-order encodings (and
100 /// their high-bit-set variants), via
101 /// `sumchain_crypto::is_low_order_x25519_public_key`, with
102 /// `TxStatus::Failed(22)` and reason
103 /// `"low-order x25519 public key rejected"` — this prevents griefing where
104 /// a registered low-order pubkey would force every legitimate sender's
105 /// wrap operation to fail.
106 ///
107 /// Rotation does not retro-revoke previously-issued bundles encrypted under
108 /// the prior X25519 pubkey — the old private key still decrypts old bundles.
109 /// Reissuing bundles after rotation is the client's (SNIP's) responsibility.
110 RegisterEncryptionKey {
111 encryption_pubkey: [u8; 32],
112 },
113}
114
115/// Transaction data for V2 node registry operations.
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub struct NodeRegistryV2TxData {
118 pub operation: NodeRegistryOperationV2,
119}