diff --git a/lib/state/block.rs b/lib/state/block.rs index 16241b2..1f0e999 100644 --- a/lib/state/block.rs +++ b/lib/state/block.rs @@ -566,7 +566,7 @@ pub fn disconnect_tip( rwtxn, &filled_tx, (**bitname_updates).clone(), - height - 1, + height, )?; } Some(TxData::BatchIcann(batch_icann_data)) => { @@ -650,3 +650,164 @@ pub fn disconnect_tip( } Ok(()) } + +#[cfg(test)] +mod test { + use bitcoin::hashes::Hash as _; + + use crate::{ + authorization::{self, SigningKey}, + state::{ + block::{connect, disconnect_tip, validate}, + test::fresh_state, + }, + types::{ + BitName, BitNameDataUpdates, BitNameSeqId, BlockHash, Body, + FilledOutput, FilledOutputContent, FilledTransaction, Hash, Header, + MerkleRoot, MutableBitNameData, OutPoint, OutPointKey, Output, + OutputContent, Transaction, TxData, Update, + }, + }; + + fn header( + prev_side_hash: Option, + merkle_root: MerkleRoot, + ) -> Header { + Header { + merkle_root, + prev_side_hash, + prev_main_hash: bitcoin::BlockHash::from_byte_array([0; 32]), + } + } + + fn all_retained_updates() -> BitNameDataUpdates { + BitNameDataUpdates { + commitment: Update::Retain, + socket_addr_v4: Update::Retain, + socket_addr_v6: Update::Retain, + encryption_pubkey: Update::Retain, + signing_pubkey: Update::Retain, + paymail_fee_sats: Update::Retain, + } + } + + #[test] + fn disconnect_bitname_data_update() -> anyhow::Result<()> { + let (env, state) = fresh_state("disconnect_bitname_data_update")?; + let signing_key = SigningKey::from_bytes(&[7; 32]); + let verifying_key = signing_key.verifying_key().into(); + let address = authorization::get_address(&verifying_key); + + let name_hash: Hash = [1; 32]; + let revealed_nonce: Hash = [2; 32]; + let commitment: Hash = + blake3::keyed_hash(&revealed_nonce, &name_hash).into(); + let reservation_txid = [3; 32].into(); + let bitname = BitName(name_hash); + let bitname_outpoint = OutPoint::Regular { + txid: [5; 32].into(), + vout: 0, + }; + + { + let mut rwtxn = env.write_txn()?; + state.bitnames.put_reservation( + &mut rwtxn, + &reservation_txid, + &commitment, + )?; + let registration_tx = Transaction { + inputs: vec![OutPoint::Regular { + txid: reservation_txid, + vout: 0, + }], + outputs: Vec::new(), + memo: Vec::new(), + data: Some(TxData::BitNameRegistration { + name_hash: bitname, + revealed_nonce, + bitname_data: Box::new(MutableBitNameData::default()), + }), + }; + let registration_filled = crate::types::FilledTransaction { + transaction: registration_tx, + spent_utxos: vec![FilledOutput { + address, + content: FilledOutputContent::BitNameReservation( + reservation_txid, + commitment, + ), + memo: Vec::new(), + }], + }; + state.bitnames.apply_registration( + &mut rwtxn, + ®istration_filled, + bitname, + &MutableBitNameData::default(), + 0, + )?; + let seq = state.bitnames.next_seq(&rwtxn)?; + anyhow::ensure!(seq == BitNameSeqId::new(1)); + state.utxos.put( + &mut rwtxn, + &OutPointKey::from_outpoint(&bitname_outpoint), + &FilledOutput { + address, + content: FilledOutputContent::BitName(bitname), + memo: Vec::new(), + }, + )?; + rwtxn.commit()?; + } + + let genesis_body = Body { + coinbase: Vec::new(), + transactions: Vec::new(), + authorizations: Vec::new(), + }; + let genesis_merkle_root = + Body::compute_merkle_root::(&[], &[])?; + let genesis_header = header(None, genesis_merkle_root); + { + let mut rwtxn = env.write_txn()?; + connect(&state, &mut rwtxn, &genesis_header, &genesis_body)?; + rwtxn.commit()?; + } + + let mut updates = all_retained_updates(); + updates.commitment = Update::Set([9; 32]); + let update_tx = Transaction { + inputs: vec![bitname_outpoint], + outputs: vec![Output::new(address, OutputContent::BitName)], + memo: Vec::new(), + data: Some(TxData::BitNameUpdate(Box::new(updates))), + }; + let filled_update_tx = { + let rotxn = env.read_txn()?; + state.fill_transaction(&rotxn, &update_tx)? + }; + let authorized_update = + authorization::authorize(&[(address, &signing_key)], update_tx)?; + let update_body = Body::new(vec![authorized_update], Vec::new()); + let update_merkle_root = + Body::compute_merkle_root(&[], &[filled_update_tx])?; + let update_header = + header(Some(genesis_header.hash()), update_merkle_root); + + { + let rotxn = env.read_txn()?; + validate(&state, &rotxn, &update_header, &update_body)?; + } + { + let mut rwtxn = env.write_txn()?; + connect(&state, &mut rwtxn, &update_header, &update_body)?; + rwtxn.commit()?; + } + + let mut rwtxn = env.write_txn()?; + let () = + disconnect_tip(&state, &mut rwtxn, &update_header, &update_body)?; + Ok(()) + } +}