<commit_before>use std::time::{SystemTime, UNIX_EPOCH};
use parking_lot::Mutex;
use std::str::FromStr;
use std::sync::Arc;
use std::path::*;
use log::info;

pub mod block;
pub mod index;
pub mod store;

pub use self::store::BlockStore;
pub use self::index::Indexer;
pub use self::block::*;

use crate::asset::{self, Asset, AssetSymbol, Balance, EMPTY_GOLD};
use crate::constants;
use crate::crypto::*;
use crate::tx::*;

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>
}

#[derive(Clone, Debug)]
pub struct Properties {
    pub height: u64,
    pub token_supply: Balance,
    pub network_fee: Balance
}

impl Blockchain {

    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Blockchain {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store)
        }
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self.get_network_fee().expect("unexpected error retrieving network fee")
        }
    }

    #[inline(always)]
    pub fn get_bond(&self, minter: &PublicKey) -> Option<BondTx> {
        self.indexer.get_bond(minter)
    }

    #[inline(always)]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_total_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        let net_fee = self.get_network_fee()?;
        let addr_fee = self.get_address_fee(hash)?;
        Some(Balance {
            gold: net_fee.gold.add(&addr_fee.gold)?,
            silver: net_fee.silver.add(&addr_fee.silver)?
        })
    }

    pub fn get_address_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        use crate::constants::*;
        let mut delta = 0;
        let mut tx_count = 1;

        let head = self.get_chain_height();
        for i in (0..=head).rev() {
            delta += 1;
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                let has_match = match tx {
                    TxVariant::RewardTx(_) => { false },
                    TxVariant::BondTx(tx) => { &tx.staker == hash },
                    TxVariant::TransferTx(tx) => { &tx.from == hash }
                };
                if has_match { tx_count += 1; }
            }
            if delta == FEE_RESET_WINDOW { break; }
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        Some(Balance { gold, silver })
    }

    pub fn get_network_fee(&self) -> Option<Balance> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        if tx_count > u64::from(u16::max_value()) { return None }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;

        Some(Balance { gold, silver })
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Balance {
        self.indexer.get_balance(hash).unwrap_or_default()
    }

    pub fn get_balance_with_txs(&self, hash: &ScriptHash, txs: &[TxVariant]) -> Option<Balance> {
        let mut bal = self.indexer.get_balance(hash).unwrap_or_default();
        for tx in txs {
            match tx {
                TxVariant::RewardTx(tx) => {
                    if &tx.to == hash {
                        for reward in &tx.rewards {
                            bal.add(&reward)?;
                        }
                    }
                },
                TxVariant::BondTx(tx) => {
                    if &tx.staker == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.bond_fee)?;
                        bal.sub(&tx.stake_amt)?;
                    }
                },
                TxVariant::TransferTx(tx) => {
                    if &tx.from == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.amount)?;
                    } else if &tx.to == hash {
                        bal.add(&tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), String> {
        self.verify_block(&block, &self.get_chain_head())?;
        for tx in &block.transactions { self.index_tx(tx); }
        self.store.lock().insert(block);

        Ok(())
    }

    fn verify_block(&self, block: &SignedBlock, prev_block: &SignedBlock) -> Result<(), String> {
        if prev_block.height + 1 != block.height {
            return Err("invalid block height".to_owned())
        } else if !block.verify_tx_merkle_root() {
            return Err("invalid merkle root".to_owned())
        } else if !block.verify_previous_hash(prev_block) {
            return Err("invalid previous hash".to_owned())
        }

        if self.indexer.get_bond(&block.sig_pair.pub_key).is_none() {
            return Err("bond not found".to_owned())
        } else if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err("invalid bond signature".to_owned())
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(s) = self.verify_tx(tx, txs) {
                return Err(format!("tx verification failed: {}", s))
            }
        }

        Ok(())
    }

    pub fn verify_tx(&self, tx: &TxVariant, additional_txs: &[TxVariant]) -> Result<(), String> {
        macro_rules! check_amt {
            ($asset:expr, $name:expr) => {
                if $asset.amount < 0 { return Err(format!("{} must be greater than 0", $name)) }
            }
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 { return Err("insufficient balance".to_owned()) }
            }
        }

        check_amt!(tx.fee, "fee");
        if tx.tx_type != TxType::REWARD {
            if tx.signature_pairs.len() != 1 {
                return Err("must have 1 signature".to_owned())
            }

            let mut v = Vec::with_capacity(4096);
            tx.encode(&mut v);
            if !tx.signature_pairs[0].verify(&v) {
                return Err("signature verification failed".to_owned())
            }
        }

        match tx {
            TxVariant::RewardTx(tx) => {
                if !tx.signature_pairs.is_empty() {
                    return Err("reward transaction must not be signed".to_owned())
                }
            },
            TxVariant::BondTx(tx) => {
                if !(tx.bond_fee.symbol == AssetSymbol::GOLD
                        && tx.stake_amt.symbol == AssetSymbol::GOLD
                        && tx.fee.symbol == AssetSymbol::GOLD) {
                    return Err("fees and stake amount must be in gold".to_owned())
                }
                check_amt!(&tx.bond_fee, "bond_fee");
                check_amt!(&tx.stake_amt, "stake_amt");

                if tx.bond_fee.lt(&constants::BOND_FEE).unwrap() {
                    return Err("bond_fee is too small".to_owned())
                }

                let mut bal = self.get_balance_with_txs(&tx.staker, additional_txs).ok_or_else(|| {
                    "failed to get balance"
                })?;
                bal.sub(&tx.fee).ok_or("failed to subtract fee")?
                    .sub(&tx.bond_fee).ok_or("failed to subtract bond_fee")?
                    .sub(&tx.stake_amt).ok_or("failed to subtract stake_amt")?;
                check_suf_bal!(bal.gold);
            },
            TxVariant::TransferTx(tx) => {
                if tx.fee.symbol != tx.amount.symbol {
                    return Err("symbol mismatch between fee and amount".to_owned())
                }
                let mut bal = self.get_balance_with_txs(&tx.from, additional_txs).ok_or_else(|| {
                    "failed to get balance"
                })?;
                bal.sub(&tx.fee).ok_or("failed to subtract fee")?
                    .sub(&tx.amount).ok_or("failed to subtract amount")?;
                check_suf_bal!(&bal.gold);
                check_suf_bal!(&bal.silver);
            }
        }
        Ok(())
    }

    fn index_tx(&self, tx: &TxVariant) {
        match tx {
            TxVariant::RewardTx(tx) => {
                let mut bal = self.get_balance(&tx.to);
                let mut supply = self.indexer.get_token_supply();
                for r in &tx.rewards {
                    bal.add(r).unwrap();
                    supply.add(r).unwrap();
                }
                self.indexer.set_balance(&tx.to, &bal);
                self.indexer.set_token_supply(&supply);
            },
            TxVariant::BondTx(tx) => {
                let mut bal = self.get_balance(&tx.staker);
                bal.sub(&tx.fee).unwrap()
                    .sub(&tx.bond_fee).unwrap()
                    .sub(&tx.stake_amt).unwrap();
                self.indexer.set_balance(&tx.staker, &bal);
                self.indexer.set_bond(tx);

                let mut supply = self.indexer.get_token_supply();
                supply.sub(&tx.bond_fee).unwrap();
                self.indexer.set_token_supply(&supply);
            },
            TxVariant::TransferTx(tx) => {
                let mut from_bal = self.get_balance(&tx.from);
                let mut to_bal = self.get_balance(&tx.to);

                from_bal.sub(&tx.fee).unwrap()
                        .sub(&tx.amount).unwrap();
                to_bal.add(&tx.amount).unwrap();

                self.indexer.set_balance(&tx.from, &from_bal);
                self.indexer.set_balance(&tx.to, &to_bal);
            }
        }
    }

    ///
    /// Create and store a generated genesis block. By default the provided
    /// `minter_key` will be the genesis minter and will stake with a small
    /// amount of GOLD tokens.
    ///
    pub fn create_genesis_block(&self, minter_key: &KeyPair) {
        use sodiumoxide::crypto::hash::sha256::Digest;

        info!("=> Generating new block chain");
        let staker_key = KeyPair::gen_keypair();
        info!("=> Staker private key: {}", staker_key.1.to_wif());
        info!("=> Staker public key: {}", staker_key.0.to_wif());

        let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32;

        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                fee: Asset::from_str("0 GOLD").unwrap(),
                timestamp,
                signature_pairs: Vec::new()
            },
            minter: minter_key.0.clone(),
            staker: staker_key.0.clone().into(),
            bond_fee: EMPTY_GOLD,
            stake_amt: Asset::from_str("1 GOLD").unwrap()
        };

        let transactions = {
            let mut vec = Vec::with_capacity(2);
            vec.push(TxVariant::RewardTx(RewardTx {
                base: Tx {
                    tx_type: TxType::REWARD,
                    fee: Asset::from_str("0 GOLD").unwrap(),
                    timestamp,
                    signature_pairs: Vec::new()
                },
                to: staker_key.0.into(),
                rewards: vec![Asset::from_str("1 GOLD").unwrap()]
            }));
            vec.push(TxVariant::BondTx(bond_tx.clone()));
            vec
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp: timestamp as u32,
            transactions
        }).sign(&minter_key);

        self.store.lock().insert_genesis(block);
        self.indexer.set_bond(&bond_tx);
    }
}
<commit_msg>Validate scripts in the transfer tx<commit_after>use std::time::{SystemTime, UNIX_EPOCH};
use parking_lot::Mutex;
use std::str::FromStr;
use std::sync::Arc;
use std::path::*;
use log::info;

