Implement Process
This commit is contained in:
parent
8688b6ed79
commit
b2c070642e
@ -16,4 +16,5 @@ serde_json = "1.0.108"
|
||||
# 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" }
|
||||
uuid = { version = "1.10.0", features = ["v4"] }
|
||||
wasm-bindgen = "0.2.91"
|
||||
|
158
src/device.rs
Normal file
158
src/device.rs
Normal file
@ -0,0 +1,158 @@
|
||||
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::{Deserialize, Serialize};
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use sp_client::silentpayments::utils::SilentPaymentAddress;
|
||||
use sp_client::spclient::{OutputList, SpWallet, SpendKey};
|
||||
|
||||
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],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_wallet(&self) -> &SpWallet {
|
||||
&self.sp_wallet
|
||||
}
|
||||
|
||||
pub fn get_mut_wallet(&mut self) -> &mut SpWallet {
|
||||
&mut self.sp_wallet
|
||||
}
|
||||
|
||||
pub fn is_linked(&self) -> bool {
|
||||
self.paired_device.is_some()
|
||||
}
|
||||
|
||||
pub fn is_pairing(&self) -> bool {
|
||||
self.current_session_key == [0u8; 32]
|
||||
}
|
||||
|
||||
pub fn get_paired_device_info(&self) -> Option<PairedDevice> {
|
||||
self.paired_device.clone()
|
||||
}
|
||||
|
||||
pub fn get_next_output_to_spend(&self) -> OutPoint {
|
||||
self.current_session_outpoint
|
||||
}
|
||||
|
||||
pub fn get_session_revokation_outpoint(&self) -> OutPoint {
|
||||
self.current_session_revokation_outpoint
|
||||
}
|
||||
|
||||
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<()> {
|
||||
if !self.is_linked() {
|
||||
return Err(Error::msg("Can't update an unpaired device"));
|
||||
}
|
||||
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(())
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
pub use sp_client;
|
||||
|
||||
pub mod crypto;
|
||||
pub mod device;
|
||||
pub mod error;
|
||||
pub mod network;
|
||||
pub mod process;
|
||||
pub mod silentpayments;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::{default, fmt};
|
||||
use std::str::FromStr;
|
||||
use std::{default, fmt};
|
||||
|
||||
use aes_gcm::{AeadCore, Aes256Gcm, KeyInit};
|
||||
use anyhow::{Error, Result};
|
||||
@ -12,6 +12,7 @@ use sp_client::bitcoin::consensus::serialize;
|
||||
use sp_client::bitcoin::hashes::sha256::Hash;
|
||||
// use sp_client::bitcoin::hashes::Hash;
|
||||
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;
|
||||
@ -19,6 +20,7 @@ use tsify::Tsify;
|
||||
|
||||
use crate::crypto::{Aes256Decryption, Aes256Encryption, Purpose};
|
||||
use crate::error::AnkError;
|
||||
use crate::process::{Member, Process};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
@ -112,24 +114,67 @@ 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 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(sender: SilentPaymentAddress, key: [u8; 32], pcd_commitment: Hash) -> Self {
|
||||
Self {
|
||||
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>> {
|
||||
@ -225,6 +270,7 @@ pub enum CachedMessageStatus {
|
||||
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 commitment: Option<String>, // content of the op_return
|
||||
@ -232,9 +278,9 @@ pub struct CachedMessage {
|
||||
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 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 confirmed_by: Option<OutPoint>, // If this None, Sender keeps sending
|
||||
pub timestamp: u64,
|
||||
@ -275,11 +321,10 @@ impl CachedMessage {
|
||||
|
||||
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",
|
||||
));
|
||||
return Err(Error::msg("Can't try decrypt this message, there's no prd"));
|
||||
}
|
||||
let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher, self.prd.as_ref().unwrap().key)?;
|
||||
let aes_decrypt =
|
||||
Aes256Decryption::new(Purpose::Arbitrary, cipher, self.prd.as_ref().unwrap().key)?;
|
||||
|
||||
aes_decrypt.decrypt_with_key()
|
||||
}
|
||||
@ -293,7 +338,8 @@ impl CachedMessage {
|
||||
|
||||
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 cipher = Vec::from_hex(&self.prd_cipher.as_ref().unwrap())
|
||||
.expect("Shouldn't keep an invalid hex as cipher");
|
||||
|
||||
let aes_decrypt = Aes256Decryption::new(Purpose::Arbitrary, cipher, key)?;
|
||||
aes_decrypt.decrypt_with_key()
|
||||
@ -356,10 +402,15 @@ impl TrustedChannel {
|
||||
self.revoked_in_block != [0u8; 32]
|
||||
}
|
||||
|
||||
pub fn encrypt_msg_for(&self, msg: String) -> Result<String> {
|
||||
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 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())
|
||||
}
|
||||
|
170
src/process.rs
Normal file
170
src/process.rs
Normal file
@ -0,0 +1,170 @@
|
||||
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 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
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
impl ValidationRules {
|
||||
pub fn new(quorum: f32, min_permission: Role) -> Self {
|
||||
Self {
|
||||
quorum,
|
||||
min_permission,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
) -> 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]
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ use anyhow::{Error, Result};
|
||||
use rand::{thread_rng, Rng};
|
||||
use sp_client::bitcoin::consensus::deserialize;
|
||||
use sp_client::bitcoin::psbt::raw;
|
||||
use sp_client::bitcoin::{Psbt, Amount, OutPoint};
|
||||
use sp_client::bitcoin::{Amount, OutPoint, Psbt};
|
||||
use sp_client::constants::{
|
||||
self, DUST_THRESHOLD, PSBT_SP_ADDRESS_KEY, PSBT_SP_PREFIX, PSBT_SP_SUBTYPE,
|
||||
};
|
||||
@ -27,9 +27,7 @@ pub fn create_transaction(
|
||||
.to_spendable_list()
|
||||
// filter out freezed utxos
|
||||
.into_iter()
|
||||
.filter(|(outpoint, _)| {
|
||||
!freezed_utxos.contains(outpoint)
|
||||
})
|
||||
.filter(|(outpoint, _)| !freezed_utxos.contains(outpoint))
|
||||
.collect();
|
||||
|
||||
// if we have a payload, it means we are notifying, so let's add a revokation output
|
||||
@ -37,13 +35,17 @@ pub fn create_transaction(
|
||||
recipients.push(Recipient {
|
||||
address: sp_wallet.get_client().get_receiving_address(),
|
||||
amount: DUST_THRESHOLD,
|
||||
nb_outputs: 1
|
||||
nb_outputs: 1,
|
||||
})
|
||||
}
|
||||
|
||||
let sum_outputs = recipients.iter().fold(Amount::from_sat(0), |acc, x| acc + x.amount);
|
||||
let sum_outputs = recipients
|
||||
.iter()
|
||||
.fold(Amount::from_sat(0), |acc, x| acc + x.amount);
|
||||
|
||||
let zero_value_recipient = recipients.iter_mut().find(|r| r.amount == Amount::from_sat(0));
|
||||
let zero_value_recipient = recipients
|
||||
.iter_mut()
|
||||
.find(|r| r.amount == Amount::from_sat(0));
|
||||
|
||||
let mut inputs: HashMap<OutPoint, OwnedOutput> = HashMap::new();
|
||||
let mut total_available = Amount::from_sat(0);
|
||||
@ -89,7 +91,8 @@ pub fn create_transaction(
|
||||
if let Some(address) = fee_payer {
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, address)?;
|
||||
} else {
|
||||
let candidates: Vec<Option<String>> = new_psbt.outputs
|
||||
let candidates: Vec<Option<String>> = new_psbt
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|o| {
|
||||
if let Some(value) = o.proprietary.get(&raw::ProprietaryKey {
|
||||
@ -112,17 +115,17 @@ pub fn create_transaction(
|
||||
for candidate in candidates {
|
||||
if let Some(c) = candidate {
|
||||
if c == change_address {
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, change_address.clone())?;
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, change_address.clone())?;
|
||||
fee_set = true;
|
||||
break;
|
||||
} else if c == sender_address {
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, sender_address.clone())?;
|
||||
SpClient::set_fees(&mut new_psbt, fee_rate, sender_address.clone())?;
|
||||
fee_set = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !fee_set {
|
||||
return Err(Error::msg("Must specify payer for fee"));
|
||||
}
|
||||
@ -172,13 +175,14 @@ pub fn map_outputs_to_sp_address(psbt_str: &str) -> Result<HashMap<String, Vec<u
|
||||
mod tests {
|
||||
use std::io::Write;
|
||||
|
||||
use crate::network::{Pcd, Prd};
|
||||
use crate::network::{Pcd, 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};
|
||||
use sp_client::bitcoin::{ScriptBuf, Transaction, Txid};
|
||||
use sp_client::silentpayments::utils::receiving::{
|
||||
calculate_tweak_data, get_pubkey_from_input,
|
||||
};
|
||||
@ -193,6 +197,7 @@ mod tests {
|
||||
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();
|
||||
@ -227,7 +232,49 @@ mod tests {
|
||||
let pcd_hash = helper_create_commitment(pcd.to_string());
|
||||
let mut key = [0u8; 32];
|
||||
key.copy_from_slice(&Vec::from_hex(KEY).unwrap());
|
||||
let prd = Prd::new(ALICE_ADDRESS.try_into().unwrap(), key, pcd_hash);
|
||||
|
||||
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 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 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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user