Implement prd/pcd logic
This commit is contained in:
parent
665d95554d
commit
0ea4c5f118
@ -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" }
|
||||
|
@ -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;
|
||||
|
||||
|
147
src/device.rs
147
src/device.rs
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
286
src/network.rs
286
src/network.rs
@ -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
147
src/pcd.rs
Normal 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
208
src/prd.rs
Normal 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()
|
||||
}
|
||||
}
|
128
src/process.rs
128
src/process.rs
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
// }
|
||||
// }
|
||||
|
Loading…
x
Reference in New Issue
Block a user