Change address `mark_used` marker is not persisted; same change address reissued after restart

Abierto Apto para principiantes
#577 3 comentarios 0 reacciones 0 asignados Ver en GitHub

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
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
rust

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

bug

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

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de bitcoindevkit/bdk_wallet

Todos los issues de bitcoindevkit/bdk_wallet

Issues similares

Más issues de Rust

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.