Skip to content

Commit

Permalink
Merge pull request #224 from nomic-io/limit-unconfirmed-checkpoints
Browse files Browse the repository at this point in the history
Add max_unconfirmed_checkpoints parameter
  • Loading branch information
mappum authored Oct 12, 2023
2 parents 854878f + a989d7d commit 6f5b99c
Showing 1 changed file with 188 additions and 9 deletions.
197 changes: 188 additions & 9 deletions src/bitcoin/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ impl Checkpoint {
}
}

#[orga(skip(Default), version = 1)]
#[orga(skip(Default), version = 2)]
#[derive(Clone)]
pub struct Config {
pub min_checkpoint_interval: u64,
Expand All @@ -531,20 +531,22 @@ pub struct Config {
#[orga(version(V0))]
pub fee_rate: u64,
pub max_age: u64,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub target_checkpoint_inclusion: u32,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub min_fee_rate: u64,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub max_fee_rate: u64,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub sigset_threshold: (u64, u64),
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub emergency_disbursal_min_tx_amt: u64,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub emergency_disbursal_lock_time_interval: u32,
#[orga(version(V1))]
#[orga(version(V1, V2))]
pub emergency_disbursal_max_tx_size: u64,
#[orga(version(V2))]
pub max_unconfirmed_checkpoints: u32,
}

impl MigrateFrom<ConfigV0> for ConfigV1 {
Expand All @@ -555,7 +557,34 @@ impl MigrateFrom<ConfigV0> for ConfigV1 {
max_inputs: value.max_inputs,
max_outputs: value.max_outputs,
max_age: value.max_age,
..Self::default()
target_checkpoint_inclusion: ConfigV2::default().target_checkpoint_inclusion,
min_fee_rate: ConfigV2::default().min_fee_rate,
max_fee_rate: ConfigV2::default().max_fee_rate,
sigset_threshold: ConfigV2::default().sigset_threshold,
emergency_disbursal_min_tx_amt: ConfigV2::default().emergency_disbursal_min_tx_amt,
emergency_disbursal_lock_time_interval: ConfigV2::default()
.emergency_disbursal_lock_time_interval,
emergency_disbursal_max_tx_size: ConfigV2::default().emergency_disbursal_max_tx_size,
})
}
}

impl MigrateFrom<ConfigV1> for ConfigV2 {
fn migrate_from(value: ConfigV1) -> OrgaResult<Self> {
Ok(Self {
min_checkpoint_interval: value.min_checkpoint_interval,
max_checkpoint_interval: value.max_checkpoint_interval,
max_inputs: value.max_inputs,
max_outputs: value.max_outputs,
max_age: value.max_age,
target_checkpoint_inclusion: value.target_checkpoint_inclusion,
min_fee_rate: value.min_fee_rate,
max_fee_rate: value.max_fee_rate,
sigset_threshold: value.sigset_threshold,
emergency_disbursal_min_tx_amt: value.emergency_disbursal_min_tx_amt,
emergency_disbursal_lock_time_interval: value.emergency_disbursal_lock_time_interval,
emergency_disbursal_max_tx_size: value.emergency_disbursal_max_tx_size,
..Default::default()
})
}
}
Expand Down Expand Up @@ -584,6 +613,7 @@ impl Config {
emergency_disbursal_min_tx_amt: 1000,
emergency_disbursal_lock_time_interval: 60 * 60 * 24 * 7, // one week
emergency_disbursal_max_tx_size: 50_000,
max_unconfirmed_checkpoints: 15,
}
}
}
Expand Down Expand Up @@ -1384,6 +1414,11 @@ impl CheckpointQueue {
}
}

let unconfs = self.num_unconfirmed()?;
if unconfs >= self.config.max_unconfirmed_checkpoints {
return Ok(false);
}

