Skip to content

Commit 1fb5d69

Browse files
committed
fix off-by-one height bug when disconnecting tip
1 parent 58e9088 commit 1fb5d69

1 file changed

Lines changed: 162 additions & 1 deletion

File tree

lib/state/block.rs

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ pub fn disconnect_tip(
566566
rwtxn,
567567
&filled_tx,
568568
(**bitname_updates).clone(),
569-
height - 1,
569+
height,
570570
)?;
571571
}
572572
Some(TxData::BatchIcann(batch_icann_data)) => {
@@ -650,3 +650,164 @@ pub fn disconnect_tip(
650650
}
651651
Ok(())
652652
}
653+
654+
#[cfg(test)]
655+
mod test {
656+
use bitcoin::hashes::Hash as _;
657+
658+
use crate::{
659+
authorization::{self, SigningKey},
660+
state::{
661+
block::{connect, disconnect_tip, validate},
662+
test::fresh_state,
663+
},
664+
types::{
665+
BitName, BitNameDataUpdates, BitNameSeqId, BlockHash, Body,
666+
FilledOutput, FilledOutputContent, FilledTransaction, Hash, Header,
667+
MerkleRoot, MutableBitNameData, OutPoint, OutPointKey, Output,
668+
OutputContent, Transaction, TxData, Update,
669+
},
670+
};
671+
672+
fn header(
673+
prev_side_hash: Option<BlockHash>,
674+
merkle_root: MerkleRoot,
675+
) -> Header {
676+
Header {
677+
merkle_root,
678+
prev_side_hash,
679+
prev_main_hash: bitcoin::BlockHash::from_byte_array([0; 32]),
680+
}
681+
}
682+
683+
fn all_retained_updates() -> BitNameDataUpdates {
684+
BitNameDataUpdates {
685+
commitment: Update::Retain,
686+
socket_addr_v4: Update::Retain,
687+
socket_addr_v6: Update::Retain,
688+
encryption_pubkey: Update::Retain,
689+
signing_pubkey: Update::Retain,
690+
paymail_fee_sats: Update::Retain,
691+
}
692+
}
693+
694+
#[test]
695+
fn disconnect_bitname_data_update() -> anyhow::Result<()> {
696+
let (env, state) = fresh_state("disconnect_bitname_data_update")?;
697+
let signing_key = SigningKey::from_bytes(&[7; 32]);
698+
let verifying_key = signing_key.verifying_key().into();
699+
let address = authorization::get_address(&verifying_key);
700+
701+
let name_hash: Hash = [1; 32];
702+
let revealed_nonce: Hash = [2; 32];
703+
let commitment: Hash =
704+
blake3::keyed_hash(&revealed_nonce, &name_hash).into();
705+
let reservation_txid = [3; 32].into();
706+
let bitname = BitName(name_hash);
707+
let bitname_outpoint = OutPoint::Regular {
708+
txid: [5; 32].into(),
709+
vout: 0,
710+
};
711+
712+
{
713+
let mut rwtxn = env.write_txn()?;
714+
state.bitnames.put_reservation(
715+
&mut rwtxn,
716+
&reservation_txid,
717+
&commitment,
718+
)?;
719+
let registration_tx = Transaction {
720+
inputs: vec![OutPoint::Regular {
721+
txid: reservation_txid,
722+
vout: 0,
723+
}],
724+
outputs: Vec::new(),
725+
memo: Vec::new(),
726+
data: Some(TxData::BitNameRegistration {
727+
name_hash: bitname,
728+
revealed_nonce,
729+
bitname_data: Box::new(MutableBitNameData::default()),
730+
}),
731+
};
732+
let registration_filled = crate::types::FilledTransaction {
733+
transaction: registration_tx,
734+
spent_utxos: vec![FilledOutput {
735+
address,
736+
content: FilledOutputContent::BitNameReservation(
737+
reservation_txid,
738+
commitment,
739+
),
740+
memo: Vec::new(),
741+
}],
742+
};
743+
state.bitnames.apply_registration(
744+
&mut rwtxn,
745+
&registration_filled,
746+
bitname,
747+
&MutableBitNameData::default(),
748+
0,
749+
)?;
750+
let seq = state.bitnames.next_seq(&rwtxn)?;
751+
anyhow::ensure!(seq == BitNameSeqId::new(1));
752+
state.utxos.put(
753+
&mut rwtxn,
754+
&OutPointKey::from_outpoint(&bitname_outpoint),
755+
&FilledOutput {
756+
address,
757+
content: FilledOutputContent::BitName(bitname),
758+
memo: Vec::new(),
759+
},
760+
)?;
761+
rwtxn.commit()?;
762+
}
763+
764+
let genesis_body = Body {
765+
coinbase: Vec::new(),
766+
transactions: Vec::new(),
767+
authorizations: Vec::new(),
768+
};
769+
let genesis_merkle_root =
770+
Body::compute_merkle_root::<FilledTransaction>(&[], &[])?;
771+
let genesis_header = header(None, genesis_merkle_root);
772+
{
773+
let mut rwtxn = env.write_txn()?;
774+
connect(&state, &mut rwtxn, &genesis_header, &genesis_body)?;
775+
rwtxn.commit()?;
776+
}
777+
778+
let mut updates = all_retained_updates();
779+
updates.commitment = Update::Set([9; 32]);
780+
let update_tx = Transaction {
781+
inputs: vec![bitname_outpoint],
782+
outputs: vec![Output::new(address, OutputContent::BitName)],
783+
memo: Vec::new(),
784+
data: Some(TxData::BitNameUpdate(Box::new(updates))),
785+
};
786+
let filled_update_tx = {
787+
let rotxn = env.read_txn()?;
788+
state.fill_transaction(&rotxn, &update_tx)?
789+
};
790+
let authorized_update =
791+
authorization::authorize(&[(address, &signing_key)], update_tx)?;
792+
let update_body = Body::new(vec![authorized_update], Vec::new());
793+
let update_merkle_root =
794+
Body::compute_merkle_root(&[], &[filled_update_tx])?;
795+
let update_header =
796+
header(Some(genesis_header.hash()), update_merkle_root);
797+
798+
{
799+
let rotxn = env.read_txn()?;
800+
validate(&state, &rotxn, &update_header, &update_body)?;
801+
}
802+
{
803+
let mut rwtxn = env.write_txn()?;
804+
connect(&state, &mut rwtxn, &update_header, &update_body)?;
805+
rwtxn.commit()?;
806+
}
807+
808+
let mut rwtxn = env.write_txn()?;
809+
let () =
810+
disconnect_tip(&state, &mut rwtxn, &update_header, &update_body)?;
811+
Ok(())
812+
}
813+
}

0 commit comments

Comments
 (0)