neptune-rpc-api 0.14.0

Neptune JSON-RPC API contract: the RpcApi trait and its data-transfer model types
use neptune_consensus::block::Block;
use neptune_consensus::block::pow::LustrationStatus;
use neptune_consensus::block::pow::PowMastPaths;
use neptune_primitives::difficulty_control::Difficulty;
use serde::Deserialize;
use serde::Serialize;
use tasm_lib::prelude::Digest;
use tasm_lib::triton_vm::prelude::BFieldElement;

use crate::model::block::RpcBlock;
use crate::model::common::RpcNativeCurrencyAmount;

/// Data required to attempt to solve the proof-of-work puzzle that allows the
/// minting of the next block.
#[derive(Clone, Debug, Copy, Serialize, Deserialize)]
pub struct RpcBlockTemplateMetadata {
    /// The pre-PoW digest of the block, serving as the unique template ID.
    pub digest: Digest,

    /// Indicates whether template is invalid due to the presence of a new tip.
    /// Can be used to reset templates in pools that perform local checks before
    /// submitting a solution to the node.
    pub prev_block: Digest,

    /// The threshold digest that defines when a PoW solution is valid. The
    /// block's hash must be less than or equal to this value.
    pub threshold: Digest,

    /// The total reward, timelocked plus liquid, for a successful guess.
    pub total_guesser_reward: RpcNativeCurrencyAmount,

    // All fields public since used downstream by mining pool software.
    pub pow_mast_paths: PowMastPaths,

    /// The lustration status that must be set in the pow field of the header
    /// for the block to be valid. Should only be used once hardfork-beta is
    /// active. Otherwise, ignore.
    pub lustration_status: Option<LustrationStatus>,

    /// The version field in the header.
    pub version: BFieldElement,
}

impl RpcBlockTemplateMetadata {
    /// Extracts template metadata assuming that the caller has already set the
    /// correct guesser digest.
    /// # Warning
    /// - The provided difficulty will be used, regardless of the consensus
    ///   rule set. So it must be correct.
    // TODO: Remove 2nd argument when hardfork-beta is activated
    pub fn new(block_proposal: &Block, difficulty: Difficulty) -> Self {
        let digest = block_proposal.hash();
        let prev_block = block_proposal.header().prev_block_digest;
        let threshold = difficulty.target();
        let guesser_reward = block_proposal
            .body()
            .total_guesser_reward()
            .expect("Block proposal must have well-defined guesser reward");
        let auth_paths = block_proposal.pow_mast_paths();

        let lustration_status = block_proposal.header().pow.lustration_status().ok();

        Self {
            digest,
            prev_block,
            threshold,
            total_guesser_reward: guesser_reward.into(),
            pow_mast_paths: auth_paths,
            lustration_status,
            version: block_proposal.header().version,
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RpcBlockTemplate {
    pub block: RpcBlock,
    pub metadata: RpcBlockTemplateMetadata,
}

#[cfg(any(test, feature = "test-helpers"))]
impl RpcBlockTemplateMetadata {
    pub fn solve(
        &self,
        consensus_rule_set: neptune_consensus::consensus_rule_set::ConsensusRuleSet,
    ) -> crate::model::block::header::RpcBlockPow {
        use neptune_consensus::block::block_header::BlockPow;
        use tasm_lib::twenty_first::bfe_array;

        assert!(
            !consensus_rule_set.memory_hard_pow(),
            "solving pre-hardfork-beta (memory-hard) proof-of-work is not supported"
        );

        let solution = (0u64..u64::MAX)
            .map(|i| {
                let nonce = Digest(bfe_array![0, 0, 0, 0, i]);

                BlockPow::guess(
                    &self.pow_mast_paths,
                    nonce,
                    self.threshold,
                    self.lustration_status,
                    Some(self.version),
                )
            })
            .find_map(|x| x)
            .expect("Should find solution within 2^{64} attempts");

        solution.into()
    }
}