Update prd/pcd to keep commitments of each field
This commit is contained in:
parent
4885066bd2
commit
c2fde13e95
@ -1,3 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use aes_gcm::aead::{Aead, Payload};
|
||||
use aes_gcm::{Aes256Gcm, KeyInit};
|
||||
use anyhow::{Error, Result};
|
||||
@ -12,7 +14,7 @@ use tsify::Tsify;
|
||||
|
||||
use crate::crypto::AAD;
|
||||
use crate::error::AnkError;
|
||||
use crate::pcd::Member;
|
||||
use crate::pcd::{Member, RoleDefinition};
|
||||
use crate::signature::Proof;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Tsify)]
|
||||
@ -72,8 +74,8 @@ impl AnkFlag {
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct CommitMessage {
|
||||
pub init_tx: String, // Can be tx or txid of the first transaction of the chain, which is maybe not ideal
|
||||
pub encrypted_pcd: Map<String, Value>,
|
||||
pub keys: Map<String, Value>,
|
||||
pub pcd_commitment: Value, // map of field <=> hash of the clear value
|
||||
pub roles: HashMap<String, RoleDefinition>, // Can be hashed and compared with the value above
|
||||
pub validation_tokens: Vec<Proof>,
|
||||
pub error: Option<AnkError>,
|
||||
}
|
||||
@ -84,13 +86,13 @@ impl CommitMessage {
|
||||
/// validation_tokens must be empty
|
||||
pub fn new_first_commitment(
|
||||
transaction: Transaction,
|
||||
encrypted_pcd: Map<String, Value>,
|
||||
keys: Map<String, Value>,
|
||||
pcd_commitment: Value,
|
||||
roles: HashMap<String, RoleDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
init_tx: serialize(&transaction).to_lower_hex_string(),
|
||||
encrypted_pcd,
|
||||
keys,
|
||||
pcd_commitment,
|
||||
roles,
|
||||
validation_tokens: vec![],
|
||||
error: None,
|
||||
}
|
||||
@ -101,13 +103,13 @@ impl CommitMessage {
|
||||
/// validation_tokens must be empty
|
||||
pub fn new_update_commitment(
|
||||
init_tx: OutPoint,
|
||||
encrypted_pcd: Map<String, Value>,
|
||||
keys: Map<String, Value>,
|
||||
pcd_commitment: Value,
|
||||
roles: HashMap<String, RoleDefinition>,
|
||||
) -> Self {
|
||||
Self {
|
||||
init_tx: init_tx.to_string(),
|
||||
encrypted_pcd,
|
||||
keys,
|
||||
pcd_commitment,
|
||||
roles,
|
||||
validation_tokens: vec![],
|
||||
error: None,
|
||||
}
|
||||
|
112
src/pcd.rs
112
src/pcd.rs
@ -1,5 +1,6 @@
|
||||
use anyhow::{Error, Result};
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
use rs_merkle::{algorithms::Sha256, Hasher, MerkleTree};
|
||||
use std::{collections::{HashMap, HashSet}, str::FromStr};
|
||||
|
||||
use aes_gcm::{
|
||||
aead::{Aead, Payload},
|
||||
@ -11,9 +12,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use sp_client::{
|
||||
bitcoin::{
|
||||
hashes::{sha256t_hash_newtype, Hash, HashEngine},
|
||||
hex::{DisplayHex, FromHex},
|
||||
XOnlyPublicKey,
|
||||
consensus::serialize, hashes::{sha256t_hash_newtype, Hash, HashEngine}, hex::{DisplayHex, FromHex}, secp256k1::PublicKey, OutPoint, XOnlyPublicKey
|
||||
},
|
||||
silentpayments::utils::SilentPaymentAddress,
|
||||
};
|
||||
@ -85,17 +84,57 @@ impl AnkPcdHash {
|
||||
AnkPcdHash::from_engine(eng)
|
||||
}
|
||||
|
||||
pub fn from_map(map: &Map<String, Value>) -> Self {
|
||||
let value = Value::Object(map.clone());
|
||||
/// Adding the root_commitment guarantee that the same clear value across different processes wont' produce the same hash
|
||||
pub fn from_value_with_outpoint(value: &Value, outpoint: &[u8]) -> Self {
|
||||
let mut eng = AnkPcdHash::engine();
|
||||
eng.input(outpoint);
|
||||
eng.input(value.to_string().as_bytes());
|
||||
AnkPcdHash::from_engine(eng)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||
fn tagged_hash(&self) -> AnkPcdHash {
|
||||
AnkPcdHash::from_value(&self.to_value())
|
||||
fn from_string(str: &str) -> Result<Value> {
|
||||
let value: Value = serde_json::from_str(str)?;
|
||||
|
||||
match value {
|
||||
Value::Object(_) => Ok(value),
|
||||
_ => Err(Error::msg("Not a Pcd: not a valid JSON object"))
|
||||
}
|
||||
}
|
||||
|
||||
fn hash_fields(&self, root_commitment: OutPoint) -> Result<Map<String, Value>> {
|
||||
let map = self.to_value_object()?;
|
||||
|
||||
let outpoint = serialize(&root_commitment);
|
||||
|
||||
let mut field2hash = Map::with_capacity(map.len());
|
||||
// this could be optimised since there's a midstate we're reusing
|
||||
for (field, value) in map {
|
||||
let tagged_hash = AnkPcdHash::from_value_with_outpoint(&value, &outpoint);
|
||||
field2hash.insert(field, Value::String(tagged_hash.to_string()));
|
||||
}
|
||||
|
||||
Ok(field2hash)
|
||||
}
|
||||
|
||||
/// We need to run `hash_fields` before
|
||||
/// This will just take all the hash value and produces a merkle tree
|
||||
fn create_merkle_tree(fields2hash: &Value) -> Result<MerkleTree<Sha256>> {
|
||||
let leaves: Vec<[u8; 32]> = fields2hash
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(_, value)| {
|
||||
let mut res = [0u8; 32];
|
||||
res.copy_from_slice(&Vec::from_hex(value.as_str().unwrap()).unwrap());
|
||||
res
|
||||
})
|
||||
.collect();
|
||||
|
||||
let merkle_tree = MerkleTree::<Sha256>::from_leaves(&leaves);
|
||||
|
||||
Ok(merkle_tree)
|
||||
}
|
||||
|
||||
fn encrypt_fields(
|
||||
@ -104,14 +143,11 @@ pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||
fields2keys: &mut Map<String, Value>,
|
||||
fields2cipher: &mut Map<String, Value>,
|
||||
) -> Result<()> {
|
||||
let as_value = self.to_value();
|
||||
let as_map = as_value
|
||||
.as_object()
|
||||
.ok_or_else(|| Error::msg("Expected object"))?;
|
||||
let map = self.to_value_object()?;
|
||||
let mut rng = thread_rng();
|
||||
|
||||
for (field, value) in as_map {
|
||||
if fields_to_encrypt.contains(field) {
|
||||
for (field, value) in map {
|
||||
if fields_to_encrypt.contains(&field) {
|
||||
let aes_key = Aes256Gcm::generate_key(&mut rng);
|
||||
let nonce = Aes256Gcm::generate_nonce(&mut rng);
|
||||
fields2keys.insert(
|
||||
@ -125,9 +161,9 @@ pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||
msg: value_string.as_bytes(),
|
||||
aad: AAD,
|
||||
};
|
||||
let cipher = encrypt_eng
|
||||
.encrypt(&nonce, payload)
|
||||
.map_err(|e| Error::msg(format!("Encryption failed for field {}: {}", field, e)))?;
|
||||
let cipher = encrypt_eng.encrypt(&nonce, payload).map_err(|e| {
|
||||
Error::msg(format!("Encryption failed for field {}: {}", field, e))
|
||||
})?;
|
||||
|
||||
let mut res = Vec::with_capacity(nonce.len() + cipher.len());
|
||||
res.extend_from_slice(&nonce);
|
||||
@ -147,8 +183,7 @@ pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||
fields2keys: &Map<String, Value>,
|
||||
fields2plain: &mut Map<String, Value>,
|
||||
) -> Result<()> {
|
||||
let value = self.to_value();
|
||||
let map = value.as_object().unwrap();
|
||||
let map = self.to_value_object()?;
|
||||
|
||||
for (field, encrypted_value) in map.iter() {
|
||||
if let Some(aes_key) = fields2keys.get(field) {
|
||||
@ -190,8 +225,41 @@ pub trait Pcd<'a>: Serialize + Deserialize<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from_str(&serde_json::to_string(&self).unwrap()).unwrap()
|
||||
fn to_value_object(&self) -> Result<Map<String, Value>> {
|
||||
let value = serde_json::to_value(self)?;
|
||||
|
||||
match value {
|
||||
Value::Object(map) => Ok(map),
|
||||
_ => Err(Error::msg("self is not a valid json object"))
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_roles(&self) -> Result<HashMap<String, RoleDefinition>> {
|
||||
let obj = self.to_value_object()?;
|
||||
|
||||
let parse_roles_map = |m: &Map<String, Value>| {
|
||||
let mut res: HashMap<String, RoleDefinition> = HashMap::new();
|
||||
for (name, role_def) in m {
|
||||
res.insert(name.clone(), serde_json::from_value(role_def.clone())?);
|
||||
}
|
||||
<Result<HashMap<String, RoleDefinition>, Error>>::Ok(res)
|
||||
};
|
||||
|
||||
if let Some(roles) = obj.get("roles") {
|
||||
match roles {
|
||||
Value::Object(m) => {
|
||||
parse_roles_map(m)
|
||||
},
|
||||
Value::String(s) => {
|
||||
let m: Map<String, Value> = serde_json::from_str(&s)?;
|
||||
|
||||
parse_roles_map(&m)
|
||||
}
|
||||
_ => Err(Error::msg("\"roles\" is not an object"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::msg("No \"roles\" key in this pcd"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,7 +381,7 @@ impl ValidationRule {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Tsify)]
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Tsify)]
|
||||
#[tsify(into_wasm_abi, from_wasm_abi)]
|
||||
pub struct RoleDefinition {
|
||||
pub members: Vec<Member>,
|
||||
|
87
src/prd.rs
87
src/prd.rs
@ -7,8 +7,8 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use sp_client::bitcoin::hashes::{sha256t_hash_newtype, Hash, HashEngine};
|
||||
use sp_client::bitcoin::hex::FromHex;
|
||||
use sp_client::bitcoin::secp256k1::SecretKey;
|
||||
use sp_client::bitcoin::{OutPoint, Psbt, XOnlyPublicKey};
|
||||
use sp_client::bitcoin::secp256k1::{PublicKey, SecretKey};
|
||||
use sp_client::bitcoin::{OutPoint, Psbt};
|
||||
use sp_client::silentpayments::utils::SilentPaymentAddress;
|
||||
use sp_client::spclient::SpWallet;
|
||||
use tsify::Tsify;
|
||||
@ -26,9 +26,9 @@ pub enum PrdType {
|
||||
Message,
|
||||
Update, // Update an existing process
|
||||
List, // request a list of items
|
||||
Response,
|
||||
Confirm,
|
||||
TxProposal, // Send a psbt asking for recipient signature, used for login not sure about other use cases
|
||||
Response, // Validate (or disagree) with a prd update
|
||||
Confirm, // Confirm we received an update
|
||||
TxProposal, // Send a psbt asking for recipient signature
|
||||
}
|
||||
|
||||
sha256t_hash_newtype! {
|
||||
@ -61,8 +61,9 @@ pub struct Prd {
|
||||
pub root_commitment: String,
|
||||
pub sender: String,
|
||||
pub keys: Map<String, Value>, // key is a key in pcd, value is the key to decrypt it
|
||||
pub pcd_commitments: Value,
|
||||
pub validation_tokens: Vec<Proof>,
|
||||
pub payload: String, // Payload depends on the actual type
|
||||
pub payload: String, // Additional information depending on the type
|
||||
pub proof: Option<Proof>, // This must be None up to the creation of the network message
|
||||
}
|
||||
|
||||
@ -76,6 +77,7 @@ impl Prd {
|
||||
Self {
|
||||
prd_type: PrdType::Connect,
|
||||
root_commitment: String::default(),
|
||||
pcd_commitments: Value::Null,
|
||||
sender: serde_json::to_string(&sender).unwrap(),
|
||||
validation_tokens,
|
||||
keys: Map::new(),
|
||||
@ -87,8 +89,9 @@ impl Prd {
|
||||
pub fn new_update(
|
||||
root_commitment: OutPoint,
|
||||
sender: String, // Should take Member as argument
|
||||
encrypted_pcd: Map<String, Value>,
|
||||
encrypted_values_merkle_root: String,
|
||||
keys: Map<String, Value>,
|
||||
pcd_commitments: Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
prd_type: PrdType::Update,
|
||||
@ -96,19 +99,8 @@ impl Prd {
|
||||
sender,
|
||||
validation_tokens: vec![],
|
||||
keys,
|
||||
payload: Value::Object(encrypted_pcd).to_string(),
|
||||
proof: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tx_proposal(root_commitment: OutPoint, sender: Member, psbt: Psbt) -> Self {
|
||||
Self {
|
||||
prd_type: PrdType::TxProposal,
|
||||
root_commitment: root_commitment.to_string(),
|
||||
sender: serde_json::to_string(&sender).unwrap(),
|
||||
validation_tokens: vec![],
|
||||
keys: Map::new(),
|
||||
payload: serde_json::to_string(&psbt).unwrap(),
|
||||
pcd_commitments,
|
||||
payload: encrypted_values_merkle_root,
|
||||
proof: None,
|
||||
}
|
||||
}
|
||||
@ -117,7 +109,7 @@ impl Prd {
|
||||
root_commitment: OutPoint,
|
||||
sender: String,
|
||||
validation_tokens: Vec<Proof>,
|
||||
pcd_commitment: AnkPcdHash,
|
||||
pcd_commitments: Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
prd_type: PrdType::Response,
|
||||
@ -125,7 +117,8 @@ impl Prd {
|
||||
sender,
|
||||
validation_tokens: validation_tokens,
|
||||
keys: Map::new(),
|
||||
payload: pcd_commitment.to_string(),
|
||||
pcd_commitments,
|
||||
payload: String::default(),
|
||||
proof: None,
|
||||
}
|
||||
}
|
||||
@ -133,29 +126,22 @@ impl Prd {
|
||||
pub fn new_confirm(
|
||||
root_commitment: OutPoint,
|
||||
sender: Member,
|
||||
pcd_commitment: AnkPcdHash,
|
||||
pcd_commitments: Value,
|
||||
) -> Self {
|
||||
Self {
|
||||
prd_type: PrdType::Confirm,
|
||||
root_commitment: root_commitment.to_string(),
|
||||
pcd_commitments,
|
||||
sender: serde_json::to_string(&sender).unwrap(),
|
||||
validation_tokens: vec![],
|
||||
keys: Map::new(),
|
||||
payload: pcd_commitment.to_string(),
|
||||
payload: String::default(),
|
||||
proof: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn _extract_from_message(plain: &[u8], local_address: SilentPaymentAddress, commitment: Option<&AnkPrdHash>) -> Result<Self> {
|
||||
pub fn extract_from_message(plain: &[u8], local_address: SilentPaymentAddress) -> Result<Self> {
|
||||
let prd: Prd = serde_json::from_slice(plain)?;
|
||||
if let Some(commitment) = commitment {
|
||||
// check that the hash of the prd is consistent with what's commited in the op_return
|
||||
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
|
||||
if let Some(proof) = prd.proof {
|
||||
@ -190,23 +176,9 @@ impl Prd {
|
||||
} else {
|
||||
log::warn!("No proof for prd with root_commitment {}", prd.root_commitment);
|
||||
}
|
||||
// check that the commitment outpoint is valid, just in case
|
||||
OutPoint::from_str(&prd.root_commitment)?;
|
||||
Ok(prd)
|
||||
}
|
||||
|
||||
pub fn extract_from_message(plain: &[u8], local_address: SilentPaymentAddress) -> Result<Self> {
|
||||
Self::_extract_from_message(plain, local_address, None)
|
||||
}
|
||||
|
||||
pub fn extract_from_message_with_commitment(
|
||||
plain: &[u8],
|
||||
local_address: SilentPaymentAddress,
|
||||
commitment: &AnkPrdHash,
|
||||
) -> Result<Self> {
|
||||
Self::_extract_from_message(plain, local_address, Some(commitment))
|
||||
}
|
||||
|
||||
pub fn filter_keys(&mut self, to_keep: HashSet<String>) {
|
||||
let current_keys = self.keys.clone();
|
||||
let filtered_keys: Map<String, Value> = current_keys
|
||||
@ -216,25 +188,6 @@ impl Prd {
|
||||
self.keys = filtered_keys;
|
||||
}
|
||||
|
||||
/// We commit to everything except the keys and the proof
|
||||
/// Because 1) we need one commitment to common data for all recipients of the transaction
|
||||
/// 2) we already commit to the keys in the sender proof anyway
|
||||
pub fn create_commitment(&self) -> AnkPrdHash {
|
||||
let mut to_commit = self.clone();
|
||||
to_commit.keys = Map::new();
|
||||
to_commit.proof = None;
|
||||
to_commit.validation_tokens = vec![];
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
/// Generate the signed proof and serialize to send over the network
|
||||
pub fn to_network_msg(&self, sp_wallet: &SpWallet) -> Result<String> {
|
||||
let spend_sk: SecretKey = sp_wallet.get_client().get_spend_key().try_into()?;
|
||||
@ -255,6 +208,6 @@ impl Prd {
|
||||
}
|
||||
|
||||
pub fn to_value(&self) -> Value {
|
||||
Value::from_str(&self.to_string()).unwrap()
|
||||
serde_json::to_value(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
170
src/process.rs
170
src/process.rs
@ -5,12 +5,11 @@ use std::{
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use sp_client::{bitcoin::OutPoint, silentpayments::utils::SilentPaymentAddress};
|
||||
use sp_client::bitcoin::OutPoint;
|
||||
|
||||
use crate::{
|
||||
crypto::AnkSharedSecretHash,
|
||||
pcd::{AnkPcdHash, Pcd, RoleDefinition},
|
||||
prd::Prd,
|
||||
prd::{Prd, PrdType},
|
||||
signature::Proof,
|
||||
MutexExt,
|
||||
};
|
||||
@ -18,12 +17,20 @@ use crate::{
|
||||
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ProcessState {
|
||||
pub commited_in: OutPoint,
|
||||
pub pcd_commitment: Value, // If we can't modify a field, we just copy the previous value
|
||||
pub encrypted_pcd: Value, // Some fields may be clear, if the owner of the process decides so
|
||||
pub keys: Map<String, Value>, // We may not always have all the keys
|
||||
pub validation_tokens: Vec<Proof>, // Signature of the hash of the encrypted pcd tagged with some decision like "yes" or "no"
|
||||
}
|
||||
|
||||
impl ProcessState {
|
||||
pub fn decrypt_pcd(&self) -> Value {
|
||||
// TODO add real error management
|
||||
let mut fields2plain = Map::new();
|
||||
let _ = self.encrypted_pcd.decrypt_fields(&self.keys, &mut fields2plain);
|
||||
Value::Object(fields2plain)
|
||||
}
|
||||
|
||||
fn compute_modified_fields(&self, previous_state: Option<&ProcessState>) -> Vec<String> {
|
||||
let new_state = &self.encrypted_pcd;
|
||||
|
||||
@ -134,10 +141,16 @@ impl ProcessState {
|
||||
Err(anyhow::anyhow!("Not enough valid proofs"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.encrypted_pcd == Value::Null ||
|
||||
self.pcd_commitment == Value::Null
|
||||
}
|
||||
}
|
||||
|
||||
/// A process is basically a succession of states
|
||||
/// If a process has nothing to do with us, impending_requests will be empty
|
||||
/// The latest state MUST be an empty state with only the commited_in field set at the last unspent outpoint
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Process {
|
||||
states: Vec<ProcessState>,
|
||||
@ -155,8 +168,38 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_state(&mut self, state: ProcessState) {
|
||||
self.states.push(state);
|
||||
pub fn get_last_unspent_outpoint(&self) -> anyhow::Result<OutPoint> {
|
||||
if self.states.is_empty() { return Err(anyhow::Error::msg("Empty Process")); }
|
||||
let last_state = self.states.last().unwrap();
|
||||
Ok(last_state.commited_in)
|
||||
}
|
||||
|
||||
/// We want to always keep an empty state with only the latest unspent commited_in value at the last position
|
||||
pub fn insert_state(&mut self, prd_update: &Prd) -> anyhow::Result<()> {
|
||||
if prd_update.prd_type != PrdType::Update { return Err(anyhow::Error::msg("Only update prd allowed")) }
|
||||
if self.states.is_empty() { return Err(anyhow::Error::msg("Empty Process")); }
|
||||
let new_encrypted_pcd: Value = serde_json::from_str(&prd_update.payload).unwrap();
|
||||
let last_index = self.states.len() - 1;
|
||||
let last_value = self.states.get(last_index).unwrap();
|
||||
if last_value.encrypted_pcd != Value::Null ||
|
||||
last_value.keys != Map::new() ||
|
||||
last_value.pcd_commitment != Value::Null ||
|
||||
last_value.validation_tokens != vec![]
|
||||
{
|
||||
return Err(anyhow::Error::msg("Last state is not empty"));
|
||||
}
|
||||
let empty_state = self.states.remove(last_index);
|
||||
let new_state = ProcessState {
|
||||
commited_in: empty_state.commited_in,
|
||||
pcd_commitment: prd_update.pcd_commitments.clone(),
|
||||
encrypted_pcd: new_encrypted_pcd,
|
||||
keys: prd_update.keys.clone(),
|
||||
validation_tokens: vec![]
|
||||
};
|
||||
self.states.push(new_state);
|
||||
// We always keep an empty state at the end
|
||||
self.states.push(empty_state);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_state_at(&self, index: usize) -> Option<&ProcessState> {
|
||||
@ -195,63 +238,79 @@ impl Process {
|
||||
/// This is useful when multiple unvalidated states are pending waiting for enough validations
|
||||
/// It returns the latest state and all the previous states that have the same commited_in
|
||||
/// It means that all the returned states are unvalidated and except for the one that get validated they will be pruned
|
||||
pub fn get_latest_concurrent_states(&self) -> Vec<&ProcessState> {
|
||||
let mut states = vec![];
|
||||
let latest_state = self.get_latest_state();
|
||||
|
||||
if latest_state.is_none() {
|
||||
return states;
|
||||
pub fn get_latest_concurrent_states(&self) -> anyhow::Result<Vec<&ProcessState>> {
|
||||
if self.get_number_of_states() == 0 {
|
||||
// This should never happen, but we better get rid of it now
|
||||
return Err(anyhow::Error::msg("process is empty".to_owned()));
|
||||
}
|
||||
|
||||
let latest_state_outpoint = latest_state.unwrap().commited_in;
|
||||
|
||||
let mut states = vec![];
|
||||
let mut previous_commited_in = OutPoint::null();
|
||||
// We iterate backwards until we find a state that has a different commited_in
|
||||
for state in self.states.iter().rev() {
|
||||
if state.commited_in != latest_state_outpoint {
|
||||
log::debug!("state: {:#?}", state);
|
||||
if previous_commited_in == OutPoint::null() {
|
||||
previous_commited_in = state.commited_in;
|
||||
} else if previous_commited_in != state.commited_in {
|
||||
break;
|
||||
}
|
||||
states.push(state);
|
||||
}
|
||||
|
||||
states
|
||||
Ok(states)
|
||||
}
|
||||
|
||||
pub fn get_latest_concurrent_states_mut(&mut self) -> Vec<&mut ProcessState> {
|
||||
let mut states = vec![];
|
||||
let latest_state = self.get_latest_state();
|
||||
|
||||
if latest_state.is_none() {
|
||||
return states;
|
||||
pub fn get_latest_concurrent_states_mut(&mut self) -> anyhow::Result<Vec<&mut ProcessState>> {
|
||||
if self.get_number_of_states() == 0 {
|
||||
// This should never happen, but we better get rid of it now
|
||||
return Err(anyhow::Error::msg("process is empty".to_owned()));
|
||||
}
|
||||
|
||||
let latest_state_outpoint = latest_state.unwrap().commited_in;
|
||||
|
||||
let mut states = vec![];
|
||||
let mut previous_commited_in = OutPoint::null();
|
||||
// We iterate backwards until we find a state that has a different commited_in
|
||||
for state in self.states.iter_mut().rev() {
|
||||
if state.commited_in != latest_state_outpoint {
|
||||
if previous_commited_in == OutPoint::null() {
|
||||
previous_commited_in = state.commited_in;
|
||||
} else if previous_commited_in != state.commited_in {
|
||||
break;
|
||||
}
|
||||
states.push(state);
|
||||
}
|
||||
|
||||
states
|
||||
Ok(states)
|
||||
}
|
||||
|
||||
pub fn remove_latest_concurrent_states(&mut self) -> Vec<ProcessState> {
|
||||
if self.states.is_empty() {
|
||||
return vec![];
|
||||
pub fn remove_latest_concurrent_states(&mut self) -> anyhow::Result<Vec<ProcessState>> {
|
||||
if self.get_number_of_states() == 0 {
|
||||
// This should never happen, but we better get rid of it now
|
||||
return Err(anyhow::Error::msg("process is empty".to_owned()));
|
||||
}
|
||||
|
||||
let latest_state = self.get_latest_state().unwrap();
|
||||
let latest_outpoint = latest_state.commited_in;
|
||||
let mut previous_commited_in = OutPoint::null();
|
||||
// Iterate backwards, find the reverse position, and adjust to forward position
|
||||
let reverse_position = self.states.iter().rev().position(|state| {
|
||||
if previous_commited_in == OutPoint::null() {
|
||||
previous_commited_in = state.commited_in;
|
||||
false // Continue iterating
|
||||
} else if previous_commited_in != state.commited_in {
|
||||
true // Stop when a different commited_in is found
|
||||
} else {
|
||||
false // Continue iterating
|
||||
}
|
||||
});
|
||||
|
||||
let pos = self
|
||||
.states
|
||||
.iter()
|
||||
.position(|s| s.commited_in == latest_outpoint)
|
||||
.unwrap();
|
||||
let forward_position = reverse_position.map(|pos| self.states.len() - pos - 1);
|
||||
|
||||
self.states.split_off(pos)
|
||||
match forward_position {
|
||||
Some(pos) => Ok(self.states.split_off(pos)),
|
||||
None => {
|
||||
// We take everything out
|
||||
let res = self.states.to_vec();
|
||||
self.states = vec![];
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_latest_commited_state(&self) -> Option<&ProcessState> {
|
||||
@ -259,30 +318,18 @@ impl Process {
|
||||
return None;
|
||||
}
|
||||
|
||||
let latest_state = self.get_latest_state().unwrap();
|
||||
let last_state = self.states.last().unwrap();
|
||||
|
||||
// a commited outpoint with an index of u32::MAX is a pending state
|
||||
if latest_state.commited_in.vout != u32::MAX {
|
||||
// This state is commited, there's no pending state
|
||||
return Some(latest_state);
|
||||
}
|
||||
debug_assert!(last_state.is_empty()); // Last state must always be empty
|
||||
|
||||
// We look for the last state before all the pending states
|
||||
let latest_outpoint = latest_state.commited_in;
|
||||
// We look for the last commited in before all the pending states
|
||||
let latest_outpoint = last_state.commited_in;
|
||||
|
||||
let pos = self
|
||||
return self
|
||||
.states
|
||||
.iter()
|
||||
.position(|s| s.commited_in == latest_outpoint)
|
||||
.unwrap();
|
||||
|
||||
if pos == 0 {
|
||||
// There's no commited states, we just return None
|
||||
return None;
|
||||
} else {
|
||||
// The state just before is last commited state
|
||||
return self.get_state_at(pos-1);
|
||||
}
|
||||
.rev()
|
||||
.find(|s| s.commited_in != latest_outpoint);
|
||||
}
|
||||
|
||||
pub fn insert_impending_request(&mut self, request: Prd) {
|
||||
@ -297,6 +344,12 @@ impl Process {
|
||||
self.impending_requests.iter_mut().collect()
|
||||
}
|
||||
|
||||
pub fn prune_impending_requests(&mut self) {
|
||||
self.impending_requests = self.impending_requests.clone().into_iter()
|
||||
.filter(|r| r.prd_type != PrdType::None)
|
||||
.collect();
|
||||
}
|
||||
|
||||
pub fn get_state_index(&self, state: &ProcessState) -> Option<usize> {
|
||||
// Get the commited_in value of the provided state
|
||||
let target_commited_in = state.commited_in;
|
||||
@ -444,6 +497,10 @@ mod tests {
|
||||
}
|
||||
});
|
||||
|
||||
let outpoint = OutPoint::null();
|
||||
|
||||
let pcd_commitment = encrypted_pcd.hash_fields(outpoint).unwrap();
|
||||
|
||||
let mut fields2keys = Map::new();
|
||||
let mut fields2cipher = Map::new();
|
||||
// let field_to_encrypt: Vec<String> = encrypted_pcd.as_object().unwrap().keys().map(|k| k.clone()).collect();
|
||||
@ -454,7 +511,8 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
ProcessState {
|
||||
commited_in: OutPoint::null(),
|
||||
commited_in: outpoint,
|
||||
pcd_commitment: Value::Object(pcd_commitment),
|
||||
encrypted_pcd,
|
||||
keys: Map::new(),
|
||||
validation_tokens: vec![],
|
||||
|
Loading…
x
Reference in New Issue
Block a user