Browse Source

contract/money/pow_reward: simplyfied call to use last block information directly from database overlay

skoupidi 2 years ago
parent
commit
07b47fd521

+ 0 - 2
bin/darkfid/src/task/miner.rs

@@ -191,7 +191,6 @@ fn generate_pow_transaction(
     // Grab extended proposal info
     let last_proposal = fork.last_proposal()?;
     let last_nonce = last_proposal.block.header.nonce;
-    let fork_hash = last_proposal.hash;
     let fork_previous_hash = last_proposal.block.header.previous;
 
     // We're just going to be using a zero spend-hook and user-data
@@ -204,7 +203,6 @@ fn generate_pow_transaction(
         recipient: *recipient,
         block_height,
         last_nonce,
-        fork_hash,
         fork_previous_hash,
         spend_hook,
         user_data,

+ 1 - 9
src/contract/money/src/client/pow_reward_v1.rs

@@ -73,8 +73,6 @@ pub struct PoWRewardCallBuilder {
     pub block_height: u64,
     /// Extending fork last proposal/block nonce
     pub last_nonce: pallas::Base,
-    /// Extending fork last proposal/block hash
-    pub fork_hash: blake3::Hash,
     /// Extending fork second to last proposal/block hash
     pub fork_previous_hash: blake3::Hash,
     /// Merkle tree of coins used to create inclusion proofs
@@ -162,13 +160,7 @@ impl PoWRewardCallBuilder {
         vrf_input.extend_from_slice(&pallas::Base::from(self.block_height).to_repr());
         let vrf_proof = VrfProof::prove(self.secret, &vrf_input, &mut OsRng);
 
-        let params = MoneyPoWRewardParamsV1 {
-            input: c_input,
-            output: c_output,
-            fork_hash: self.fork_hash,
-            fork_previous_hash: self.fork_previous_hash,
-            vrf_proof,
-        };
+        let params = MoneyPoWRewardParamsV1 { input: c_input, output: c_output, vrf_proof };
         let debris = PoWRewardCallDebris { params, proofs: vec![proof] };
         Ok(debris)
     }

+ 22 - 30
src/contract/money/src/entrypoint/pow_reward_v1.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_sdk::{
-    blockchain::{expected_reward, Slot, POW_CUTOFF},
+    blockchain::expected_reward,
     crypto::{
         pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, ContractId, MerkleNode,
         DARK_TOKEN_ID,
@@ -27,7 +27,7 @@ use darkfi_sdk::{
     error::{ContractError, ContractResult},
     merkle_add, msg,
     pasta::pallas,
-    util::{get_slot, get_verifying_block_height},
+    util::{get_last_block_info, get_verifying_block_height},
     ContractCall,
 };
 use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
@@ -84,43 +84,35 @@ pub(crate) fn money_pow_reward_process_instruction_v1(
     let self_ = &calls[call_idx as usize].data;
     let params: MoneyPoWRewardParamsV1 = deserialize(&self_.data[1..])?;
 
-    // Verify this contract call is verified against a block height(slot) before PoS transition,
-    // excluding genesis.
+    // Verify this contract call is not verified against genesis block
     let verifying_block_height = get_verifying_block_height();
-    if verifying_block_height == 0 || verifying_block_height > POW_CUTOFF {
-        msg!(
-            "[PoWRewardV1] Error: Call is executed for block height {}(cutoff block height {})",
-            verifying_block_height,
-            POW_CUTOFF
-        );
-        return Err(MoneyError::PoWRewardCallAfterCutoffBlockHeight.into())
+    if verifying_block_height == 0 {
+        msg!("[PoWRewardV1] Error: Call is executed for genesis block");
+        return Err(MoneyError::PoWRewardCallOnGenesisBlock.into())
     }
 
-    // Grab the slot to validate consensus params against
-    let Some(slot) = get_slot(verifying_block_height)? else {
-        msg!("[PoWRewardV1] Error: Missing slot {} from db", verifying_block_height);
-        return Err(MoneyError::PoWRewardMissingSlot.into())
+    // Grab last block information to use in the VRF
+    let Some(last_block_info) = get_last_block_info()? else {
+        msg!("[PoWRewardV1] Error: Could not receive last block from db");
+        return Err(MoneyError::PoWRewardRetrieveLastBlockError.into())
     };
-    let slot: Slot = deserialize(&slot)?;
-
-    // Verify proposal extends a known fork
-    if !slot.previous.last_hashes.contains(&params.fork_hash) {
-        msg!("[PoWRewardV1] Error: Block extends unknown fork {}", params.fork_hash);
-        return Err(MoneyError::PoWRewardExtendsUnknownFork.into())
-    }
+    let height: u64 = deserialize(&last_block_info[..8])?;
 
-    // Verify sequence is correct
-    if !slot.previous.second_to_last_hashes.contains(&params.fork_previous_hash) {
-        let fork_prev = &params.fork_previous_hash;
-        msg!("[PoWRewardV1] Error: Block extends unknown fork {}", fork_prev);
-        return Err(MoneyError::PoWRewardExtendsUnknownFork.into())
+    // Verify this contract call is verified against next block height
+    if verifying_block_height != height + 1 {
+        msg!(
+            "[PoWRewardV1] Error: Call is executed for block height {}, not next one: {}",
+            verifying_block_height,
+            height
+        );
+        return Err(MoneyError::PoWRewardCallNotOnNextBlockHeight.into())
     }
 
     // Construct VRF input
     let mut vrf_input = Vec::with_capacity(32 + blake3::OUT_LEN + 32);
-    vrf_input.extend_from_slice(&slot.last_nonce.to_repr());
-    vrf_input.extend_from_slice(params.fork_previous_hash.as_bytes());
-    vrf_input.extend_from_slice(&pallas::Base::from(slot.id).to_repr());
+    vrf_input.extend_from_slice(&last_block_info[8..40]);
+    vrf_input.extend_from_slice(&last_block_info[40..]);
+    vrf_input.extend_from_slice(&pallas::Base::from(verifying_block_height).to_repr());
 
     // Verify VRF proof
     if !params.vrf_proof.verify(params.input.signature_public, &vrf_input) {

+ 9 - 9
src/contract/money/src/error.rs

@@ -97,14 +97,14 @@ pub enum MoneyError {
     #[error("Missing nullifier in set")]
     MissingNullifier,
 
-    #[error("Call is executed after cutoff block height")]
-    PoWRewardCallAfterCutoffBlockHeight,
+    #[error("Call is executed on genesis block height")]
+    PoWRewardCallOnGenesisBlock,
 
-    #[error("Missing slot from db")]
-    PoWRewardMissingSlot,
+    #[error("Could not retrieve last block from db")]
+    PoWRewardRetrieveLastBlockError,
 
-    #[error("Block extends unknown fork")]
-    PoWRewardExtendsUnknownFork,
+    #[error("Call is not executed on next block height")]
+    PoWRewardCallNotOnNextBlockHeight,
 
     #[error("Eta VRF proof couldn't be verified")]
     PoWRewardErroneousVrfProof,
@@ -148,9 +148,9 @@ impl From<MoneyError> for ContractError {
             MoneyError::GenesisCallNonGenesisBlock => Self::Custom(23),
             MoneyError::GenesisCallNonGenesisSlot => Self::Custom(24),
             MoneyError::MissingNullifier => Self::Custom(25),
-            MoneyError::PoWRewardCallAfterCutoffBlockHeight => Self::Custom(26),
-            MoneyError::PoWRewardMissingSlot => Self::Custom(27),
-            MoneyError::PoWRewardExtendsUnknownFork => Self::Custom(28),
+            MoneyError::PoWRewardCallOnGenesisBlock => Self::Custom(26),
+            MoneyError::PoWRewardRetrieveLastBlockError => Self::Custom(27),
+            MoneyError::PoWRewardCallNotOnNextBlockHeight => Self::Custom(28),
             MoneyError::PoWRewardErroneousVrfProof => Self::Custom(29),
             MoneyError::FeeMissingInputs => Self::Custom(30),
             MoneyError::InsufficientFee => Self::Custom(31),

+ 0 - 4
src/contract/money/src/model.rs

@@ -264,10 +264,6 @@ pub struct MoneyPoWRewardParamsV1 {
     pub input: ClearInput,
     /// Anonymous output
     pub output: Output,
-    /// Extending fork last proposal/block hash
-    pub fork_hash: blake3::Hash,
-    /// Extending fork second to last proposal/block hash
-    pub fork_previous_hash: blake3::Hash,
     /// VRF proof for block rank calculation
     pub vrf_proof: VrfProof,
 }

+ 0 - 4
src/contract/money/tests/pow_reward.rs

@@ -60,7 +60,6 @@ fn pow_reward() -> Result<()> {
             .await?;
 
         current_height += 1;
-        th.generate_slot(current_height).await?;
 
         let alice_reward = expected_reward(current_height);
         info!(target: "money", "[Malicious] ================================");
@@ -129,9 +128,6 @@ fn pow_reward() -> Result<()> {
         alice_owncoins.push(alice_oc);
 
         // Alice can also send her PoW reward directly to bob
-        current_height += 1;
-        th.generate_slot(current_height).await?;
-
         info!(target: "money", "[Alice] ==============================");
         info!(target: "money", "[Alice] Building PoW reward tx for Bob");
         info!(target: "money", "[Alice] ==============================");

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

@@ -56,7 +56,7 @@ impl TestHarness {
 
         // Proposals always extend genesis block
         let last_nonce = self.genesis_block.header.nonce;
-        let fork_hash = self.genesis_block.hash()?;
+        let fork_previous_hash = self.genesis_block.header.previous;
 
         // We're just going to be using a zero spend-hook and user-data
         let spend_hook = pallas::Base::zero();
@@ -74,8 +74,7 @@ impl TestHarness {
             recipient,
             block_height,
             last_nonce,
-            fork_hash,
-            fork_previous_hash: fork_hash,
+            fork_previous_hash,
             spend_hook,
             user_data,
             mint_zkbin: mint_zkbin.clone(),

+ 52 - 0
src/runtime/import/util.rs

@@ -19,6 +19,8 @@
 use log::error;
 use wasmer::{FunctionEnvMut, WasmPtr};
 
+use darkfi_sdk::crypto::pasta_prelude::PrimeField;
+
 use super::acl::acl_allow;
 use crate::runtime::vm_runtime::{ContractSection, Env};
 
@@ -260,6 +262,56 @@ pub(crate) fn get_verifying_slot_epoch(ctx: FunctionEnvMut<Env>) -> u64 {
     ctx.data().time_keeper.verifying_slot_epoch()
 }
 
+/// Grabs last block from the `Blockchain` overlay and then copies its
+/// height, nonce and previous block hash into the VM, by appending the data
+/// to the VM's object store.
+///
+/// On success, returns the index of the new object in the object store.
+/// Otherwise, returns an error code.
+pub(crate) fn get_last_block_info(mut ctx: FunctionEnvMut<Env>) -> i64 {
+    let (env, mut store) = ctx.data_and_store_mut();
+    let cid = &env.contract_id;
+
+    // Enforce function ACL
+    if let Err(e) = acl_allow(env, &[ContractSection::Exec]) {
+        error!(
+            target: "runtime::db::get_last_block_info",
+            "[WASM] [{}] get_last_block_info(): Called in unauthorized section: {}", cid, e,
+        );
+        return darkfi_sdk::error::CALLER_ACCESS_DENIED
+    }
+
+    // Grab current last block
+    let block = match env.blockchain.lock().unwrap().last_block() {
+        Ok(b) => b,
+        Err(e) => {
+            error!(
+                target: "runtime::db::get_last_block_info",
+                "[WASM] [{}] get_last_block_info(): Internal error getting from blocks tree: {}", cid, e,
+            );
+            return darkfi_sdk::error::DB_GET_FAILED
+        }
+    };
+
+    // Create the return object
+    let mut ret = Vec::with_capacity(8 + 32 + blake3::OUT_LEN);
+    ret.extend_from_slice(&block.header.height.to_be_bytes());
+    ret.extend_from_slice(&block.header.nonce.to_repr());
+    ret.extend_from_slice(block.header.previous.as_bytes());
+
+    // Subtract used gas. Here we count the size of the object.
+    env.subtract_gas(&mut store, ret.len() as u64);
+
+    // Copy Vec<u8> to the VM
+    let mut objects = env.objects.borrow_mut();
+    objects.push(ret.to_vec());
+    if objects.len() > u32::MAX as usize {
+        return darkfi_sdk::error::DATA_TOO_LARGE
+    }
+
+    (objects.len() - 1) as i64
+}
+
 /// Copies the data of requested slot from `SlotStore` into the VM by appending
 /// the data to the VM's object store.
 ///

+ 6 - 0
src/runtime/vm_runtime.rs

@@ -330,6 +330,12 @@ impl Runtime {
                     &ctx,
                     import::util::get_blockchain_time,
                 ),
+
+                "get_last_block_info_" => Function::new_typed_with_env(
+                    &mut store,
+                    &ctx,
+                    import::util::get_last_block_info,
+                ),
             }
         };
 

+ 11 - 0
src/sdk/src/util.rs

@@ -163,6 +163,16 @@ pub fn get_blockchain_time() -> u64 {
     unsafe { get_blockchain_time_() }
 }
 
+/// Only exec() can call this. Will return last block information.
+///
+/// ```
+/// last_block_info = get_last_block_info();
+/// ```
+pub fn get_last_block_info() -> GenericResult<Option<Vec<u8>>> {
+    let ret = unsafe { get_last_block_info_() };
+    parse_ret(ret)
+}
+
 extern "C" {
     fn set_return_data_(ptr: *const u8, len: u32) -> i64;
     fn put_object_bytes_(ptr: *const u8, len: u32) -> i64;
@@ -178,4 +188,5 @@ extern "C" {
     fn get_verifying_slot_epoch_() -> u64;
     fn get_slot_(slot: u64) -> i64;
     fn get_blockchain_time_() -> u64;
+    fn get_last_block_info_() -> i64;
 }