use serde::Deserialize;
use tari_crypto::{ristretto::RistrettoSecretKey, tari_utilities::ByteArray};
use tari_ootle_transaction::{UnsealedTransactionV1, UnsignedTransactionV1};
use crate::{error::OotleWasmError, hash::public_key_bytes_from_bytes};
#[derive(Deserialize)]
#[serde(untagged)]
enum TransactionInput {
Unsealed(UnsealedTransactionV1),
Unsigned(UnsignedTransactionV1),
}
impl TransactionInput {
fn into_unsealed(self) -> UnsealedTransactionV1 {
match self {
Self::Unsigned(tx) => UnsealedTransactionV1::new(tx, vec![]),
Self::Unsealed(tx) => tx,
}
}
}
fn parse_transaction_json(tx_json: &str) -> Result<TransactionInput, OotleWasmError> {
serde_json::from_str(tx_json).map_err(Into::into)
}
fn secret_key_from_bytes(bytes: &[u8]) -> Result<RistrettoSecretKey, OotleWasmError> {
RistrettoSecretKey::from_canonical_bytes(bytes).map_err(|e| OotleWasmError::InvalidSecretKey(e.to_string()))
}
pub fn seal_transaction_json(tx_json: &str, seal_signer_secret_key: &[u8]) -> Result<String, OotleWasmError> {
let unsealed = parse_transaction_json(tx_json)?.into_unsealed();
let secret = secret_key_from_bytes(seal_signer_secret_key)?;
let sealed = unsealed.seal(&secret);
Ok(serde_json::to_string(&sealed)?)
}
pub fn add_transaction_signer_json(
tx_json: &str,
signer_secret_key: &[u8],
seal_signer_public_key: &[u8],
) -> Result<String, OotleWasmError> {
let unsealed = parse_transaction_json(tx_json)?.into_unsealed();
let secret = secret_key_from_bytes(signer_secret_key)?;
let seal_signer = public_key_bytes_from_bytes(seal_signer_public_key)?;
let unsealed = unsealed.add_signer(&seal_signer, &secret);
Ok(serde_json::to_string(&unsealed)?)
}
#[cfg(test)]
mod tests {
use ootle_byte_type::ToByteType;
use rand::rngs::OsRng;
use tari_crypto::{
keys::{PublicKey, SecretKey},
ristretto::{RistrettoPublicKey, RistrettoSecretKey},
tari_utilities::ByteArray,
};
use tari_ootle_transaction::{Transaction, UnsignedTransactionV1};
use super::*;
fn make_unsigned_tx() -> UnsignedTransactionV1 {
UnsignedTransactionV1::new(0u8, vec![], vec![], Default::default(), None, None, false)
}
#[test]
fn seal_unsigned_transaction() {
let secret = RistrettoSecretKey::random(&mut OsRng);
let unsigned_json = serde_json::to_string(&make_unsigned_tx()).unwrap();
let sealed_json = seal_transaction_json(&unsigned_json, secret.as_bytes()).unwrap();
let sealed: Transaction = serde_json::from_str(&sealed_json).unwrap();
assert!(sealed.verify_all_signatures());
}
#[test]
fn seal_unsealed_transaction() {
let seal_secret = RistrettoSecretKey::random(&mut OsRng);
let signer_secret = RistrettoSecretKey::random(&mut OsRng);
let seal_pk = RistrettoPublicKey::from_secret_key(&seal_secret);
let unsigned_json = serde_json::to_string(&make_unsigned_tx()).unwrap();
let unsealed_json =
add_transaction_signer_json(&unsigned_json, signer_secret.as_bytes(), seal_pk.as_bytes()).unwrap();
let sealed_json = seal_transaction_json(&unsealed_json, seal_secret.as_bytes()).unwrap();
let sealed: Transaction = serde_json::from_str(&sealed_json).unwrap();
assert!(sealed.verify_all_signatures());
}
#[test]
fn add_signer_to_unsigned_transaction() {
let seal_secret = RistrettoSecretKey::random(&mut OsRng);
let seal_pk = RistrettoPublicKey::from_secret_key(&seal_secret);
let signer_secret = RistrettoSecretKey::random(&mut OsRng);
let unsigned_json = serde_json::to_string(&make_unsigned_tx()).unwrap();
let unsealed_json =
add_transaction_signer_json(&unsigned_json, signer_secret.as_bytes(), seal_pk.as_bytes()).unwrap();
let unsealed: UnsealedTransactionV1 = serde_json::from_str(&unsealed_json).unwrap();
assert_eq!(unsealed.signatures().len(), 1);
assert!(unsealed.verify_all_signatures(&seal_pk.to_byte_type()));
}
#[test]
fn add_multiple_signers() {
let seal_secret = RistrettoSecretKey::random(&mut OsRng);
let seal_pk = RistrettoPublicKey::from_secret_key(&seal_secret);
let signer1 = RistrettoSecretKey::random(&mut OsRng);
let signer2 = RistrettoSecretKey::random(&mut OsRng);
let unsigned_json = serde_json::to_string(&make_unsigned_tx()).unwrap();
let unsealed_json =
add_transaction_signer_json(&unsigned_json, signer1.as_bytes(), seal_pk.as_bytes()).unwrap();
let unsealed_json =
add_transaction_signer_json(&unsealed_json, signer2.as_bytes(), seal_pk.as_bytes()).unwrap();
let sealed_json = seal_transaction_json(&unsealed_json, seal_secret.as_bytes()).unwrap();
let sealed: Transaction = serde_json::from_str(&sealed_json).unwrap();
assert!(sealed.verify_all_signatures());
assert_eq!(sealed.signatures().len(), 2);
}
}