pub mod block;
pub mod index;
pub mod store;

pub use self::store::BlockStore;
pub use self::index::Indexer;
pub use self::block::*;

use crate::asset::{self, Asset, AssetSymbol, Balance, EMPTY_GOLD};
use crate::script::*;
use crate::constants;
use crate::crypto::*;
use crate::tx::*;

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>
}

#[derive(Clone, Debug)]
pub struct Properties {
    pub height: u64,
    pub token_supply: Balance,
    pub network_fee: Balance
}

impl Blockchain {

    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Blockchain {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store)
        }
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self.get_network_fee().expect("unexpected error retrieving network fee")
        }
    }

    #[inline(always)]
    pub fn get_bond(&self, minter: &PublicKey) -> Option<BondTx> {
        self.indexer.get_bond(minter)
    }

    #[inline(always)]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_total_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        let net_fee = self.get_network_fee()?;
        let addr_fee = self.get_address_fee(hash)?;
        Some(Balance {
            gold: net_fee.gold.add(&addr_fee.gold)?,
            silver: net_fee.silver.add(&addr_fee.silver)?
        })
    }

    pub fn get_address_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        use crate::constants::*;
        let mut delta = 0;
        let mut tx_count = 1;

        let head = self.get_chain_height();
        for i in (0..=head).rev() {
            delta += 1;
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                let has_match = match tx {
                    TxVariant::RewardTx(_) => { false },
                    TxVariant::BondTx(tx) => { &tx.staker == hash },
                    TxVariant::TransferTx(tx) => { &tx.from == hash }
                };
                if has_match { tx_count += 1; }
            }
            if delta == FEE_RESET_WINDOW { break; }
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        Some(Balance { gold, silver })
    }

    pub fn get_network_fee(&self) -> Option<Balance> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        if tx_count > u64::from(u16::max_value()) { return None }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;

        Some(Balance { gold, silver })
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Balance {
        self.indexer.get_balance(hash).unwrap_or_default()
    }

    pub fn get_balance_with_txs(&self, hash: &ScriptHash, txs: &[TxVariant]) -> Option<Balance> {
        let mut bal = self.indexer.get_balance(hash).unwrap_or_default();
        for tx in txs {
            match tx {
                TxVariant::RewardTx(tx) => {
                    if &tx.to == hash {
                        for reward in &tx.rewards {
                            bal.add(&reward)?;
                        }
                    }
                },
                TxVariant::BondTx(tx) => {
                    if &tx.staker == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.bond_fee)?;
                        bal.sub(&tx.stake_amt)?;
                    }
                },
                TxVariant::TransferTx(tx) => {
                    if &tx.from == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.amount)?;
                    } else if &tx.to == hash {
                        bal.add(&tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), String> {
        self.verify_block(&block, &self.get_chain_head())?;
        for tx in &block.transactions { self.index_tx(tx); }
        self.store.lock().insert(block);

        Ok(())
    }

    fn verify_block(&self, block: &SignedBlock, prev_block: &SignedBlock) -> Result<(), String> {
        if prev_block.height + 1 != block.height {
            return Err("invalid block height".to_owned())
        } else if !block.verify_tx_merkle_root() {
            return Err("invalid merkle root".to_owned())
        } else if !block.verify_previous_hash(prev_block) {
            return Err("invalid previous hash".to_owned())
        }

        if self.indexer.get_bond(&block.sig_pair.pub_key).is_none() {
            return Err("bond not found".to_owned())
        } else if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err("invalid bond signature".to_owned())
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(s) = self.verify_tx(tx, txs) {
                return Err(format!("tx verification failed: {}", s))
            }
        }

        Ok(())
    }

    pub fn verify_tx(&self, tx: &TxVariant, additional_txs: &[TxVariant]) -> Result<(), String> {
        macro_rules! check_amt {
            ($asset:expr, $name:expr) => {
                if $asset.amount < 0 { return Err(format!("{} must be greater than 0", $name)) }
            }
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 { return Err("insufficient balance".to_owned()) }
            }
        }

        check_amt!(tx.fee, "fee");
        if tx.tx_type != TxType::REWARD {
            if tx.signature_pairs.len() != 1 {
                return Err("must have 1 signature".to_owned())
            }

            let mut v = Vec::with_capacity(4096);
            tx.encode(&mut v);
            if !tx.signature_pairs[0].verify(&v) {
                return Err("signature verification failed".to_owned())
            }
        }

        match tx {
            TxVariant::RewardTx(tx) => {
                if !tx.signature_pairs.is_empty() {
                    return Err("reward transaction must not be signed".to_owned())
                }
            },
            TxVariant::BondTx(tx) => {
                if !(tx.bond_fee.symbol == AssetSymbol::GOLD
                        && tx.stake_amt.symbol == AssetSymbol::GOLD
                        && tx.fee.symbol == AssetSymbol::GOLD) {
                    return Err("fees and stake amount must be in gold".to_owned())
                }
                check_amt!(&tx.bond_fee, "bond_fee");
                check_amt!(&tx.stake_amt, "stake_amt");

                if tx.bond_fee.lt(&constants::BOND_FEE).unwrap() {
                    return Err("bond_fee is too small".to_owned())
                }

                let mut bal = self.get_balance_with_txs(&tx.staker, additional_txs).ok_or_else(|| {
                    "failed to get balance"
                })?;
                bal.sub(&tx.fee).ok_or("failed to subtract fee")?
                    .sub(&tx.bond_fee).ok_or("failed to subtract bond_fee")?
                    .sub(&tx.stake_amt).ok_or("failed to subtract stake_amt")?;
                check_suf_bal!(bal.gold);
            },
            TxVariant::TransferTx(tx) => {
                if tx.fee.symbol != tx.amount.symbol {
                    return Err("symbol mismatch between fee and amount".to_owned())
                } else if tx.from != ScriptHash::from(tx.script.clone()) {
                    return Err("from and script hash mismatch".to_owned())
                }

                let success = ScriptEngine::new(tx.script.clone()).ok_or_else(|| {
                    "failed to initialize script engine"
                })?.eval().map_err(|e| {
                    format!("{}: {:?}", e.pos, e.err)
                })?;
                if !success {
                    return Err("script returned false".to_owned())
                }

                let mut bal = self.get_balance_with_txs(&tx.from, additional_txs).ok_or_else(|| {
                    "failed to get balance"
                })?;
                bal.sub(&tx.fee).ok_or("failed to subtract fee")?
                    .sub(&tx.amount).ok_or("failed to subtract amount")?;
                check_suf_bal!(&bal.gold);
                check_suf_bal!(&bal.silver);
            }
        }
        Ok(())
    }

    fn index_tx(&self, tx: &TxVariant) {
        match tx {
            TxVariant::RewardTx(tx) => {
                let mut bal = self.get_balance(&tx.to);
                let mut supply = self.indexer.get_token_supply();
                for r in &tx.rewards {
                    bal.add(r).unwrap();
                    supply.add(r).unwrap();
                }
                self.indexer.set_balance(&tx.to, &bal);
                self.indexer.set_token_supply(&supply);
            },
            TxVariant::BondTx(tx) => {
                let mut bal = self.get_balance(&tx.staker);
                bal.sub(&tx.fee).unwrap()
                    .sub(&tx.bond_fee).unwrap()
                    .sub(&tx.stake_amt).unwrap();
                self.indexer.set_balance(&tx.staker, &bal);
                self.indexer.set_bond(tx);

                let mut supply = self.indexer.get_token_supply();
                supply.sub(&tx.bond_fee).unwrap();
                self.indexer.set_token_supply(&supply);
            },
            TxVariant::TransferTx(tx) => {
                let mut from_bal = self.get_balance(&tx.from);
                let mut to_bal = self.get_balance(&tx.to);

                from_bal.sub(&tx.fee).unwrap()
                        .sub(&tx.amount).unwrap();
                to_bal.add(&tx.amount).unwrap();

                self.indexer.set_balance(&tx.from, &from_bal);
                self.indexer.set_balance(&tx.to, &to_bal);
            }
        }
    }

    ///
    /// Create and store a generated genesis block. By default the provided
    /// `minter_key` will be the genesis minter and will stake with a small
    /// amount of GOLD tokens.
    ///
    pub fn create_genesis_block(&self, minter_key: &KeyPair) {
        use sodiumoxide::crypto::hash::sha256::Digest;

        info!("=> Generating new block chain");
        let staker_key = KeyPair::gen_keypair();
        info!("=> Staker private key: {}", staker_key.1.to_wif());
        info!("=> Staker public key: {}", staker_key.0.to_wif());

        let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as u32;

        let bond_tx = BondTx {
            base: Tx {
                tx_type: TxType::BOND,
                fee: Asset::from_str("0 GOLD").unwrap(),
                timestamp,
                signature_pairs: Vec::new()
            },
            minter: minter_key.0.clone(),
            staker: staker_key.0.clone().into(),
            bond_fee: EMPTY_GOLD,
            stake_amt: Asset::from_str("1 GOLD").unwrap()
        };

        let transactions = {
            let mut vec = Vec::with_capacity(2);
            vec.push(TxVariant::RewardTx(RewardTx {
                base: Tx {
                    tx_type: TxType::REWARD,
                    fee: Asset::from_str("0 GOLD").unwrap(),
                    timestamp,
                    signature_pairs: Vec::new()
                },
                to: staker_key.0.into(),
                rewards: vec![Asset::from_str("1 GOLD").unwrap()]
            }));
            vec.push(TxVariant::BondTx(bond_tx.clone()));
            vec
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp: timestamp as u32,
            transactions
        }).sign(&minter_key);

        self.store.lock().insert_genesis(block);
        self.indexer.set_bond(&bond_tx);
    }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;
