| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use darkfi_sdk::{
- crypto::{
- ecvrf::VrfProof, pasta_prelude::PrimeField, PublicKey, CONSENSUS_CONTRACT_ID,
- DAO_CONTRACT_ID, MONEY_CONTRACT_ID,
- },
- pasta::{group::ff::FromUniformBytes, pallas},
- };
- use darkfi_serial::{serialize_async, AsyncDecodable};
- use log::info;
- use smol::io::Cursor;
- use crate::{
- blockchain::{BlockInfo, BlockchainOverlayPtr},
- error::TxVerifyFailed,
- runtime::vm_runtime::Runtime,
- tx::Transaction,
- util::time::TimeKeeper,
- validator::consensus::{Fork, Proposal},
- Error, Result,
- };
- /// Deploy DarkFi native wasm contracts to provided blockchain overlay.
- /// If overlay already contains the contracts, it will just open the
- /// necessary db and trees, and give back what it has. This means that
- /// on subsequent runs, our native contracts will already be in a deployed
- /// state, so what we actually do here is a redeployment. This kind of
- /// operation should only modify the contract's state in case it wasn't
- /// deployed before (meaning the initial run). Otherwise, it shouldn't
- /// touch anything, or just potentially update the db schemas or whatever
- /// is necessary. This logic should be handled in the init function of
- /// the actual contract, so make sure the native contracts handle this well.
- pub async fn deploy_native_contracts(
- overlay: &BlockchainOverlayPtr,
- time_keeper: &TimeKeeper,
- faucet_pubkeys: &Vec<PublicKey>,
- ) -> Result<()> {
- info!(target: "validator::utils::deploy_native_contracts", "Deploying native WASM contracts");
- // The faucet pubkeys are pubkeys which are allowed to create clear inputs
- // in the Money contract.
- let money_contract_deploy_payload = serialize_async(faucet_pubkeys).await;
- // The DAO contract uses an empty payload to deploy itself.
- let dao_contract_deploy_payload = vec![];
- // The Consensus contract uses an empty payload to deploy itself.
- let consensus_contract_deploy_payload = vec![];
- let native_contracts = vec![
- (
- "Money Contract",
- *MONEY_CONTRACT_ID,
- include_bytes!("../contract/money/darkfi_money_contract.wasm").to_vec(),
- money_contract_deploy_payload,
- ),
- (
- "DAO Contract",
- *DAO_CONTRACT_ID,
- include_bytes!("../contract/dao/darkfi_dao_contract.wasm").to_vec(),
- dao_contract_deploy_payload,
- ),
- (
- "Consensus Contract",
- *CONSENSUS_CONTRACT_ID,
- include_bytes!("../contract/consensus/darkfi_consensus_contract.wasm").to_vec(),
- consensus_contract_deploy_payload,
- ),
- ];
- for nc in native_contracts {
- info!(target: "validator::utils::deploy_native_contracts", "Deploying {} with ContractID {}", nc.0, nc.1);
- let mut runtime = Runtime::new(&nc.2[..], overlay.clone(), nc.1, time_keeper.clone())?;
- runtime.deploy(&nc.3)?;
- info!(target: "validator::utils::deploy_native_contracts", "Successfully deployed {}", nc.0);
- }
- info!(target: "validator::utils::deploy_native_contracts", "Finished deployment of native WASM contracts");
- Ok(())
- }
- /// Compute a block's rank, assuming the its valid.
- /// Genesis block has rank 0.
- /// First 2 blocks rank is equal to their nonce, since their previous
- /// previous block producer doesn't exist or have a VRF.
- pub async fn block_rank(
- block: &BlockInfo,
- previous_previous: &BlockInfo,
- pos_testing_mode: bool,
- ) -> Result<u64> {
- // Genesis block has rank 0
- if block.header.height == 0 {
- return Ok(0)
- }
- // Compute nonce u64
- let mut nonce = [0u8; 8];
- nonce.copy_from_slice(&block.header.nonce.to_repr()[..8]);
- let nonce = u64::from_be_bytes(nonce);
- // First 2 blocks or testing ones have rank equal to their nonce
- if block.header.height < 3 || pos_testing_mode {
- return Ok(nonce)
- }
- // Extract VRF proof from the previous previous producer transaction
- let tx = previous_previous.txs.last().unwrap();
- let data = &tx.calls[0].data.data;
- let position = match previous_previous.header.version {
- // PoW uses MoneyPoWRewardParamsV1
- 1 => 563,
- // PoS uses ConsensusProposalParamsV1
- 2 => 490,
- _ => return Err(Error::BlockVersionIsInvalid(previous_previous.header.version)),
- };
- let mut decoder = Cursor::new(&data);
- decoder.set_position(position);
- let vrf_proof: VrfProof = AsyncDecodable::decode_async(&mut decoder).await?;
- // Compute VRF u64
- let mut vrf = [0u8; 64];
- vrf[..blake3::OUT_LEN].copy_from_slice(vrf_proof.hash_output().as_bytes());
- let vrf_pallas = pallas::Base::from_uniform_bytes(&vrf);
- let mut vrf = [0u8; 8];
- vrf.copy_from_slice(&vrf_pallas.to_repr()[..8]);
- let vrf = u64::from_be_bytes(vrf);
- // Finally, compute the rank
- let rank = nonce % vrf;
- Ok(rank)
- }
- /// Auxiliary function to calculate the middle value between provided u64 numbers
- pub fn get_mid(a: u64, b: u64) -> u64 {
- (a / 2) + (b / 2) + ((a - 2 * (a / 2)) + (b - 2 * (b / 2))) / 2
- }
- /// Auxiliary function to calculate the median of a given `Vec<u64>`.
- /// The function sorts the vector internally.
- pub fn median(mut v: Vec<u64>) -> u64 {
- if v.len() == 1 {
- return v[0]
- }
- let n = v.len() / 2;
- v.sort_unstable();
- if v.len() % 2 == 0 {
- v[n]
- } else {
- get_mid(v[n - 1], v[n])
- }
- }
- /// Auxiliary function to calculate the total amount of minted tokens in provided
- /// genesis transactions set. This includes both staked and normal tokens.
- /// If a non-genesis transaction is found, execution fails.
- /// Set must also include the genesis transaction(empty) at last position.
- pub async fn genesis_txs_total(txs: &[Transaction]) -> Result<u64> {
- let mut total = 0;
- if txs.is_empty() {
- return Ok(total)
- }
- // Iterate transactions, exluding producer(last) one
- for tx in &txs[..txs.len() - 1] {
- // Transaction must contain a single Consensus::GenesisStake (0x00)
- // or Money::GenesisMint (0x01) call
- if tx.calls.len() != 1 {
- return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
- }
- let call = &tx.calls[0];
- let data = &call.data.data;
- let function = data[0];
- if !(call.data.contract_id == *CONSENSUS_CONTRACT_ID ||
- call.data.contract_id == *MONEY_CONTRACT_ID) ||
- (call.data.contract_id == *CONSENSUS_CONTRACT_ID && function != 0x00_u8) ||
- (call.data.contract_id == *MONEY_CONTRACT_ID && function != 0x01_u8)
- {
- return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
- }
- // Extract transaction input value.
- // Consensus::GenesisStake uses ConsensusGenesisStakeParamsV1, while
- // Money::GenesisMint uses MoneyGenesisMintParamsV1. Both params structs
- // have the value at same position (1).
- let position = 1;
- let mut decoder = Cursor::new(&data);
- decoder.set_position(position);
- let value: u64 = AsyncDecodable::decode_async(&mut decoder).await?;
- total += value;
- }
- let tx = txs.last().unwrap();
- if tx != &Transaction::default() {
- return Err(TxVerifyFailed::ErroneousTxs(vec![tx.clone()]).into())
- }
- Ok(total)
- }
- /// Retrieve previous slot producers, last proposal hashes,
- /// and their second to last hashes, from all provided forks.
- pub fn previous_slot_info(
- forks: &Vec<Fork>,
- slot: u64,
- ) -> Result<(u64, Vec<blake3::Hash>, Vec<blake3::Hash>)> {
- let mut producers = 0;
- let mut last_hashes = vec![];
- let mut second_to_last_hashes = vec![];
- for fork in forks {
- let last_proposal = fork.last_proposal()?;
- if last_proposal.block.header.height == slot {
- producers += 1;
- }
- last_hashes.push(last_proposal.hash);
- second_to_last_hashes.push(last_proposal.block.header.previous);
- }
- Ok((producers, last_hashes, second_to_last_hashes))
- }
- /// Given a proposal, find the index of the fork chain it extends, along with the specific
- /// extended proposal index.
- pub fn find_extended_fork_index(forks: &[Fork], proposal: &Proposal) -> Result<(usize, usize)> {
- for (f_index, fork) in forks.iter().enumerate() {
- // Traverse fork proposals sequence in reverse
- for (p_index, p_hash) in fork.proposals.iter().enumerate().rev() {
- if &proposal.block.header.previous == p_hash {
- return Ok((f_index, p_index))
- }
- }
- }
- Err(Error::ExtendedChainIndexNotFound)
- }
- /// Auxiliary function to find best ranked forks indexes.
- pub fn best_forks_indexes(forks: &[Fork]) -> Result<Vec<usize>> {
- // Check if node has any forks
- if forks.is_empty() {
- return Err(Error::ForksNotFound)
- }
- // Find the best ranked forks
- let mut best = 0;
- let mut indexes = vec![];
- for (f_index, fork) in forks.iter().enumerate() {
- let rank = fork.rank;
- // Fork ranks lower that current best
- if rank < best {
- continue
- }
- // Fork has same rank as current best
- if rank == best {
- indexes.push(f_index);
- continue
- }
- // Fork ranks higher that current best
- best = rank;
- indexes = vec![f_index];
- }
- Ok(indexes)
- }
|