nectar_primitives/
signing.rs1use alloy_primitives::Address;
38
39use crate::{NetworkId, Nonce, SwarmAddress, Timestamp};
40
41pub const SIGN_DATA_PREFIX: &[u8] = b"bee-handshake-";
43
44#[must_use]
52pub fn sign_data(
53 underlay_bytes: &[u8],
54 overlay: &SwarmAddress,
55 network_id: NetworkId,
56 nonce: &Nonce,
57 timestamp: Timestamp,
58 chequebook: Option<&Address>,
59) -> Vec<u8> {
60 let mut buf = Vec::with_capacity(
61 SIGN_DATA_PREFIX.len()
62 + underlay_bytes.len()
63 + 32 + 8 + 32 + 8 + 20, );
69 buf.extend_from_slice(SIGN_DATA_PREFIX);
70 buf.extend_from_slice(underlay_bytes);
71 buf.extend_from_slice(overlay.as_bytes());
72 buf.extend_from_slice(&network_id.to_be_bytes());
73 buf.extend_from_slice(nonce.as_slice());
74 buf.extend_from_slice(×tamp.to_be_bytes());
75 match chequebook {
76 Some(addr) => buf.extend_from_slice(addr.as_slice()),
77 None => buf.extend_from_slice(&[0u8; 20]),
78 }
79 buf
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::compute_overlay;
86 use alloy_primitives::{address, b256};
87 use alloy_signer::SignerSync;
88 use alloy_signer_local::LocalSigner;
89
90 #[test]
91 fn layout_matches_bee_spec() {
92 let overlay = SwarmAddress::new([0xaa; 32]);
93 let net = NetworkId::MAINNET;
94 let nonce = Nonce::new([0xbb; 32]);
95 let ts = Timestamp::from(0x0102_0304_0506_0708_i64);
96 let cb = address!("00112233445566778899aabbccddeeff00112233");
97 let underlay = b"\x01\x02\x03";
98
99 let buf = sign_data(underlay, &overlay, net, &nonce, ts, Some(&cb));
100
101 assert_eq!(&buf[0..14], b"bee-handshake-");
102 assert_eq!(&buf[14..17], b"\x01\x02\x03");
103 assert_eq!(&buf[17..49], &[0xaa; 32]);
104 assert_eq!(&buf[49..57], &1u64.to_be_bytes());
105 assert_eq!(&buf[57..89], &[0xbb; 32]);
106 assert_eq!(
107 &buf[89..97],
108 &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
109 );
110 assert_eq!(&buf[97..117], cb.as_slice());
111 assert_eq!(buf.len(), 117);
112 }
113
114 #[test]
115 fn no_chequebook_is_byte_identical_to_zero_chequebook() {
116 let a = sign_data(
117 &[],
118 &SwarmAddress::zero(),
119 NetworkId::MAINNET,
120 &Nonce::ZERO,
121 Timestamp::ZERO,
122 None,
123 );
124 let b = sign_data(
125 &[],
126 &SwarmAddress::zero(),
127 NetworkId::MAINNET,
128 &Nonce::ZERO,
129 Timestamp::ZERO,
130 Some(&Address::ZERO),
131 );
132 assert_eq!(a, b);
133 assert_eq!(&a[a.len() - 20..], &[0u8; 20]);
134 }
135
136 #[test]
140 fn caller_flow_sign_recover_verify_overlay() {
141 let signer = LocalSigner::random();
142 let eth = signer.address();
143 let net = NetworkId::TESTNET;
144 let nonce = Nonce::new([0x55; 32]);
145 let overlay = compute_overlay(ð, net, &nonce);
146 let ts = Timestamp::from(1_700_000_000_i64);
147
148 let data = sign_data(b"/ip4/127.0.0.1/tcp/1634", &overlay, net, &nonce, ts, None);
149
150 let sig = signer.sign_message_sync(&data).expect("sign");
152 let recovered = sig.recover_address_from_msg(&data).expect("recover");
154 assert_eq!(recovered, eth);
155 assert_eq!(compute_overlay(&recovered, net, &nonce), overlay);
157 }
158
159 #[test]
163 fn tampered_nonce_breaks_overlay_check() {
164 let signer = LocalSigner::random();
165 let eth = signer.address();
166 let net = NetworkId::MAINNET;
167 let nonce = Nonce::from(b256!(
168 "0202020202020202020202020202020202020202020202020202020202020202"
169 ));
170 let overlay = compute_overlay(ð, net, &nonce);
171 let wrong_nonce = Nonce::new([0x88; 32]);
172 assert_ne!(compute_overlay(ð, net, &wrong_nonce), overlay);
173 }
174}