diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2062342..74feefd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -128,7 +128,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: llvm-cov - args: --no-default-features --features=full,feat-ibc,testnet,emergency-disbursal --workspace --lcov --output-path lcov.info + args: --no-default-features --features=full,feat-ibc,testnet --workspace --lcov --output-path lcov.info - name: Upload to codecov.io uses: codecov/codecov-action@v1 with: @@ -177,4 +177,4 @@ jobs: uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - args: --no-default-features --features=full,feat-ibc,testnet,emergency-disbursal -- -D warnings + args: --no-default-features --features=full,feat-ibc,testnet -- -D warnings diff --git a/Cargo.toml b/Cargo.toml index e217774a..6622fa45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,6 @@ full = [ feat-ibc = ["orga/feat-ibc"] testnet = [] devnet = [] -emergency-disbursal = [] legacy-bin = [] [profile.release] diff --git a/src/app.rs b/src/app.rs index 6ed1c990..8ecc18a6 100644 --- a/src/app.rs +++ b/src/app.rs @@ -94,7 +94,7 @@ pub struct InnerApp { #[orga] impl InnerApp { - pub const CONSENSUS_VERSION: u8 = 4; + pub const CONSENSUS_VERSION: u8 = 5; #[cfg(feature = "full")] fn configure_faucets(&mut self) -> Result<()> { diff --git a/src/bitcoin/checkpoint.rs b/src/bitcoin/checkpoint.rs index 8452b58a..bbc60c94 100644 --- a/src/bitcoin/checkpoint.rs +++ b/src/bitcoin/checkpoint.rs @@ -347,7 +347,6 @@ impl Checkpoint { #[allow(unused_mut)] let mut intermediate_tx_batch = Batch::default(); - #[cfg(feature = "emergency-disbursal")] intermediate_tx_batch.push_back(BitcoinTx::default())?; checkpoint.batches.push_back(intermediate_tx_batch)?; @@ -582,7 +581,6 @@ type BuildingAdvanceRes = ( ); impl<'a> BuildingCheckpointMut<'a> { - #[cfg(feature = "emergency-disbursal")] fn link_intermediate_tx(&mut self, tx: &mut BitcoinTx) -> Result<()> { let sigset = self.sigset.clone(); let output_script = sigset.output_script(&[0u8])?; @@ -617,7 +615,6 @@ impl<'a> BuildingCheckpointMut<'a> { } //TODO: Unit tests - #[cfg(feature = "emergency-disbursal")] fn deduct_emergency_disbursal_fees(&mut self, fee_rate: u64) -> Result<()> { let intermediate_tx_fee = { let mut intermediate_tx_batch = self @@ -669,7 +666,6 @@ impl<'a> BuildingCheckpointMut<'a> { } //TODO: Generalize emergency disbursal to dynamic tree structure for intermediate tx overflow - #[cfg(feature = "emergency-disbursal")] fn generate_emergency_disbursal_txs( &mut self, nbtc_accounts: &Accounts, @@ -679,6 +675,10 @@ impl<'a> BuildingCheckpointMut<'a> { fee_rate: u64, reserve_value: u64, ) -> Result<()> { + #[cfg(not(feature = "full"))] + unimplemented!(); + + #[cfg(feature = "full")] { //TODO: Pull bitcoin config from state let bitcoin_config = super::Bitcoin::config(); @@ -880,7 +880,6 @@ impl<'a> BuildingCheckpointMut<'a> { vout: 0, }; - #[cfg(feature = "emergency-disbursal")] self.generate_emergency_disbursal_txs( nbtc_accounts, recovery_scripts, @@ -916,73 +915,6 @@ impl CheckpointQueue { Ok(()) } - pub fn rewind(&mut self, to_index: u32) -> Result<()> { - if to_index > self.index || self.index - to_index > self.queue.len() as u32 { - return Err(OrgaError::App("Invalid index".to_string()).into()); - } - - let mut inputs = vec![]; - let mut outputs = vec![]; - let mut checkpoint = loop { - let mut removed = self.queue.pop_back()?.unwrap().into_inner(); - - let mut checkpoint_batch = removed - .batches - .get_mut(BatchType::Checkpoint as u64)? - .unwrap(); - checkpoint_batch.signed_txs = 0; - - let mut checkpoint_tx = checkpoint_batch.back_mut()?.unwrap(); - checkpoint_tx.signed_inputs = 0; - - while let Some(input) = checkpoint_tx.input.pop_back()? { - if checkpoint_tx.input.is_empty() && self.index != to_index { - // skip reserve input (except on target index) - continue; - } - let mut input = input.into_inner(); - input.signatures.clear_sigs()?; - inputs.push(input); - } - - while let Some(output) = checkpoint_tx.output.pop_back()? { - if checkpoint_tx.output.is_empty() { - // skip reserve output - continue; - } - if output.value <= output.script_pubkey.dust_value().to_sat() { - // skip dust outputs - continue; - } - outputs.push(output.into_inner()); - } - - if self.index == to_index { - break removed; - } - self.index -= 1; - }; - - checkpoint.status = CheckpointStatus::Building; - - let mut checkpoint_batch = checkpoint - .batches - .get_mut(BatchType::Checkpoint as u64)? - .unwrap(); - let mut checkpoint_tx = checkpoint_batch.back_mut()?.unwrap(); - checkpoint_tx.input.push_back(inputs.pop().unwrap())?; - for input in inputs { - checkpoint_tx.input.push_back(input)?; - } - for output in outputs { - checkpoint_tx.output.push_back(output)?; - } - - self.queue.push_back(checkpoint)?; - - Ok(()) - } - #[query] pub fn get(&self, index: u32) -> Result> { let index = self.get_deque_index(index)?; @@ -1090,33 +1022,24 @@ impl CheckpointQueue { } #[query] - pub fn emergency_disbursal_txs( - &self, - limit: u32, - ) -> Result>> { - #[cfg(not(feature = "emergency-disbursal"))] - unimplemented!("{}", limit); - - #[cfg(feature = "emergency-disbursal")] - { - let mut vec = vec![]; - - if let Some(completed) = self.completed(limit)?.last() { - let intermediate_tx_batch = completed - .batches - .get(BatchType::IntermediateTx as u64)? - .unwrap(); - let intermediate_tx = intermediate_tx_batch.get(0)?.unwrap(); - vec.push(Adapter::new(intermediate_tx.to_bitcoin_tx()?)); - - let disbursal_batch = completed.batches.get(BatchType::Disbursal as u64)?.unwrap(); - for tx in disbursal_batch.iter()? { - vec.push(Adapter::new(tx?.to_bitcoin_tx()?)); - } - } + pub fn emergency_disbursal_txs(&self) -> Result>> { + let mut txs = vec![]; - Ok(vec) + if let Some(completed) = self.completed(1)?.last() { + let intermediate_tx_batch = completed + .batches + .get(BatchType::IntermediateTx as u64)? + .unwrap(); + let intermediate_tx = intermediate_tx_batch.get(0)?.unwrap(); + txs.push(Adapter::new(intermediate_tx.to_bitcoin_tx()?)); + + let disbursal_batch = completed.batches.get(BatchType::Disbursal as u64)?.unwrap(); + for tx in disbursal_batch.iter()? { + txs.push(Adapter::new(tx?.to_bitcoin_tx()?)); + } } + + Ok(txs) } #[query] @@ -1323,15 +1246,15 @@ impl CheckpointQueue { #[cfg(test)] mod test { - #[cfg(all(feature = "full", not(feature = "emergency-disbursal")))] + #[cfg(all(feature = "full"))] use bitcoin::{ util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}, OutPoint, PubkeyHash, Script, Txid, }; - #[cfg(all(feature = "full", not(feature = "emergency-disbursal")))] + #[cfg(all(feature = "full"))] use rand::Rng; - #[cfg(all(feature = "full", not(feature = "emergency-disbursal")))] + #[cfg(all(feature = "full"))] use crate::bitcoin::{signatory::Signatory, threshold_sig::Share}; use super::*; @@ -1380,215 +1303,4 @@ mod test { } //TODO: More fee deduction tests - - #[cfg(all(feature = "full", not(feature = "emergency-disbursal")))] - #[serial_test::serial] - #[test] - fn rewind() -> Result<()> { - // TODO: use CheckpointQueue step method to create initial checkpoint - // state instead of rawly constructing checkpoints - - // TODO: extract signer into core logic and run it against CheckpointQueue - - orga::context::Context::add(orga::plugins::Time::from_seconds(0)); - - let seed: [u8; 32] = rand::thread_rng().gen(); - let xpriv = ExtendedPrivKey::new_master(bitcoin::Network::Testnet, seed.as_slice())?; - - let secp = bitcoin::secp256k1::Secp256k1::new(); - let xpub = ExtendedPubKey::from_priv(&secp, &xpriv); - - let sigset = |index| { - let derive_path = [ChildNumber::from_normal_idx(index)?]; - let pubkey = xpub.derive_pub(&secp, &derive_path)?.public_key.into(); - - let mut sigset = SignatorySet::default(); - sigset.index = index; - sigset.possible_vp = 100; - sigset.present_vp = 100; - sigset.signatories.push(Signatory { - voting_power: 100, - pubkey, - }); - Ok::<_, Error>(sigset) - }; - - let txid = |n| Txid::from_slice(&[n; 32]); - - let building_input = |sigset_index, prev_txid, dest, amount| { - let sigset = sigset(sigset_index)?; - let mut input = Input::new(OutPoint::new(prev_txid, 0), &sigset, dest, amount)?; - input.signatures = ThresholdSig::from_sigset(&sigset)?; - input.signatures.sigs.insert( - sigset.signatories[0].pubkey.into(), - Share { - power: 100, - sig: None, - }, - )?; - Ok::<_, Error>(input) - }; - - let signing_input = |sigset_index, prev_txid, dest, amount| { - let mut input = building_input(sigset_index, prev_txid, dest, amount)?; - input.signatures.set_message([123; 32]); - Ok::<_, Error>(input) - }; - - let signed_input = |sigset_index, prev_txid, dest, amount| { - let sigset = sigset(sigset_index)?; - let mut input = signing_input(sigset_index, prev_txid, dest, amount)?; - input.signatures.sigs.insert( - sigset.signatories[0].pubkey.into(), - Share { - power: 100, - sig: Some(Signature([123; 64])), - }, - )?; - input.signatures.signed = 100; - Ok::<_, Error>(input) - }; - - let mut checkpoints = CheckpointQueue::default(); - - let mut cp = Checkpoint::new(sigset(0)?)?; - cp.status = CheckpointStatus::Complete; - let mut cp_batch = cp.batches.get_mut(BatchType::Checkpoint as u64)?.unwrap(); - let mut cp_tx = cp_batch.get_mut(0)?.unwrap(); - cp_tx - .input - .push_back(signed_input(0, txid(0)?, &[0], 10_000)?)?; - cp_tx.signed_inputs = 1; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: sigset(1)?.output_script(&[0])?, - value: 9_900, - }))?; - let prev_txid = cp_tx.txid()?; - cp_batch.signed_txs = 1; - checkpoints.queue.push_back(cp)?; - - let mut cp = Checkpoint::new(sigset(1)?)?; - cp.status = CheckpointStatus::Complete; - let mut cp_batch = cp.batches.get_mut(BatchType::Checkpoint as u64)?.unwrap(); - let mut cp_tx = cp_batch.get_mut(0)?.unwrap(); - cp_tx - .input - .push_back(signed_input(1, prev_txid, &[0], 9_900)?)?; - cp_tx - .input - .push_back(signed_input(0, txid(1)?, &[1; 20], 10_000)?)?; - cp_tx.signed_inputs = 2; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: sigset(2)?.output_script(&[0])?, - value: 19_799, - }))?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: Script::new_p2pkh(&PubkeyHash::from_slice(&[1; 20])?), - value: 1, - }))?; - let prev_txid = cp_tx.txid()?; - cp_batch.signed_txs = 1; - checkpoints.queue.push_back(cp)?; - - let mut cp = Checkpoint::new(sigset(2)?)?; - cp.status = CheckpointStatus::Complete; - let mut cp_batch = cp.batches.get_mut(BatchType::Checkpoint as u64)?.unwrap(); - let mut cp_tx = cp_batch.get_mut(0)?.unwrap(); - cp_tx - .input - .push_back(signed_input(2, prev_txid, &[0], 19_799)?)?; - cp_tx - .input - .push_back(signed_input(2, txid(2)?, &[2; 20], 10_000)?)?; - cp_tx.signed_inputs = 2; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: sigset(3)?.output_script(&[0])?, - value: 28_699, - }))?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: Script::new_p2pkh(&PubkeyHash::from_slice(&[2; 20])?), - value: 1_000, - }))?; - let prev_txid = cp_tx.txid()?; - cp_batch.signed_txs = 1; - checkpoints.queue.push_back(cp)?; - - let mut cp = Checkpoint::new(sigset(3)?)?; - cp.status = CheckpointStatus::Signing; - let mut cp_batch = cp.batches.get_mut(BatchType::Checkpoint as u64)?.unwrap(); - let mut cp_tx = cp_batch.get_mut(0)?.unwrap(); - cp_tx - .input - .push_back(signing_input(3, prev_txid, &[0], 28_699)?)?; - cp_tx - .input - .push_back(signing_input(3, txid(3)?, &[2; 20], 10_000)?)?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: sigset(4)?.output_script(&[0])?, - value: 37_599, - }))?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: Script::new_p2pkh(&PubkeyHash::from_slice(&[3; 20])?), - value: 1_000, - }))?; - let prev_txid = cp_tx.txid()?; - checkpoints.queue.push_back(cp)?; - - let mut cp = Checkpoint::new(sigset(4)?)?; - cp.status = CheckpointStatus::Building; - let mut cp_batch = cp.batches.get_mut(BatchType::Checkpoint as u64)?.unwrap(); - let mut cp_tx = cp_batch.get_mut(0)?.unwrap(); - cp_tx - .input - .push_back(building_input(4, prev_txid, &[0], 37_599)?)?; - cp_tx - .input - .push_back(building_input(3, txid(4)?, &[2; 20], 10_000)?)?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: sigset(4)?.output_script(&[0])?, - value: 46_499, - }))?; - cp_tx.output.push_back(Adapter::new(TxOut { - script_pubkey: Script::new_p2pkh(&PubkeyHash::from_slice(&[4; 20])?), - value: 1_000, - }))?; - checkpoints.queue.push_back(cp)?; - checkpoints.index = (checkpoints.queue.len() - 1) as u32; - - checkpoints.rewind(1)?; - - assert_eq!(checkpoints.len()?, 2); - assert_eq!(checkpoints.index, 1); - let cp = checkpoints.queue.back()?.unwrap(); - assert_eq!(cp.status, CheckpointStatus::Building); - assert_eq!(cp.sigset, sigset(1)?); - let cp_batch = cp.batches.get(BatchType::Checkpoint as u64)?.unwrap(); - assert_eq!(cp_batch.signed_txs, 0); - assert!(!cp_batch.signed()); - let cp_tx = cp_batch.get(0)?.unwrap(); - assert_eq!(cp_tx.signed_inputs, 0); - assert_eq!(cp_tx.input.len(), 5); - assert_eq!(cp_tx.output.len(), 3); - assert!(!cp_tx.signed()); - - checkpoints.building_mut()?.advance( - &Accounts::default(), - &Map::new(), - vec![].into_iter(), - &Config::regtest(), - )?; - checkpoints.queue.push_back(Checkpoint::new(sigset(5)?)?)?; - checkpoints.index += 1; - - assert_eq!( - checkpoints - .signing_mut()? - .unwrap() - .to_sign(xpub.clone().into())? - .len(), - 5 - ); - - Ok(()) - } } diff --git a/src/bitcoin/relayer.rs b/src/bitcoin/relayer.rs index 679558ce..72a9e493 100644 --- a/src/bitcoin/relayer.rs +++ b/src/bitcoin/relayer.rs @@ -274,7 +274,6 @@ impl Relayer { Ok(tip) } - #[cfg(feature = "emergency-disbursal")] pub async fn start_emergency_disbursal_transaction_relay(&mut self) -> Result<()> { info!("Starting emergency disbursal transaction relay..."); @@ -289,14 +288,13 @@ impl Relayer { } } - #[cfg(feature = "emergency-disbursal")] async fn relay_emergency_disbursal_transactions(&mut self) -> Result<()> { use std::time::{SystemTime, UNIX_EPOCH}; let mut relayed = HashSet::new(); loop { let disbursal_txs = app_client(&self.app_client_addr) - .query(|app| Ok(app.bitcoin.checkpoints.emergency_disbursal_txs(1_000)?)) + .query(|app| Ok(app.bitcoin.checkpoints.emergency_disbursal_txs()?)) .await?; for tx in disbursal_txs.iter() { diff --git a/src/bitcoin/signatory.rs b/src/bitcoin/signatory.rs index 144b5a5a..83f6fcab 100644 --- a/src/bitcoin/signatory.rs +++ b/src/bitcoin/signatory.rs @@ -133,7 +133,7 @@ impl SignatorySet { } pub fn signature_threshold(&self) -> u64 { - ((self.present_vp as u128) * 2 / 3) as u64 + ((self.present_vp as u128) * 9 / 10) as u64 } pub fn quorum_threshold(&self) -> u64 { diff --git a/src/bitcoin/threshold_sig.rs b/src/bitcoin/threshold_sig.rs index e67cfc23..db7f324a 100644 --- a/src/bitcoin/threshold_sig.rs +++ b/src/bitcoin/threshold_sig.rs @@ -221,7 +221,7 @@ impl ThresholdSig { } // TODO: get threshold ratio from somewhere else - ts.threshold = ((total_vp as u128) * 2 / 3) as u64; + ts.threshold = ((total_vp as u128) * 9 / 10) as u64; Ok(ts) } @@ -239,7 +239,7 @@ impl ThresholdSig { } // TODO: get threshold ratio from somewhere else - ts.threshold = ((total_vp as u128) * 2 / 3) as u64; + ts.threshold = ((total_vp as u128) * 9 / 10) as u64; ts.len = len; Ok(ts) diff --git a/tests/bitcoin.rs b/tests/bitcoin.rs index 900c3be6..0fc7cdae 100644 --- a/tests/bitcoin.rs +++ b/tests/bitcoin.rs @@ -160,7 +160,7 @@ async fn withdraw_bitcoin( async fn get_signatory_script() -> Result