Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions lwk_test_util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,29 @@ pub fn update_v2_test_vector_after_many_transactions() -> Vec<u8> {
include_bytes!("../test_data/update_v2_after_many_txs.bin").to_vec()
}

/// First of 3 consecutive updates for merge testing
/// Contains initial wallet sync (tip only)
pub fn update_merge_test_1() -> Vec<u8> {
include_bytes!("../test_data/merge_updates/update_merge_1.bin").to_vec()
}

/// Second of 3 consecutive updates for merge testing
/// Contains wallet funding transaction
pub fn update_merge_test_2() -> Vec<u8> {
include_bytes!("../test_data/merge_updates/update_merge_2.bin").to_vec()
}

/// Third of 3 consecutive updates for merge testing
/// Contains spending transaction
pub fn update_merge_test_3() -> Vec<u8> {
include_bytes!("../test_data/merge_updates/update_merge_3.bin").to_vec()
}

/// Descriptor used for the merge test updates
pub fn update_merge_test_descriptor() -> String {
include_str!("../test_data/merge_updates/descriptor.txt").to_string()
}

pub fn update_test_vector_encrypted_bytes() -> Vec<u8> {
Vec::<u8>::from_hex(include_str!(
"../test_data/update_test_vector_encrypted.hex"
Expand Down
34 changes: 34 additions & 0 deletions lwk_test_util/test_data/merge_updates/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Merge Test Updates

This directory contains 3 consecutive `Update` structs for a regtest wallet with descriptor:

ct(slip77(e12440f9ab46536bee060598e3b6064c0c1fa46049731d614b5c973ed054b363),elwpkh(tpubD6NzVbkrYhZ4Y2AL6uijCXAKHRNLMt7rxWnZt3bjPuYv8ibEQvGmEhRPDqbLUX7a7yCwEyEgEV37gu2CUa3a6CCpjLfrv3Xks9pGitNtd7z/*))

## Files

- `update_merge_1.bin` - Initial wallet sync (tip only update)
- `update_merge_2.bin` - Wallet funding transaction
- `update_merge_3.bin` - Spending transaction
- `descriptor.txt` - The wallet descriptor used for these updates

## Expected Wallet States

When these updates are applied sequentially to a fresh wallet, the expected states are:

### After Update 1
- **Balance**: 0 L-BTC
- **Transactions**: 0
- **UTXOs**: 0
- **Description**: Initial sync of an empty wallet, only updates the blockchain tip

### After Update 2
- **Balance**: 1,000,000
- **Transactions**: 1
- **UTXOs**: 1
- **Description**: Wallet received funding transaction of 1,000,000 sats

### After Update 3
- **Balance**: 899,974 sats
- **Transactions**: 2
- **UTXOs**: 1 (change output)
- **Description**: Wallet sent 100,000 sats to an external address with a fee of 26 sats
1 change: 1 addition & 0 deletions lwk_test_util/test_data/merge_updates/descriptor.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ct(slip77(d48cc2f9c22d8afadf38f9656d6ebc2ad7773273f34cbf62e4b15f0e5fea46f2),elwpkh(tpubD6NzVbkrYhZ4X2UC8rSu2hqEuaZis6FakbAKRxjqMJLGWHyqLy4iKu41teNY4BuSkvgRhfG3Gi1LJnN4HzNkpuGxxRYYJYTVBow8xEs9MD2/*))
Binary file not shown.
Binary file not shown.
Binary file not shown.
40 changes: 40 additions & 0 deletions lwk_wollet/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,46 @@ impl Update {
.map_err(|e| Error::Generic(e.to_string()))?;
Self::deserialize_decrypted(&vec, desc)
}

/// Merge another update into this one.
///
/// This is used to squash multiple sequential updates into a single update.
///
/// NOTE: it's caller responsibility to ensure that the following update is the next in sequence
/// and updates are not mixed up.
pub(crate) fn merge(&mut self, following: Update) {
// Merge new transactions: add new ones, replace existing ones
for (txid, tx) in following.new_txs.txs {
self.new_txs.txs.retain(|(t, _)| *t != txid);
self.new_txs.txs.push((txid, tx));
}
self.new_txs.unblinds.extend(following.new_txs.unblinds);

// Merge txid_height_new: union with override (later wins)
for (txid, height) in following.txid_height_new {
self.txid_height_new.retain(|(t, _)| *t != txid);
self.txid_height_new.push((txid, height));
}

// Remove deleted txids from txid_height_new
for txid in &following.txid_height_delete {
self.txid_height_new.retain(|(t, _)| t != txid);
}

// Merge deletes
self.txid_height_delete.extend(following.txid_height_delete);

// Merge timestamps and scripts
self.timestamps.extend(following.timestamps);
self.scripts_with_blinding_pubkey
.extend(following.scripts_with_blinding_pubkey);

// Update tip to other's tip
self.tip = following.tip;

// Update version to latest
self.version = following.version;
}
}

fn default_blockheader() -> BlockHeader {
Expand Down
Loading
Loading