Implement prd/pcd logic

This commit is contained in:
Sosthene 2024-08-28 09:40:19 +02:00
parent 665d95554d
commit 0ea4c5f118
10 changed files with 712 additions and 704 deletions

View File

@ -10,9 +10,10 @@ crate-type = ["cdylib", "rlib"]
aes-gcm = "0.10.3"
anyhow = "1.0"
js-sys = "0.3.69"
log = "0.4.6"
rand = "0.8.5"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
serde_json = { version = "1.0.108", features = ["preserve_order"]}
# sp_client = { path = "../sp-client" }
sp_client = { git = "https://github.com/Sosthene00/sp-client.git", branch = "master" }
tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" }

View File

@ -20,7 +20,7 @@ use aes_gcm::aead::{Aead, Payload};
pub use aes_gcm::{AeadCore, Aes256Gcm, KeyInit};
use rand::thread_rng;
const AAD: &[u8] = "4nk".as_bytes();
pub const AAD: &[u8] = "4nk".as_bytes();
const HALFKEYSIZE: usize = SECRET_KEY_SIZE / 2;

View File

@ -1,70 +1,29 @@
use std::collections::HashMap;
use anyhow::{Error, Result};
use serde_json::{Map, Value};
use sp_client::bitcoin::consensus::serialize;
use sp_client::bitcoin::hashes::Hash;
use sp_client::bitcoin::secp256k1::SecretKey;
use sp_client::bitcoin::{
Network, OutPoint, ScriptBuf, Transaction, Txid, XOnlyPublicKey,
};
use serde_json::Value;
use serde::{Deserialize, Serialize};
use tsify::Tsify;
use uuid::Uuid;
use wasm_bindgen::prelude::*;
use sp_client::silentpayments::utils::SilentPaymentAddress;
use sp_client::spclient::{OutputList, SpWallet, SpendKey};
use sp_client::{silentpayments::utils::SilentPaymentAddress, spclient::SpWallet};
pub const SESSION_INDEX: u32 = 0;
pub const REVOKATION_INDEX: u32 = 1;
#[derive(Debug, Serialize, Deserialize, Clone, Default, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct PairedDevice {
pub address: String,
pub outgoing_pairing_transaction: [u8; 32],
pub revokation_index: u32,
pub incoming_pairing_transaction: [u8; 32],
pub current_remote_key: [u8; 32],
pub current_session_outpoint: OutPoint, // This will be spend by remote device to notify us of next login
pub current_session_revokation_outpoint: OutPoint, // remote device can revoke current session by spending this
}
impl PairedDevice {
pub fn new(address: SilentPaymentAddress, pairing_txid: Txid, revokation_index: u32) -> Self {
let mut pairing_transaction_buf = [0u8; 32];
pairing_transaction_buf.copy_from_slice(&serialize(&pairing_txid));
Self {
address: address.into(),
outgoing_pairing_transaction: pairing_transaction_buf,
revokation_index,
incoming_pairing_transaction: [0u8; 32],
current_session_revokation_outpoint: OutPoint::default(),
current_session_outpoint: OutPoint::default(),
current_remote_key: [0u8; 32],
}
}
}
use crate::pcd::Member;
#[derive(Debug, Serialize, Deserialize, Clone, Default, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Device {
sp_wallet: SpWallet,
current_session_outpoint: OutPoint, // This is the notification output of incoming login tx
current_session_key: [u8; 32],
current_session_revokation_outpoint: OutPoint, // This is the revokation outpoint of outgoing login tx
paired_device: Option<PairedDevice>,
pairing_process_uuid: Option<String>,
paired_devices: Vec<String>, // map an address to the pairing process
latest_known_state: Value
}
impl Device {
pub fn new(sp_wallet: SpWallet) -> Self {
Self {
sp_wallet,
current_session_outpoint: OutPoint::default(),
current_session_key: [0u8; 32],
current_session_revokation_outpoint: OutPoint::default(),
paired_device: None,
pairing_process_uuid: None,
paired_devices: vec![],
latest_known_state: Value::Null
}
}
@ -77,82 +36,40 @@ impl Device {
}
pub fn is_linked(&self) -> bool {
self.paired_device.is_some()
self.pairing_process_uuid.is_some()
}
pub fn is_pairing(&self) -> bool {
self.current_session_key == [0u8; 32]
pub fn get_process_uuid(&self) -> Option<String> {
self.pairing_process_uuid.clone()
}
pub fn get_paired_device_info(&self) -> Option<PairedDevice> {
self.paired_device.clone()
pub fn set_process_uuid(&mut self, uuid: Uuid) {
self.pairing_process_uuid = Some(uuid.to_string());
}
pub fn get_next_output_to_spend(&self) -> OutPoint {
self.current_session_outpoint
pub fn get_paired_devices(&self) -> Vec<String> {
self.paired_devices.clone()
}
pub fn get_session_revokation_outpoint(&self) -> OutPoint {
self.current_session_revokation_outpoint
pub fn push_paired_device(&mut self, new_device_address: SilentPaymentAddress) {
self.paired_devices.push(new_device_address.into())
}
pub fn sign_with_current_session_key(&self) -> Result<()> {
unimplemented!();
}
pub fn encrypt_with_current_session_key(&self) -> Result<()> {
unimplemented!();
}
pub fn new_link(
&mut self,
link_with: SilentPaymentAddress,
outgoing_pairing_tx: Txid,
revokation_output: u32,
incoming_pairing_tx: Txid,
) -> Result<()> {
// let address_looked_for: String = link_with.into();
if let Some(paired_device) = self.paired_device.as_ref() {
return Err(Error::msg(format!(
"Found an already paired device with address {} and revokable by {}:{}",
paired_device.address,
Txid::from_byte_array(paired_device.outgoing_pairing_transaction),
paired_device.revokation_index
)));
} else {
let mut new_device =
PairedDevice::new(link_with, outgoing_pairing_tx, revokation_output);
new_device.incoming_pairing_transaction = incoming_pairing_tx.to_byte_array();
self.paired_device = Some(new_device);
}
Ok(())
}
// We call that when we spent to the remote device and it similarly spent to us
pub fn update_session(
&mut self,
new_session_key: SecretKey,
new_session_outpoint: OutPoint,
new_revokation_outpoint: OutPoint,
new_remote_key: XOnlyPublicKey,
new_remote_session_outpoint: OutPoint,
new_remote_revokation_outpoint: OutPoint,
) -> Result<()> {
pub fn to_member(&self) -> anyhow::Result<Member> {
if !self.is_linked() {
return Err(Error::msg("Can't update an unpaired device"));
return Err(anyhow::Error::msg("device is not linked"));
}
self.paired_device
.as_mut()
.map(|d| {
d.current_remote_key = new_remote_key.serialize();
d.current_session_outpoint = new_remote_session_outpoint;
d.current_session_revokation_outpoint = new_remote_revokation_outpoint;
});
self.current_session_key = new_session_key.secret_bytes();
self.current_session_outpoint = new_session_outpoint;
self.current_session_revokation_outpoint = new_revokation_outpoint;
Ok(())
let member = self.latest_known_state.as_object().unwrap().get("member").unwrap().clone();
let res: Member = serde_json::from_value(member)?;
Ok(res)
}
pub fn get_latest_state(&self) -> Value {
self.latest_known_state.clone()
}
pub fn update_latest_state(&mut self, update: Value) {
self.latest_known_state = update;
}
}

View File

@ -1,9 +1,13 @@
pub use sp_client;
pub use uuid;
pub use log;
pub mod crypto;
pub mod device;
pub mod error;
pub mod network;
pub mod pcd;
pub mod prd;
pub mod process;
pub mod silentpayments;
pub mod signature;

View File

@ -1,26 +1,17 @@
use std::str::FromStr;
use std::{default, fmt};
use aes_gcm::{AeadCore, Aes256Gcm, KeyInit};
use aes_gcm::aead::{Aead, Payload};
use aes_gcm::{Aes256Gcm, KeyInit};
use anyhow::{Error, Result};
use js_sys::Date;
use rand::{thread_rng, RngCore};
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sp_client::bitcoin::consensus::serialize;
use sp_client::bitcoin::hashes::sha256::Hash;
// use sp_client::bitcoin::hashes::Hash;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sp_client::bitcoin::hex::{DisplayHex, FromHex};
use sp_client::bitcoin::secp256k1::schnorr::Signature;
use sp_client::bitcoin::secp256k1::PublicKey;
use sp_client::bitcoin::{BlockHash, OutPoint, Transaction};
use sp_client::silentpayments::utils::SilentPaymentAddress;
use sp_client::bitcoin::OutPoint;
use tsify::Tsify;
use crate::crypto::{Aes256Decryption, Aes256Encryption, Purpose};
use crate::crypto::AAD;
use crate::error::AnkError;
use crate::process::{Member, Process};
use crate::pcd::Member;
#[derive(Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
@ -114,103 +105,6 @@ impl NewTxMessage {
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub enum PrdType {
#[default]
None,
Message,
Update,
List,
Response,
Confirm,
}
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
pub struct ValidationToken {
member: Member,
message: Hash, // Hash of Pcd | {"yes/no/blank"}
sig: Signature,
sig_alt: Signature, // User must sign with it's 2 paired devices
}
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct Prd {
pub prd_type: PrdType,
pub process: Process,
pub sender: String,
pub key: [u8; 32],
pub validation_tokens: Vec<ValidationToken>,
pub pcd_commitment: Hash, // hash of the pcd
pub error: Option<AnkError>,
}
impl Prd {
pub fn new(
prd_type: PrdType,
process: Process,
sender: SilentPaymentAddress,
key: [u8; 32],
pcd_commitment: Hash,
) -> Result<Self> {
let mut res = Self {
prd_type,
process,
sender: sender.into(),
validation_tokens: vec![],
key,
pcd_commitment,
error: None,
};
Ok(res)
}
pub fn add_validation_token(&mut self, validation_token: ValidationToken) -> Result<()> {
match self.prd_type {
PrdType::Confirm => self.validation_tokens.push(validation_token),
_ => return Err(Error::msg("This Prd type doesn't allow validation tokens"))
}
Ok(())
}
pub fn encrypt_pcd(&self, pcd: &Pcd) -> Result<Vec<u8>> {
let plain = pcd.to_string().into_bytes();
let nonce: [u8; 12] = Aes256Gcm::generate_nonce(thread_rng()).into();
let aes_encrypt = Aes256Encryption::import_key(Purpose::Arbitrary, plain, self.key, nonce)?;
let cipher = aes_encrypt.encrypt_with_aes_key()?;
Ok(cipher)
}
pub fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct Pcd {
pub message: String,
pub error: Option<AnkError>,
}
impl Pcd {
pub fn new(message: String) -> Self {
Self {
message,
error: None,
}
}
pub fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Envelope {
pub flag: AnkFlag,
@ -239,52 +133,28 @@ impl Envelope {
pub enum CachedMessageStatus {
#[default]
NoStatus,
FaucetWaiting,
Pairing,
Login,
CipherWaitingTx,
TxWaitingPrd,
GotPrdWaitingPcd,
SentWaitingConfirmation, // we're sender and wait for commited_in to be spent
ReceivedMustConfirm, // we're receiver and we spend commited_in to sender
MustSpendConfirmation, // we're sender and we must spend confirmed_by
Trusted, // Can receive more messages
Closed, // No more messages will be trusted from this handshake
Opened, // Can receive more messages
}
/// Unique struct for both 4nk messages and notification/key exchange, both rust and ts
/// 0. Faucet: commited_in with nothing else, status is NoStatus
/// 1. notification:
/// 0. sender: ciphertext, plaintext, commited_in, sender, recipient, shared_secret, key
/// 1. receiver (without tx): ciphertext
/// 2. receiver (tx without msg): commited_in, commitment, recipient, shared_secret
/// 3. receiver (receive tx after msg): plaintext, key, sender, commited_in, commitment, recipient, shared_secret
/// 4. receiver (msg after tx): ciphertext, key, plaintext, sender
/// 2. confirmation:
/// 0. receiver (spend the smallest vout that pays him in the first tx): confirmed_by
/// 1. sender (detect a transaction that pays him and spend commited_by): confirmed_by
/// 2. sender toggle status to complete when it spent confirmed_by, receiver when it detects the confirmed_by is spent
#[derive(Debug, Default, Serialize, Deserialize, Tsify, Clone)]
#[derive(Debug, Default, Serialize, Deserialize, Tsify, Clone, PartialEq)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct CachedMessage {
pub id: u32,
pub status: CachedMessageStatus,
pub prd_type: PrdType,
pub commited_in: Option<OutPoint>,
pub tied_by: Option<u32>, // index of the output that ties the proposal
pub transaction: Option<String>,
pub commitment: Option<String>, // content of the op_return
pub sender: Option<String>, // Never None when message sent
pub recipient: Option<String>, // Never None when message sent
pub shared_secret: Option<String>, // Never None when message sent
pub prd_cipher: Option<String>, // When we receive message we can't decrypt we only have this and commited_in_tx
pub prd: Option<Prd>, // Never None when message sent
pub pcd_cipher: Option<String>,
pub pcd: Option<Pcd>, // Never None when message sent
pub pcd_commitment: Option<String>,
pub sender: Option<Member>, // Never None when message sent
pub recipient: Option<Member>, // Never None when message sent
pub shared_secrets: Vec<String>, // Max 2 secrets in case we send to both address of the recipient
pub cipher: Vec<String>, // Max 2 ciphers in case we send to both address of the recipient
pub prd: Option<String>, // Never None when message sent
pub pcd: Option<Value>, // Value is here an alias for impl Pcd
pub confirmed_by: Option<OutPoint>, // If this None, Sender keeps sending
pub timestamp: u64,
pub error: Option<AnkError>,
}
impl CachedMessage {
@ -306,112 +176,54 @@ impl CachedMessage {
serde_json::to_string(self).unwrap()
}
pub fn try_decrypt_prd(&self, cipher: Vec<u8>) -> Result<Vec<u8>> {
if self.shared_secret.is_none() {
pub fn try_decrypt_message(&self, cipher: Vec<u8>) -> Result<Vec<u8>> {
if self.shared_secrets.is_empty() {
return Err(Error::msg(
"Can't try decrypt this message, there's no shared secret",
));
}
let mut shared_secret = [0u8; 32];
shared_secret.copy_from_slice(&Vec::from_hex(self.shared_secret.as_ref().unwrap())?);
let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher, shared_secret)?;
for shared_secret in &self.shared_secrets {
let mut key = [0u8; 32];
let mut nonce = [0u8; 12];
key.copy_from_slice(&Vec::from_hex(shared_secret)?);
nonce.copy_from_slice(&cipher[..12]);
aes_decrypt.decrypt_with_key()
}
pub fn try_decrypt_pcd(&self, cipher: Vec<u8>) -> Result<Vec<u8>> {
if self.prd.is_none() {
return Err(Error::msg("Can't try decrypt this message, there's no prd"));
let engine = Aes256Gcm::new(&key.into());
let payload = Payload {
msg: &cipher[12..],
aad: AAD,
};
match engine.decrypt(&nonce.into(), payload) {
Ok(plain) => return Ok(plain),
Err(_) => continue
}
}
let aes_decrypt =
Aes256Decryption::new(Purpose::Arbitrary, cipher, self.prd.as_ref().unwrap().key)?;
aes_decrypt.decrypt_with_key()
Err(Error::msg("Failed to decrypt message"))
}
pub fn try_decrypt_with_shared_secret(&self, shared_secret: [u8; 32]) -> Result<Vec<u8>> {
if self.prd_cipher.is_none() || self.shared_secret.is_some() {
if self.cipher.is_empty() || !self.shared_secrets.is_empty() {
return Err(Error::msg(
"Can't try decrypt this message, ciphertext is none or shared_secret already found",
));
}
for prd_cipher in &self.cipher {
let cipher = Vec::from_hex(prd_cipher)?;
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&cipher[..12]);
let mut key = [0u8; 32];
key.copy_from_slice(&shared_secret);
let cipher = Vec::from_hex(&self.prd_cipher.as_ref().unwrap())
.expect("Shouldn't keep an invalid hex as cipher");
let engine = Aes256Gcm::new(&shared_secret.into());
let payload = Payload {
msg: &cipher[12..],
aad: AAD,
};
match engine.decrypt(&nonce.into(), payload) {
Ok(plain) => return Ok(plain),
Err(_) => continue
}
}
let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher, key)?;
aes_decrypt.decrypt_with_key()
}
}
#[derive(Debug, Serialize, Deserialize, Tsify, Default)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct TrustedChannel {
id: u32, // Just take the id of the cached message?
revokation_outpoint: OutPoint, // revokation output is locked by the shared_secret
shared_secret: [u8; 32],
revoked_in_block: [u8; 32],
with: String, // Silent payment address
}
impl TrustedChannel {
pub fn new(with: String) -> Result<Self> {
// check that with is valid silent payment address
SilentPaymentAddress::try_from(with.as_str())?;
// Generating random id
let mut new = Self::default();
let mut buf = [0u8; 4];
thread_rng().fill_bytes(&mut buf);
new.id = u32::from_be_bytes(buf);
new.with = with;
Ok(new)
}
pub fn set_revokation(
&mut self,
revokation_outpoint: String,
shared_secret: String,
) -> Result<()> {
let mut buf = [0u8; 32];
buf.copy_from_slice(&Vec::from_hex(&shared_secret)?);
self.revokation_outpoint = OutPoint::from_str(&revokation_outpoint)?;
self.shared_secret.copy_from_slice(&buf);
Ok(())
}
pub fn revoke(&mut self, revoked_in_block: String) -> Result<()> {
let block_hash = BlockHash::from_str(&revoked_in_block)?;
let mut buf = [0u8; 32];
buf.copy_from_slice(&serialize::<BlockHash>(&block_hash));
self.revoked_in_block = buf;
Ok(())
}
pub fn is_set(&self) -> bool {
self.revokation_outpoint == OutPoint::default()
}
pub fn is_revoked(&self) -> bool {
self.revoked_in_block != [0u8; 32]
}
pub fn encrypt_msg_for(&self, msg: String) -> Result<String> {
let mut rng = thread_rng();
let nonce: [u8; 12] = Aes256Gcm::generate_nonce(&mut rng).into();
let aes256_encryption = Aes256Encryption::import_key(
Purpose::Arbitrary,
msg.into_bytes(),
self.shared_secret,
nonce,
)?;
let cipher = aes256_encryption.encrypt_with_aes_key()?;
Ok(cipher.to_lower_hex_string())
Err(Error::msg("Failed to decrypt message"))
}
}

147
src/pcd.rs Normal file
View File

@ -0,0 +1,147 @@
use std::str::FromStr;
use anyhow::{Result, Error};
use aes_gcm::{aead::{Aead, Payload}, AeadCore, Aes256Gcm, KeyInit};
use log::debug;
use rand::thread_rng;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use sp_client::{bitcoin::{hashes::{sha256t_hash_newtype, Hash, HashEngine}, hex::{DisplayHex, FromHex}, Txid}, silentpayments::utils::SilentPaymentAddress};
use tsify::Tsify;
use crate::crypto::AAD;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Tsify, PartialEq, Eq, Hash, PartialOrd)]
pub enum Role {
User,
Manager,
Admin,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct Member {
nym: String,
sp_address_a: String,
sp_address_b: String,
role: Role,
}
impl Member {
pub fn new(
nym: String,
sp_address_a: SilentPaymentAddress,
sp_address_b: SilentPaymentAddress,
role: Role,
) -> Self {
Self {
nym,
sp_address_a: sp_address_a.into(),
sp_address_b: sp_address_b.into(),
role,
}
}
pub fn get_nym(&self) -> String {
self.nym.clone()
}
pub fn get_addresses(&self) -> (String, String) {
(self.sp_address_a.clone(), self.sp_address_b.clone())
}
pub fn get_role(&self) -> Role {
self.role
}
}
sha256t_hash_newtype! {
pub struct AnkPcdTag = hash_str("4nk/Pcd");
#[hash_newtype(forward)]
pub struct AnkPcdHash(_);
}
impl AnkPcdHash {
pub fn from_value(value: &Value) -> Self {
let mut eng = AnkPcdHash::engine();
eng.input(value.to_string().as_bytes());
AnkPcdHash::from_engine(eng)
}
pub fn from_map(map: &Map<String, Value>) -> Self {
let value = Value::Object(map.clone());
let mut eng = AnkPcdHash::engine();
eng.input(value.to_string().as_bytes());
AnkPcdHash::from_engine(eng)
}
}
pub trait Pcd<'a>: Serialize + Deserialize<'a> {
fn hash(&self) -> AnkPcdHash {
AnkPcdHash::from_value(&self.to_value())
}
fn encrypt_fields(&self, fields2keys: &mut Map<String, Value>, fields2cipher: &mut Map<String, Value>) -> Result<()> {
let as_value = self.to_value();
let as_map = as_value.as_object().unwrap();
let mut rng = thread_rng();
for (key, value) in as_map {
let aes_key: [u8; 32] = Aes256Gcm::generate_key(&mut rng).into();
let nonce: [u8; 12] = Aes256Gcm::generate_nonce(&mut rng).into();
fields2keys.insert(key.to_owned(), Value::String(aes_key.to_lower_hex_string()));
let encryption = Aes256Gcm::new(&aes_key.into());
let value_string = value.to_string();
let payload = Payload {
msg: value_string.as_bytes(),
aad: AAD,
};
let cipher = encryption.encrypt(&nonce.into(), payload)
.map_err(|e| Error::msg(format!("{}", e)))?;
let mut res = Vec::with_capacity(nonce.len() + cipher.len());
res.extend_from_slice(&nonce);
res.extend_from_slice(&cipher);
fields2cipher.insert(key.to_owned(), Value::String(res.to_lower_hex_string()));
}
Ok(())
}
fn decrypt_fields(&mut self, fields2keys: &Map<String, Value>) -> Result<()> {
let as_value = self.to_value();
let as_map = as_value.as_object().unwrap();
for (key, value) in as_map {
if let Some(aes_key) = fields2keys.get(key) {
let mut nonce = [0u8; 12];
let mut key_buf = [0u8; 32];
key_buf.copy_from_slice(&Vec::from_hex(&aes_key.to_string().trim_matches('\"'))?);
let decrypt = Aes256Gcm::new(&key_buf.into());
let raw_cipher = Vec::from_hex(&value.to_string().trim_matches('\"'))?;
nonce.copy_from_slice(&raw_cipher[..12]);
let payload = Payload {
msg: &raw_cipher[12..],
aad: AAD,
};
let plain = decrypt.decrypt(&nonce.into(), payload)
.map_err(|_| Error::msg(format!("Failed to decrypt field {}", key)))?;
self.to_value()
.as_object_mut()
.unwrap()
.insert(key.to_owned(), Value::String(plain.to_lower_hex_string()));
} else {
continue;
}
}
Ok(())
}
fn to_value(&self) -> Value {
Value::from_str(&serde_json::to_string(&self).unwrap()).unwrap()
}
}
impl Pcd<'_> for Value {}

208
src/prd.rs Normal file
View File

@ -0,0 +1,208 @@
use std::str::FromStr;
use anyhow::{Result, Error};
use serde::{Serialize, Deserialize};
use serde_json::{Map, Value};
use sp_client::bitcoin::secp256k1::SecretKey;
use sp_client::silentpayments::utils::SilentPaymentAddress;
use sp_client::spclient::SpWallet;
use sp_client::bitcoin::secp256k1::schnorr::Signature;
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
use tsify::Tsify;
use uuid::Uuid;
use crate::pcd::{AnkPcdHash, Member};
use crate::signature::{Proof};
#[derive(Debug, Default, Clone, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub enum PrdType {
#[default]
None,
Message,
Init, // Create a new process
Update, // Update an existing process
List, // request a list of items
Response,
Confirm,
}
sha256t_hash_newtype! {
pub struct AnkValidationYesTag = hash_str("4nk/yes");
#[hash_newtype(forward)]
pub struct AnkValidationYesHash(_);
}
impl AnkValidationYesHash {
pub fn from_value(value: &Value) -> Self {
let mut eng = AnkValidationYesHash::engine();
eng.input(value.to_string().as_bytes());
AnkValidationYesHash::from_engine(eng)
}
pub fn from_map(map: &Map<String, Value>) -> Self {
let value = Value::Object(map.clone());
let mut eng = AnkValidationYesHash::engine();
eng.input(value.to_string().as_bytes());
AnkValidationYesHash::from_engine(eng)
}
}
sha256t_hash_newtype! {
pub struct AnkValidationNoTag = hash_str("4nk/no");
#[hash_newtype(forward)]
pub struct AnkValidationNoHash(_);
}
impl AnkValidationNoHash {
pub fn from_value(value: &Value) -> Self {
let mut eng = AnkValidationNoHash::engine();
eng.input(value.to_string().as_bytes());
AnkValidationNoHash::from_engine(eng)
}
pub fn from_map(map: &Map<String, Value>) -> Self {
let value = Value::Object(map.clone());
let mut eng = AnkValidationNoHash::engine();
eng.input(value.to_string().as_bytes());
AnkValidationNoHash::from_engine(eng)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
pub struct ValidationToken {
member: Member,
message: [u8; 32],
sig_a: Signature,
sig_b: Signature, // User must sign with its 2 paired devices
}
sha256t_hash_newtype! {
pub struct AnkPrdTag = hash_str("4nk/Prd");
#[hash_newtype(forward)]
pub struct AnkPrdHash(_);
}
impl AnkPrdHash {
pub fn from_value(value: &Value) -> Self {
let mut eng = AnkPrdHash::engine();
eng.input(value.to_string().as_bytes());
AnkPrdHash::from_engine(eng)
}
pub fn from_map(map: &Map<String, Value>) -> Self {
let value = Value::Object(map.clone());
let mut eng = AnkPrdHash::engine();
eng.input(value.to_string().as_bytes());
AnkPrdHash::from_engine(eng)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
#[allow(non_camel_case_types)]
pub struct Prd {
pub prd_type: PrdType,
pub process_uuid: String, // stringification of Uuid
pub sender: String,
pub keys: Map<String, Value>, // key is a key in pcd, value is the key to decrypt it
pub validation_tokens: Vec<ValidationToken>,
pub pcd_commitment: String,
pub proof: Option<Proof>, // This must be None up to the creation of the network message
}
impl Prd {
pub fn new(
prd_type: PrdType,
uuid: Uuid,
sender: String,
encrypted_pcd: Map<String, Value>,
keys: Map<String, Value>
) -> Result<Self> {
let res = Self {
prd_type,
process_uuid: uuid.to_string(),
sender,
validation_tokens: vec![],
keys,
pcd_commitment: AnkPcdHash::from_map(&encrypted_pcd).to_string(),
proof: None,
};
Ok(res)
}
pub fn extract_from_message(plain: &[u8], commitment: [u8; 32]) -> Result<Self> {
let prd: Prd = serde_json::from_slice(plain)?;
// check that the hash of the prd is consistent with what's commited in the op_return
if prd.create_commitment().to_byte_array() != commitment {
return Err(anyhow::Error::msg("Received prd is not what was commited in the transaction"));
}
// check that the proof is consistent
let sender: Member = serde_json::from_str(&prd.sender)?;
if let Some(proof) = prd.proof {
// take the 2 spending keys in sender
let (address_a, address_b) = sender.get_addresses();
let spend_key_a = <SilentPaymentAddress>::try_from(address_a)?.get_spend_key().x_only_public_key().0;
let spend_key_b = <SilentPaymentAddress>::try_from(address_b)?.get_spend_key().x_only_public_key().0;
// The key in proof must be one of the 2 sender keys
let proof_key = proof.get_key();
if spend_key_a != proof_key && spend_key_b != proof_key {
return Err(anyhow::Error::msg("Proof signed with an unknown key"));
}
proof.verify()?;
}
Ok(prd)
}
pub fn add_validation_token(&mut self, validation_token: ValidationToken) -> Result<()> {
match self.prd_type {
PrdType::Confirm => self.validation_tokens.push(validation_token),
_ => return Err(Error::msg("This Prd type doesn't allow validation tokens"))
}
Ok(())
}
pub fn filter_keys(&mut self, to_keep: Vec<String>) {
let current_keys = self.keys.clone();
let filtered_keys: Map<String, Value> = current_keys.into_iter()
.filter(|(field, _)| to_keep.contains(field))
.collect();
self.keys = filtered_keys;
}
/// We commit to everything except the keys and the proof
/// Because 1) we need one commitment to common data for all recipients of the transaction
/// 2) we already commit to the keys in the sender proof anyway
pub fn create_commitment(&self) -> AnkPrdHash {
let mut to_commit = self.clone();
to_commit.keys = Map::new();
to_commit.proof = None;
AnkPrdHash::from_value(&to_commit.to_value())
}
/// Generate the signed proof and serialize to send over the network
pub fn to_network_msg(&self, sp_wallet: &SpWallet) -> Result<String> {
let spend_sk: SecretKey = sp_wallet.get_client().get_spend_key().try_into()?;
let to_sign = self.to_string(); // we sign the whole prd, incl the keys, for each recipient
let proof = Proof::new(to_sign.as_bytes(), spend_sk);
let mut res = self.clone();
res.proof = Some(proof);
Ok(res.to_string())
}
pub fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
pub fn to_value(&self) -> Value {
Value::from_str(&self.to_string()).unwrap()
}
}

View File

@ -2,71 +2,27 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sp_client::bitcoin::hashes::Hash;
use sp_client::bitcoin::secp256k1::schnorr::Signature;
use sp_client::bitcoin::{OutPoint, Txid};
use sp_client::silentpayments::utils::SilentPaymentAddress;
use sp_client::{bitcoin::OutPoint, silentpayments::utils::SilentPaymentAddress};
use tsify::Tsify;
use uuid::Uuid;
use wasm_bindgen::prelude::*;
use crate::device::Device;
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Tsify, PartialEq, PartialOrd)]
pub enum Role {
User,
Manager,
Admin,
}
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
pub struct Member {
nym: String,
sp_address: String,
sp_address_alt: String,
role: Role,
}
impl Member {
pub fn new(
nym: String,
sp_address: SilentPaymentAddress,
sp_address_alt: SilentPaymentAddress,
role: Role,
) -> Self {
Self {
nym,
sp_address: sp_address.into(),
sp_address_alt: sp_address_alt.into(),
role,
}
}
pub fn get_nym(&self) -> String {
self.nym.clone()
}
pub fn get_addresses(&self) -> (String, String) {
(self.sp_address.clone(), self.sp_address_alt.clone())
}
pub fn get_role(&self) -> Role {
self.role
}
}
use crate::pcd::Role;
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct ValidationRules {
quorum: f32, // Must be > 0.0, <= 1.0
min_permission: Role, // Only users with at least that Role can send a token
min_signatures_member: f32, // Must be > 0.0, <= 1.0, does each member need to sign with all it's devices?
}
impl ValidationRules {
pub fn new(quorum: f32, min_permission: Role) -> Self {
pub fn new(quorum: f32, min_permission: Role, min_signatures_member: f32) -> Self {
Self {
quorum,
min_permission,
min_signatures_member,
}
}
}
@ -76,95 +32,33 @@ impl ValidationRules {
pub struct Process {
pub uuid: String,
pub name: String,
pub version: u32,
pub roles: Vec<Member>,
pub validation_rules: ValidationRules,
pub initial_commit_tx: Txid,
pub latest_commit_tx: Txid,
pub html: String,
pub style: String,
pub script: String,
pub pcd_template: Value
pub init_state: Value,
pub commited_in: OutPoint,
}
impl Process {
pub fn new(
name: String,
roles: Vec<Member>,
validation_rules: ValidationRules,
initial_commit_tx: Txid,
html: String,
style: String,
script: String,
pcd_template: Value,
init_state: Value,
commited_in: OutPoint,
) -> Self {
Self {
uuid: Uuid::new_v4().to_string(),
name,
version: 1,
roles,
validation_rules,
initial_commit_tx,
latest_commit_tx: initial_commit_tx,
html,
style,
script,
pcd_template
}
}
pub fn new_pairing_process(
pcd: PairingPcd,
initial_commit_tx: Txid,
) -> Self {
let member = Member::new(
pcd.nym.clone(),
pcd.addresses[0].clone().try_into().unwrap(),
pcd.addresses[1].clone().try_into().unwrap(),
Role::Admin,
);
let validation_rules = ValidationRules::new(1.0, Role::Admin);
Self::new(
"pairing".to_owned(),
vec![member],
validation_rules,
initial_commit_tx,
"".to_owned(),
"".to_owned(),
"".to_owned(),
Value::from_str(&serde_json::to_string(&pcd).unwrap()).unwrap()
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PairingPcd {
nym: String,
addresses: [String; 2],
session_index: u32,
revokation_index: u32,
pairing_txs: [Txid; 2],
current_session_txs: [Txid; 2]
}
impl PairingPcd {
pub fn new(
nym: String,
local_address: SilentPaymentAddress,
remote_address: SilentPaymentAddress,
session_index: u32,
revokation_index: u32,
incoming_pairing_tx: Txid,
outgoing_pairing_tx: Txid,
) -> Self {
let empty_txid = Txid::from_byte_array([0u8; Txid::LEN]);
Self {
nym,
addresses: [local_address.into(), remote_address.into()],
session_index,
revokation_index,
pairing_txs: [incoming_pairing_tx, outgoing_pairing_tx],
current_session_txs: [empty_txid, empty_txid]
init_state,
commited_in,
}
}
}

View File

@ -1,5 +1,6 @@
use anyhow::Result;
use rand::{thread_rng, RngCore};
use serde::{Serialize, Deserialize};
use sp_client::bitcoin::key::Secp256k1;
use sp_client::bitcoin::secp256k1::schnorr::Signature;
use sp_client::bitcoin::secp256k1::{Keypair, Message, SecretKey, XOnlyPublicKey};
@ -20,26 +21,48 @@ impl AnkMessageHash {
}
}
pub fn sign_message(message: &[u8], signing_key: SecretKey) -> Result<Signature> {
let message_hash = AnkMessageHash::from_message(message);
let secp = Secp256k1::signing_only();
let keypair = Keypair::from_secret_key(&secp, &signing_key);
let mut aux_rand = [0u8; 32];
thread_rng().fill_bytes(&mut aux_rand);
let sig = secp.sign_schnorr_with_aux_rand(&Message::from_digest(message_hash.to_byte_array()), &keypair, &aux_rand);
Ok(sig)
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Proof {
signature: Signature,
message: AnkMessageHash,
key: XOnlyPublicKey
}
pub fn verify(sig: Signature, message: &[u8], x_only_key: XOnlyPublicKey) -> Result<()> {
let secp = Secp256k1::verification_only();
let message_hash = AnkMessageHash::from_message(message);
secp.verify_schnorr(&sig, &Message::from_digest(message_hash.to_byte_array()), &x_only_key)?;
impl Proof {
pub fn new(message: &[u8], signing_key: SecretKey) -> Self {
let message_hash = AnkMessageHash::from_message(message);
let secp = Secp256k1::signing_only();
let keypair = Keypair::from_secret_key(&secp, &signing_key);
let mut aux_rand = [0u8; 32];
thread_rng().fill_bytes(&mut aux_rand);
let sig = secp.sign_schnorr_with_aux_rand(&Message::from_digest(message_hash.to_byte_array()), &keypair, &aux_rand);
Self {
signature: sig,
message: message_hash,
key: keypair.x_only_public_key().0
}
}
pub fn get_key(&self) -> XOnlyPublicKey {
self.key
}
pub fn verify(&self) -> Result<()> {
let secp = Secp256k1::verification_only();
secp.verify_schnorr(&self.signature, &Message::from_digest(self.message.to_byte_array()), &self.key)?;
Ok(())
}
pub fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
Ok(())
}

View File

@ -171,245 +171,247 @@ pub fn map_outputs_to_sp_address(psbt_str: &str) -> Result<HashMap<String, Vec<u
Ok(res)
}
#[cfg(test)]
mod tests {
use std::io::Write;
// #[cfg(test)]
// mod tests {
// use std::io::Write;
use crate::network::{Pcd, Prd, PrdType};
use crate::process::{Member, Process, Role, ValidationRules};
// use crate::pcd::Pcd;
// use crate::prd::{Prd, PrdType};
// use crate::process::{Member, Process, Role, ValidationRules};
use super::*;
use sp_client::bitcoin::hashes::{sha256, Hash};
use sp_client::bitcoin::hex::FromHex;
use sp_client::bitcoin::secp256k1::PublicKey;
use sp_client::bitcoin::{ScriptBuf, Transaction, Txid};
use sp_client::silentpayments::utils::receiving::{
calculate_tweak_data, get_pubkey_from_input,
};
// use super::*;
// use serde_json::Value;
// use sp_client::bitcoin::hashes::{sha256, Hash};
// use sp_client::bitcoin::hex::FromHex;
// use sp_client::bitcoin::secp256k1::PublicKey;
// use sp_client::bitcoin::{ScriptBuf, Transaction, Txid};
// use sp_client::silentpayments::utils::receiving::{
// calculate_tweak_data, get_pubkey_from_input,
// };
const ALICE_WALLET: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"}}}}";
const BOB_WALLET: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
const ALICE_WALLET_CONFIRMATION: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d\"}},\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"}}}}";
const BOB_WALLET_CONFIRMATION: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":\"Unspent\"},\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
const ALICE_WALLET_ANSWER: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"},\"bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9:0\":{\"blockheight\":0,\"tweak\":\"3bf77beab9c053e1ed974288d5d246962376d2badddc623af1f2ef7af57067b7\",\"amount\":1046,\"script\":\"5120646bdb98d89a2573acc6064a5c806d00e34beb65588c91a32733b62255b4dafa\",\"label\":null,\"spend_status\":\"Unspent\"},\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d\"}}}}}";
const BOB_WALLET_ANSWER: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"},\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":{\"Spent\":\"bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9\"}}}}}";
const ALICE_ADDRESS: &str = "sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q";
const BOB_ADDRESS: &str = "sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u";
const FEE_RATE: Amount = Amount::from_sat(1);
const KEY: &str = "442a5ea418921c4aa8ce3f7a95427d9450059a3ac11db3dced9abb709b2d9f42";
const INITIAL_COMMIT_TX: &str = "bc7d2ef1820cf67edb900e4c59cae6c692663cd690e691a55df919f002ede841";
// const ALICE_WALLET: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"}}}}";
// const BOB_WALLET: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
// const ALICE_WALLET_CONFIRMATION: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d\"}},\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"}}}}";
// const BOB_WALLET_CONFIRMATION: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":\"Unspent\"},\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}}}";
// const ALICE_WALLET_ANSWER: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"e3d8922a41a7cb1a84a90f4334e987bb5ea2df6a1fdf44f789b5302de119f9e2\",\"spend_key\":{\"Secret\":\"93292e5b21042c6cfc742ba30e9d2a1e01609b12d154a1825184ed12c7b9631b\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,104,242,105,185,6,124,208,34,44,149,52,163,38,63,221,150,12,198,24,95,143,126,235,37,149,233,88,118,32,86,233,152],\"spend_pubkey\":[3,198,82,196,243,12,59,126,109,143,144,157,128,176,168,94,54,134,232,139,115,102,11,178,128,244,239,251,40,228,67,153,72],\"change_label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"labels\":[[\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",[2,244,223,255,57,50,216,27,133,112,138,69,120,126,85,110,6,242,141,33,136,191,82,164,241,54,179,115,84,161,145,174,154]]]}},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:1\":{\"blockheight\":0,\"tweak\":\"28994b2f2ee8e5f35d6d2dcdee1580d0455fe3dc37f81e0a36864473ee86f5c4\",\"amount\":3937246,\"script\":\"51207d06144e982b6fd38a85d6152f1f95746b059553258a31e04df97fe6b5f19ea1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"},\"bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9:0\":{\"blockheight\":0,\"tweak\":\"3bf77beab9c053e1ed974288d5d246962376d2badddc623af1f2ef7af57067b7\",\"amount\":1046,\"script\":\"5120646bdb98d89a2573acc6064a5c806d00e34beb65588c91a32733b62255b4dafa\",\"label\":null,\"spend_status\":\"Unspent\"},\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d\"}}}}}";
// const BOB_WALLET_ANSWER: &str = "{\"tx_history\": [],\"client\":{\"network\":\"regtest\",\"label\":\"default\",\"scan_sk\":\"0de90b7195c1380d5fde13de3f1d66d53423a9896314839e36ba672653af60b4\",\"spend_key\":{\"Secret\":\"affe686075ecbe17b8ce7de45ec31314804259d0a4bc1c863de21ffd6dc598f8\"},\"mnemonic\":null,\"sp_receiver\":{\"version\":0,\"network\":\"Regtest\",\"scan_pubkey\":[2,85,96,92,243,247,237,192,205,9,178,146,101,237,132,232,15,2,69,138,31,118,76,140,142,207,90,13,192,94,254,150,133],\"spend_pubkey\":[3,5,157,91,250,169,41,61,190,37,30,98,152,253,180,138,250,86,162,102,82,148,130,220,44,153,127,83,43,246,93,17,232],\"change_label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"labels\":[[\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",[2,237,237,247,213,154,87,34,239,168,235,87,122,152,94,41,35,101,184,201,58,201,6,185,58,157,52,208,129,167,2,224,198]]]}},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"},\"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":{\"Spent\":\"bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9\"}}}}}";
// const ALICE_ADDRESS: &str = "sprt1qqf50y6deqe7dqg3vj562xf3lmktqe3sct78ha6e9jh54sa3q2m5esq7x2tz0xrpm0ekclyyaszc2sh3ksm5gkumxpwegpa80lv5wgsuefqaufx8q";
// const BOB_ADDRESS: &str = "sprt1qqf2kqh8n7lkupngfk2fxtmvyaq8sy3v2ramyeryweadqmsz7l6tg2qc9n4dl42ff8klz28nznr7mfzh6263xv555stwzextl2v4lvhg3aqq7ru8u";
// const FEE_RATE: Amount = Amount::from_sat(1);
// const KEY: &str = "442a5ea418921c4aa8ce3f7a95427d9450059a3ac11db3dced9abb709b2d9f42";
// const INITIAL_COMMIT_TX: &str = "bc7d2ef1820cf67edb900e4c59cae6c692663cd690e691a55df919f002ede841";
fn helper_get_tweak_data(tx: &Transaction, spk: ScriptBuf) -> PublicKey {
let prevout = tx.input.get(0).unwrap().to_owned();
let outpoint_data = (
prevout.previous_output.txid.to_string(),
prevout.previous_output.vout,
);
let input_pubkey =
get_pubkey_from_input(&vec![], &prevout.witness.to_vec(), spk.as_bytes()).unwrap();
let tweak_data =
calculate_tweak_data(&vec![&input_pubkey.unwrap()], &vec![outpoint_data]).unwrap();
tweak_data
}
// fn helper_get_tweak_data(tx: &Transaction, spk: ScriptBuf) -> PublicKey {
// let prevout = tx.input.get(0).unwrap().to_owned();
// let outpoint_data = (
// prevout.previous_output.txid.to_string(),
// prevout.previous_output.vout,
// );
// let input_pubkey =
// get_pubkey_from_input(&vec![], &prevout.witness.to_vec(), spk.as_bytes()).unwrap();
// let tweak_data =
// calculate_tweak_data(&vec![&input_pubkey.unwrap()], &vec![outpoint_data]).unwrap();
// tweak_data
// }
fn helper_create_commitment(payload_to_hash: String) -> sha256::Hash {
let mut engine = sha256::HashEngine::default();
engine.write_all(&payload_to_hash.as_bytes()).unwrap();
let hash = sha256::Hash::from_engine(engine);
hash
}
// fn helper_create_commitment(payload_to_hash: String) -> sha256::Hash {
// let mut engine = sha256::HashEngine::default();
// engine.write_all(&payload_to_hash.as_bytes()).unwrap();
// let hash = sha256::Hash::from_engine(engine);
// hash
// }
#[test]
fn it_creates_notification_transaction() {
let recipient = Recipient {
address: BOB_ADDRESS.to_owned(),
amount: Amount::from_sat(1200),
nb_outputs: 1,
};
let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET).unwrap();
let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET).unwrap();
let pcd = Pcd::new("TEST".to_owned());
let pcd_hash = helper_create_commitment(pcd.to_string());
let mut key = [0u8; 32];
key.copy_from_slice(&Vec::from_hex(KEY).unwrap());
// #[test]
// fn it_creates_notification_transaction() {
// let recipient = Recipient {
// address: BOB_ADDRESS.to_owned(),
// amount: Amount::from_sat(1200),
// nb_outputs: 1,
// };
// let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET).unwrap();
// let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET).unwrap();
// let pcd = Pcd::new(Value::String("TEST".to_owned()));
// let pcd_hash = helper_create_commitment(pcd.to_string());
// let mut key = [0u8; 32];
// key.copy_from_slice(&Vec::from_hex(KEY).unwrap());
let alice_member = Member::new(
"alice".to_owned(),
alice_wallet.get_client().get_receiving_address().try_into().unwrap(),
alice_wallet.get_client().sp_receiver.get_change_address().try_into().unwrap(),
Role::Admin,
);
let bob_member = Member::new(
"bob".to_owned(),
bob_wallet.get_client().get_receiving_address().try_into().unwrap(),
bob_wallet.get_client().sp_receiver.get_change_address().try_into().unwrap(),
Role::User,
);
// let alice_member = Member::new(
// "alice".to_owned(),
// alice_wallet.get_client().get_receiving_address().try_into().unwrap(),
// alice_wallet.get_client().sp_receiver.get_change_address().try_into().unwrap(),
// Role::Admin,
// );
// let bob_member = Member::new(
// "bob".to_owned(),
// bob_wallet.get_client().get_receiving_address().try_into().unwrap(),
// bob_wallet.get_client().sp_receiver.get_change_address().try_into().unwrap(),
// Role::User,
// );
let validation_rules = ValidationRules::new(0.5, Role::User);
// let validation_rules = ValidationRules::new(0.5, Role::User);
let pcd_template = serde_json::json!({
"int": 0,
"string": "exemple_data",
"array": [
"element1",
"element2"
]
});
// let pcd_template = serde_json::json!({
// "int": 0,
// "string": "exemple_data",
// "array": [
// "element1",
// "element2"
// ]
// });
let process = Process::new(
"default".to_owned(),
vec![alice_member, bob_member],
validation_rules,
Txid::from_str(INITIAL_COMMIT_TX).unwrap(),
"".to_owned(),
"".to_owned(),
"".to_owned(),
pcd_template,
);
// let process = Process::new(
// "default".to_owned(),
// vec![alice_member, bob_member],
// validation_rules,
// Txid::from_str(INITIAL_COMMIT_TX).unwrap(),
// "".to_owned(),
// "".to_owned(),
// "".to_owned(),
// pcd_template,
// );
let prd = Prd::new(
PrdType::Update,
process,
ALICE_ADDRESS.try_into().unwrap(),
key,
pcd_hash,
).unwrap();
let commitment = helper_create_commitment(serde_json::to_string(&prd).unwrap());
// let prd = Prd::new(
// PrdType::Update,
// process,
// ALICE_ADDRESS.try_into().unwrap(),
// key,
// pcd_hash,
// ).unwrap();
// let commitment = helper_create_commitment(serde_json::to_string(&prd).unwrap());
let psbt = create_transaction(
&vec![],
&HashSet::new(),
&alice_wallet,
vec![recipient],
Some(commitment.as_byte_array().to_vec()),
FEE_RATE,
None,
)
.unwrap();
// let psbt = create_transaction(
// &vec![],
// &HashSet::new(),
// &alice_wallet,
// vec![recipient],
// Some(commitment.as_byte_array().to_vec()),
// FEE_RATE,
// None,
// )
// .unwrap();
let final_tx = psbt.extract_tx().unwrap();
let spk = "51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1";
// let final_tx = psbt.extract_tx().unwrap();
// let spk = "51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1";
let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// Check that Alice and Bob are both able to find that transaction
let alice_update = alice_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(alice_update.len() > 0);
let bob_update = bob_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(bob_update.len() > 0);
println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
}
// // Check that Alice and Bob are both able to find that transaction
// let alice_update = alice_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(alice_update.len() > 0);
// let bob_update = bob_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(bob_update.len() > 0);
// println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
// println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
// }
#[test]
fn it_creates_confirmation_transaction() {
let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET_CONFIRMATION).unwrap();
let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET_CONFIRMATION).unwrap();
// #[test]
// fn it_creates_confirmation_transaction() {
// let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET_CONFIRMATION).unwrap();
// let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET_CONFIRMATION).unwrap();
// Bob must spend notification output
let (confirmation_outpoint, _) = bob_wallet
.get_outputs()
.get_outpoint(
OutPoint::from_str(
"148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0",
)
.unwrap(),
)
.unwrap();
// // Bob must spend notification output
// let (confirmation_outpoint, _) = bob_wallet
// .get_outputs()
// .get_outpoint(
// OutPoint::from_str(
// "148e0faa2f203b6e9488e2da696d8a49ebff4212946672f0bb072ced0909360d:0",
// )
// .unwrap(),
// )
// .unwrap();
let recipient = Recipient {
address: ALICE_ADDRESS.to_owned(),
amount: Amount::from_sat(0),
nb_outputs: 1,
};
// let recipient = Recipient {
// address: ALICE_ADDRESS.to_owned(),
// amount: Amount::from_sat(0),
// nb_outputs: 1,
// };
let psbt = create_transaction(
&vec![&confirmation_outpoint],
&HashSet::new(),
&bob_wallet,
vec![recipient],
None,
FEE_RATE,
Some(ALICE_ADDRESS.to_owned()),
)
.unwrap();
// let psbt = create_transaction(
// &vec![&confirmation_outpoint],
// &HashSet::new(),
// &bob_wallet,
// vec![recipient],
// None,
// FEE_RATE,
// Some(ALICE_ADDRESS.to_owned()),
// )
// .unwrap();
let final_tx = psbt.extract_tx().unwrap();
// println!(
// "{}",
// serialize::<Transaction>(&final_tx).to_lower_hex_string()
// );
let spk = "512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c";
// let final_tx = psbt.extract_tx().unwrap();
// // println!(
// // "{}",
// // serialize::<Transaction>(&final_tx).to_lower_hex_string()
// // );
// let spk = "512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c";
let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// Check that Alice and Bob are both able to find that transaction
let alice_update = alice_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(alice_update.len() > 0);
let bob_update = bob_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(bob_update.len() > 0);
println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
}
// // Check that Alice and Bob are both able to find that transaction
// let alice_update = alice_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(alice_update.len() > 0);
// let bob_update = bob_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(bob_update.len() > 0);
// println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
// println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
// }
#[test]
fn it_creates_answer_transaction() {
let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET_ANSWER).unwrap();
let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET_ANSWER).unwrap();
// #[test]
// fn it_creates_answer_transaction() {
// let mut alice_wallet: SpWallet = serde_json::from_str(ALICE_WALLET_ANSWER).unwrap();
// let mut bob_wallet: SpWallet = serde_json::from_str(BOB_WALLET_ANSWER).unwrap();
// Bob must spend notification output
let (confirmation_outpoint, _) = alice_wallet
.get_outputs()
.get_outpoint(
OutPoint::from_str(
"bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9:0",
)
.unwrap(),
)
.unwrap();
// // Bob must spend notification output
// let (confirmation_outpoint, _) = alice_wallet
// .get_outputs()
// .get_outpoint(
// OutPoint::from_str(
// "bc207c02bc4f1d4359fcd604296c0938bf1e6ff827662a56410676b8cbd768d9:0",
// )
// .unwrap(),
// )
// .unwrap();
let recipient = Recipient {
address: BOB_ADDRESS.to_owned(),
amount: Amount::from_sat(0),
nb_outputs: 1,
};
// let recipient = Recipient {
// address: BOB_ADDRESS.to_owned(),
// amount: Amount::from_sat(0),
// nb_outputs: 1,
// };
let psbt = create_transaction(
&vec![&confirmation_outpoint],
&HashSet::new(),
&alice_wallet,
vec![recipient],
None,
FEE_RATE,
Some(BOB_ADDRESS.to_owned()),
)
.unwrap();
// let psbt = create_transaction(
// &vec![&confirmation_outpoint],
// &HashSet::new(),
// &alice_wallet,
// vec![recipient],
// None,
// FEE_RATE,
// Some(BOB_ADDRESS.to_owned()),
// )
// .unwrap();
let final_tx = psbt.extract_tx().unwrap();
// println!("{}", serialize::<Transaction>(&final_tx).to_lower_hex_string());
let spk = "5120646bdb98d89a2573acc6064a5c806d00e34beb65588c91a32733b62255b4dafa";
// let final_tx = psbt.extract_tx().unwrap();
// // println!("{}", serialize::<Transaction>(&final_tx).to_lower_hex_string());
// let spk = "5120646bdb98d89a2573acc6064a5c806d00e34beb65588c91a32733b62255b4dafa";
let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// let tweak_data = helper_get_tweak_data(&final_tx, ScriptBuf::from_hex(spk).unwrap());
// Check that Alice and Bob are both able to find that transaction
let alice_update = alice_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(alice_update.len() > 0);
let bob_update = bob_wallet
.update_wallet_with_transaction(&final_tx, 0, tweak_data)
.unwrap();
assert!(bob_update.len() > 0);
println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
}
}
// // Check that Alice and Bob are both able to find that transaction
// let alice_update = alice_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(alice_update.len() > 0);
// let bob_update = bob_wallet
// .update_wallet_with_transaction(&final_tx, 0, tweak_data)
// .unwrap();
// assert!(bob_update.len() > 0);
// println!("{:?}", alice_wallet.get_outputs().to_outpoints_list());
// println!("{:?}", bob_wallet.get_outputs().to_outpoints_list());
// }
// }