use crate::{BlockBuilder, SnapshotVaultIndex};
use ex3_blockchain_public_types::{ActivatedSecretVault, SnapshotCursor};
use ex3_canister_types::range::ReportRange;
use ex3_crypto::sha256;
use ex3_node_types::number::Ex3Uint;
use ex3_node_types::range::CandidRange;
use ex3_node_types::transaction::EncodedTransaction;
use ex3_node_types::{BlockHeight, SecretVaultSeqId, WalletRegistrySeqId};
use ex3_node_types::{MerkleNode, WalletRegisterId};
use ex3_secret_vault_public_types::SecretUpdateTxShardingSubReport;
use ex3_serde::bincode::serialize;
use num_traits::{CheckedSub, One, ToPrimitive, Zero};
use rs_merkle::algorithms::Sha256;
use rs_merkle::MerkleTree;
use std::cmp::max;
use std::collections::{BTreeMap, VecDeque};
impl BlockBuilder {
pub(crate) fn generate_secret_update_tx_sharding_sub_reports(
&self,
tx_map: BTreeMap<SecretVaultSeqId, Vec<(EncodedTransaction, usize)>>,
txs_merkle_tree: &MerkleTree<Sha256>,
snapshot_vault_cursor_of_pre_height: &SnapshotCursor,
) -> BTreeMap<WalletRegistrySeqId, Vec<SecretUpdateTxShardingSubReport>> {
let mut secret_update_tx_sharding_sub_reports = BTreeMap::new();
let max_secret_vault_seq_id = max(
snapshot_vault_cursor_of_pre_height.max_activated_secret_vault_seq_id,
tx_map.keys().max().unwrap_or(&0).clone(),
);
let mut next_secret_vault_seq_id: SecretVaultSeqId = 0;
let mut tx_map_mut = tx_map;
while next_secret_vault_seq_id <= max_secret_vault_seq_id {
let mut secret_update_txs_sub_reports = vec![];
if !tx_map_mut.contains_key(&next_secret_vault_seq_id) {
next_secret_vault_seq_id += 1;
continue;
}
let mut secret_update_txs = tx_map_mut
.remove(&next_secret_vault_seq_id)
.unwrap()
.into_iter()
.collect::<VecDeque<_>>();
let total_tx_count_in_sharding = secret_update_txs.len();
loop {
let mut txs = vec![];
for _ in 0..self.max_secret_update_txs_per_sharding_sub_report {
if let Some(tx) = secret_update_txs.pop_front() {
txs.push(tx);
} else {
break;
}
}
if txs.len() == 0 {
break;
}
let (txs, indexes) =
txs.into_iter()
.map(|(tx, index)| (tx, index.clone()))
.unzip::<EncodedTransaction, usize, Vec<EncodedTransaction>, Vec<usize>>();
let merkle_proof = txs_merkle_tree.proof(&indexes);
let report_start = secret_update_txs_sub_reports.len()
* self.max_secret_update_txs_per_sharding_sub_report.clone();
let report_end = report_start + indexes.len();
let range = ReportRange {
total_count: txs_merkle_tree.leaves_len().try_into().unwrap(),
sharding_range: CandidRange {
start: 0u32,
end: total_tx_count_in_sharding.try_into().unwrap(),
},
report_range: CandidRange {
start: report_start.try_into().unwrap(),
end: report_end.try_into().unwrap(),
},
};
let report = SecretUpdateTxShardingSubReport {
merkle_proof: merkle_proof.to_bytes(),
range,
txs,
secret_tx_indexes: indexes.into_iter().map(|i| i.try_into().unwrap()).collect(),
};
secret_update_txs_sub_reports.push(report);
}
secret_update_tx_sharding_sub_reports
.insert(next_secret_vault_seq_id, secret_update_txs_sub_reports);
next_secret_vault_seq_id += 1;
}
secret_update_tx_sharding_sub_reports
}
pub(crate) fn generate_secret_vault_data_integrity(
&self,
current_block_height: &BlockHeight,
secret_update_tx_sharding_sub_reports: &BTreeMap<
WalletRegistrySeqId,
Vec<SecretUpdateTxShardingSubReport>,
>,
snapshot_vault_cursor: &SnapshotCursor,
snapshot_vault_index: &SnapshotVaultIndex,
) -> (
BTreeMap<WalletRegistrySeqId, MerkleNode>,
Vec<ActivatedSecretVault>,
) {
let mut secret_vault_data_integrity = BTreeMap::new();
let mut new_activated_secret_vaults = vec![];
secret_update_tx_sharding_sub_reports
.iter()
.for_each(|(secret_vault_seq_id, reports)| {
let mut pre_block_height_processed: Option<BlockHeight> = None;
if secret_vault_seq_id <= &snapshot_vault_cursor.max_activated_secret_vault_seq_id
&& *current_block_height > BlockHeight::zero()
{
pre_block_height_processed = Some(
current_block_height
.checked_sub(&BlockHeight::one())
.unwrap(),
);
}
let total_vaults_count = snapshot_vault_index.get_total_vaults_count();
let total_merkle_node_count = snapshot_vault_index.get_total_merkle_node_count();
let secret_vault_index =
snapshot_vault_index.get_secret_vault_index(secret_vault_seq_id);
let mut data_buffer = vec![];
data_buffer.extend(
serialize(&pre_block_height_processed).expect("serialize should not fail"),
);
data_buffer.extend(current_block_height.0.to_bytes_le());
data_buffer.extend(secret_vault_seq_id.to_le_bytes());
let secret_update_tx_count: u32 = reports
.iter()
.fold(0, |acc, report| acc + report.txs.len())
.try_into()
.unwrap();
data_buffer.extend(secret_update_tx_count.to_le_bytes());
data_buffer.extend(total_merkle_node_count.to_le_bytes());
data_buffer.extend(secret_vault_index.to_le_bytes());
reports.iter().for_each(|report| {
data_buffer.extend(&report.merkle_proof);
});
let data_integrity: MerkleNode = sha256(data_buffer);
secret_vault_data_integrity.insert(secret_vault_seq_id.clone(), data_integrity);
if pre_block_height_processed.is_none() {
new_activated_secret_vaults.push(ActivatedSecretVault {
canister_seq_id: secret_vault_seq_id.clone(),
canister_total_count: total_vaults_count,
canister_index: secret_vault_index,
hash_of_sharding: data_integrity,
});
}
});
(secret_vault_data_integrity, new_activated_secret_vaults)
}
pub fn get_secret_vault_seq_id_by_wallet_id(
&self,
wallet_id: &WalletRegisterId,
) -> SecretVaultSeqId {
let wallet_id = wallet_id
.checked_sub(&Ex3Uint::from(self.start_wallet_id.clone()))
.unwrap();
(wallet_id.0 / self.max_wallets_per_secret_vault_canister.clone())
.to_u64()
.unwrap()
}
}