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

darkfid/rpc_xmr: use wallet as the mm block templates keys so we return the same one on diff instances with same wallet

skoupidi 8 месяцев назад
Родитель
Сommit
20ce209a2d
3 измененных файлов с 59 добавлено и 79 удалено
  1. 1 2
      bin/darkfid/src/lib.rs
  2. 57 76
      bin/darkfid/src/rpc_xmr.rs
  3. 1 1
      bin/darkfid/src/task/consensus.rs

+ 1 - 2
bin/darkfid/src/lib.rs

@@ -56,7 +56,6 @@ use rpc::{DefaultRpcHandler, MinerRpcClient, MmRpcHandler};
 mod rpc_blockchain;
 mod rpc_tx;
 mod rpc_xmr;
-use rpc_xmr::BlockTemplateHash;
 
 /// Validator async tasks
 pub mod task;
@@ -86,7 +85,7 @@ pub struct DarkfiNode {
     /// HTTP JSON-RPC connection tracker
     mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
     /// Merge mining block templates
-    mm_blocktemplates: Mutex<HashMap<BlockTemplateHash, (BlockInfo, SecretKey)>>,
+    mm_blocktemplates: Mutex<HashMap<[u8; 32], (BlockInfo, f64, SecretKey)>>,
     /// PowRewardV1 ZK data
     powrewardv1_zk: PowRewardV1Zk,
 }

+ 57 - 76
bin/darkfid/src/rpc_xmr.rs