use std::path::*;
use std::sync::Arc;

pub mod block;
pub mod index;
pub mod store;
pub mod verify;

pub use self::block::*;
pub use self::index::Indexer;
pub use self::store::BlockStore;

use crate::asset::{self, Balance};
use crate::crypto::*;
use crate::script::*;
use crate::tx::*;

#[derive(Clone, Debug)]
pub struct Properties {
    pub height: u64,
    pub owner: Box<OwnerTx>,
    pub token_supply: Balance,
    pub network_fee: Balance,
}

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>,
}

impl Blockchain {
    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Blockchain {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store),
        }
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            owner: Box::new(self.get_owner()),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self
                .get_network_fee()
                .expect("unexpected error retrieving network fee"),
        }
    }

    #[inline]
    pub fn get_owner(&self) -> OwnerTx {
        self.indexer
            .get_owner()
            .expect("Failed to retrieve owner from index")
    }

    #[inline]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_total_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        let net_fee = self.get_network_fee()?;
        let mut addr_fee = self.get_address_fee(hash)?;
        addr_fee.add_bal(&net_fee)?;
        Some(addr_fee)
    }

    pub fn get_address_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        use crate::constants::*;

        let mut tx_count = 1;
        let head = self.get_chain_height();
        for (delta, i) in (0..=head).rev().enumerate() {
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                let has_match = match tx {
                    TxVariant::OwnerTx(_) => false,
                    TxVariant::MintTx(_) => false,
                    TxVariant::RewardTx(_) => false,
                    TxVariant::TransferTx(tx) => &tx.from == hash,
                };
                if has_match {
                    tx_count += 1;
                }
            }
            if delta + 1 == FEE_RESET_WINDOW {
                break;
            }
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        Balance::from(gold, silver)
    }

    pub fn get_network_fee(&self) -> Option<Balance> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        tx_count /= NETWORK_FEE_AVG_WINDOW;
        if tx_count > u64::from(u16::max_value()) {
            return None;
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;

        Balance::from(gold, silver)
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Balance {
        self.indexer.get_balance(hash).unwrap_or_default()
    }

    pub fn get_balance_with_txs(&self, hash: &ScriptHash, txs: &[TxVariant]) -> Option<Balance> {
        let mut bal = self.indexer.get_balance(hash).unwrap_or_default();
        for tx in txs {
            match tx {
                TxVariant::OwnerTx(_) => {}
                TxVariant::MintTx(tx) => {
                    if &tx.to == hash {
                        bal.add_bal(&tx.amount)?;
                    }
                }
                TxVariant::RewardTx(tx) => {
                    if &tx.to == hash {
                        bal.add_bal(&tx.rewards)?;
                    }
                }
                TxVariant::TransferTx(tx) => {
                    if &tx.from == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.amount)?;
                    } else if &tx.to == hash {
                        bal.add(&tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), String> {
        static CONFIG: verify::Config = verify::Config { skip_reward: true };
        self.verify_block(&block, &self.get_chain_head(), CONFIG)?;
        for tx in &block.transactions {
            self.index_tx(tx);
        }
        self.store.lock().insert(block);

        Ok(())
    }

    fn verify_block(
        &self,
        block: &SignedBlock,
        prev_block: &SignedBlock,
        config: verify::Config,
    ) -> Result<(), String> {
        if prev_block.height + 1 != block.height {
            return Err("invalid block height".to_owned());
        } else if !block.verify_tx_merkle_root() {
            return Err("invalid merkle root".to_owned());
        } else if !block.verify_previous_hash(prev_block) {
            return Err("invalid previous hash".to_owned());
        }

        let owner = self.get_owner();
        if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err("invalid block signature".to_owned());
        } else if block.sig_pair.pub_key != owner.minter {
            return Err("invalid owner signature".to_owned());
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(s) = self.verify_tx(tx, txs, config) {
                return Err(format!("tx verification failed: {}", s));
            }
        }

        Ok(())
    }

    pub fn verify_tx(
        &self,
        tx: &TxVariant,
        additional_txs: &[TxVariant],
        config: verify::Config,
    ) -> Result<(), String> {
        macro_rules! check_amt {
            ($asset:expr, $name:expr) => {
                if $asset.amount < 0 {
                    return Err(format!("{} must be greater than 0", $name));
                }
            };
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 {
                    return Err("insufficient balance".to_owned());
                }
            };
        }

        if !(tx.tx_type == TxType::OWNER || tx.tx_type == TxType::MINT) {
            check_amt!(tx.fee, "fee");
        }

        match tx {
            TxVariant::OwnerTx(new_owner) => {
                let owner = self.get_owner();
                if owner.wallet != (&new_owner.script).into() {
                    return Err("script hash does not match previous wallet address".to_owned());
                }

                let success = ScriptEngine::checked_new(tx, &new_owner.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }
            }
            TxVariant::MintTx(mint_tx) => {
                let owner = self.get_owner();
                if owner.wallet != (&mint_tx.script).into() {
                    return Err("script hash does not match current wallet address".to_owned());
                }
                let success = ScriptEngine::checked_new(tx, &mint_tx.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }

                // Sanity check to ensure too many new coins can't be minted
                self.get_balance_with_txs(&mint_tx.to, additional_txs)
                    .ok_or_else(|| "failed to get balance")?
                    .add_bal(&mint_tx.amount)
                    .ok_or_else(|| "failed to add balance")?;

                self.indexer
                    .get_token_supply()
                    .add_bal(&mint_tx.amount)
                    .ok_or_else(|| "failed to add balance")?;
            }
            TxVariant::RewardTx(tx) => {
                if !config.skip_reward {
                    return Err("reward transactions are prohibited".to_owned());
                } else if !tx.signature_pairs.is_empty() {
                    return Err("reward transaction must not be signed".to_owned());
                }
            }
            TxVariant::TransferTx(transfer) => {
                if transfer.fee.symbol != transfer.amount.symbol {
                    return Err("symbol mismatch between fee and amount".to_owned());
                } else if transfer.from != (&transfer.script).into() {
                    return Err("from and script hash mismatch".to_owned());
                }

                let success = ScriptEngine::checked_new(tx, &transfer.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }

                let mut bal = self
                    .get_balance_with_txs(&transfer.from, additional_txs)
                    .ok_or_else(|| "failed to get balance")?;
                bal.sub(&transfer.fee)
                    .ok_or("failed to subtract fee")?
                    .sub(&transfer.amount)
                    .ok_or("failed to subtract amount")?;
                check_suf_bal!(bal.gold());
                check_suf_bal!(bal.silver());
            }
        }
        Ok(())
    }

    fn index_tx(&self, tx: &TxVariant) {
        match tx {
            TxVariant::OwnerTx(tx) => {
                self.indexer.set_owner(tx);
            }
            TxVariant::MintTx(tx) => {
                let mut supply = self.indexer.get_token_supply();
                supply.add_bal(&tx.amount).unwrap();
                self.indexer.set_token_supply(&supply);

                let mut bal = self.get_balance(&tx.to);
                bal.add_bal(&tx.amount).unwrap();
                self.indexer.set_balance(&tx.to, &bal);
            }
            TxVariant::RewardTx(tx) => {
                let mut bal = self.get_balance(&tx.to);
                bal.add_bal(&tx.rewards).unwrap();
                self.indexer.set_balance(&tx.to, &bal);
            }
            TxVariant::TransferTx(tx) => {
                let mut from_bal = self.get_balance(&tx.from);
                let mut to_bal = self.get_balance(&tx.to);

                from_bal.sub(&tx.fee).unwrap().sub(&tx.amount).unwrap();
                to_bal.add(&tx.amount).unwrap();

                self.indexer.set_balance(&tx.from, &from_bal);
                self.indexer.set_balance(&tx.to, &to_bal);
            }
        }
    }

    pub fn create_genesis_block(&self, minter_key: KeyPair) -> GenesisBlockInfo {
        use crate::crypto::Digest;
        use std::time::{SystemTime, UNIX_EPOCH};

        let info = GenesisBlockInfo::new(minter_key);

        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        let owner_tx = OwnerTx {
            base: Tx {
                tx_type: TxType::OWNER,
                fee: "0 GOLD".parse().unwrap(),
                timestamp,
                signature_pairs: Vec::new(),
            },
            minter: info.minter_key.0.clone(),
            wallet: (&info.script).into(),
            script: Builder::new().push(OpFrame::False).build(),
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp,
            transactions: vec![TxVariant::OwnerTx(owner_tx.clone())],
        })
        .sign(&info.minter_key);

        self.store.lock().insert_genesis(block);
        self.indexer.set_owner(&owner_tx);

        info
    }
}

