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

darkfid: Use an ephemeral key for signing blocks

parazyd 1 год назад
Родитель
Сommit
c6ff8cfc41

+ 6 - 1
bin/darkfid/src/rpc_xmr.rs

@@ -24,6 +24,8 @@ use tracing::error;
 
 use crate::DarkfiNode;
 
+// https://github.com/SChernykh/p2pool/blob/master/docs/MERGE_MINING.MD
+
 impl DarkfiNode {
     // RPCAPI:
     // Gets a unique ID that identifies this merge mined chain and
@@ -48,7 +50,10 @@ impl DarkfiNode {
             }
         };
 
-        let resp_obj = HashMap::from([("chain_id".to_string(), genesis_hash.to_string().into())]);
+        let genesis_hex = genesis_hash.to_string();
+        assert!(genesis_hex.len() == 32);
+
+        let resp_obj = HashMap::from([("chain_id".to_string(), genesis_hex.into())]);
         JsonResponse::new(resp_obj.into(), id).into()
     }
 

+ 23 - 38
bin/darkfid/src/task/miner.rs

@@ -57,7 +57,7 @@ pub struct MinerRewardsRecipientConfig {
 /// Async task used for participating in the PoW block production.
 ///
 /// Miner initializes their setup and waits for next confirmation,
-/// by listenning for new proposals from the network, for optimal
+/// by listening for new proposals from the network, for optimal
 /// conditions. After confirmation occurs, they start the actual
 /// miner loop, where they first grab the best ranking fork to extend,
 /// and start mining procedure for its next block. Additionally, they
@@ -84,13 +84,6 @@ pub async fn miner_task(
     let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
     let pk = ProvingKey::build(zkbin.k, &circuit);
 
-    // Generate a random master secret key, to derive all signing keys from.
-    // This enables us to deanonimize proposals from reward recipient(miner).
-    // TODO: maybe miner wants to keep this master secret so they can
-    //       verify their signature in the future?
-    info!(target: "darkfid::task::miner_task", "Generating signing key...");
-    let mut secret = SecretKey::random(&mut OsRng);
-
     // Grab blocks subscriber
     let block_sub = node.subscribers.get("blocks").unwrap();
 
@@ -167,16 +160,7 @@ pub async fn miner_task(
         // Start listenning for network proposals and mining next block for best fork.
         match smol::future::or(
             listen_to_network(node, last_proposal_hash, &subscription, &sender),
-            mine(
-                node,
-                extended_fork,
-                &mut secret,
-                recipient_config,
-                &zkbin,
-                &pk,
-                &stop_signal,
-                skip_sync,
-            ),
+            mine(node, extended_fork, recipient_config, &zkbin, &pk, &stop_signal, skip_sync),
         )
         .await
         {
@@ -276,7 +260,6 @@ async fn listen_to_network(
 async fn mine(
     node: &DarkfiNodePtr,
     extended_fork: Fork,
-    secret: &mut SecretKey,
     recipient_config: &MinerRewardsRecipientConfig,
     zkbin: &ZkBinary,
     pk: &ProvingKey,
@@ -285,7 +268,7 @@ async fn mine(
 ) -> Result<()> {
     smol::future::or(
         wait_stop_signal(stop_signal),
-        mine_next_block(node, extended_fork, secret, recipient_config, zkbin, pk, skip_sync),
+        mine_next_block(node, extended_fork, recipient_config, zkbin, pk, skip_sync),
     )
     .await
 }
@@ -307,16 +290,14 @@ pub async fn wait_stop_signal(stop_signal: &Receiver<()>) -> Result<()> {
 async fn mine_next_block(
     node: &DarkfiNodePtr,
     mut extended_fork: Fork,
-    secret: &mut SecretKey,
     recipient_config: &MinerRewardsRecipientConfig,
     zkbin: &ZkBinary,
     pk: &ProvingKey,
     skip_sync: bool,
 ) -> Result<()> {
     // Grab next target and block
-    let (next_target, mut next_block) = generate_next_block(
+    let (next_target, mut next_block, block_signing_secret) = generate_next_block(
         &mut extended_fork,
-        secret,
         recipient_config,
         zkbin,
         pk,
@@ -333,7 +314,7 @@ async fn mine_next_block(
     next_block.header.nonce = *response.get::<f64>().unwrap() as u64;
 
     // Sign the mined block
-    next_block.sign(secret);
+    next_block.sign(&block_signing_secret);
 
     // Verify it
     extended_fork.module.verify_current_block(&next_block)?;
@@ -357,13 +338,12 @@ async fn mine_next_block(
 /// Auxiliary function to generate next block in an atomic manner.
 async fn generate_next_block(
     extended_fork: &mut Fork,
-    secret: &mut SecretKey,
     recipient_config: &MinerRewardsRecipientConfig,
     zkbin: &ZkBinary,
     pk: &ProvingKey,
     block_target: u32,
     verify_fees: bool,
-) -> Result<(BigUint, BlockInfo)> {
+) -> Result<(BigUint, BlockInfo, SecretKey)> {
     // Grab forks' last block proposal(previous)
     let last_proposal = extended_fork.last_proposal()?;
 
@@ -375,15 +355,20 @@ async fn generate_next_block(
         .unproposed_txs(&extended_fork.blockchain, next_block_height, block_target, verify_fees)
         .await?;
 
-    // We are deriving the next secret key for optimization.
-    // Next secret is the poseidon hash of:
-    //  [prefix, current(previous) secret, signing(block) height].
-    let prefix = pallas::Base::from_raw([4, 0, 0, 0]);
-    let next_secret = poseidon_hash([prefix, secret.inner(), (next_block_height as u64).into()]);
-    *secret = SecretKey::from(next_secret);
+    // Create an ephemeral block signing key. It will be stored in the PowReward
+    // transaction's encrypted note for later retrieval. It is encrypted towards
+    // the recipient's public key.
+    let block_signing_secret = SecretKey::random(&mut OsRng);
 
     // Generate reward transaction
-    let tx = generate_transaction(next_block_height, fees, secret, recipient_config, zkbin, pk)?;
+    let tx = generate_transaction(
+        next_block_height,
+        fees,
+        &block_signing_secret,
+        recipient_config,
+        zkbin,
+        pk,
+    )?;
 
     // Apply producer transaction in the overlay
     let _ = apply_producer_transaction(
@@ -419,21 +404,21 @@ async fn generate_next_block(
     // Grab the next mine target
     let target = extended_fork.module.next_mine_target()?;
 
-    Ok((target, next_block))
+    Ok((target, next_block, block_signing_secret))
 }
 
 /// Auxiliary function to generate a Money::PoWReward transaction.
 fn generate_transaction(
     block_height: u32,
     fees: u64,
-    secret: &SecretKey,
+    block_signing_secret: &SecretKey,
     recipient_config: &MinerRewardsRecipientConfig,
     zkbin: &ZkBinary,
     pk: &ProvingKey,
 ) -> Result<Transaction> {
     // Build the transaction debris
     let debris = PoWRewardCallBuilder {
-        signature_public: PublicKey::from_secret(*secret),
+        signature_public: PublicKey::from_secret(*block_signing_secret),
         block_height,
         fees,
         recipient: Some(recipient_config.recipient),
@@ -442,7 +427,7 @@ fn generate_transaction(
         mint_zkbin: zkbin.clone(),
         mint_pk: pk.clone(),
     }
-    .build()?;
+    .build(block_signing_secret)?;
 
     // Generate and sign the actual transaction
     let mut data = vec![MoneyFunction::PoWRewardV1 as u8];
@@ -451,7 +436,7 @@ fn generate_transaction(
     let mut tx_builder =
         TransactionBuilder::new(ContractCallLeaf { call, proofs: debris.proofs }, vec![])?;
     let mut tx = tx_builder.build()?;
-    let sigs = tx.create_sigs(&[*secret])?;
+    let sigs = tx.create_sigs(&[*block_signing_secret])?;
     tx.signatures = vec![sigs];
 
     Ok(tx)

+ 1 - 1
bin/darkfid/src/tests/harness.rs

@@ -211,7 +211,7 @@ impl Harness {
             mint_zkbin: zkbin.clone(),
             mint_pk: pk.clone(),
         }
-        .build()?;
+        .build(&keypair.secret)?;
 
         // Generate and sign the actual transaction
         let mut data = vec![MoneyFunction::PoWRewardV1 as u8];

+ 12 - 7
src/contract/money/src/client/pow_reward_v1.rs

@@ -23,9 +23,10 @@ use darkfi::{
 };
 use darkfi_sdk::{
     blockchain::expected_reward,
-    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Blind, FuncId, PublicKey},
+    crypto::{note::AeadEncryptedNote, pasta_prelude::*, Blind, FuncId, PublicKey, SecretKey},
     pasta::pallas,
 };
+use darkfi_serial::serialize;
 use rand::rngs::OsRng;
 use tracing::debug;
 
@@ -79,7 +80,7 @@ pub struct PoWRewardCallBuilder {
 }
 
 impl PoWRewardCallBuilder {
-    fn _build(&self, value: u64) -> Result<PoWRewardCallDebris> {
+    fn _build(&self, value: u64, block_signing_secret: &SecretKey) -> Result<PoWRewardCallDebris> {
         debug!(target: "contract::money::client::pow_reward", "Building Money::PoWRewardV1 contract call");
 
         // In this call, we will build one clear input and one anonymous output.
@@ -132,7 +133,7 @@ impl PoWRewardCallBuilder {
             coin_blind,
             value_blind,
             token_blind,
-            memo: vec![],
+            memo: serialize(block_signing_secret),
         };
 
         let encrypted_note = AeadEncryptedNote::encrypt(&note, &output.public_key, &mut OsRng)?;
@@ -149,13 +150,17 @@ impl PoWRewardCallBuilder {
         Ok(debris)
     }
 
-    pub fn build(&self) -> Result<PoWRewardCallDebris> {
+    pub fn build(&self, block_signing_key: &SecretKey) -> Result<PoWRewardCallDebris> {
         let reward = expected_reward(self.block_height) + self.fees;
-        self._build(reward)
+        self._build(reward, block_signing_key)
     }
 
     /// This function should only be used for testing, as PoW reward values are predefined
-    pub fn build_with_custom_reward(&self, reward: u64) -> Result<PoWRewardCallDebris> {
-        self._build(reward + self.fees)
+    pub fn build_with_custom_reward(
+        &self,
+        reward: u64,
+        block_signing_key: &SecretKey,
+    ) -> Result<PoWRewardCallDebris> {
+        self._build(reward + self.fees, block_signing_key)
     }
 }

+ 9 - 3
src/contract/test-harness/src/money_pow_reward.rs

@@ -16,6 +16,9 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+use log::info;
+use rand::rngs::OsRng;
+
 use darkfi::{
     blockchain::{BlockInfo, BlockchainOverlay, Header},
     tx::{ContractCallLeaf, Transaction, TransactionBuilder},
@@ -28,7 +31,7 @@ use darkfi_money_contract::{
     MoneyFunction, MONEY_CONTRACT_ZKAS_MINT_NS_V1,
 };
 use darkfi_sdk::{
-    crypto::{contract_id::MONEY_CONTRACT_ID, MerkleNode, MerkleTree},
+    crypto::{contract_id::MONEY_CONTRACT_ID, MerkleNode, MerkleTree, SecretKey},
     ContractCall,
 };
 use darkfi_serial::AsyncEncodable;
@@ -65,6 +68,9 @@ impl TestHarness {
         // If there's fees paid, use them, otherwise set to zero
         let fees = fees.unwrap_or_default();
 
+        // Generate a random block signing key
+        let block_signing_key = SecretKey::random(&mut OsRng);
+
         // Build the transaction
         let builder = PoWRewardCallBuilder {
             signature_public: wallet.keypair.public,
@@ -78,8 +84,8 @@ impl TestHarness {
         };
 
         let debris = match reward {
-            Some(value) => builder.build_with_custom_reward(value)?,
-            None => builder.build()?,
+            Some(value) => builder.build_with_custom_reward(value, &block_signing_key)?,
+            None => builder.build(&block_signing_key)?,
         };
 
         // Encode the transaction

+ 1 - 1
src/validator/mod.rs

@@ -914,7 +914,7 @@ impl Validator {
             let (next_target, next_difficulty) = module.next_mine_target_and_difficulty()?;
 
             // Calculate block rank
-            let (target_distance_sq, hash_distance_sq) = block_rank(&block, &next_target);
+            let (target_distance_sq, hash_distance_sq) = block_rank(&block, &next_target)?;
 
             // Update chain ranks
             targets_rank += target_distance_sq.clone();