@@ -16,7 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::{collections::HashMap, fmt, str::FromStr};
+use std::{collections::HashMap, str::FromStr};
 
 use darkfi::{
     blockchain::{
@@ -32,7 +32,7 @@ use darkfi::{
     validator::consensus::Proposal,
 };
 use darkfi_sdk::crypto::PublicKey;
-use darkfi_serial::{async_trait, serialize_async, SerialDecodable, SerialEncodable};
+use darkfi_serial::serialize_async;
 use hex::FromHex;
 use tinyjson::JsonValue;
 use tracing::{error, info};
@@ -41,43 +41,11 @@ use crate::{
     proto::ProposalMessage,
     server_error,
     task::miner::{generate_next_block, MinerRewardsRecipientConfig},
-    DarkfiNode, Error, Result, RpcError,
+    DarkfiNode, RpcError,
 };
 
 // https://github.com/SChernykh/p2pool/blob/master/docs/MERGE_MINING.MD
 
-/// Wrapper structure representing the combination `blake3::Hash` of a
-/// wallet address and merge mining job.
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, SerialEncodable, SerialDecodable)]
-pub struct BlockTemplateHash(pub [u8; 32]);
-
-impl BlockTemplateHash {
-    pub fn new(address: &PublicKey, aux_hash: &HeaderHash) -> Self {
-        let mut buf = [0u8; 64];
-        buf[..32].copy_from_slice(&address.to_bytes());
-        buf[32..].copy_from_slice(aux_hash.inner());
-        Self(*blake3::hash(&buf).as_bytes())
-    }
-
-    pub fn as_string(&self) -> String {
-        blake3::Hash::from_bytes(self.0).to_string()
-    }
-}
-
-impl FromStr for BlockTemplateHash {
-    type Err = Error;
-
-    fn from_str(header_hash_str: &str) -> Result<Self> {
-        Ok(Self(*blake3::Hash::from_str(header_hash_str)?.as_bytes()))
-    }
-}
-
-impl fmt::Display for BlockTemplateHash {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{}", self.as_string())
-    }
-}
-
 impl DarkfiNode {
     // RPCAPI:
     // Gets a unique ID that identifies this merge mined chain and
@@ -117,21 +85,19 @@ impl DarkfiNode {
     }
 
     // RPCAPI:
-    // Gets a blob of data (usually a new block for the merge mined chain)
-    // and its hash to be used for merge mining.
+    // Gets a blob of data, the blocks hash and difficutly used for
+    // merge mining.
     //
     // **Request:**
-    // * `address` - A wallet address on the merge mined chain
-    // * `aux_hash` - Merge mining job that is currently being used
-    // * `height` - Monero height
-    // * `prev_id` - Hash of the previous Monero block
+    // * `address` : A wallet address on the merge mined chain
+    // * `aux_hash`: Merge mining job that is currently being polled
+    // * `height`  : Monero height
+    // * `prev_id` : Hash of the previous Monero block
     //
     // **Response:**
-    // * `aux_blob` - A hex-encoded blob of data. Merge mined chain defines the
-    //   contents of this blob. It's opaque to p2pool and will not be changed by it
-    // * `aux_diff` - Mining difficulty (decimal number)
-    // * `aux_hash` - A 32-byte hex-encoded hash of the `aux_blob`. Merge mined chain
-    //   defines how exactly this hash is calculated. It's opaque to p2pool.
+    // * `aux_blob`: The hex-encoded wallet address blob
+    // * `aux_diff`: Mining difficulty (decimal number)
+    // * `aux_hash`: A 32-byte hex-encoded hash of merge mined block
     //
     // --> {"jsonrpc":"2.0", "method": "merge_mining_get_aux_block", "params": {"address": "MERGE_MINED_CHAIN_ADDRESS", "aux_hash": "f6952d6eef555ddd87aca66e56b91530222d6e318414816f3ba7cf5bf694bf0f", "height": 3000000, "prev_id":"ad505b0be8a49b89273e307106fa42133cbd804456724c5e7635bd953215d92a"}, "id": 1}
     // <-- {"jsonrpc":"2.0", "result": {"aux_blob": "4c6f72656d20697073756d", "aux_diff": 123456, "aux_hash":"f6952d6eef555ddd87aca66e56b91530222d6e318414816f3ba7cf5bf694bf0f"}, "id": 1}
@@ -197,14 +163,16 @@ impl DarkfiNode {
         let prev_id = monero::Hash::from_slice(&prev_id);
 
         // Now that method params format is correct, we can check if we
-        // already have this mining job. If we already have it, we
-        // check if the fork it extends is still the best one. If both
-        // checks pass, we can just return an empty response. In case
-        // the best fork has changed, we drop this job and generate a
-        // new one. We'll also obtain a lock here to avoid getting
-        // polled multiple times and potentially missing a job. The
-        // lock is released when this function exits.
-        let template_key = BlockTemplateHash::new(&address, &aux_hash);
+        // already have a mining job for this wallet. If we already
+        // have it, we check if the fork it extends is still the best
+        // one. If both checks pass, we can just return an empty
+        // response if the request `aux_hash` matches the job one,
+        // otherwise return the job block template hash. In case the
+        // best fork has changed, we drop this job and generate a
+        // new one. If we don't know this wallet, we create a new job.
+        // We'll also obtain a lock here to avoid getting polled
+        // multiple times and potentially missing a job. The lock is
+        // released when this function exits.
         let mut mm_blocktemplates = self.mm_blocktemplates.lock().await;
         let mut extended_fork = match self.best_current_fork().await {
             Ok(f) => f,
@@ -216,7 +184,8 @@ impl DarkfiNode {
                 return JsonError::new(ErrorCode::InternalError, None, id).into()
             }
         };
-        if let Some((block, _)) = mm_blocktemplates.get(&template_key) {
+        let address_bytes = address.to_bytes();
+        if let Some((block, difficulty, _)) = mm_blocktemplates.get(&address_bytes) {
             let last_proposal = match extended_fork.last_proposal() {
                 Ok(p) => p,
                 Err(e) => {
@@ -228,16 +197,24 @@ impl DarkfiNode {
                 }
             };
             if last_proposal.hash == block.header.previous {
-                return JsonResponse::new(JsonValue::from(HashMap::new()), id).into()
+                let blockhash = block.header.template_hash();
+                return if blockhash != aux_hash {
+                    JsonResponse::new(
+                        JsonValue::from(HashMap::from([
+                            ("aux_blob".to_string(), JsonValue::from(hex::encode(address_bytes))),
+                            ("aux_diff".to_string(), JsonValue::from(*difficulty)),
+                            ("aux_hash".to_string(), JsonValue::from(blockhash.as_string())),
+                        ])),
+                        id,
+                    )
+                    .into()
+                } else {
+                    JsonResponse::new(JsonValue::from(HashMap::new()), id).into()
+                }
             }
-            mm_blocktemplates.remove(&template_key);
+            mm_blocktemplates.remove(&address_bytes);
         }
 
-        info!(
-            target: "darkfid::rpc_xmr::xmr_merge_mining_get_aux_block",
-            "[RPC-XMR] Got blocktemplate request: address={address}, aux_hash={aux_hash}, height={height}, prev_id={prev_id}"
-        );
-
         // At this point, we should query the Validator for a new blocktemplate.
         // We first need to construct `MinerRewardsRecipientConfig` from the
         // address provided to us through the RPC.
@@ -283,15 +260,14 @@ impl DarkfiNode {
         // Now we have the blocktemplate. We'll mark it down in memory,
         // and then ship it to RPC.
         let blockhash = blocktemplate.header.template_hash();
-        let template_key = BlockTemplateHash::new(&address, &blockhash);
-        mm_blocktemplates.insert(template_key, (blocktemplate, block_signing_secret));
+        mm_blocktemplates.insert(address_bytes, (blocktemplate, difficulty, block_signing_secret));
         info!(
             target: "darkfid::rpc_xmr::xmr_merge_mining_get_aux_block",
-            "[RPC-XMR] Created blocktemplate with key: {template_key}"
+            "[RPC-XMR] Created new blocktemplate: address={address}, aux_hash={blockhash}, height={height}, prev_id={prev_id}"
         );
 
         let response = JsonValue::from(HashMap::from([
-            ("aux_blob".to_string(), JsonValue::from(hex::encode(address.to_bytes()))),
+            ("aux_blob".to_string(), JsonValue::from(hex::encode(address_bytes))),
             ("aux_diff".to_string(), JsonValue::from(difficulty)),
             ("aux_hash".to_string(), JsonValue::from(blockhash.as_string())),
         ]));
@@ -306,7 +282,7 @@ impl DarkfiNode {
     //
     // **Request:**
     // * `aux_blob`: Blob of data returned by `merge_mining_get_aux_block`
-    // * `aux_hash`: A 32-byte hex-encoded hash of the `aux_blob`, same as above.
+    // * `aux_hash`: A 32-byte hex-encoded hash of merge mined block
     // * `blob`: Monero block template that has enough PoW to satisfy the difficulty
     //   returned by `merge_mining_get_aux_block`. It must also have a merge mining
     //   tag in `tx_extra` of the coinbase transaction.
@@ -345,7 +321,7 @@ impl DarkfiNode {
             return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
                 .into()
         };
-        let Ok(address) = PublicKey::from_bytes(address_bytes) else {
+        if PublicKey::from_bytes(address_bytes).is_err() {
             return JsonError::new(InvalidParams, Some("invalid aux_blob format".to_string()), id)
                 .into()
         };
@@ -364,11 +340,9 @@ impl DarkfiNode {
         };
 
         // If we don't know about this job, we can just abort here.
-        let template_key = BlockTemplateHash::new(&address, &aux_hash);
         let mut mm_blocktemplates = self.mm_blocktemplates.lock().await;
-        if !mm_blocktemplates.contains_key(&template_key) {
-            return JsonError::new(InvalidParams, Some("unknown template key".to_string()), id)
-                .into()
+        if !mm_blocktemplates.contains_key(&address_bytes) {
+            return JsonError::new(InvalidParams, Some("unknown address".to_string()), id).into()
         }
 
         // Parse blob
@@ -449,7 +423,7 @@ impl DarkfiNode {
 
         info!(
             target: "darkfid::rpc_xmr::xmr_merge_mining_submit_solution",
-            "[RPC-XMR] Got solution submission: key={template_key}, aux_hash={aux_hash}",
+            "[RPC-XMR] Got solution submission: aux_hash={aux_hash}",
         );
 
         // Construct the MoneroPowData
@@ -478,7 +452,7 @@ impl DarkfiNode {
         };
 
         // Append MoneroPowData to the DarkFi block and sign it
-        let (block, secret) = &mm_blocktemplates.get(&template_key).unwrap();
+        let (block, _, secret) = &mm_blocktemplates.get(&address_bytes).unwrap();
         let mut block = block.clone();
         block.header.pow_data = PowData::Monero(monero_pow_data);
         block.sign(secret);
@@ -486,7 +460,7 @@ impl DarkfiNode {
         // At this point we should be able to remove the submitted job.
         // We still won't release the lock in hope of proposing the block
         // first.
-        mm_blocktemplates.remove(&template_key);
+        mm_blocktemplates.remove(&address_bytes);
 
         // Propose the new block
         info!(
@@ -499,7 +473,14 @@ impl DarkfiNode {
                 target: "darkfid::rpc_xmr::xmr_merge_submit_solution",
                 "[RPC-XMR] Error proposing new block: {e}",
             );
-            return JsonError::new(ErrorCode::InternalError, None, id).into()
+            return JsonResponse::new(
+                JsonValue::from(HashMap::from([(
+                    "status".to_string(),
+                    JsonValue::from("rejected".to_string()),
+                )])),
+                id,
+            )
+            .into()
         }
 
         let proposals_sub = self.subscribers.get("proposals").unwrap();

+ 1 - 1
bin/darkfid/src/task/consensus.rs

@@ -272,7 +272,7 @@ pub async fn clean_mm_blocktemplates(node: &DarkfiNodePtr) -> Result<()> {
 
     // Loop through templates to find which can be dropped
     let mut dropped_templates = vec![];
-    'outer: for (key, (block, _)) in mm_blocktemplates.iter() {
+    'outer: for (key, (block, _, _)) in mm_blocktemplates.iter() {
         // Loop through all the forks
         for fork in forks.iter() {
             // Traverse fork proposals sequence in reverse