Functional prd/pcd
This commit is contained in:
parent
62b2137c77
commit
f3aa1bc2d0
@ -3,7 +3,7 @@ use tsify::Tsify;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use sp_client::spclient::SpWallet;
|
use sp_client::{bitcoin::{hashes::Hash, OutPoint, Txid}, spclient::SpWallet};
|
||||||
|
|
||||||
use crate::pcd::Member;
|
use crate::pcd::Member;
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ use crate::pcd::Member;
|
|||||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
sp_wallet: SpWallet,
|
sp_wallet: SpWallet,
|
||||||
pairing_process_uuid: Option<String>,
|
pairing_process_commitment: Option<Txid>,
|
||||||
paired_member: Option<Member>,
|
paired_member: Option<Member>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ impl Device {
|
|||||||
pub fn new(sp_wallet: SpWallet) -> Self {
|
pub fn new(sp_wallet: SpWallet) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sp_wallet,
|
sp_wallet,
|
||||||
pairing_process_uuid: None,
|
pairing_process_commitment: None,
|
||||||
paired_member: None,
|
paired_member: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,16 +32,26 @@ impl Device {
|
|||||||
&mut self.sp_wallet
|
&mut self.sp_wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_linking(&self) -> bool {
|
||||||
|
match self.pairing_process_commitment {
|
||||||
|
Some(ref value) => value.as_raw_hash().as_byte_array().iter().all(|&b| b == 0),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_linked(&self) -> bool {
|
pub fn is_linked(&self) -> bool {
|
||||||
self.pairing_process_uuid.is_some()
|
match self.pairing_process_commitment {
|
||||||
|
Some(ref value) => !value.as_raw_hash().as_byte_array().iter().all(|&b| b == 0),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_process_uuid(&self) -> Option<String> {
|
pub fn get_process_commitment(&self) -> Option<Txid> {
|
||||||
self.pairing_process_uuid.clone()
|
self.pairing_process_commitment.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pair(&mut self, uuid: Uuid, member: Member) {
|
pub fn pair(&mut self, commitment_tx: Txid, member: Member) {
|
||||||
self.pairing_process_uuid = Some(uuid.to_string());
|
self.pairing_process_commitment = Some(commitment_tx);
|
||||||
self.paired_member = Some(member);
|
self.paired_member = Some(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
src/pcd.rs
118
src/pcd.rs
@ -1,4 +1,4 @@
|
|||||||
use std::{collections::{HashMap, HashSet}, str::FromStr};
|
use std::{collections::HashSet, str::FromStr};
|
||||||
use anyhow::{Result, Error};
|
use anyhow::{Result, Error};
|
||||||
|
|
||||||
use aes_gcm::{aead::{Aead, Payload}, AeadCore, Aes256Gcm, KeyInit};
|
use aes_gcm::{aead::{Aead, Payload}, AeadCore, Aes256Gcm, KeyInit};
|
||||||
@ -69,61 +69,68 @@ impl AnkPcdHash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||||
fn hash(&self) -> AnkPcdHash {
|
fn tagged_hash(&self) -> AnkPcdHash {
|
||||||
AnkPcdHash::from_value(&self.to_value())
|
AnkPcdHash::from_value(&self.to_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_fields(&self, fields2keys: &mut Map<String, Value>, fields2cipher: &mut Map<String, Value>) -> Result<()> {
|
fn encrypt_fields(&self, fields2keys: &mut Map<String, Value>, fields2cipher: &mut Map<String, Value>) -> Result<()> {
|
||||||
let as_value = self.to_value();
|
let as_value = self.to_value();
|
||||||
let as_map = as_value.as_object().unwrap();
|
let as_map = as_value.as_object().ok_or_else(|| Error::msg("Expected object"))?;
|
||||||
let mut rng = thread_rng();
|
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());
|
for (field, value) in as_map {
|
||||||
|
let aes_key = Aes256Gcm::generate_key(&mut rng);
|
||||||
|
let nonce = Aes256Gcm::generate_nonce(&mut rng);
|
||||||
|
fields2keys.insert(field.to_owned(), Value::String(aes_key.to_lower_hex_string()));
|
||||||
|
|
||||||
|
let encrypt_eng = Aes256Gcm::new(&aes_key);
|
||||||
let value_string = value.to_string();
|
let value_string = value.to_string();
|
||||||
let payload = Payload {
|
let payload = Payload {
|
||||||
msg: value_string.as_bytes(),
|
msg: value_string.as_bytes(),
|
||||||
aad: AAD,
|
aad: AAD,
|
||||||
};
|
};
|
||||||
let cipher = encryption.encrypt(&nonce.into(), payload)
|
let cipher = encrypt_eng.encrypt(&nonce, payload)
|
||||||
.map_err(|e| Error::msg(format!("{}", e)))?;
|
.map_err(|e| Error::msg(format!("Encryption failed for field {}: {}", field, e)))?;
|
||||||
|
|
||||||
let mut res = Vec::with_capacity(nonce.len() + cipher.len());
|
let mut res = Vec::with_capacity(nonce.len() + cipher.len());
|
||||||
res.extend_from_slice(&nonce);
|
res.extend_from_slice(&nonce);
|
||||||
res.extend_from_slice(&cipher);
|
res.extend_from_slice(&cipher);
|
||||||
|
|
||||||
fields2cipher.insert(key.to_owned(), Value::String(res.to_lower_hex_string()));
|
fields2cipher.insert(field.to_owned(), Value::String(res.to_lower_hex_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_fields(&mut self, fields2keys: &Map<String, Value>) -> Result<()> {
|
fn decrypt_fields(&self, fields2keys: &Map<String, Value>, fields2plain: &mut Map<String, Value>) -> Result<()> {
|
||||||
let as_value = self.to_value();
|
let value = self.to_value();
|
||||||
let as_map = as_value.as_object().unwrap();
|
let map = value.as_object().unwrap();
|
||||||
for (key, value) in as_map {
|
|
||||||
if let Some(aes_key) = fields2keys.get(key) {
|
for (field, encrypted_value) in map.iter() {
|
||||||
let mut nonce = [0u8; 12];
|
if let Some(aes_key) = fields2keys.get(field) {
|
||||||
let mut key_buf = [0u8; 32];
|
|
||||||
key_buf.copy_from_slice(&Vec::from_hex(&aes_key.to_string().trim_matches('\"'))?);
|
let key_buf = 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('\"'))?;
|
let decrypt_eng = Aes256Gcm::new(key_buf.as_slice().into());
|
||||||
nonce.copy_from_slice(&raw_cipher[..12]);
|
|
||||||
|
let raw_cipher = Vec::from_hex(&encrypted_value.as_str().ok_or_else(|| Error::msg("Expected string"))?.trim_matches('\"'))?;
|
||||||
|
|
||||||
|
if raw_cipher.len() < 28 {
|
||||||
|
return Err(Error::msg(format!("Invalid ciphertext length for field {}", field)));
|
||||||
|
}
|
||||||
|
|
||||||
let payload = Payload {
|
let payload = Payload {
|
||||||
msg: &raw_cipher[12..],
|
msg: &raw_cipher[12..],
|
||||||
aad: AAD,
|
aad: AAD,
|
||||||
};
|
};
|
||||||
let plain = decrypt.decrypt(&nonce.into(), payload)
|
|
||||||
.map_err(|_| Error::msg(format!("Failed to decrypt field {}", key)))?;
|
let plain = decrypt_eng.decrypt(raw_cipher[..12].into(), payload)
|
||||||
self.to_value()
|
.map_err(|_| Error::msg(format!("Failed to decrypt field {}", field)))?;
|
||||||
.as_object_mut()
|
let decrypted_value: String = String::from_utf8(plain)?;
|
||||||
.unwrap()
|
|
||||||
.insert(key.to_owned(), Value::String(plain.to_lower_hex_string()));
|
fields2plain.insert(field.to_owned(), Value::String(decrypted_value));
|
||||||
} else {
|
} else {
|
||||||
continue;
|
fields2plain.insert(field.to_owned(), Value::Null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,8 +183,51 @@ pub struct RoleDefinition {
|
|||||||
pub validation_rules: Vec<ValidationRule>,
|
pub validation_rules: Vec<ValidationRule>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
pub fn compare_maps(map1: &Map<String, Value>, map2: &Map<String, Value>) -> bool {
|
||||||
// #[tsify(into_wasm_abi, from_wasm_abi)]
|
// First, check if both maps have the same keys
|
||||||
// pub struct Roles {
|
if map1.keys().collect::<Vec<&String>>() != map2.keys().collect::<Vec<&String>>() {
|
||||||
// pub roles: HashMap<String, RoleDefinition>
|
return false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
// Then, check if the corresponding values have the same type
|
||||||
|
for key in map1.keys() {
|
||||||
|
let value1 = map1.get(key).unwrap();
|
||||||
|
let value2 = map2.get(key).unwrap();
|
||||||
|
|
||||||
|
if !compare_values(value1, value2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_values(value1: &Value, value2: &Value) -> bool {
|
||||||
|
if value1.is_null() && value2.is_null() {
|
||||||
|
return true;
|
||||||
|
} else if value1.is_boolean() && value2.is_boolean() {
|
||||||
|
return true;
|
||||||
|
} else if value1.is_number() && value2.is_number() {
|
||||||
|
return true;
|
||||||
|
} else if value1.is_string() && value2.is_string() {
|
||||||
|
return true;
|
||||||
|
} else if value1.is_array() && value2.is_array() {
|
||||||
|
return compare_arrays(value1.as_array().unwrap(), value2.as_array().unwrap());
|
||||||
|
} else if value1.is_object() && value2.is_object() {
|
||||||
|
// Recursive comparison for nested objects
|
||||||
|
return compare_maps(value1.as_object().unwrap(), value2.as_object().unwrap());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compare_arrays(array1: &Vec<Value>, array2: &Vec<Value>) -> bool {
|
||||||
|
// Compare the type of each element in the arrays
|
||||||
|
for (elem1, elem2) in array1.iter().zip(array2.iter()) {
|
||||||
|
if !compare_values(elem1, elem2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
163
src/prd.rs
163
src/prd.rs
@ -5,81 +5,29 @@ use anyhow::{Result, Error};
|
|||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
|
use sp_client::bitcoin::hex::FromHex;
|
||||||
use sp_client::bitcoin::secp256k1::SecretKey;
|
use sp_client::bitcoin::secp256k1::SecretKey;
|
||||||
use sp_client::bitcoin::XOnlyPublicKey;
|
use sp_client::bitcoin::{OutPoint, XOnlyPublicKey};
|
||||||
use sp_client::silentpayments::utils::SilentPaymentAddress;
|
use sp_client::silentpayments::utils::SilentPaymentAddress;
|
||||||
use sp_client::spclient::SpWallet;
|
use sp_client::spclient::SpWallet;
|
||||||
use sp_client::bitcoin::secp256k1::schnorr::Signature;
|
|
||||||
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
||||||
use tsify::Tsify;
|
use tsify::Tsify;
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::pcd::{AnkPcdHash, Member};
|
use crate::pcd::{AnkPcdHash, Member, Pcd};
|
||||||
use crate::signature::{Proof};
|
use crate::signature::{AnkHash, AnkMessageHash, Proof};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, Tsify)]
|
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize, Tsify)]
|
||||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum PrdType {
|
pub enum PrdType {
|
||||||
#[default]
|
#[default]
|
||||||
None,
|
None,
|
||||||
Message,
|
Message,
|
||||||
Init, // Create a new process
|
|
||||||
Update, // Update an existing process
|
Update, // Update an existing process
|
||||||
List, // request a list of items
|
List, // request a list of items
|
||||||
Response,
|
Response,
|
||||||
Confirm,
|
Confirm,
|
||||||
}
|
TxProposal, // Send a psbt asking for recipient signature, used for login not sure about other use cases
|
||||||
|
|
||||||
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],
|
|
||||||
sigs: Vec<Signature>, // User must sign with the requested number of devices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sha256t_hash_newtype! {
|
sha256t_hash_newtype! {
|
||||||
@ -104,45 +52,78 @@ impl AnkPrdHash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Tsify)]
|
||||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct Prd {
|
pub struct Prd {
|
||||||
pub prd_type: PrdType,
|
pub prd_type: PrdType,
|
||||||
pub process_uuid: String, // stringification of Uuid
|
pub root_commitment: String,
|
||||||
pub sender: String,
|
pub sender: String,
|
||||||
pub keys: Map<String, Value>, // key is a key in pcd, value is the key to decrypt it
|
pub keys: Map<String, Value>, // key is a key in pcd, value is the key to decrypt it
|
||||||
pub validation_tokens: Vec<ValidationToken>,
|
pub validation_tokens: Vec<Proof>,
|
||||||
pub pcd_commitment: String,
|
pub payload: String, // Payload depends on the actual type
|
||||||
pub proof: Option<Proof>, // This must be None up to the creation of the network message
|
pub proof: Option<Proof>, // This must be None up to the creation of the network message
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Prd {
|
impl Prd {
|
||||||
pub fn new(
|
pub fn new_update(
|
||||||
prd_type: PrdType,
|
root_commitment: OutPoint,
|
||||||
uuid: Uuid,
|
sender: String, // Should take Member as argument
|
||||||
sender: String,
|
|
||||||
encrypted_pcd: Map<String, Value>,
|
encrypted_pcd: Map<String, Value>,
|
||||||
keys: Map<String, Value>
|
keys: Map<String, Value>
|
||||||
) -> Result<Self> {
|
) -> Self {
|
||||||
let res = Self {
|
Self {
|
||||||
prd_type,
|
prd_type: PrdType::Update,
|
||||||
process_uuid: uuid.to_string(),
|
root_commitment: root_commitment.to_string(),
|
||||||
sender,
|
sender,
|
||||||
validation_tokens: vec![],
|
validation_tokens: vec![],
|
||||||
keys,
|
keys,
|
||||||
pcd_commitment: AnkPcdHash::from_map(&encrypted_pcd).to_string(),
|
payload: Value::Object(encrypted_pcd).to_string(),
|
||||||
proof: None,
|
proof: None,
|
||||||
};
|
}
|
||||||
|
|
||||||
Ok(res)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_from_message(plain: &[u8], commitment: [u8; 32]) -> Result<Self> {
|
pub fn new_response(
|
||||||
|
root_commitment: OutPoint,
|
||||||
|
sender: String,
|
||||||
|
validation_token: Proof,
|
||||||
|
pcd_commitment: AnkPcdHash,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
prd_type: PrdType::Response,
|
||||||
|
root_commitment: root_commitment.to_string(),
|
||||||
|
sender,
|
||||||
|
validation_tokens: vec![validation_token],
|
||||||
|
keys: Map::new(),
|
||||||
|
payload: pcd_commitment.to_string(),
|
||||||
|
proof: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_confirm(
|
||||||
|
root_commitment: OutPoint,
|
||||||
|
sender: Member,
|
||||||
|
pcd_commitment: AnkPcdHash,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
prd_type: PrdType::Confirm,
|
||||||
|
root_commitment: root_commitment.to_string(),
|
||||||
|
sender: serde_json::to_string(&sender).unwrap(),
|
||||||
|
validation_tokens: vec![],
|
||||||
|
keys: Map::new(),
|
||||||
|
payload: pcd_commitment.to_string(),
|
||||||
|
proof: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn _extract_from_message(plain: &[u8], commitment: Option<&AnkPrdHash>) -> Result<Self> {
|
||||||
let prd: Prd = serde_json::from_slice(plain)?;
|
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 let Some(commitment) = commitment {
|
||||||
if prd.create_commitment().to_byte_array() != commitment {
|
// check that the hash of the prd is consistent with what's commited in the op_return
|
||||||
return Err(anyhow::Error::msg("Received prd is not what was commited in the transaction"));
|
if prd.create_commitment() != *commitment {
|
||||||
|
return Err(anyhow::Error::msg("Received prd is not what was commited in the transaction"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// check that the proof is consistent
|
// check that the proof is consistent
|
||||||
let sender: Member = serde_json::from_str(&prd.sender)?;
|
let sender: Member = serde_json::from_str(&prd.sender)?;
|
||||||
@ -167,15 +148,17 @@ impl Prd {
|
|||||||
}
|
}
|
||||||
proof.verify()?;
|
proof.verify()?;
|
||||||
}
|
}
|
||||||
|
// check that the commitment outpoint is valid, just in case
|
||||||
|
OutPoint::from_str(&prd.root_commitment)?;
|
||||||
Ok(prd)
|
Ok(prd)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_validation_token(&mut self, validation_token: ValidationToken) -> Result<()> {
|
pub fn extract_from_message(plain: &[u8]) -> Result<Self> {
|
||||||
match self.prd_type {
|
Self::_extract_from_message(plain, None)
|
||||||
PrdType::Confirm => self.validation_tokens.push(validation_token),
|
}
|
||||||
_ => return Err(Error::msg("This Prd type doesn't allow validation tokens"))
|
|
||||||
}
|
pub fn extract_from_message_with_commitment(plain: &[u8], commitment: &AnkPrdHash) -> Result<Self> {
|
||||||
Ok(())
|
Self::_extract_from_message(plain, Some(commitment))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_keys(&mut self, to_keep: HashSet<String>) {
|
pub fn filter_keys(&mut self, to_keep: HashSet<String>) {
|
||||||
@ -193,14 +176,22 @@ impl Prd {
|
|||||||
let mut to_commit = self.clone();
|
let mut to_commit = self.clone();
|
||||||
to_commit.keys = Map::new();
|
to_commit.keys = Map::new();
|
||||||
to_commit.proof = None;
|
to_commit.proof = None;
|
||||||
|
|
||||||
|
if to_commit.payload.len() != 64 && Vec::from_hex(&to_commit.payload).is_err() {
|
||||||
|
to_commit.payload = Value::from_str(&to_commit.payload).unwrap().tagged_hash().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
AnkPrdHash::from_value(&to_commit.to_value())
|
AnkPrdHash::from_value(&to_commit.to_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the signed proof and serialize to send over the network
|
/// Generate the signed proof and serialize to send over the network
|
||||||
pub fn to_network_msg(&self, sp_wallet: &SpWallet) -> Result<String> {
|
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 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 to_sign = self.clone(); // we sign the whole prd, incl the keys, for each recipient
|
||||||
let proof = Proof::new(to_sign.as_bytes(), spend_sk);
|
|
||||||
|
let message_hash = AnkHash::Message(AnkMessageHash::from_message(to_sign.to_string().as_bytes()));
|
||||||
|
|
||||||
|
let proof = Proof::new(message_hash, spend_sk);
|
||||||
|
|
||||||
let mut res = self.clone();
|
let mut res = self.clone();
|
||||||
res.proof = Some(proof);
|
res.proof = Some(proof);
|
||||||
|
@ -6,11 +6,23 @@ use sp_client::bitcoin::secp256k1::schnorr::Signature;
|
|||||||
use sp_client::bitcoin::secp256k1::{Keypair, Message, SecretKey, XOnlyPublicKey};
|
use sp_client::bitcoin::secp256k1::{Keypair, Message, SecretKey, XOnlyPublicKey};
|
||||||
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
||||||
|
|
||||||
|
use crate::pcd::AnkPcdHash;
|
||||||
|
|
||||||
sha256t_hash_newtype! {
|
sha256t_hash_newtype! {
|
||||||
pub struct AnkMessageTag = hash_str("4nk/Message");
|
pub struct AnkMessageTag = hash_str("4nk/Message");
|
||||||
|
|
||||||
#[hash_newtype(forward)]
|
#[hash_newtype(forward)]
|
||||||
pub struct AnkMessageHash(_);
|
pub struct AnkMessageHash(_);
|
||||||
|
|
||||||
|
pub struct AnkValidationYesTag = hash_str("4nk/yes");
|
||||||
|
|
||||||
|
#[hash_newtype(forward)]
|
||||||
|
pub struct AnkValidationYesHash(_);
|
||||||
|
|
||||||
|
pub struct AnkValidationNoTag = hash_str("4nk/no");
|
||||||
|
|
||||||
|
#[hash_newtype(forward)]
|
||||||
|
pub struct AnkValidationNoHash(_);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnkMessageHash {
|
impl AnkMessageHash {
|
||||||
@ -21,17 +33,48 @@ impl AnkMessageHash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
impl AnkValidationYesHash {
|
||||||
|
pub fn from_commitment(commitment: AnkPcdHash) -> Self {
|
||||||
|
let mut eng = AnkValidationYesHash::engine();
|
||||||
|
eng.input(&commitment.to_byte_array());
|
||||||
|
AnkValidationYesHash::from_engine(eng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnkValidationNoHash {
|
||||||
|
pub fn from_commitment(commitment: AnkPcdHash) -> Self {
|
||||||
|
let mut eng = AnkValidationNoHash::engine();
|
||||||
|
eng.input(&commitment.to_byte_array());
|
||||||
|
AnkValidationNoHash::from_engine(eng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum AnkHash {
|
||||||
|
Message(AnkMessageHash),
|
||||||
|
ValidationYes(AnkValidationYesHash),
|
||||||
|
ValidationNo(AnkValidationNoHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnkHash {
|
||||||
|
pub fn to_byte_array(&self) -> [u8; 32] {
|
||||||
|
match self {
|
||||||
|
AnkHash::Message(hash) => hash.to_byte_array(),
|
||||||
|
AnkHash::ValidationYes(hash) => hash.to_byte_array(),
|
||||||
|
AnkHash::ValidationNo(hash) => hash.to_byte_array(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Proof {
|
pub struct Proof {
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
message: AnkMessageHash,
|
message: AnkHash,
|
||||||
key: XOnlyPublicKey
|
key: XOnlyPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Proof {
|
impl Proof {
|
||||||
pub fn new(message: &[u8], signing_key: SecretKey) -> Self {
|
pub fn new(message_hash: AnkHash, signing_key: SecretKey) -> Self {
|
||||||
let message_hash = AnkMessageHash::from_message(message);
|
|
||||||
|
|
||||||
let secp = Secp256k1::signing_only();
|
let secp = Secp256k1::signing_only();
|
||||||
|
|
||||||
let keypair = Keypair::from_secret_key(&secp, &signing_key);
|
let keypair = Keypair::from_secret_key(&secp, &signing_key);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user