Merge branch 'minimal_pcd_prd' into dev
This commit is contained in:
commit
a592001fdb
@ -15,11 +15,9 @@ wasm-bindgen = "0.2.91"
|
|||||||
getrandom = { version="0.2.12", features = ["js"] }
|
getrandom = { version="0.2.12", features = ["js"] }
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
log = "0.4.6"
|
|
||||||
tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" }
|
tsify = { git = "https://github.com/Sosthene00/tsify", branch = "next" }
|
||||||
# sdk_common = { path = "../sdk_common" }
|
sdk_common = { path = "../sdk_common" }
|
||||||
sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "dev" }
|
# sdk_common = { git = "https://git.4nkweb.com/4nk/sdk_common.git", branch = "dev" }
|
||||||
shamir = { git = "https://github.com/Sosthene00/shamir", branch = "master" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
|
1798
src/api.rs
1798
src/api.rs
File diff suppressed because it is too large
Load Diff
18
src/lib.rs
18
src/lib.rs
@ -1,16 +1,11 @@
|
|||||||
#![allow(warnings)]
|
#![allow(warnings)]
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use sdk_common::crypto::AnkSharedSecret;
|
|
||||||
use sdk_common::network::CachedMessage;
|
use sdk_common::network::CachedMessage;
|
||||||
use serde::{Deserialize, Serialize};
|
use sdk_common::MutexExt;
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||||
use tsify::Tsify;
|
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
mod peers;
|
mod peers;
|
||||||
mod process;
|
|
||||||
mod user;
|
mod user;
|
||||||
mod wallet;
|
mod wallet;
|
||||||
|
|
||||||
@ -21,14 +16,3 @@ pub fn lock_messages() -> Result<MutexGuard<'static, Vec<CachedMessage>>, Error>
|
|||||||
.get_or_init(|| Mutex::new(vec![]))
|
.get_or_init(|| Mutex::new(vec![]))
|
||||||
.lock_anyhow()
|
.lock_anyhow()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait MutexExt<T> {
|
|
||||||
fn lock_anyhow(&self) -> Result<MutexGuard<T>, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Debug> MutexExt<T> for Mutex<T> {
|
|
||||||
fn lock_anyhow(&self) -> Result<MutexGuard<T>, Error> {
|
|
||||||
self.lock()
|
|
||||||
.map_err(|e| Error::msg(format!("Failed to lock: {}", e)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
405
src/process.rs
405
src/process.rs
@ -1,405 +0,0 @@
|
|||||||
use std::fmt::DebugStruct;
|
|
||||||
|
|
||||||
use sdk_common::sp_client::silentpayments::utils::SilentPaymentAddress;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use tsify::Tsify;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
pub const HTML_KOTPART: &str = "
|
|
||||||
<div class='card'>
|
|
||||||
<div class='side-by-side'>
|
|
||||||
<h3>Send encrypted messages</h3>
|
|
||||||
<div>
|
|
||||||
<a href='#' id='send messages'>Send Messages</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form id='form4nk' action='#'>
|
|
||||||
<label for='sp_address'>Send to:</label>
|
|
||||||
<input type='text' id='sp_address' />
|
|
||||||
<hr/>
|
|
||||||
<label for='message'>Message:</label>
|
|
||||||
<input type='message' id='message' />
|
|
||||||
<hr/>
|
|
||||||
<button type='submit' id='submitButton' class='recover bg-primary'>Send</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
";
|
|
||||||
|
|
||||||
pub const HTML_STORAGE: &str = "
|
|
||||||
<div class='card'>
|
|
||||||
<div class='side-by-side'>
|
|
||||||
<h3>Send encrypted messages</h3>
|
|
||||||
<div>
|
|
||||||
<a href='#' id='send messages'>Send Messages</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form id='form4nk' action='#'>
|
|
||||||
<label for='sp_address'>Send to:</label>
|
|
||||||
<input type='text' id='sp_address' />
|
|
||||||
<hr/>
|
|
||||||
<label for='message'>Message:</label>
|
|
||||||
<input type='message' id='message' />
|
|
||||||
<hr/>
|
|
||||||
<button type='submit' id='submitButton' class='recover bg-primary'>Send</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
";
|
|
||||||
|
|
||||||
pub const HTML_MESSAGING: &str = "
|
|
||||||
<div class='card'>
|
|
||||||
<div class='side-by-side'>
|
|
||||||
<h3>Send encrypted messages</h3>
|
|
||||||
<div>
|
|
||||||
<a href='#' id='send messages'>Send Messages</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form id='form4nk' action='#'>
|
|
||||||
<div id='our_address' class='our_address'></div>
|
|
||||||
<label for='sp_address'>Send to:</label>
|
|
||||||
<input type='text' id='sp_address' />
|
|
||||||
<hr/>
|
|
||||||
<label for='message'>Message:</label>
|
|
||||||
<input type='message' id='message' />
|
|
||||||
<hr/>
|
|
||||||
<button type='submit' id='submitButton' class='recover bg-primary'>Send</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
";
|
|
||||||
|
|
||||||
pub const CSS: &str = "
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: left;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
/* flex-wrap: wrap; */
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
border: 0;
|
|
||||||
height: 1px;
|
|
||||||
background-color: #ddd;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
input, select {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 8px 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
padding: 10px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 17px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
.side-by-side {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.side-by-side>* {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
button.recover {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 17px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button.recover:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
a.btn {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 17px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.btn:hover {
|
|
||||||
background-color: #45a049;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #78a6de;
|
|
||||||
}
|
|
||||||
.bg-secondary {
|
|
||||||
background-color: #2b81ed;
|
|
||||||
}
|
|
||||||
.bg-primary {
|
|
||||||
background-color: #1A61ED;
|
|
||||||
}
|
|
||||||
.bg-primary:hover {
|
|
||||||
background-color: #457be8;
|
|
||||||
}
|
|
||||||
.card-revoke {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.card-revoke a {
|
|
||||||
max-width: 50px;
|
|
||||||
width: 100%;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.card-revoke button {
|
|
||||||
max-width: 200px;
|
|
||||||
width: 100%;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #78a6de;
|
|
||||||
}
|
|
||||||
.card-revoke svg {
|
|
||||||
width: 100%;
|
|
||||||
height: auto;
|
|
||||||
fill: #333;
|
|
||||||
}
|
|
||||||
.image-label {
|
|
||||||
display: block;
|
|
||||||
color: #fff;
|
|
||||||
padding: 5px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
.image-container {
|
|
||||||
width: 400px;
|
|
||||||
height: 300px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.image-container img {
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
object-position: center center;
|
|
||||||
}
|
|
||||||
.passwordalert {
|
|
||||||
color: #FF0000;
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
pub const CSSUPDATE: &str = "
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
font-family: 'Arial', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
background-color: #ffffff;
|
|
||||||
border-radius: 8px;
|
|
||||||
text-align: left;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(1fr, 2fr);
|
|
||||||
gap: 10px;
|
|
||||||
max-width: 400px;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-primary {
|
|
||||||
background-color: #1a61ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-primary:hover {
|
|
||||||
background-color: #457be8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-secondary {
|
|
||||||
background-color: #2b81ed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-secondary:hover {
|
|
||||||
background-color: #5f9bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
text-align: left;
|
|
||||||
padding-right: 10px;
|
|
||||||
line-height: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
grid-column: span 2;
|
|
||||||
display: inline-block;
|
|
||||||
color: #fff;
|
|
||||||
border: none;
|
|
||||||
padding: 12px 17px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.div-text-area {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-by-side {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.circle-btn {
|
|
||||||
width: 25px;
|
|
||||||
height: 25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: none;
|
|
||||||
color: white;
|
|
||||||
padding: 0px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fileInput {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px;
|
|
||||||
padding-left: 0px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
";
|
|
||||||
|
|
||||||
pub const JSUPDATE: &str = "
|
|
||||||
var addSpAddressBtn = document.getElementById('add-sp-address-btn');
|
|
||||||
var removeSpAddressBtn = document.querySelectorAll('.minus-sp-address-btn');
|
|
||||||
|
|
||||||
addSpAddressBtn.addEventListener('click', function (event) {
|
|
||||||
addDynamicField(this);
|
|
||||||
});
|
|
||||||
|
|
||||||
function addDynamicField(element) {
|
|
||||||
var addSpAddressBlock = document.getElementById('sp-address-block');
|
|
||||||
var spAddress = addSpAddressBlock.querySelector('#sp-address').value;
|
|
||||||
addSpAddressBlock.querySelector('#sp-address').value = '';
|
|
||||||
spAddress = spAddress.trim();
|
|
||||||
if (spAddress != '') {
|
|
||||||
var sideBySideDiv = document.createElement('div');
|
|
||||||
sideBySideDiv.className = 'side-by-side';
|
|
||||||
|
|
||||||
var inputElement = document.createElement('input');
|
|
||||||
inputElement.type = 'text';
|
|
||||||
inputElement.name = 'spAddresses[]';
|
|
||||||
inputElement.setAttribute('form', 'no-form');
|
|
||||||
inputElement.value = spAddress;
|
|
||||||
inputElement.disabled = true;
|
|
||||||
|
|
||||||
var buttonElement = document.createElement('button');
|
|
||||||
buttonElement.type = 'button';
|
|
||||||
buttonElement.className =
|
|
||||||
'circle-btn bg-secondary minus-sp-address-btn';
|
|
||||||
buttonElement.innerHTML = '-';
|
|
||||||
|
|
||||||
buttonElement.addEventListener('click', function (event) {
|
|
||||||
removeDynamicField(this.parentElement);
|
|
||||||
});
|
|
||||||
|
|
||||||
sideBySideDiv.appendChild(inputElement);
|
|
||||||
sideBySideDiv.appendChild(buttonElement);
|
|
||||||
|
|
||||||
addSpAddressBlock.appendChild(sideBySideDiv);
|
|
||||||
}
|
|
||||||
function removeDynamicField(element) {
|
|
||||||
element.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
";
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Default, Tsify)]
|
|
||||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
|
||||||
pub struct Process {
|
|
||||||
pub id: u32,
|
|
||||||
pub name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub members: Vec<String>,
|
|
||||||
pub html: String,
|
|
||||||
pub style: String,
|
|
||||||
pub script: String,
|
|
||||||
// Add conditions : process, member, peer, artefact
|
|
||||||
}
|
|
144
src/user.rs
144
src/user.rs
@ -21,6 +21,7 @@ use std::io::{Cursor, Read, Write};
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||||
|
|
||||||
|
use sdk_common::device::Device;
|
||||||
use sdk_common::sp_client::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
use sdk_common::sp_client::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||||
use sdk_common::sp_client::silentpayments::bitcoin_hashes::sha256;
|
use sdk_common::sp_client::silentpayments::bitcoin_hashes::sha256;
|
||||||
use sdk_common::sp_client::silentpayments::utils::{Network as SpNetwork, SilentPaymentAddress};
|
use sdk_common::sp_client::silentpayments::utils::{Network as SpNetwork, SilentPaymentAddress};
|
||||||
@ -29,9 +30,7 @@ use sdk_common::sp_client::spclient::{OutputList, SpWallet, SpendKey};
|
|||||||
use crate::peers::Peer;
|
use crate::peers::Peer;
|
||||||
use crate::wallet::generate_sp_wallet;
|
use crate::wallet::generate_sp_wallet;
|
||||||
use crate::MutexExt;
|
use crate::MutexExt;
|
||||||
use sdk_common::crypto::{
|
use sdk_common::crypto::{AeadCore, Aes256Gcm, KeyInit};
|
||||||
AeadCore, Aes256Decryption, Aes256Encryption, Aes256Gcm, HalfKey, KeyInit, Purpose,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static LOCAL_DEVICE: OnceLock<Mutex<Device>> = OnceLock::new();
|
pub static LOCAL_DEVICE: OnceLock<Mutex<Device>> = OnceLock::new();
|
||||||
|
|
||||||
@ -58,142 +57,3 @@ pub fn lock_local_device() -> Result<MutexGuard<'static, Device>> {
|
|||||||
.get_or_init(|| Mutex::new(Device::default()))
|
.get_or_init(|| Mutex::new(Device::default()))
|
||||||
.lock_anyhow()
|
.lock_anyhow()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -33,7 +33,7 @@ pub fn generate_sp_wallet(label: Option<String>, network: Network) -> anyhow::Re
|
|||||||
network,
|
network,
|
||||||
)?;
|
)?;
|
||||||
let our_address: SilentPaymentAddress = sp_client.get_receiving_address().try_into()?;
|
let our_address: SilentPaymentAddress = sp_client.get_receiving_address().try_into()?;
|
||||||
log::info!(
|
sdk_common::log::info!(
|
||||||
"Created client for sp with address: {}",
|
"Created client for sp with address: {}",
|
||||||
our_address.to_string()
|
our_address.to_string()
|
||||||
);
|
);
|
||||||
|
550
tests/browser.rs
550
tests/browser.rs
File diff suppressed because one or more lines are too long
283
tests/pairing.rs
Normal file
283
tests/pairing.rs
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use sdk_client::api::{
|
||||||
|
create_device_from_sp_wallet, create_update_transaction, dump_device, dump_process_cache,
|
||||||
|
get_address, get_outputs, get_update_proposals, pair_device, parse_cipher, reset_device,
|
||||||
|
response_prd, restore_device, set_process_cache, setup, ApiReturn,
|
||||||
|
};
|
||||||
|
use sdk_common::log::debug;
|
||||||
|
use sdk_common::pcd::{Member, Pcd, RoleDefinition};
|
||||||
|
use sdk_common::sp_client::bitcoin::OutPoint;
|
||||||
|
use sdk_common::sp_client::spclient::OwnedOutput;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
|
use tsify::JsValueSerdeExt;
|
||||||
|
use wasm_bindgen_test::*;
|
||||||
|
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
use utils::*;
|
||||||
|
|
||||||
|
wasm_bindgen_test_configure!(run_in_browser);
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_pairing() {
|
||||||
|
setup();
|
||||||
|
debug!("==============================================\nStarting test_pairing\n==============================================");
|
||||||
|
|
||||||
|
// ========================= Alice
|
||||||
|
reset_device().unwrap();
|
||||||
|
create_device_from_sp_wallet(ALICE_LOGIN_WALLET.to_owned()).unwrap();
|
||||||
|
|
||||||
|
// we get our own address
|
||||||
|
let alice_address = get_address().unwrap();
|
||||||
|
|
||||||
|
// we scan the qr code or get the address by any other means
|
||||||
|
let bob_address = helper_get_bob_address();
|
||||||
|
|
||||||
|
// Alice creates the new member with Bob address
|
||||||
|
let new_member = Member::new(vec![
|
||||||
|
alice_address.as_str().try_into().unwrap(),
|
||||||
|
bob_address.as_str().try_into().unwrap(),
|
||||||
|
])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let pairing_init_state = json!({
|
||||||
|
"html": "",
|
||||||
|
"style": "",
|
||||||
|
"script": "",
|
||||||
|
"description": "AliceBob",
|
||||||
|
"roles": {
|
||||||
|
"owner": {
|
||||||
|
"members":
|
||||||
|
[
|
||||||
|
new_member
|
||||||
|
],
|
||||||
|
"validation_rules":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"quorum": 1.0,
|
||||||
|
"fields": [
|
||||||
|
"roles",
|
||||||
|
"pairing_tx"
|
||||||
|
],
|
||||||
|
"min_sig_member": 1.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pairing_tx": OutPoint::null(),
|
||||||
|
});
|
||||||
|
|
||||||
|
debug!("Alice pairs her device");
|
||||||
|
// we can update our local device now, first with an empty txid
|
||||||
|
pair_device(OutPoint::null().to_string(), vec![helper_get_bob_address()]).unwrap();
|
||||||
|
|
||||||
|
debug!("Alice sends a transaction commiting to an update prd to Bob");
|
||||||
|
let alice_pairing_return =
|
||||||
|
create_update_transaction(None, pairing_init_state.to_string(), 1).unwrap();
|
||||||
|
|
||||||
|
let (root_outpoint, alice_init_process) = alice_pairing_return.updated_process.unwrap();
|
||||||
|
let alice_pcd_commitment = Value::from_str(
|
||||||
|
&alice_init_process
|
||||||
|
.get_impending_requests()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.payload,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.tagged_hash();
|
||||||
|
|
||||||
|
let pairing_tx = alice_pairing_return.new_tx_to_send.unwrap();
|
||||||
|
|
||||||
|
// This is only for testing, the relay takes care of that in prod
|
||||||
|
let get_outputs_result = get_outputs().unwrap();
|
||||||
|
|
||||||
|
let alice_outputs: HashMap<OutPoint, OwnedOutput> = get_outputs_result.into_serde().unwrap();
|
||||||
|
|
||||||
|
let alice_pairing_tweak_data = helper_get_tweak_data(&pairing_tx, alice_outputs);
|
||||||
|
|
||||||
|
// End of the test only part
|
||||||
|
|
||||||
|
// Alice parses her own transaction
|
||||||
|
helper_parse_transaction(&pairing_tx, &alice_pairing_tweak_data);
|
||||||
|
|
||||||
|
// Notify user that we're waiting for confirmation from the other device
|
||||||
|
// We can update the local device with the actual pairing outpoint
|
||||||
|
pair_device(
|
||||||
|
OutPoint::new(pairing_tx.txid(), 0).to_string(),
|
||||||
|
vec![helper_get_bob_address()],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// TODO unpair device
|
||||||
|
|
||||||
|
// We can produce the prd response now even if we can't use it yet
|
||||||
|
let alice_prd_response = response_prd(
|
||||||
|
root_outpoint,
|
||||||
|
alice_init_process
|
||||||
|
.get_impending_requests()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.ciphers_to_send
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
// We can also create the first login transaction and sign it
|
||||||
|
|
||||||
|
// this is only for testing, as we're playing both parts
|
||||||
|
let alice_device = dump_device().unwrap();
|
||||||
|
let alice_processes = dump_process_cache().unwrap();
|
||||||
|
|
||||||
|
// ======================= Bob
|
||||||
|
reset_device().unwrap();
|
||||||
|
create_device_from_sp_wallet(BOB_LOGIN_WALLET.to_owned()).unwrap();
|
||||||
|
|
||||||
|
// Bob receives Alice pairing transaction
|
||||||
|
debug!("Bob parses Alice pairing transaction");
|
||||||
|
helper_parse_transaction(&pairing_tx, &alice_pairing_tweak_data);
|
||||||
|
|
||||||
|
debug!("Bob receives the prd");
|
||||||
|
let mut bob_retrieved_prd: ApiReturn = ApiReturn::default();
|
||||||
|
for cipher in alice_pairing_return.ciphers_to_send.iter() {
|
||||||
|
// debug!("Parsing cipher: {:#?}", cipher);
|
||||||
|
match parse_cipher(cipher.clone()) {
|
||||||
|
Ok(res) => bob_retrieved_prd = res,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Error parsing cipher: {:#?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(bob_retrieved_prd.ciphers_to_send.len() == 1);
|
||||||
|
assert!(bob_retrieved_prd.updated_process.is_some());
|
||||||
|
|
||||||
|
debug!("Bob retrieved prd: {:#?}", bob_retrieved_prd);
|
||||||
|
|
||||||
|
let (root_commitment, relevant_process) = bob_retrieved_prd.updated_process.unwrap();
|
||||||
|
let pcd_commitment = relevant_process
|
||||||
|
.get_impending_requests()
|
||||||
|
.get(0)
|
||||||
|
.unwrap()
|
||||||
|
.payload
|
||||||
|
.clone();
|
||||||
|
let prd_confirm_cipher = bob_retrieved_prd.ciphers_to_send.iter().next().unwrap();
|
||||||
|
|
||||||
|
debug!("Bob sends a Confirm Prd to Alice");
|
||||||
|
|
||||||
|
// this is only for testing, as we're playing both parts
|
||||||
|
let bob_device = dump_device().unwrap();
|
||||||
|
let bob_processes = dump_process_cache().unwrap();
|
||||||
|
|
||||||
|
// ======================= Alice
|
||||||
|
reset_device().unwrap();
|
||||||
|
restore_device(alice_device).unwrap();
|
||||||
|
set_process_cache(alice_processes).unwrap();
|
||||||
|
|
||||||
|
debug!("Alice receives the Confirm Prd");
|
||||||
|
let alice_parsed_prd = parse_cipher(prd_confirm_cipher.clone()).unwrap();
|
||||||
|
|
||||||
|
debug!("Alice parsed Bob's Confirm Prd: {:#?}", alice_parsed_prd);
|
||||||
|
|
||||||
|
// ======================= Bob
|
||||||
|
reset_device().unwrap();
|
||||||
|
restore_device(bob_device).unwrap();
|
||||||
|
set_process_cache(bob_processes).unwrap();
|
||||||
|
|
||||||
|
debug!("Bob parses Alice's pcd");
|
||||||
|
let bob_parsed_pcd_return = parse_cipher(alice_parsed_prd.ciphers_to_send[0].clone()).unwrap();
|
||||||
|
|
||||||
|
debug!("bob_parsed_pcd: {:#?}", bob_parsed_pcd_return);
|
||||||
|
|
||||||
|
let (root_commitment, prd_update) = bob_parsed_pcd_return.updated_process.unwrap();
|
||||||
|
|
||||||
|
// At this point, user must validate the pairing proposal received from Alice
|
||||||
|
// We decrypt the content of the pcd so that we can display to user what matters
|
||||||
|
let alice_proposal =
|
||||||
|
get_update_proposals(root_commitment.clone()).unwrap();
|
||||||
|
|
||||||
|
debug!("Alice proposal: {:#?}", alice_proposal);
|
||||||
|
|
||||||
|
// get the pairing tx from the proposal
|
||||||
|
let proposal = Value::from_str(&alice_proposal.get(0).unwrap()).unwrap();
|
||||||
|
debug!("proposal: {:#?}", proposal);
|
||||||
|
let pairing_tx = proposal
|
||||||
|
.get("pairing_tx")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.trim_matches('"');
|
||||||
|
|
||||||
|
let roles = proposal
|
||||||
|
.get("roles")
|
||||||
|
.and_then(|v| Value::from_str(v.as_str().unwrap()).ok())
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|(role_name, role_value)| {
|
||||||
|
let role_def: RoleDefinition = serde_json::from_value(role_value.clone())?;
|
||||||
|
Ok((role_name.clone(), role_def))
|
||||||
|
})
|
||||||
|
.collect::<Result<HashMap<String, RoleDefinition>, anyhow::Error>>();
|
||||||
|
|
||||||
|
let roles = roles.unwrap();
|
||||||
|
|
||||||
|
// we check that the proposal contains only one member
|
||||||
|
assert!(roles.len() == 1);
|
||||||
|
assert!(roles["owner"].members.len() == 1);
|
||||||
|
|
||||||
|
// we get all the addresses of the members of the proposal
|
||||||
|
let proposal_members = roles
|
||||||
|
.iter()
|
||||||
|
.flat_map(|(_, members)| members.members.iter().flat_map(|m| m.get_addresses()))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
// we can automatically check that a pairing member contains local device address + the one that sent the proposal
|
||||||
|
assert!(proposal_members.contains(&alice_address));
|
||||||
|
assert!(proposal_members.contains(&bob_address));
|
||||||
|
assert!(proposal_members.len() == 2); // no free riders
|
||||||
|
|
||||||
|
// We remove the local address, but maybe that's the responsibility of the Member type
|
||||||
|
let proposal_members = proposal_members
|
||||||
|
.into_iter()
|
||||||
|
.filter(|m| m != &bob_address)
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
debug!("proposal_members: {:?}", proposal_members);
|
||||||
|
|
||||||
|
// we can now show all the addresses + pairing tx to the user on device to prompt confirmation
|
||||||
|
|
||||||
|
debug!("Bob pairs device with Alice");
|
||||||
|
pair_device(pairing_tx.to_owned(), proposal_members.clone()).unwrap();
|
||||||
|
|
||||||
|
let prd_to_respond = prd_update.get_impending_requests().get(0).unwrap().to_string();
|
||||||
|
|
||||||
|
// Bob signs the proposal and sends a prd response too
|
||||||
|
let bob_prd_response = response_prd(root_commitment, prd_to_respond, true)
|
||||||
|
.unwrap()
|
||||||
|
.ciphers_to_send
|
||||||
|
.get(0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// To make the pairing effective, alice and bob must now creates a new transaction where they both control one output
|
||||||
|
|
||||||
|
// login logic: user must have access to both devices to login
|
||||||
|
// user must still have access to both devices in case an action needs both devices (as defined in the roles)
|
||||||
|
// process can define that acting on some fields of the state only needs a fraction of the devices to sign
|
||||||
|
|
||||||
|
// Problem: we need both devices to sign the next login transaction.
|
||||||
|
// Simplest solution: device A creates the transaction with both inputs and outputs, signs its input and sends to device B
|
||||||
|
// device B notify user that a login is underway, user either accepts (sign the transaction and broadcast), or refuses (actually we should think of some revokation step from here)
|
||||||
|
|
||||||
|
// login();
|
||||||
|
|
||||||
|
// Once we know this tx id, we can commit to the relay
|
||||||
|
}
|
74
tests/utils.rs
Normal file
74
tests/utils.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use sdk_client::api::{parse_new_tx, ApiReturn};
|
||||||
|
use sdk_common::network::NewTxMessage;
|
||||||
|
use sdk_common::sp_client::bitcoin::consensus::serialize;
|
||||||
|
use sdk_common::sp_client::bitcoin::hex::DisplayHex;
|
||||||
|
use sdk_common::sp_client::bitcoin::secp256k1::PublicKey;
|
||||||
|
use sdk_common::sp_client::bitcoin::{OutPoint, ScriptBuf, Transaction};
|
||||||
|
use sdk_common::sp_client::silentpayments::utils::receiving::{
|
||||||
|
calculate_tweak_data, get_pubkey_from_input,
|
||||||
|
};
|
||||||
|
use sdk_common::sp_client::spclient::{OwnedOutput, SpWallet};
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
// We're using alice and bob for clarity, but it's important to remember that for pairing and login Alice and Bob are the same person
|
||||||
|
pub const ALICE_START_WALLET: &str = "{\"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\"}}},\"tx_history\":[]}";
|
||||||
|
pub const ALICE_LOGIN_WALLET: &str = "{\"client\":{\"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]]]},\"network\":\"regtest\"},\"outputs\":{\"wallet_fingerprint\":[187,119,108,230,171,125,106,11],\"birthday\":1620,\"last_scan\":2146,\"outputs\":{\"e2c6ff9927c8a5f7a60087117732c07ab7cd82c0c65462e9c780eb5ce9c35292:1\":{\"blockheight\":0,\"tweak\":\"7c84a3074c18c23a65eceaca49a327989ef6e7240e0e080421afb80f06906d73\",\"amount\":306,\"script\":\"5120eb78084d7a2ccbdb7eb7e9bba7cf875c4e54a5aba0beac57c5d3e41133bd8fdd\",\"label\":null,\"spend_status\":\"Unspent\"},\"e2c6ff9927c8a5f7a60087117732c07ab7cd82c0c65462e9c780eb5ce9c35292:2\":{\"blockheight\":0,\"tweak\":\"e3e21d551e933199f8c977bc0362616bdfb128da7ca9728bd7254977541c39cc\",\"amount\":3936897,\"script\":\"5120df3af55a63bd056c5d6d09f47a3a19c382d938c08db8bc41a59c5235970209ad\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":\"Unspent\"},\"576c2e53fe924d68deb7262cfc0e4023b5889652dec35671e1e7cf255d61c28f:0\":{\"blockheight\":0,\"tweak\":\"6e5c1d4690b9ff4fa26e5185ada25af5c2987650e629a518db8a43fdaad7a26c\",\"amount\":349,\"script\":\"512088ac90e180c87b7fa6aa33db4c72a8620cd08fda3ba1c71430b904eb068c587f\",\"label\":null,\"spend_status\":\"Unspent\"},\"9a4a67cc5a40bf882d8b300d91024d7c97024b3b68b2df7745a5b9ea1df1888c:1\":{\"blockheight\":1620,\"tweak\":\"b8b63b3ed97d297b744135cfac2fb4a344c881a77543b71f1fcd16bc67514f26\",\"amount\":3938643,\"script\":\"51205b7b324bb71d411e32f2c61fda5d1db23f5c7d6d416a77fab87c913a1b120be1\",\"label\":\"ac14a827e2d023b8f7804303a47259366117d99ed932b641d4a8eaf1b82cc992\",\"spend_status\":{\"Spent\":\"e2c6ff9927c8a5f7a60087117732c07ab7cd82c0c65462e9c780eb5ce9c35292\"}}}},\"tx_history\":[]}";
|
||||||
|
pub const BOB_START_WALLET: &str = "{\"client\":{\"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]]]},\"network\":\"regtest\"},\"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\"}}},\"tx_history\":[]}";
|
||||||
|
pub const BOB_LOGIN_WALLET: &str = "{\"client\":{\"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]]]},\"network\":\"regtest\"},\"outputs\":{\"wallet_fingerprint\":[203,200,4,248,139,36,241,232],\"birthday\":2146,\"last_scan\":2146,\"outputs\":{\"93722ea2fb9b74954210b4cdd1360e280b7ff1bc156d6b75f847e62411c588fb:0\":{\"blockheight\":0,\"tweak\":\"da5e3aa2378e3a257f99eb1e0ae4c672916f6a2f32a8ed9a8e146f2074da981b\",\"amount\":443,\"script\":\"51209eb9e950127b6a7d81668f25b4d5b164b42dafe59ce40b80e6c489ec983540d7\",\"label\":null,\"spend_status\":\"Unspent\"},\"e2c6ff9927c8a5f7a60087117732c07ab7cd82c0c65462e9c780eb5ce9c35292:0\":{\"blockheight\":0,\"tweak\":\"0e3395ff27bde9187ffaeeb2521f6277d3b83911f16ccbaf59a1a68d99a0ab93\",\"amount\":1200,\"script\":\"512010f06f764cbc923ec3198db946307bf0c06a1b4f09206055e47a6fec0a33d52c\",\"label\":null,\"spend_status\":{\"Spent\":\"576c2e53fe924d68deb7262cfc0e4023b5889652dec35671e1e7cf255d61c28f\"}},\"576c2e53fe924d68deb7262cfc0e4023b5889652dec35671e1e7cf255d61c28f:1\":{\"blockheight\":0,\"tweak\":\"f0ad83734cdc7d73575e5a32651390cf30b92cc7e44cf94ec37da46900ecaf71\",\"amount\":654,\"script\":\"5120230cc1e85829be238e666f469653cbc2f1c0e3675a9bf33e1d1f91115f5dd306\",\"label\":\"56572fc770b52096879662f97f98263d3e126f5a4a38f00f2895a9dde4c47c1c\",\"spend_status\":\"Unspent\"},\"fbd9c63e0dd08c2569b51a0d6095a79ee2acfcac66acdb594328a095f1fadb63:1\":{\"blockheight\":2146,\"tweak\":\"678dbcbdb40cd3733c8dbbd508761a0937009cf75a9f466e3c98877e79037cbc\",\"amount\":99896595,\"script\":\"5120deab0c5a3d23de30477b0b5a95a261c96e29afdd9813c665d2bf025ad2b3f919\",\"label\":null,\"spend_status\":\"Unspent\"}}},\"tx_history\":[]}";
|
||||||
|
|
||||||
|
pub const RELAY_ADDRESS: &str = "sprt1qqfmqt0ngq99y8t4ke6uhtm2a2vc2zxvhj7hjrqu599kn30d4cs9rwqn6n079mdr4dfqg72yrtvuxf43yswscw86nvvl09mc5ljx65vfh75fkza35";
|
||||||
|
pub const DEFAULT_NYM: &str = "AliceBob";
|
||||||
|
|
||||||
|
pub fn helper_get_alice_address() -> String {
|
||||||
|
let wallet: SpWallet = serde_json::from_str(ALICE_START_WALLET).unwrap();
|
||||||
|
wallet.get_client().get_receiving_address()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helper_get_bob_address() -> String {
|
||||||
|
let wallet: SpWallet = serde_json::from_str(BOB_START_WALLET).unwrap();
|
||||||
|
wallet.get_client().get_receiving_address()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helper_get_tweak_data(
|
||||||
|
tx: &Transaction,
|
||||||
|
outpoints: HashMap<OutPoint, OwnedOutput>,
|
||||||
|
) -> String {
|
||||||
|
let mut outpoint_data = vec![];
|
||||||
|
let mut witnesses = vec![];
|
||||||
|
let mut spks = vec![];
|
||||||
|
for prevout in tx.input.iter() {
|
||||||
|
outpoint_data.push((
|
||||||
|
prevout.previous_output.txid.to_string(),
|
||||||
|
prevout.previous_output.vout,
|
||||||
|
));
|
||||||
|
witnesses.push(prevout.witness.clone());
|
||||||
|
if let Some(output) = outpoints.get(&prevout.previous_output) {
|
||||||
|
spks.push(ScriptBuf::from_hex(&output.script).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut input_pubkeys = vec![];
|
||||||
|
for (spk, witness) in spks.iter().zip(witnesses) {
|
||||||
|
let input_pubkey =
|
||||||
|
get_pubkey_from_input(&vec![], &witness.to_vec(), spk.as_bytes()).unwrap();
|
||||||
|
input_pubkeys.push(input_pubkey.unwrap());
|
||||||
|
}
|
||||||
|
let ref_pubkeys: Vec<&PublicKey> = input_pubkeys.iter().collect();
|
||||||
|
let tweak_data = calculate_tweak_data(&ref_pubkeys, &outpoint_data).unwrap();
|
||||||
|
tweak_data.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn helper_parse_transaction(transaction: &Transaction, tweak_data: &str) -> ApiReturn {
|
||||||
|
let new_tx_msg = serde_json::to_string(&NewTxMessage::new(
|
||||||
|
serialize(transaction).to_lower_hex_string(),
|
||||||
|
Some(tweak_data.to_owned()),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
// debug!("new_tx_msg: {:?}", new_tx_msg);
|
||||||
|
let result = parse_new_tx(new_tx_msg, 0, 1);
|
||||||
|
match result {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => panic!("Unexpected error: {}", e.message),
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user