use bls::{Ciphertext, PublicKey, SecretKey};
use serde::{Deserialize, Serialize};
use sn_dbc::DerivationIndex;
use sn_protocol::storage::DbcAddress;
use super::error::{Error, Result};
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, custom_debug::Debug)]
pub struct Transfer {
encrypted_utxos: Vec<Ciphertext>,
}
impl Transfer {
pub fn create(utxos: Vec<Utxo>, recipient: PublicKey) -> Result<Self> {
let encrypted_utxos = utxos
.into_iter()
.map(|utxo| utxo.encrypt(recipient))
.collect::<Result<Vec<Ciphertext>>>()?;
Ok(Transfer { encrypted_utxos })
}
pub fn utxos(&self, sk: &SecretKey) -> Result<Vec<Utxo>> {
let mut utxos = Vec::new();
for cypher in &self.encrypted_utxos {
let utxo = Utxo::decrypt(cypher, sk)?;
utxos.push(utxo);
}
Ok(utxos)
}
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, custom_debug::Debug)]
pub struct Utxo {
pub derivation_index: DerivationIndex,
pub parent_spend: DbcAddress,
}
impl Utxo {
pub fn new(derivation_index: DerivationIndex, parent_spend: DbcAddress) -> Self {
Self {
derivation_index,
parent_spend,
}
}
pub fn to_bytes(&self) -> Result<Vec<u8>> {
rmp_serde::to_vec(self).map_err(|_| Error::UtxoSerialisationFailed)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
rmp_serde::from_slice(bytes).map_err(|_| Error::UtxoSerialisationFailed)
}
pub fn encrypt(&self, pk: PublicKey) -> Result<Ciphertext> {
let bytes = self.to_bytes()?;
Ok(pk.encrypt(bytes))
}
pub fn decrypt(cypher: &Ciphertext, sk: &SecretKey) -> Result<Self> {
let bytes = sk.decrypt(cypher).ok_or(Error::UtxoDecryptionFailed)?;
Self::from_bytes(&bytes)
}
}
#[cfg(test)]
mod tests {
use xor_name::XorName;
use super::*;
#[test]
fn test_utxo_conversions() {
let rng = &mut bls::rand::thread_rng();
let utxo = Utxo::new([42; 32], DbcAddress::new(XorName::random(rng)));
let sk = SecretKey::random();
let pk = sk.public_key();
let bytes = utxo.to_bytes().unwrap();
let cipher = utxo.encrypt(pk).unwrap();
let utxo2 = Utxo::from_bytes(&bytes).unwrap();
let utxo3 = Utxo::decrypt(&cipher, &sk).unwrap();
assert_eq!(utxo, utxo2);
assert_eq!(utxo, utxo3);
}
#[test]
fn test_utxo_transfer() {
let rng = &mut bls::rand::thread_rng();
let utxo = Utxo::new([42; 32], DbcAddress::new(XorName::random(rng)));
let sk = SecretKey::random();
let pk = sk.public_key();
let payment = Transfer::create(vec![utxo.clone()], pk).unwrap();
let utxos = payment.utxos(&sk).unwrap();
assert_eq!(utxos, vec![utxo]);
}
}