use alloy_primitives::Address;
use crate::{NetworkId, Nonce, SwarmAddress, Timestamp};
pub const SIGN_DATA_PREFIX: &[u8] = b"bee-handshake-";
#[must_use]
pub fn sign_data(
underlay_bytes: &[u8],
overlay: &SwarmAddress,
network_id: NetworkId,
nonce: &Nonce,
timestamp: Timestamp,
chequebook: Option<&Address>,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(
SIGN_DATA_PREFIX.len()
+ underlay_bytes.len()
+ 32 + 8 + 32 + 8 + 20, );
buf.extend_from_slice(SIGN_DATA_PREFIX);
buf.extend_from_slice(underlay_bytes);
buf.extend_from_slice(overlay.as_bytes());
buf.extend_from_slice(&network_id.to_be_bytes());
buf.extend_from_slice(nonce.as_slice());
buf.extend_from_slice(×tamp.to_be_bytes());
match chequebook {
Some(addr) => buf.extend_from_slice(addr.as_slice()),
None => buf.extend_from_slice(&[0u8; 20]),
}
buf
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compute_overlay;
use alloy_primitives::{address, b256};
use alloy_signer::SignerSync;
use alloy_signer_local::LocalSigner;
#[test]
fn layout_matches_bee_spec() {
let overlay = SwarmAddress::new([0xaa; 32]);
let net = NetworkId::MAINNET;
let nonce = Nonce::new([0xbb; 32]);
let ts = Timestamp::from(0x0102_0304_0506_0708_i64);
let cb = address!("00112233445566778899aabbccddeeff00112233");
let underlay = b"\x01\x02\x03";
let buf = sign_data(underlay, &overlay, net, &nonce, ts, Some(&cb));
assert_eq!(&buf[0..14], b"bee-handshake-");
assert_eq!(&buf[14..17], b"\x01\x02\x03");
assert_eq!(&buf[17..49], &[0xaa; 32]);
assert_eq!(&buf[49..57], &1u64.to_be_bytes());
assert_eq!(&buf[57..89], &[0xbb; 32]);
assert_eq!(
&buf[89..97],
&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
);
assert_eq!(&buf[97..117], cb.as_slice());
assert_eq!(buf.len(), 117);
}
#[test]
fn no_chequebook_is_byte_identical_to_zero_chequebook() {
let a = sign_data(
&[],
&SwarmAddress::zero(),
NetworkId::MAINNET,
&Nonce::ZERO,
Timestamp::ZERO,
None,
);
let b = sign_data(
&[],
&SwarmAddress::zero(),
NetworkId::MAINNET,
&Nonce::ZERO,
Timestamp::ZERO,
Some(&Address::ZERO),
);
assert_eq!(a, b);
assert_eq!(&a[a.len() - 20..], &[0u8; 20]);
}
#[test]
fn caller_flow_sign_recover_verify_overlay() {
let signer = LocalSigner::random();
let eth = signer.address();
let net = NetworkId::TESTNET;
let nonce = Nonce::new([0x55; 32]);
let overlay = compute_overlay(ð, net, &nonce);
let ts = Timestamp::from(1_700_000_000_i64);
let data = sign_data(b"/ip4/127.0.0.1/tcp/1634", &overlay, net, &nonce, ts, None);
let sig = signer.sign_message_sync(&data).expect("sign");
let recovered = sig.recover_address_from_msg(&data).expect("recover");
assert_eq!(recovered, eth);
assert_eq!(compute_overlay(&recovered, net, &nonce), overlay);
}
#[test]
fn tampered_nonce_breaks_overlay_check() {
let signer = LocalSigner::random();
let eth = signer.address();
let net = NetworkId::MAINNET;
let nonce = Nonce::from(b256!(
"0202020202020202020202020202020202020202020202020202020202020202"
));
let overlay = compute_overlay(ð, net, &nonce);
let wrong_nonce = Nonce::new([0x88; 32]);
assert_ne!(compute_overlay(ð, net, &wrong_nonce), overlay);
}
}