Change address `mark_used` marker is not persisted; same change address reissued after restart
Los mantenedores suelen responder en 1 día
@NaitikVerma6776 ya está trabajando en esto.
Desde el 13/10/2026.
- #599 de @NaitikVerma6776 — abierto
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 84/100
Línea de trabajo
Comienza en src/wallet/mod.rs, en Wallet::create_tx, comparando la llamada a mark_used con la ruta de persistencia reveal_to_target cercana y el ChangeSet preparado. Añade o adapta el escenario de recarga proporcionado como prueba y verifica después que persistir y volver a cargar tras una transacción que produce cambio hace que la siguiente dirección de cambio sea diferente.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug
When Wallet::create_tx produces a change output, it calls mark_used so the change
address is not handed out again (src/wallet/mod.rs, in create_tx). However
mark_used only mutates the in-memory unused set of SpkTxOutIndex; unlike the
reveal_to_target call right next to it, it is never recorded in the staged
ChangeSet, so persist() does not save it.
If the wallet is persisted and reloaded (e.g. process restart) after building a
change-producing transaction but before that transaction is seen on chain, the
change index is considered unused again. A subsequent create_tx is then given the
exact same change address, resulting in address reuse if both transactions confirm.
This issue was found by AI.
To Reproduce
use bdk_wallet::test_utils::*;
use bdk_wallet::{KeychainKind, Wallet};
use bitcoin::{Amount, Network, ScriptBuf};
#[test]
fn change_address_should_not_be_reissued_after_reload() -> anyhow::Result<()> {
use bdk_chain::rusqlite;
let mut conn = rusqlite::Connection::open_in_memory()?;
let (desc, change_desc) = get_test_tr_single_sig_xprv_and_change_desc();
let mut wallet = Wallet::create(desc, change_desc)
.network(Network::Signet)
.create_wallet(&mut conn)?;
receive_output(&mut wallet, Amount::from_sat(50_000), ReceiveTo::Mempool(0));
// Build a change-producing tx and return the change output's script.
fn create_change_tx(wallet: &mut Wallet) -> anyhow::Result<ScriptBuf> {
let change_spk = wallet.peek_address(KeychainKind::Internal, 0).script_pubkey();
let recipient = wallet.next_unused_address(KeychainKind::External).address;
let mut builder = wallet.build_tx();
builder.add_recipient(recipient, Amount::from_sat(10_000));
let psbt = builder.finish()?;
assert!(psbt.unsigned_tx.output.iter().any(|o| o.script_pubkey == change_spk));
Ok(change_spk)
}
let change1 = create_change_tx(&mut wallet)?;
wallet.persist(&mut conn)?;
// Reload the wallet (as after a restart) before the first tx is seen on chain.
let mut wallet = Wallet::load()
.load_wallet(&mut conn)?
.expect("wallet is persisted");
let change2 = create_change_tx(&mut wallet)?;
assert_ne!(change1, change2, "the same change address must not be handed out twice");
Ok(())
}
The test currently fails: change1 and change2 are identical.
Expected behavior
A change address that has been handed to a transaction should not be handed out
again after the wallet is persisted and reloaded.
- Lenguaje dominante
- Rust
- Estrellas
- 62
- Forks
- 118
- Merge medio
- 5 h 5 min
- PR fusionados (30 d)
- 8
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de bitcoindevkit/bdk_wallet
-
Change output crossing a compactsize boundary leaves the fee slightly below the requested feeratePosiblemente ocupada @artofbitcoin la tomó hace 20 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
bitcoindevkit/bdk_wallet#578 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
bdk_wallet uses `std` items without feature gatePosiblemente ocupada @Chibey-max la tomó hace 24 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
bitcoindevkit/bdk_wallet#574 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
`CaravanExport::to_descriptors` accepts duplicate xpubsPosiblemente ocupada @chukwudiikeh la tomó hace 24 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
bitcoindevkit/bdk_wallet#560 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
`PsbtUtils::fee_amount` panics when PSBT outputs exceed inputsPosiblemente ocupada @shubham5080 la tomó hace 47 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
bitcoindevkit/bdk_wallet#558 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
fix(tests): add new tests for `MissingNonWitnessUtxo` / `InvalidNonWitnessUtxo` signing errorsPosiblemente ocupada @oleonardolima la tomó hace 5 días. Abiertotests
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
bitcoindevkit/bdk_wallet#552 · 1 comentario · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de bitcoindevkit/bdk_wallet
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
libnativeapi/nativeapi#21 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
longbridge/nsh#804 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
Los mantenedores suelen responder en 1 día