mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-09 14:48:08 -05:00
drk: store each block signing key in scanned blocks and display it so miners can grab it if ever needed
This commit is contained in:
@@ -23,7 +23,7 @@ use darkfi_sdk::{
|
||||
crypto::{
|
||||
pasta_prelude::PrimeField,
|
||||
smt::{PoseidonFp, SparseMerkleTree, StorageAdapter, SMT_FP_DEPTH},
|
||||
MerkleTree,
|
||||
MerkleTree, SecretKey,
|
||||
},
|
||||
error::{ContractError, ContractResult},
|
||||
pasta::pallas,
|
||||
@@ -139,14 +139,24 @@ impl CacheOverlay {
|
||||
Ok(Self(overlay))
|
||||
}
|
||||
|
||||
/// Insert a `u32` and a block hash into overlay's scanned blocks
|
||||
/// tree. The block height is used as the key, and the serialized
|
||||
/// blockhash string is used as value.
|
||||
pub fn insert_scanned_block(&mut self, height: &u32, hash: &HeaderHash) -> Result<()> {
|
||||
/// Insert a `u32`, a block hash and an optional signing key into
|
||||
/// overlay's scanned blocks tree. The block height is used as the
|
||||
/// key, while the serialized blockhash and key strings are used as
|
||||
/// the value.
|
||||
pub fn insert_scanned_block(
|
||||
&mut self,
|
||||
height: &u32,
|
||||
hash: &HeaderHash,
|
||||
signing_key: &Option<SecretKey>,
|
||||
) -> Result<()> {
|
||||
let block_signing_key = match signing_key {
|
||||
Some(key) => key.to_string(),
|
||||
None => String::from("-"),
|
||||
};
|
||||
self.0.insert(
|
||||
SLED_SCANNED_BLOCKS_TREE,
|
||||
&height.to_be_bytes(),
|
||||
&serialize(&hash.to_string()),
|
||||
&serialize(&(hash.to_string(), block_signing_key)),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -207,3 +207,14 @@ pub fn prettytable_aliases(alimap: &HashMap<String, TokenId>) -> Table {
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
pub fn prettytable_scanned_blocks(scanned_blocks: &[(u32, String, String)]) -> Table {
|
||||
let mut table = Table::new();
|
||||
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.set_titles(row!["Height", "Hash", "Signing Key"]);
|
||||
for (height, hash, signing_key) in scanned_blocks {
|
||||
table.add_row(row![height, hash, signing_key]);
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
@@ -2687,10 +2687,11 @@ async fn handle_explorer_scanned_blocks(drk: &DrkPtr, parts: &[&str], output: &m
|
||||
}
|
||||
};
|
||||
|
||||
match lock.get_scanned_block_hash(&height) {
|
||||
Ok(hash) => {
|
||||
match lock.get_scanned_block(&height) {
|
||||
Ok((hash, signing_key)) => {
|
||||
output.push(format!("Height: {height}"));
|
||||
output.push(format!("Hash: {hash}"));
|
||||
output.push(format!("Signing key: {signing_key}"));
|
||||
}
|
||||
Err(e) => output.push(format!("Failed to retrieve scanned block record: {e}")),
|
||||
};
|
||||
@@ -2705,13 +2706,7 @@ async fn handle_explorer_scanned_blocks(drk: &DrkPtr, parts: &[&str], output: &m
|
||||
}
|
||||
};
|
||||
|
||||
// Create a prettytable with the new data:
|
||||
let mut table = Table::new();
|
||||
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.set_titles(row!["Height", "Hash"]);
|
||||
for (height, hash) in map.iter() {
|
||||
table.add_row(row![height, hash]);
|
||||
}
|
||||
let table = prettytable_scanned_blocks(&map);
|
||||
|
||||
if table.is_empty() {
|
||||
output.push(String::from("No scanned blocks records found"));
|
||||
|
||||
@@ -2258,8 +2258,8 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
|
||||
.await;
|
||||
|
||||
if let Some(height) = height {
|
||||
let hash = match drk.get_scanned_block_hash(&height) {
|
||||
Ok(h) => h,
|
||||
let (hash, signing_key) = match drk.get_scanned_block(&height) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to retrieve scanned block record: {e}");
|
||||
exit(2);
|
||||
@@ -2268,6 +2268,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
|
||||
|
||||
println!("Height: {height}");
|
||||
println!("Hash: {hash}");
|
||||
println!("Signing key: {signing_key}");
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
@@ -2280,13 +2281,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
|
||||
}
|
||||
};
|
||||
|
||||
// Create a prettytable with the new data:
|
||||
let mut table = Table::new();
|
||||
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
|
||||
table.set_titles(row!["Height", "Hash"]);
|
||||
for (height, hash) in map.iter() {
|
||||
table.add_row(row![height, hash]);
|
||||
}
|
||||
let table = prettytable_scanned_blocks(&map);
|
||||
|
||||
if table.is_empty() {
|
||||
println!("No scanned blocks records found");
|
||||
|
||||
@@ -58,7 +58,7 @@ use darkfi_sdk::{
|
||||
pasta::pallas,
|
||||
ContractCall,
|
||||
};
|
||||
use darkfi_serial::{deserialize_async, serialize, serialize_async, AsyncEncodable};
|
||||
use darkfi_serial::{deserialize, deserialize_async, serialize, serialize_async, AsyncEncodable};
|
||||
|
||||
use crate::{
|
||||
cache::CacheSmt,
|
||||
@@ -742,15 +742,16 @@ impl Drk {
|
||||
}
|
||||
|
||||
/// Auxiliary function to grab all the nullifiers, coins with their
|
||||
/// notes and freezes from a transaction money call.
|
||||
/// notes and a flag indicating if its a block reward, and freezes
|
||||
/// from a transaction money call.
|
||||
async fn parse_money_call(
|
||||
&self,
|
||||
scan_cache: &mut ScanCache,
|
||||
call_idx: &usize,
|
||||
calls: &[DarkLeaf<ContractCall>],
|
||||
) -> Result<(Vec<Nullifier>, Vec<(Coin, AeadEncryptedNote)>, Vec<TokenId>)> {
|
||||
) -> Result<(Vec<Nullifier>, Vec<(Coin, AeadEncryptedNote, bool)>, Vec<TokenId>)> {
|
||||
let mut nullifiers: Vec<Nullifier> = vec![];
|
||||
let mut coins: Vec<(Coin, AeadEncryptedNote)> = vec![];
|
||||
let mut coins: Vec<(Coin, AeadEncryptedNote, bool)> = vec![];
|
||||
let mut freezes: Vec<TokenId> = vec![];
|
||||
|
||||
let call = &calls[*call_idx];
|
||||
@@ -760,19 +761,19 @@ impl Drk {
|
||||
scan_cache.log(String::from("[parse_money_call] Found Money::FeeV1 call"));
|
||||
let params: MoneyFeeParamsV1 = deserialize_async(&data[9..]).await?;
|
||||
nullifiers.push(params.input.nullifier);
|
||||
coins.push((params.output.coin, params.output.note));
|
||||
coins.push((params.output.coin, params.output.note, false));
|
||||
}
|
||||
MoneyFunction::GenesisMintV1 => {
|
||||
scan_cache.log(String::from("[parse_money_call] Found Money::GenesisMintV1 call"));
|
||||
let params: MoneyGenesisMintParamsV1 = deserialize_async(&data[1..]).await?;
|
||||
for output in params.outputs {
|
||||
coins.push((output.coin, output.note));
|
||||
coins.push((output.coin, output.note, false));
|
||||
}
|
||||
}
|
||||
MoneyFunction::PoWRewardV1 => {
|
||||
scan_cache.log(String::from("[parse_money_call] Found Money::PoWRewardV1 call"));
|
||||
let params: MoneyPoWRewardParamsV1 = deserialize_async(&data[1..]).await?;
|
||||
coins.push((params.output.coin, params.output.note));
|
||||
coins.push((params.output.coin, params.output.note, true));
|
||||
}
|
||||
MoneyFunction::TransferV1 => {
|
||||
scan_cache.log(String::from("[parse_money_call] Found Money::TransferV1 call"));
|
||||
@@ -783,7 +784,7 @@ impl Drk {
|
||||
}
|
||||
|
||||
for output in params.outputs {
|
||||
coins.push((output.coin, output.note));
|
||||
coins.push((output.coin, output.note, false));
|
||||
}
|
||||
}
|
||||
MoneyFunction::OtcSwapV1 => {
|
||||
@@ -795,7 +796,7 @@ impl Drk {
|
||||
}
|
||||
|
||||
for output in params.outputs {
|
||||
coins.push((output.coin, output.note));
|
||||
coins.push((output.coin, output.note, false));
|
||||
}
|
||||
}
|
||||
MoneyFunction::AuthTokenMintV1 => {
|
||||
@@ -817,33 +818,38 @@ impl Drk {
|
||||
let child_call = &calls[child_idx];
|
||||
let child_params: MoneyAuthTokenMintParamsV1 =
|
||||
deserialize_async(&child_call.data.data[1..]).await?;
|
||||
coins.push((params.coin, child_params.enc_note));
|
||||
coins.push((params.coin, child_params.enc_note, false));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((nullifiers, coins, freezes))
|
||||
}
|
||||
|
||||
/// Auxiliary function to handle coins with their notes from a
|
||||
/// transaction money call.
|
||||
/// Returns our found own coins.
|
||||
/// Auxiliary function to handle coins with their notes and flag
|
||||
/// indicating if its a block reward from a transaction money call.
|
||||
/// Returns our found own coins along with the block signing key,
|
||||
/// if found.
|
||||
fn handle_money_call_coins(
|
||||
&self,
|
||||
tree: &mut MerkleTree,
|
||||
secrets: &[SecretKey],
|
||||
messages_buffer: &mut Vec<String>,
|
||||
coins: &[(Coin, AeadEncryptedNote)],
|
||||
) -> Vec<OwnCoin> {
|
||||
coins: &[(Coin, AeadEncryptedNote, bool)],
|
||||
) -> Result<(Vec<OwnCoin>, Option<SecretKey>)> {
|
||||
// Keep track of our own coins found in the vec
|
||||
let mut owncoins = vec![];
|
||||
|
||||
// Check if provided coins vec is empty
|
||||
if coins.is_empty() {
|
||||
return owncoins
|
||||
return Ok((owncoins, None))
|
||||
}
|
||||
|
||||
// Handle provided coins vector and grab our own
|
||||
for (coin, note) in coins {
|
||||
// Handle provided coins vector and grab our own,
|
||||
// along with the block signing key if its a block
|
||||
// reward coin. Only one reward call and coin exists
|
||||
// in each block.
|
||||
let mut block_signing_key = None;
|
||||
for (coin, note, is_block_reward) in coins {
|
||||
// Append the new coin to the Merkle tree.
|
||||
// Every coin has to be added.
|
||||
tree.append(MerkleNode::from(coin.inner()));
|
||||
@@ -857,12 +863,18 @@ impl Drk {
|
||||
messages_buffer
|
||||
.push(String::from("[handle_money_call_coins] Witnessing coin in Merkle tree"));
|
||||
let leaf_position = tree.mark().unwrap();
|
||||
if *is_block_reward {
|
||||
messages_buffer
|
||||
.push(String::from("[handle_money_call_coins] Grabing block signing key"));
|
||||
block_signing_key = Some(deserialize(¬e.memo)?);
|
||||
}
|
||||
let owncoin = OwnCoin { coin: *coin, note, secret: *secret, leaf_position };
|
||||
owncoins.push(owncoin);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
owncoins
|
||||
Ok((owncoins, block_signing_key))
|
||||
}
|
||||
|
||||
/// Auxiliary function to handle own coins from a transaction money
|
||||
@@ -997,7 +1009,7 @@ impl Drk {
|
||||
/// Append data related to Money contract transactions into the
|
||||
/// wallet database and update the provided scan cache.
|
||||
/// Returns a flag indicating if provided data refer to our own
|
||||
/// wallet.
|
||||
/// wallet along with the block signing key, if found.
|
||||
pub async fn apply_tx_money_data(
|
||||
&self,
|
||||
scan_cache: &mut ScanCache,
|
||||
@@ -1005,18 +1017,18 @@ impl Drk {
|
||||
calls: &[DarkLeaf<ContractCall>],
|
||||
tx_hash: &String,
|
||||
block_height: &u32,
|
||||
) -> Result<bool> {
|
||||
) -> Result<(bool, Option<SecretKey>)> {
|
||||
// Parse the call
|
||||
let (nullifiers, coins, freezes) =
|
||||
self.parse_money_call(scan_cache, call_idx, calls).await?;
|
||||
|
||||
// Parse call coins and grab our own
|
||||
let owncoins = self.handle_money_call_coins(
|
||||
let (owncoins, block_signing_key) = self.handle_money_call_coins(
|
||||
&mut scan_cache.money_tree,
|
||||
&scan_cache.notes_secrets,
|
||||
&mut scan_cache.messages_buffer,
|
||||
&coins,
|
||||
);
|
||||
)?;
|
||||
|
||||
// Update nullifiers smt
|
||||
self.smt_insert(&mut scan_cache.money_smt, &nullifiers)?;
|
||||
@@ -1041,7 +1053,7 @@ impl Drk {
|
||||
kaching().await;
|
||||
}
|
||||
|
||||
Ok(wallet_spent_coins || !owncoins.is_empty() || wallet_freezes)
|
||||
Ok((wallet_spent_coins || !owncoins.is_empty() || wallet_freezes, block_signing_key))
|
||||
}
|
||||
|
||||
/// Auxiliary function to grab all the nullifiers from a transaction money call.
|
||||
|
||||
@@ -187,6 +187,7 @@ impl Drk {
|
||||
scan_cache.log(format!("{}", block.header));
|
||||
scan_cache.log(String::from("======================================="));
|
||||
scan_cache.log(format!("[scan_block] Iterating over {} transactions", block.txs.len()));
|
||||
let mut block_signing_key = None;
|
||||
for tx in block.txs.iter() {
|
||||
let tx_hash = tx.hash();
|
||||
let tx_hash_string = tx_hash.to_string();
|
||||
@@ -195,7 +196,7 @@ impl Drk {
|
||||
for (i, call) in tx.calls.iter().enumerate() {
|
||||
if call.data.contract_id == *MONEY_CONTRACT_ID {
|
||||
scan_cache.log(format!("[scan_block] Found Money contract in call {i}"));
|
||||
if self
|
||||
let (is_wallet_tx, signing_key) = self
|
||||
.apply_tx_money_data(
|
||||
scan_cache,
|
||||
&i,
|
||||
@@ -203,9 +204,13 @@ impl Drk {
|
||||
&tx_hash_string,
|
||||
&block.header.height,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
.await?;
|
||||
if is_wallet_tx {
|
||||
wallet_tx = true;
|
||||
// Only one block signing key exists per block
|
||||
if signing_key.is_some() {
|
||||
block_signing_key = signing_key;
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -255,11 +260,11 @@ impl Drk {
|
||||
}
|
||||
|
||||
// Insert the block record
|
||||
scan_cache
|
||||
.money_smt
|
||||
.store
|
||||
.overlay
|
||||
.insert_scanned_block(&block.header.height, &block.header.hash())?;
|
||||
scan_cache.money_smt.store.overlay.insert_scanned_block(
|
||||
&block.header.height,
|
||||
&block.header.hash(),
|
||||
&block_signing_key,
|
||||
)?;
|
||||
|
||||
// Grab the overlay current diff
|
||||
let diff = scan_cache.money_smt.store.overlay.0.diff(&[])?;
|
||||
@@ -329,7 +334,7 @@ impl Drk {
|
||||
height = height.saturating_sub(1);
|
||||
while height != 0 {
|
||||
// Grab our scanned block hash for that height
|
||||
let scanned_block_hash = self.get_scanned_block_hash(&height)?;
|
||||
let (scanned_block_hash, _) = self.get_scanned_block(&height)?;
|
||||
|
||||
// Grab the block from darkfid for that height
|
||||
let block = match self.get_block_by_height(height).await {
|
||||
|
||||
@@ -28,21 +28,21 @@ use crate::{
|
||||
|
||||
impl Drk {
|
||||
/// Get a scanned block information record.
|
||||
pub fn get_scanned_block_hash(&self, height: &u32) -> WalletDbResult<String> {
|
||||
pub fn get_scanned_block(&self, height: &u32) -> WalletDbResult<(String, String)> {
|
||||
let Ok(query_result) = self.cache.scanned_blocks.get(height.to_be_bytes()) else {
|
||||
return Err(WalletDbError::QueryExecutionFailed);
|
||||
};
|
||||
let Some(hash_bytes) = query_result else {
|
||||
let Some(value_bytes) = query_result else {
|
||||
return Err(WalletDbError::RowNotFound);
|
||||
};
|
||||
let Ok(hash) = deserialize(&hash_bytes) else {
|
||||
let Ok((hash, signing_key)) = deserialize(&value_bytes) else {
|
||||
return Err(WalletDbError::ParseColumnValueError);
|
||||
};
|
||||
Ok(hash)
|
||||
Ok((hash, signing_key))
|
||||
}
|
||||
|
||||
/// Fetch all scanned block information records.
|
||||
pub fn get_scanned_block_records(&self) -> WalletDbResult<Vec<(u32, String)>> {
|
||||
pub fn get_scanned_block_records(&self) -> WalletDbResult<Vec<(u32, String, String)>> {
|
||||
let mut scanned_blocks = vec![];
|
||||
|
||||
for record in self.cache.scanned_blocks.iter() {
|
||||
@@ -54,10 +54,10 @@ impl Drk {
|
||||
Err(_) => return Err(WalletDbError::ParseColumnValueError),
|
||||
};
|
||||
let key = u32::from_be_bytes(key);
|
||||
let Ok(value) = deserialize(&value) else {
|
||||
let Ok((hash, signing_key)) = deserialize(&value) else {
|
||||
return Err(WalletDbError::ParseColumnValueError);
|
||||
};
|
||||
scanned_blocks.push((key, value));
|
||||
scanned_blocks.push((key, hash, signing_key));
|
||||
}
|
||||
|
||||
Ok(scanned_blocks)
|
||||
@@ -75,10 +75,10 @@ impl Drk {
|
||||
Err(_) => return Err(WalletDbError::ParseColumnValueError),
|
||||
};
|
||||
let key = u32::from_be_bytes(key);
|
||||
let Ok(value) = deserialize(&value) else {
|
||||
let Ok((hash, _)) = deserialize::<(String, String)>(&value) else {
|
||||
return Err(WalletDbError::ParseColumnValueError);
|
||||
};
|
||||
Ok((key, value))
|
||||
Ok((key, hash))
|
||||
}
|
||||
|
||||
/// Reset the scanned blocks information records in the cache.
|
||||
|
||||
Reference in New Issue
Block a user