fedimint_core/core/
backup.rs

1use std::fmt::{self, Debug};
2
3use bitcoin::hashes::{Hash, sha256};
4use fedimint_core::encoding::{Decodable, Encodable};
5use secp256k1::{Keypair, Message, Secp256k1, Signing, Verification};
6use serde::{Deserialize, Serialize};
7
8/// Maximum payload size of a backup request
9///
10/// Note: this is just a current hard limit,
11/// that could be changed in the future versions.
12///
13/// For comparison - at the time of writing, ecash module
14/// backup with 52 notes is around 5.1K.
15pub const BACKUP_REQUEST_MAX_PAYLOAD_SIZE_BYTES: usize = 128 * 1024;
16
17#[derive(Serialize, Deserialize, Encodable, Decodable)]
18pub struct BackupRequest {
19    pub id: secp256k1::PublicKey,
20    #[serde(with = "fedimint_core::hex::serde")]
21    pub payload: Vec<u8>,
22    pub timestamp: std::time::SystemTime,
23}
24
25impl BackupRequest {
26    fn hash(&self) -> sha256::Hash {
27        self.consensus_hash()
28    }
29
30    pub fn sign(self, keypair: &Keypair) -> anyhow::Result<SignedBackupRequest> {
31        let signature = secp256k1::SECP256K1
32            .sign_schnorr(&Message::from_digest(*self.hash().as_ref()), keypair);
33
34        Ok(SignedBackupRequest {
35            request: self,
36            signature,
37        })
38    }
39}
40
41impl fmt::Debug for BackupRequest {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        f.debug_struct("BackupRequest")
44            .field("id", &self.id)
45            .field("timestamp", &self.timestamp)
46            .field("payload_len", &self.payload.len())
47            .finish()
48    }
49}
50
51#[derive(Debug, Serialize, Deserialize)]
52pub struct SignedBackupRequest {
53    #[serde(flatten)]
54    request: BackupRequest,
55    pub signature: secp256k1::schnorr::Signature,
56}
57
58impl SignedBackupRequest {
59    pub fn verify_valid<C>(&self, ctx: &Secp256k1<C>) -> Result<&BackupRequest, secp256k1::Error>
60    where
61        C: Signing + Verification,
62    {
63        ctx.verify_schnorr(
64            &self.signature,
65            &secp256k1::Message::from_digest_slice(&self.request.hash().to_byte_array())
66                .expect("Can't fail"),
67            &self.request.id.x_only_public_key().0,
68        )?;
69
70        Ok(&self.request)
71    }
72}