let mut index = self.index;
if !self.queue.is_empty() {
index += 1;
Expand Down Expand Up @@ -1927,4 +1962,148 @@ mod test {
assert_eq!(queue.borrow().len().unwrap(), 11);
assert_eq!(queue.borrow().building().unwrap().fee_rate, 11);
}

#[cfg(feature = "full")]
#[test]
#[serial_test::serial]
fn max_unconfirmed_checkpoints() {
// TODO: extract pieces into util functions, test more cases

let paid = orga::plugins::Paid::default();
Context::add(paid);

let mut vals = orga::plugins::Validators::new(
Rc::new(RefCell::new(Some(EntryMap::new()))),
Rc::new(RefCell::new(None)),
);
vals.set_voting_power([0; 32], 100);
Context::add(vals);

let secp = Secp256k1::new();
let xpriv = ExtendedPrivKey::new_master(bitcoin::Network::Regtest, &[0]).unwrap();
let xpub = ExtendedPubKey::from_priv(&secp, &xpriv);

let mut sig_keys = Map::new();
sig_keys.insert([0; 32], Xpub::new(xpub));

let queue = Rc::new(RefCell::new(CheckpointQueue::default()));
queue.borrow_mut().config = Config {
min_fee_rate: 2,
max_fee_rate: 200,
target_checkpoint_inclusion: 2,
min_checkpoint_interval: 100,
max_unconfirmed_checkpoints: 2,
..Default::default()
};

let set_time = |time| {
let time = orga::plugins::Time::from_seconds(time);
Context::add(time);
};
let maybe_step = |btc_height| {
queue
.borrow_mut()
.maybe_step(
&sig_keys,
&Accounts::default(),
&Map::new(),
vec![Ok(bitcoin::TxOut {
script_pubkey: Script::new(),
value: 1_000_000,
})]
.into_iter(),
btc_height,
true,
vec![1, 2, 3],
)
.unwrap();
};
let push_deposit = || {
let mut input = Input::new(
OutPoint {
txid: Txid::from_slice(&[0; 32]).unwrap(),
vout: 0,
},
&queue.borrow().building().unwrap().sigset,
&[0u8],
100_000_000,
(9, 10),
)
.unwrap();
let mut queue = queue.borrow_mut();
let mut building_mut = queue.building_mut().unwrap();
let mut building_checkpoint_batch = building_mut
.batches
.get_mut(BatchType::Checkpoint as u64)
.unwrap()
.unwrap();
let mut checkpoint_tx = building_checkpoint_batch.get_mut(0).unwrap().unwrap();
checkpoint_tx.input.push_back(input).unwrap();
};
let sign_batch = |btc_height| {
let mut queue = queue.borrow_mut();
let cp = queue.signing().unwrap().unwrap();
let sigset_index = cp.sigset.index;
let to_sign = cp.to_sign(Xpub::new(xpub.clone())).unwrap();
let secp2 = Secp256k1::signing_only();
let sigs = crate::bitcoin::signer::sign(&secp2, &xpriv, &to_sign).unwrap();
drop(cp);
queue
.sign(Xpub::new(xpub), sigs, sigset_index, btc_height)
.unwrap();
};
let sign_cp = |btc_height| {
sign_batch(btc_height);
sign_batch(btc_height);
if queue.borrow().signing().unwrap().is_some() {
sign_batch(btc_height);
}
};
let confirm_cp = |index, btc_height| {
let mut queue = queue.borrow_mut();
queue.confirmed_index = Some(index);
};

assert_eq!(queue.borrow().len().unwrap(), 0);

set_time(0);
maybe_step(8);
push_deposit();
maybe_step(8);

set_time(1_000);
maybe_step(8);
sign_cp(8);
confirm_cp(0, 9);

set_time(2_000);
push_deposit();
maybe_step(10);
sign_cp(10);

set_time(3_000);
push_deposit();
maybe_step(10);
sign_cp(10);

assert_eq!(queue.borrow().len().unwrap(), 4);

set_time(4_000);
push_deposit();
maybe_step(10);

assert_eq!(queue.borrow().len().unwrap(), 4);

set_time(5_000);
push_deposit();
maybe_step(10);

assert_eq!(queue.borrow().len().unwrap(), 4);

confirm_cp(2, 11);
set_time(6_000);
maybe_step(11);

assert_eq!(queue.borrow().len().unwrap(), 5);
}
}

0 comments on commit 6f5b99c

Please sign in to comment.