pub struct GenesisBlockInfo {
    pub minter_key: KeyPair,
    pub wallet_keys: [KeyPair; 4],
    pub script: Script,
}

impl GenesisBlockInfo {
    pub fn new(minter_key: KeyPair) -> Self {
        let wallet_keys = [
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
        ];

        let script = Builder::new()
            .push(OpFrame::PubKey(wallet_keys[0].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[1].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[2].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[3].0.clone()))
            .push(OpFrame::OpCheckMultiSig(2, 4))
            .build();

        Self {
            minter_key,
            wallet_keys,
            script,
        }
    }
}
<commit_msg>Minor cleanup<commit_after>use parking_lot::Mutex;
use std::{path::*, sync::Arc};

pub mod block;
pub mod index;
pub mod store;
pub mod verify;

pub use self::{block::*, index::Indexer, store::BlockStore};

use crate::{
    asset::{self, Balance},
    crypto::*,
    script::*,
    tx::*,
};

#[derive(Clone, Debug)]
pub struct Properties {
    pub height: u64,
    pub owner: Box<OwnerTx>,
    pub token_supply: Balance,
    pub network_fee: Balance,
}

pub struct Blockchain {
    indexer: Arc<Indexer>,
    store: Mutex<BlockStore>,
}

impl Blockchain {
    ///
    /// Creates a new `Blockchain` with an associated indexer and backing
    /// storage is automatically created based on the given `path`.
    ///
    pub fn new(path: &Path) -> Blockchain {
        let indexer = Arc::new(Indexer::new(&Path::join(path, "index")));
        let store = BlockStore::new(&Path::join(path, "blklog"), Arc::clone(&indexer));
        Blockchain {
            indexer,
            store: Mutex::new(store),
        }
    }

    pub fn get_properties(&self) -> Properties {
        Properties {
            height: self.get_chain_height(),
            owner: Box::new(self.get_owner()),
            token_supply: self.indexer.get_token_supply(),
            network_fee: self
                .get_network_fee()
                .expect("unexpected error retrieving network fee"),
        }
    }

    #[inline]
    pub fn get_owner(&self) -> OwnerTx {
        self.indexer
            .get_owner()
            .expect("Failed to retrieve owner from index")
    }

    #[inline]
    pub fn get_chain_height(&self) -> u64 {
        self.indexer.get_chain_height()
    }

    pub fn get_chain_head(&self) -> Arc<SignedBlock> {
        let store = self.store.lock();
        let height = store.get_chain_height();
        store.get(height).expect("Failed to get blockchain head")
    }

    pub fn get_block(&self, height: u64) -> Option<Arc<SignedBlock>> {
        let store = self.store.lock();
        store.get(height)
    }

