Просмотр исходного кода

drk: store each block signing key in scanned blocks and display it so miners can grab it if ever needed

skoupidi 7 месяцев назад
Родитель
Сommit
86c740d9a0

+ 16 - 6
bin/drk/src/cache.rs

@@ -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(())
     }

+ 11 - 0
bin/drk/src/common.rs

@@ -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
+}

+ 4 - 9
bin/drk/src/interactive.rs

@@ -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"));

+ 4 - 9
bin/drk/src/main.rs

@@ -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");

+ 36 - 24
bin/drk/src/money.rs

@@ -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(&note.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.

+ 14 - 9
bin/drk/src/rpc.rs

@@ -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 {

+ 9 - 9
bin/drk/src/scanned_blocks.rs

@@ -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.