Skip to content

Commit c5f63a6

Browse files
committed
Ensure backward compatibility of the "checksum inception" bug
`Wallet` stores the descriptors' checksum in the database for safety. Previously, the checksum used was a checksum of a descriptor that already had a checksum. This PR allows for backward-compatibility of databases created with this bug.
1 parent fd34956 commit c5f63a6

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

src/wallet/mod.rs

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx};
6464
use crate::blockchain::{GetHeight, NoopProgress, Progress, WalletSync};
6565
use crate::database::memory::MemoryDatabase;
6666
use crate::database::{AnyDatabase, BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
67+
use crate::descriptor::checksum::get_checksum_bytes;
6768
use crate::descriptor::derived::AsDerived;
6869
use crate::descriptor::policy::BuildSatisfaction;
6970
use crate::descriptor::{
@@ -203,28 +204,28 @@ where
203204
let secp = Secp256k1::new();
204205

205206
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
206-
database.check_descriptor_checksum(
207+
Self::db_checksum(
208+
&mut database,
209+
&descriptor.to_string(),
207210
KeychainKind::External,
208-
get_checksum(&descriptor.to_string())?.as_bytes(),
209211
)?;
210212
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp));
213+
211214
let (change_descriptor, change_signers) = match change_descriptor {
212215
Some(desc) => {
213216
let (change_descriptor, change_keymap) =
214217
into_wallet_descriptor_checked(desc, &secp, network)?;
215-
database.check_descriptor_checksum(
218+
Self::db_checksum(
219+
&mut database,
220+
&change_descriptor.to_string(),
216221
KeychainKind::Internal,
217-
get_checksum(&change_descriptor.to_string())?.as_bytes(),
218222
)?;
219223

220224
let change_signers = Arc::new(SignersContainer::build(
221225
change_keymap,
222226
&change_descriptor,
223227
&secp,
224228
));
225-
// if !parsed.same_structure(descriptor.as_ref()) {
226-
// return Err(Error::DifferentDescriptorStructure);
227-
// }
228229

229230
(Some(change_descriptor), change_signers)
230231
}
@@ -243,6 +244,19 @@ where
243244
})
244245
}
245246

247+
/// This checks the checksum within [`BatchDatabase`] twice (if needed). The first time with the
248+
/// actual checksum, and the second time with the checksum of `descriptor+checksum`. The second
249+
/// check is necessary for backwards compatibility of a checksum-inception bug.
250+
fn db_checksum(db: &mut D, desc: &str, kind: KeychainKind) -> Result<(), Error> {
251+
let checksum = get_checksum_bytes(desc, true)?;
252+
if db.check_descriptor_checksum(kind, checksum).is_ok() {
253+
return Ok(());
254+
}
255+
256+
let checksum_inception = get_checksum_bytes(desc, false)?;
257+
db.check_descriptor_checksum(kind, checksum_inception)
258+
}
259+
246260
/// Get the Bitcoin network the wallet is using.
247261
pub fn network(&self) -> Network {
248262
self.network
@@ -1949,6 +1963,34 @@ pub(crate) mod test {
19491963
);
19501964
}
19511965

1966+
#[test]
1967+
fn test_db_checksum() {
1968+
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
1969+
let desc = wallet.descriptor.to_string();
1970+
1971+
let checksum = get_checksum_bytes(&desc, true).unwrap();
1972+
let checksum_inception = get_checksum_bytes(&desc, false).unwrap();
1973+
let checksum_invalid = ['q' as u8; 8];
1974+
1975+
let mut db = MemoryDatabase::new();
1976+
db.check_descriptor_checksum(KeychainKind::External, checksum)
1977+
.expect("failed to save actual checksum");
1978+
Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
1979+
.expect("db that uses actual checksum should be supported");
1980+
1981+
let mut db = MemoryDatabase::new();
1982+
db.check_descriptor_checksum(KeychainKind::External, checksum_inception)
1983+
.expect("failed to save checksum inception");
1984+
Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
1985+
.expect("db that uses checksum inception should be supported");
1986+
1987+
let mut db = MemoryDatabase::new();
1988+
db.check_descriptor_checksum(KeychainKind::External, checksum_invalid)
1989+
.expect("failed to save invalid checksum");
1990+
Wallet::db_checksum(&mut db, &desc, KeychainKind::External)
1991+
.expect_err("db that uses invalid checksum should fail");
1992+
}
1993+
19521994
#[test]
19531995
fn test_get_funded_wallet_balance() {
19541996
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());

0 commit comments

Comments
 (0)