    pub fn get_total_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        let net_fee = self.get_network_fee()?;
        let mut addr_fee = self.get_address_fee(hash)?;
        addr_fee.add_bal(&net_fee)?;
        Some(addr_fee)
    }

    pub fn get_address_fee(&self, hash: &ScriptHash) -> Option<Balance> {
        use crate::constants::*;

        let mut tx_count = 1;
        let head = self.get_chain_height();
        for (delta, i) in (0..=head).rev().enumerate() {
            let block = self.get_block(i).unwrap();
            for tx in &block.transactions {
                let has_match = match tx {
                    TxVariant::OwnerTx(_) => false,
                    TxVariant::MintTx(_) => false,
                    TxVariant::RewardTx(_) => false,
                    TxVariant::TransferTx(tx) => &tx.from == hash,
                };
                if has_match {
                    tx_count += 1;
                }
            }
            if delta + 1 == FEE_RESET_WINDOW {
                break;
            }
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_MULT.pow(tx_count as u16, prec)?, prec)?;
        Balance::from(gold, silver)
    }

    pub fn get_network_fee(&self) -> Option<Balance> {
        // The network fee adjusts every 5 blocks so that users have a bigger time
        // frame to confirm the fee they want to spend without suddenly changing.
        use crate::constants::*;
        let max_height = self.get_chain_height();
        let max_height = max_height - (max_height % 5);
        let min_height = if max_height > NETWORK_FEE_AVG_WINDOW {
            max_height - NETWORK_FEE_AVG_WINDOW
        } else {
            0
        };

        let mut tx_count: u64 = 1;
        for i in min_height..=max_height {
            tx_count += self.get_block(i).unwrap().transactions.len() as u64;
        }
        tx_count /= NETWORK_FEE_AVG_WINDOW;
        if tx_count > u64::from(u16::max_value()) {
            return None;
        }

        let prec = asset::MAX_PRECISION;
        let gold = GOLD_FEE_MIN.mul(&GOLD_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;
        let silver = SILVER_FEE_MIN.mul(&SILVER_FEE_NET_MULT.pow(tx_count as u16, prec)?, prec)?;

        Balance::from(gold, silver)
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Balance {
        self.indexer.get_balance(hash).unwrap_or_default()
    }

    pub fn get_balance_with_txs(&self, hash: &ScriptHash, txs: &[TxVariant]) -> Option<Balance> {
        let mut bal = self.indexer.get_balance(hash).unwrap_or_default();
        for tx in txs {
            match tx {
                TxVariant::OwnerTx(_) => {}
                TxVariant::MintTx(tx) => {
                    if &tx.to == hash {
                        bal.add_bal(&tx.amount)?;
                    }
                }
                TxVariant::RewardTx(tx) => {
                    if &tx.to == hash {
                        bal.add_bal(&tx.rewards)?;
                    }
                }
                TxVariant::TransferTx(tx) => {
                    if &tx.from == hash {
                        bal.sub(&tx.fee)?;
                        bal.sub(&tx.amount)?;
                    } else if &tx.to == hash {
                        bal.add(&tx.amount)?;
                    }
                }
            }
        }

        Some(bal)
    }

    pub fn insert_block(&self, block: SignedBlock) -> Result<(), String> {
        static CONFIG: verify::Config = verify::Config { skip_reward: true };
        self.verify_block(&block, &self.get_chain_head(), CONFIG)?;
        for tx in &block.transactions {
            self.index_tx(tx);
        }
        self.store.lock().insert(block);

        Ok(())
    }

    fn verify_block(
        &self,
        block: &SignedBlock,
        prev_block: &SignedBlock,
        config: verify::Config,
    ) -> Result<(), String> {
        if prev_block.height + 1 != block.height {
            return Err("invalid block height".to_owned());
        } else if !block.verify_tx_merkle_root() {
            return Err("invalid merkle root".to_owned());
        } else if !block.verify_previous_hash(prev_block) {
            return Err("invalid previous hash".to_owned());
        }

        let owner = self.get_owner();
        if !block.sig_pair.verify(block.calc_hash().as_ref()) {
            return Err("invalid block signature".to_owned());
        } else if block.sig_pair.pub_key != owner.minter {
            return Err("invalid owner signature".to_owned());
        }

        let len = block.transactions.len();
        for i in 0..len {
            let tx = &block.transactions[i];
            let txs = &block.transactions[0..i];
            if let Err(s) = self.verify_tx(tx, txs, config) {
                return Err(format!("tx verification failed: {}", s));
            }
        }

        Ok(())
    }

    pub fn verify_tx(
        &self,
        tx: &TxVariant,
        additional_txs: &[TxVariant],
        config: verify::Config,
    ) -> Result<(), String> {
        macro_rules! check_amt {
            ($asset:expr, $name:expr) => {
                if $asset.amount < 0 {
                    return Err(format!("{} must be greater than 0", $name));
                }
            };
        }

        macro_rules! check_suf_bal {
            ($asset:expr) => {
                if $asset.amount < 0 {
                    return Err("insufficient balance".to_owned());
                }
            };
        }

        if !(tx.tx_type == TxType::OWNER || tx.tx_type == TxType::MINT) {
            check_amt!(tx.fee, "fee");
        }

        match tx {
            TxVariant::OwnerTx(new_owner) => {
                let owner = self.get_owner();
                if owner.wallet != (&new_owner.script).into() {
                    return Err("script hash does not match previous wallet address".to_owned());
                }

                let success = ScriptEngine::checked_new(tx, &new_owner.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }
            }
            TxVariant::MintTx(mint_tx) => {
                let owner = self.get_owner();
                if owner.wallet != (&mint_tx.script).into() {
                    return Err("script hash does not match current wallet address".to_owned());
                }
                let success = ScriptEngine::checked_new(tx, &mint_tx.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }

                // Sanity check to ensure too many new coins can't be minted
                self.get_balance_with_txs(&mint_tx.to, additional_txs)
                    .ok_or_else(|| "failed to get balance")?
                    .add_bal(&mint_tx.amount)
                    .ok_or_else(|| "failed to add balance")?;

                self.indexer
                    .get_token_supply()
                    .add_bal(&mint_tx.amount)
                    .ok_or_else(|| "failed to add balance")?;
            }
            TxVariant::RewardTx(tx) => {
                if !config.skip_reward {
                    return Err("reward transactions are prohibited".to_owned());
                } else if !tx.signature_pairs.is_empty() {
                    return Err("reward transaction must not be signed".to_owned());
                }
            }
            TxVariant::TransferTx(transfer) => {
                if transfer.fee.symbol != transfer.amount.symbol {
                    return Err("symbol mismatch between fee and amount".to_owned());
                } else if transfer.from != (&transfer.script).into() {
                    return Err("from and script hash mismatch".to_owned());
                }

                let success = ScriptEngine::checked_new(tx, &transfer.script)
                    .ok_or_else(|| "failed to initialize script engine")?
                    .eval()
                    .map_err(|e| format!("{}: {:?}", e.pos, e.err))?;
                if !success {
                    return Err("script returned false".to_owned());
                }

                let mut bal = self
                    .get_balance_with_txs(&transfer.from, additional_txs)
                    .ok_or_else(|| "failed to get balance")?;
                bal.sub(&transfer.fee)
                    .ok_or("failed to subtract fee")?
                    .sub(&transfer.amount)
                    .ok_or("failed to subtract amount")?;
                check_suf_bal!(bal.gold());
                check_suf_bal!(bal.silver());
            }
        }
        Ok(())
    }

    fn index_tx(&self, tx: &TxVariant) {
        match tx {
            TxVariant::OwnerTx(tx) => {
                self.indexer.set_owner(tx);
            }
            TxVariant::MintTx(tx) => {
                let mut supply = self.indexer.get_token_supply();
                supply.add_bal(&tx.amount).unwrap();
                self.indexer.set_token_supply(&supply);

                let mut bal = self.get_balance(&tx.to);
                bal.add_bal(&tx.amount).unwrap();
                self.indexer.set_balance(&tx.to, &bal);
            }
            TxVariant::RewardTx(tx) => {
                let mut bal = self.get_balance(&tx.to);
                bal.add_bal(&tx.rewards).unwrap();
                self.indexer.set_balance(&tx.to, &bal);
            }
            TxVariant::TransferTx(tx) => {
                let mut from_bal = self.get_balance(&tx.from);
                let mut to_bal = self.get_balance(&tx.to);

                from_bal.sub(&tx.fee).unwrap().sub(&tx.amount).unwrap();
                to_bal.add(&tx.amount).unwrap();

                self.indexer.set_balance(&tx.from, &from_bal);
                self.indexer.set_balance(&tx.to, &to_bal);
            }
        }
    }

    pub fn create_genesis_block(&self, minter_key: KeyPair) -> GenesisBlockInfo {
        use crate::crypto::Digest;
        use std::time::{SystemTime, UNIX_EPOCH};

        let info = GenesisBlockInfo::new(minter_key);

        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        let owner_tx = OwnerTx {
            base: Tx {
                tx_type: TxType::OWNER,
                fee: "0 GOLD".parse().unwrap(),
                timestamp,
                signature_pairs: Vec::new(),
            },
            minter: info.minter_key.0.clone(),
            wallet: (&info.script).into(),
            script: Builder::new().push(OpFrame::False).build(),
        };

        let block = (Block {
            height: 0,
            previous_hash: Digest::from_slice(&[0u8; 32]).unwrap(),
            tx_merkle_root: Digest::from_slice(&[0u8; 32]).unwrap(),
            timestamp,
            transactions: vec![TxVariant::OwnerTx(owner_tx.clone())],
        })
        .sign(&info.minter_key);

        self.store.lock().insert_genesis(block);
        self.indexer.set_owner(&owner_tx);

        info
    }
}

pub struct GenesisBlockInfo {
    pub minter_key: KeyPair,
    pub wallet_keys: [KeyPair; 4],
    pub script: Script,
}

impl GenesisBlockInfo {
    pub fn new(minter_key: KeyPair) -> Self {
        let wallet_keys = [
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
            KeyPair::gen_keypair(),
        ];

        let script = Builder::new()
            .push(OpFrame::PubKey(wallet_keys[0].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[1].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[2].0.clone()))
            .push(OpFrame::PubKey(wallet_keys[3].0.clone()))
            .push(OpFrame::OpCheckMultiSig(2, 4))
            .build();

        Self {
            minter_key,
            wallet_keys,
            script,
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The bulk of the HTML parser integration is in `script::parse::html`.
//! This module is mostly about its interaction with DOM memory management.

use document_loader::LoadType;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ServoHTMLParserBinding;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::Document;
use dom::node::{Node, window_from_node};
use dom::text::Text;
use dom::window::Window;
use encoding::all::UTF_8;
use encoding::types::{DecoderTrap, Encoding};
use html5ever::tokenizer;
use html5ever::tree_builder;
use html5ever::tree_builder::{NodeOrText, TreeBuilder, TreeBuilderOpts};
use hyper::header::ContentType;
use hyper::mime::{Mime, SubLevel, TopLevel};
use js::jsapi::JSTracer;
use msg::constellation_msg::{PipelineId, SubpageId};
use net_traits::{AsyncResponseListener, Metadata};
use network_listener::PreInvoke;
use parse::Parser;
use script_task::{ScriptChan, ScriptTask};
use std::cell::Cell;
use std::default::Default;
use url::Url;

#[must_root]
#[derive(JSTraceable, HeapSizeOf)]
pub struct Sink {
    pub base_url: Option<Url>,
    pub document: JS<Document>,
}

impl Sink {
    #[allow(unrooted_must_root)] // method is only run at parse time
    pub fn get_or_create(&self, child: NodeOrText<JS<Node>>) -> Root<Node> {
        match child {
            NodeOrText::AppendNode(n) => n.root(),
            NodeOrText::AppendText(t) => {
                let text = Text::new(t.into(), &self.document);
                NodeCast::from_root(text)
            }
        }
    }
}

/// FragmentContext is used only to pass this group of related values
/// into functions.
#[derive(Copy, Clone)]
pub struct FragmentContext<'a> {
    pub context_elem: &'a Node,
    pub form_elem: Option<&'a Node>,
}

pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<JS<Node>, Sink>>;

/// The context required for asynchronously fetching a document and parsing it progressively.
pub struct ParserContext {
    /// The parser that initiated the request.
    parser: Option<Trusted<ServoHTMLParser>>,
    /// Is this document a synthesized document for a single image?
    is_image_document: bool,
    /// The pipeline associated with this document.
    id: PipelineId,
    /// The subpage associated with this document.
    subpage: Option<SubpageId>,
    /// The target event loop for the response notifications.
    script_chan: Box


<commit_before>mod object;
mod pointer;

pub use self::{
    object::{Builtin, Closure, Data, Int, Object, Struct, Symbol, Text},
    pointer::Pointer,
};
use crate::builtin_functions::BuiltinFunction;
use itertools::Itertools;
use num_bigint::BigInt;
use std::{cmp::Ordering, collections::HashMap};

#[derive(Clone)]
pub struct Heap {
    objects: HashMap<Pointer, Object>,
    next_address: Pointer,
}

impl std::fmt::Debug for Heap {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut objects = self.objects.clone().into_iter().collect_vec();
        objects.sort_by_key(|(address, _)| address.raw());

        writeln!(f, "{{")?;
        for (address, object) in objects {
            writeln!(
                f,
                "{address}: {} {}",
                object.reference_count,
                address.format(self),
            )?;
        }
        write!(f, "}}")
    }
}

impl Default for Heap {
    fn default() -> Self {
        Self {
            objects: HashMap::new(),
            next_address: Pointer::from_raw(1),
        }
    }
}
impl Heap {
    pub fn get(&self, address: Pointer) -> &Object {
        self.objects
            .get(&address)
            .unwrap_or_else(|| panic!("Couldn't get object {address}."))
    }
    pub fn get_mut(&mut self, address: Pointer) -> &mut Object {
        self.objects
            .get_mut(&address)
            .unwrap_or_else(|| panic!("Couldn't get object {address}."))
    }

    pub fn dup(&mut self, address: Pointer) {
        self.get_mut(address).reference_count += 1;
        log::trace!(
            "RefCount of {address} increased to {}. Value: {}",
            self.get(address).reference_count,
            address.format(self),
        );
    }
    pub fn drop(&mut self, address: Pointer) {
        let formatted_value = address.format(self);
        let object = self.get_mut(address);
        object.reference_count -= 1;
        log::trace!(
            "RefCount of {address} reduced to {}. Value: {formatted_value}",
            object.reference_count,
        );
        if object.reference_count == 0 {
            self.free(address);
        }
    }

    pub fn create(&mut self, object: Data) -> Pointer {
        let address = self.next_address;
        self.objects.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        log::trace!("Created object {} at {address}.", address.format(self));
        self.next_address = Pointer::from_raw(self.next_address.raw() + 1);
        address
    }
    pub fn free(&mut self, address: Pointer) {
        let object = self.objects.remove(&address).unwrap();
        log::trace!("Freeing object at {address}.");
        assert_eq!(object.reference_count, 0);
        for child in object.children() {
            self.drop(child);
        }
    }

    /// Clones all objects at the `root_addresses` into the `other` heap and
    /// returns a list of their addresses in the other heap.
    pub fn clone_multiple_to_other_heap(
        &self,
        other: &mut Heap,
        addresses: &[Pointer],
    ) -> Vec<Pointer> {
        let mut objects_to_refcounts = HashMap::new();
        for address in addresses {
            self.gather_objects_to_clone(&mut objects_to_refcounts, *address);
        }
        let num_objects = objects_to_refcounts.len();

        let address_map: HashMap<Pointer, Pointer> = objects_to_refcounts
            .keys()
            .cloned()
            .zip(
                (other.next_address.raw()..other.next_address.raw() + num_objects)
                    .map(Pointer::from_raw),
            )
            .collect();

        for (address, refcount) in objects_to_refcounts {
            other.objects.insert(
                address_map[&address],
                Object {
                    reference_count: refcount,
                    data: Self::map_addresses_in_data(&address_map, &self.get(address).data),
                },
            );
        }
        other.next_address = Pointer::from_raw(other.next_address.raw() + num_objects);

        addresses
            .iter()
            .map(|address| address_map[address])
            .collect()
    }
    fn gather_objects_to_clone(
        &self,
        objects_to_refcounts: &mut HashMap<Pointer, usize>,
        address: Pointer,
    ) {
        *objects_to_refcounts.entry(address).or_default() += 1;
        for child in self.get(address).children() {
            self.gather_objects_to_clone(objects_to_refcounts, child);
        }
    }
    fn map_addresses_in_data(address_map: &HashMap<Pointer, Pointer>, data: &Data) -> Data {
        match data {
            Data::Int(int) => Data::Int(int.clone()),
            Data::Text(text) => Data::Text(text.clone()),
            Data::Symbol(symbol) => Data::Symbol(symbol.clone()),
            Data::Struct(struct_) => Data::Struct(Struct {
                fields: struct_
                    .fields
                    .iter()
                    .map(|(hash, key, value)| (*hash, address_map[key], address_map[value]))
                    .collect_vec(),
            }),
            Data::Closure(closure) => Data::Closure(Closure {
                captured: closure
                    .captured
                    .iter()
                    .map(|address| address_map[address])
                    .collect(),
                num_args: closure.num_args,
                body: closure.body.clone(),
            }),
            Data::Builtin(builtin) => Data::Builtin(builtin.clone()),
        }
    }
    pub fn clone_single_to_other_heap(&self, other: &mut Heap, address: Pointer) -> Pointer {
        self.clone_multiple_to_other_heap(other, &[address])
            .pop()
            .unwrap()
    }

    pub fn create_int(&mut self, int: BigInt) -> Pointer {
        self.create(Data::Int(Int { value: int }))
    }
    pub fn create_text(&mut self, text: String) -> Pointer {
        self.create(Data::Text(Text { value: text }))
    }
    pub fn create_symbol(&mut self, symbol: String) -> Pointer {
        self.create(Data::Symbol(Symbol { value: symbol }))
    }
    pub fn create_struct(&mut self, fields: HashMap<Pointer, Pointer>) -> Pointer {
        self.create(Data::Struct(Struct::from_fields(self, fields)))
    }
    pub fn create_closure(&mut self, closure: Closure) -> Pointer {
        self.create(Data::Closure(closure))
    }
    pub fn create_builtin(&mut self, builtin: BuiltinFunction) -> Pointer {
        self.create(Data::Builtin(Builtin { function: builtin }))
    }
    pub fn create_nothing(&mut self) -> Pointer {
        self.create_symbol("Nothing".to_string())
    }
    pub fn create_list(&mut self, items: Vec<Pointer>) -> Pointer {
        let mut fields = vec![];
        for (index, item) in items.into_iter().enumerate() {
            fields.push((self.create_int(index.into()), item));
        }
        self.create_struct(fields.into_iter().collect())
    }
    pub fn create_bool(&mut self, value: bool) -> Pointer {
        self.create_symbol(if value { "True" } else { "False" }.to_string())
    }
    pub fn create_result(&mut self, result: Result<Pointer, Pointer>) -> Pointer {
        let (type_, value) = match result {
            Ok(it) => ("Ok".to_string(), it),
            Err(it) => ("Error".to_string(), it),
        };
        let fields = HashMap::from([
            (
                self.create_symbol("Type".to_string()),
                self.create_symbol(type_),
            ),
            (self.create_symbol("Value".to_string()), value),
        ]);
        self.create_struct(fields)
    }
    pub fn create_ordering(&mut self, ordering: Ordering) -> Pointer {
        self.create_symbol(
            match ordering {
                Ordering::Less => "Less",
                Ordering::Equal => "Equal",
                Ordering::Greater => "Greater",
            }
            .to_string(),
        )
    }
}
<commit_msg>Use copied instead of cloned<commit_after>mod object;
mod pointer;

pub use self::{
    object::{Builtin, Closure, Data, Int, Object, Struct, Symbol, Text},
    pointer::Pointer,
};
use crate::builtin_functions::BuiltinFunction;
use itertools::Itertools;
use num_bigint::BigInt;
use std::{cmp::Ordering, collections::HashMap};

#[derive(Clone)]
pub struct Heap {
    objects: HashMap<Pointer, Object>,
    next_address: Pointer,
}

impl std::fmt::Debug for Heap {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut objects = self.objects.clone().into_iter().collect_vec();
        objects.sort_by_key(|(address, _)| address.raw());

        writeln!(f, "{{")?;
        for (address, object) in objects {
            writeln!(
                f,
                "{address}: {} {}",
                object.reference_count,
                address.format(self),
            )?;
        }
        write!(f, "}}")
    }
}

impl Default for Heap {
    fn default() -> Self {
        Self {
            objects: HashMap::new(),
            next_address: Pointer::from_raw(1),
        }
    }
}
impl Heap {
    pub fn get(&self, address: Pointer) -> &Object {
        self.objects
            .get(&address)
            .unwrap_or_else(|| panic!("Couldn't get object {address}."))
    }
    pub fn get_mut(&mut self, address: Pointer) -> &mut Object {
        self.objects
            .get_mut(&address)
            .unwrap_or_else(|| panic!("Couldn't get object {address}."))
    }

    pub fn dup(&mut self, address: Pointer) {
        self.get_mut(address).reference_count += 1;
        log::trace!(
            "RefCount of {address} increased to {}. Value: {}",
            self.get(address).reference_count,
            address.format(self),
        );
    }
    pub fn drop(&mut self, address: Pointer) {
        let formatted_value = address.format(self);
        let object = self.get_mut(address);
        object.reference_count -= 1;
        log::trace!(
            "RefCount of {address} reduced to {}. Value: {formatted_value}",
            object.reference_count,
        );
        if object.reference_count == 0 {
            self.free(address);
        }
    }

    pub fn create(&mut self, object: Data) -> Pointer {
        let address = self.next_address;
        self.objects.insert(
            address,
            Object {
                reference_count: 1,
                data: object,
            },
        );
        log::trace!("Created object {} at {address}.", address.format(self));
        self.next_address = Pointer::from_raw(self.next_address.raw() + 1);
        address
    }
    pub fn free(&mut self, address: Pointer) {
        let object = self.objects.remove(&address).unwrap();
        log::trace!("Freeing object at {address}.");
        assert_eq!(object.reference_count, 0);
        for child in object.children() {
            self.drop(child);
        }
    }

    /// Clones all objects at the `root_addresses` into the `other` heap and
    /// returns a list of their addresses in the other heap.
    pub fn clone_multiple_to_other_heap(
        &self,
        other: &mut Heap,
        addresses: &[Pointer],
    ) -> Vec<Pointer> {
        let mut objects_to_refcounts = HashMap::new();
        for address in addresses {
            self.gather_objects_to_clone(&mut objects_to_refcounts, *address);
        }
        let num_objects = objects_to_refcounts.len();

        let address_map: HashMap<Pointer, Pointer> = objects_to_refcounts
            .keys()
            .copied()
            .zip(
                (other.next_address.raw()..other.next_address.raw() + num_objects)
                    .map(Pointer::from_raw),
            )
            .collect();

        for (address, refcount) in objects_to_refcounts {
            other.objects.insert(
                address_map[&address],
                Object {
                    reference_count: refcount,
                    data: Self::map_addresses_in_data(&address_map, &self.get(address).data),
                },
            );
        }
        other.next_address = Pointer::from_raw(other.next_address.raw() + num_objects);

        addresses
            .iter()
            .map(|address| address_map[address])
            .collect()
    }
    fn gather_objects_to_clone(
        &self,
        objects_to_refcounts: &mut HashMap<Pointer, usize>,
        address: Pointer,
    ) {
        *objects_to_refcounts.entry(address).or_default() += 1;
        for child in self.get(address).children() {
            self.gather_objects_to_clone(objects_to_refcounts, child);
        }
    }
    fn map_addresses_in_data(address_map: &HashMap<Pointer, Pointer>, data: &Data) -> Data {
        match data {
            Data::Int(int) => Data::Int(int.clone()),
            Data::Text(text) => Data::Text(text.clone()),
            Data::Symbol(symbol) => Data::Symbol(symbol.clone()),
            Data::Struct(struct_) => Data::Struct(Struct {
                fields: struct_
                    .fields
                    .iter()
                    .map(|(hash, key, value)| (*hash, address_map[key], address_map[value]))
                    .collect_vec(),
            }),
            Data::Closure(closure) => Data::Closure(Closure {
                captured: closure
                    .captured
                    .iter()
                    .map(|address| address_map[address])
                    .collect(),
                num_args: closure.num_args,
                body: closure.body.clone(),
            }),
            Data::Builtin(builtin) => Data::Builtin(builtin.clone()),
        }
    }
    pub fn clone_single_to_other_heap(&self, other: &mut Heap, address: Pointer) -> Pointer {
        self.clone_multiple_to_other_heap(other, &[address])
            .pop()
            .unwrap()
    }

    pub fn create_int(&mut self, int: BigInt) -> Pointer {
        self.create(Data::Int(Int { value: int }))
    }
    pub fn create_text(&mut self, text: String) -> Pointer {
        self.create(Data::Text(Text { value: text }))
    }
    pub fn create_symbol(&mut self, symbol: String) -> Pointer {
        self.create(Data::Symbol(Symbol { value: symbol }))
    }
    pub fn create_struct(&mut self, fields: HashMap<Pointer, Pointer>) -> Pointer {
        self.create(Data::Struct(Struct::from_fields(self, fields)))
    }
    pub fn create_closure(&mut self, closure: Closure) -> Pointer {
        self.create(Data::Closure(closure))
    }
    pub fn create_builtin(&mut self, builtin: BuiltinFunction) -> Pointer {
        self.create(Data::Builtin(Builtin { function: builtin }))
    }
    pub fn create_nothing(&mut self) -> Pointer {
        self.create_symbol("Nothing".to_string())
    }
    pub fn create_list(&mut self, items: Vec<Pointer>) -> Pointer {
        let mut fields = vec![];
        for (index, item) in items.into_iter().enumerate() {
            fields.push((self.create_int(index.into()), item));
        }
        self.create_struct(fields.into_iter().collect())
    }
    pub fn create_bool(&mut self, value: bool) -> Pointer {
        self.create_symbol(if value { "True" } else { "False" }.to_string())
    }
    pub fn create_result(&mut self, result: Result<Pointer, Pointer>) -> Pointer {
        let (type_, value) = match result {
            Ok(it) => ("Ok".to_string(), it),
            Err(it) => ("Error".to_string(), it),
        };
        let fields = HashMap::from([
            (
                self.create_symbol("Type".to_string()),
                self.create_symbol(type_),
            ),
            (self.create_symbol("Value".to_string()), value),
        ]);
        self.create_struct(fields)
    }
    pub fn create_ordering(&mut self, ordering: Ordering) -> Pointer {
        self.create_symbol(
            match ordering {
                Ordering::Less => "Less",
                Ordering::Equal => "Equal",
                Ordering::Greater => "Greater",
            }
            .to_string(),
        )
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::sync::mpsc::channel;
use std::os;
use std::thread::Thread;

// This is a simple bench that creates M pairs of tasks. These
// tasks ping-pong back and forth over a pair of streams. This is a
// canonical message-passing benchmark as it heavily strains message
// passing and almost nothing else.

fn ping_pong_bench(n: uint, m: uint) {

    // Create pairs of tasks that pingpong back and forth.
    fn run_pair(n: uint) {
        // Create a channel: A->B
        let (atx, arx) = channel();
        // Create a channel: B->A
        let (btx, brx) = channel();

        Thread::spawn(move|| {
            let (tx, rx) = (atx, brx);
            for _ in range(0, n) {
                tx.send(()).unwrap();
                rx.recv().unwrap();
            }
        }).detach();

        Thread::spawn(move|| {
            let (tx, rx) = (btx, arx);
            for _ in range(0, n) {
                rx.recv().unwrap();
                tx.send(()).unwrap();
            }
        }).detach();
    }

    for _ in range(0, m) {
        run_pair(n)
    }
}

fn main() {

    let args = os::args();
    let args = args.as_slice();
    let n = if args.len() == 3 {
        args[1].parse::<uint>().unwrap()
    } else {
        10000
    };

    let m = if args.len() == 3 {
        args[2].parse::<uint>().unwrap()
    } else {
        4
    };

    ping_pong_bench(n, m);

}
<commit_msg>bench: do not quit rt-messaging-ping-pong.rs early<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::sync::mpsc::channel;
use std::os;
use std::thread::Thread;

// This is a simple bench that creates M pairs of tasks. These
// tasks ping-pong back and forth over a pair of streams. This is a
// canonical message-passing benchmark as it heavily strains message
// passing and almost nothing else.

fn ping_pong_bench(n: uint, m: uint) {

    // Create pairs of tasks that pingpong back and forth.
    fn run_pair(n: uint) {
        // Create a channel: A->B
        let (atx, arx) = channel();
        // Create a channel: B->A
        let (btx, brx) = channel();

        let guard_a = Thread::spawn(move|| {
            let (tx, rx) = (atx, brx);
            for _ in range(0, n) {
                tx.send(()).unwrap();
                rx.recv().unwrap();
            }
        });

        let guard_b = Thread::spawn(move|| {
            let (tx, rx) = (btx, arx);
            for _ in range(0, n) {
                rx.recv().unwrap();
                tx.send(()).unwrap();
            }
        });

        guard_a.join().ok();
        guard_b.join().ok();
    }

    for _ in range(0, m) {
        run_pair(n)
    }
}

fn main() {

    let args = os::args();
    let args = args.as_slice();
    let n = if args.len() == 3 {
        args[1].parse::<uint>().unwrap()
    } else {
        10000
    };

    let m = if args.len() == 3 {
        args[2].parse::<uint>().unwrap()
    } else {
        4
    };

    ping_pong_bench(n, m);

}
<|endoftext|>


<commit_before>use walkdir::WalkDir;
use std::path::PathBuf;
use std::{env, fs};

fn main() {
    // The src directory (we are in src/tools/error_index_generator)
    // Note that we could skip one of the .. but this ensures we at least loosely find the right
    // directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out_dir.join("error_codes.rs");

    let error_codes_path = "../../../src/librustc_error_codes/error_codes.rs";

    println!("cargo:rerun-if-changed={}", error_codes_path);
    let file = fs::read_to_string(error_codes_path).unwrap()
                  .replace("crate::register_diagnostics!", "register_diagnostics!")
                  .replace(": include_str!(\"./error_codes/", ": include_str!(\"./");
    let contents = format!("(|| {{\n{}\n}})()", file);
    fs::write(&out_dir.join("all_error_codes.rs"), &contents).unwrap();

    // We copy the md files as well to the target directory.
    for entry in WalkDir::new("../../../src/librustc_error_codes/error_codes") {
        let entry = entry.unwrap();
        match entry.path().extension() {
            Some(s) if s == "md" => {}
            _ => continue,
        }
        println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
        let md_content = fs::read_to_string(entry.path()).unwrap();
        fs::write(&out_dir.join(entry.file_name()), &md_content).unwrap();
    }

    let mut all = String::new();
    all.push_str(r###"
fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
    let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
    macro_rules! register_diagnostics {
        ($($ecode:ident: $message:expr,)*) => (
            register_diagnostics!{$($ecode:$message,)* ;}
        );

        ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
            $(
                {long_codes.extend([
                    (stringify!($ecode), Some($message)),
                ].iter());}
            )*
            $(
                {long_codes.extend([
                    stringify!($code),
                ].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());}
            )*
        )
    }
"###);
    all.push_str(r#"include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs"));"#);
    all.push_str("\nlong_codes\n");
    all.push_str("}\n");

    fs::write(&dest, all).unwrap();
}
<commit_msg>Rollup merge of #66678 - GuillaumeGomez:remove-useless-error-index-line, r=Dylan-DPC<commit_after>use walkdir::WalkDir;
use std::path::PathBuf;
use std::{env, fs};

fn main() {
    // The src directory (we are in src/tools/error_index_generator)
    // Note that we could skip one of the .. but this ensures we at least loosely find the right
    // directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let dest = out_dir.join("error_codes.rs");

    let error_codes_path = "../../../src/librustc_error_codes/error_codes.rs";

    println!("cargo:rerun-if-changed={}", error_codes_path);
    let file = fs::read_to_string(error_codes_path).unwrap()
                  .replace(": include_str!(\"./error_codes/", ": include_str!(\"./");
    let contents = format!("(|| {{\n{}\n}})()", file);
    fs::write(&out_dir.join("all_error_codes.rs"), &contents).unwrap();

    // We copy the md files as well to the target directory.
    for entry in WalkDir::new("../../../src/librustc_error_codes/error_codes") {
        let entry = entry.unwrap();
        match entry.path().extension() {
            Some(s) if s == "md" => {}
            _ => continue,
        }
        println!("cargo:rerun-if-changed={}", entry.path().to_str().unwrap());
        let md_content = fs::read_to_string(entry.path()).unwrap();
        fs::write(&out_dir.join(entry.file_name()), &md_content).unwrap();
    }

    let mut all = String::new();
    all.push_str(r###"
fn register_all() -> Vec<(&'static str, Option<&'static str>)> {
    let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new();
    macro_rules! register_diagnostics {
        ($($ecode:ident: $message:expr,)*) => (
            register_diagnostics!{$($ecode:$message,)* ;}
        );

        ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => (
            $(
                {long_codes.extend([
                    (stringify!($ecode), Some($message)),
                ].iter());}
            )*
            $(
                {long_codes.extend([
                    stringify!($code),
                ].iter().cloned().map(|s| (s, None)).collect::<Vec<_>>());}
            )*
        )
    }
"###);
    all.push_str(r#"include!(concat!(env!("OUT_DIR"), "/all_error_codes.rs"));"#);
    all.push_str("\nlong_codes\n");
    all.push_str("}\n");

    fs::write(&dest, all).unwrap();
}
<|endoftext|>