Răsfoiți Sursa

darkfid: Move transaction simulation into a separate fn and add a sim RPC endpoint.

Luther Blissett 3 ani în urmă
părinte
comite
b658d77b0d

+ 23 - 0
bin/darkfid/src/internal.rs

@@ -0,0 +1,23 @@
+use darkfi::{
+    consensus::ValidatorState,
+    node::{state::StateUpdate, MemoryState},
+    tx::Transaction,
+    Result,
+};
+
+use super::Darkfid;
+
+impl Darkfid {
+    /// Apply a new `MemoryState` from the current validator state and simulate a state
+    /// transition with the given `Transaction`. Returns a vec of `StateUpdate` on success.
+    pub async fn simulate_transaction(&self, tx: &Transaction) -> Result<Vec<StateUpdate>> {
+        // Grab the current state and apply a new MemoryState
+        let validator_state = self.validator_state.read().await;
+        let state = validator_state.state_machine.lock().await;
+        let mem_state = MemoryState::new(state.clone());
+        drop(state);
+        drop(validator_state);
+
+        ValidatorState::validate_state_transitions(mem_state, &[tx.clone()])
+    }
+}

+ 3 - 0
bin/darkfid/src/main.rs

@@ -175,6 +175,9 @@ mod rpc_misc;
 mod rpc_tx;
 mod rpc_wallet;
 
+// Internal methods
+mod internal;
+
 #[async_trait]
 impl RequestHandler for Darkfid {
     async fn handle_request(&self, req: JsonRequest) -> JsonResult {

+ 45 - 10
bin/darkfid/src/rpc_tx.rs

@@ -4,9 +4,7 @@ use log::{error, warn};
 use serde_json::{json, Value};
 
 use darkfi::{
-    consensus::ValidatorState,
     crypto::{address::Address, keypair::PublicKey, token_id},
-    node::MemoryState,
     rpc::jsonrpc::{ErrorCode::InvalidParams, JsonError, JsonResponse, JsonResult},
     serial::{deserialize, serialize},
     tx::Transaction,
@@ -100,6 +98,49 @@ impl Darkfid {
         JsonResponse::new(json!(tx_hash), id).into()
     }
 
+    // RPCAPI:
+    // Simulate a network state transition with the given transaction.
+    // Returns `true` if the transaction is valid, otherwise, a corresponding
+    // error.
+    //
+    // --> {"jsonrpc": "2.0", "method": "tx.simulate", "params": ["base58encodedTX"], "id": 1}
+    // <-- {"jsonrpc": "2.0", "result": true, "id": 1}
+    pub async fn tx_simulate(&self, id: Value, params: &[Value]) -> JsonResult {
+        if params.len() != 1 || !params[0].is_string() {
+            return JsonError::new(InvalidParams, None, id).into()
+        }
+
+        if !(*self.synced.lock().await) {
+            error!("[RPC] tx.simulate: Blockchain is not synced");
+            return server_error(RpcError::NotSynced, id, None)
+        }
+
+        // Try to deserialize the transaction
+        let tx_bytes = match bs58::decode(params[0].as_str().unwrap().trim()).into_vec() {
+            Ok(v) => v,
+            Err(e) => {
+                error!("[RPC] tx.simulate: Failed decoding base58 transaction: {}", e);
+                return server_error(RpcError::ParseError, id, None)
+            }
+        };
+
+        let tx: Transaction = match deserialize(&tx_bytes) {
+            Ok(v) => v,
+            Err(e) => {
+                error!("[RPC] tx.simulate: Failed deserializing bytes into Transaction: {}", e);
+                return server_error(RpcError::ParseError, id, None)
+            }
+        };
+
+        // Simulate state transition
+        if let Err(e) = self.simulate_transaction(&tx).await {
+            error!("[RPC] tx.broadcast: Failed to validate state transition: {}", e);
+            return server_error(RpcError::TxSimulationFail, id, None)
+        }
+
+        JsonResponse::new(json!(true), id).into()
+    }
+
     // RPCAPI:
     // Broadcast a given transaction to the P2P network.
     // The function will first simulate the state transition in order to see
@@ -135,19 +176,13 @@ impl Darkfid {
             }
         };
 
-        // Grab the current state and apply a new MemoryState
-        let validator_state = self.validator_state.read().await;
-        let state = validator_state.state_machine.lock().await;
-        let mem_state = MemoryState::new(state.clone());
-        drop(state);
-        drop(validator_state);
-
         // Simulate state transition
-        if let Err(e) = ValidatorState::validate_state_transitions(mem_state, &[tx.clone()]) {
+        if let Err(e) = self.simulate_transaction(&tx).await {
             error!("[RPC] tx.broadcast: Failed to validate state transition: {}", e);
             return server_error(RpcError::TxSimulationFail, id, None)
         }
 
+        // TODO: Should we apply the state transition locally before broadcasting it?
         if let Some(sync_p2p) = &self.sync_p2p {
             if let Err(e) = sync_p2p.broadcast(tx.clone()).await {
                 error!("[RPC] tx.broadcast: Failed broadcasting transaction: {}", e);

+ 4 - 3
src/stakeholder/stakeholder.rs

@@ -367,7 +367,7 @@ impl Stakeholder {
         // it's value is dependent on the tekonomics,
         // set to one untill then.
         let reward = pallas::Base::one();
-        let num_slots = num_slots+epochs*epoch_len;
+        let num_slots = num_slots + epochs * epoch_len;
         let sigma: pallas::Base = pallas::Base::from(num_slots) * reward;
         epoch.create_coins(sigma); // set epoch interal fields working space with competing coins
         self.epoch = epoch.clone();
@@ -383,10 +383,11 @@ impl Stakeholder {
     /// * `e` - epoch index
     /// * `sl` - slot relative index
     fn new_slot(&mut self, e: u64, sl: u64) {
-        info!(target: LOG_T, "[new slot] e:{}, rel sl:{}", self, e, sl);
+        info!(target: LOG_T, "[new slot] e:{}, rel sl:{}", e, sl);
         let st: blake3::Hash = if e > 0 || (e == 0 && sl > 0) {
             self.workspace.block.blockhash()
-        } else {  blake3::hash(b"")
+        } else {
+            blake3::hash(b"")
         };
         let is_leader: bool = self.epoch.is_leader(sl);
         // if is leader create proof