Skip to content

Error fetching script balance from current electrs server #45

@sgeisler

Description

@sgeisler

There seem to be two different kinds of electrum server implementations out there (or it's just a bug) when it comes to the result of script_get_balance:

  • one (electrum.blockstream.info) returns a big positive number for outgoing unconfirmed value such that 2^64-value==actual unconfirmed outgoing balance, this might be an unpatched bug in an earlier electrs version, otoh most clients seem to cope with it, so maybe the standard is weird and abuses some overflow arithmetic?
  • current electrs versions (e.g. mine at bcwat.ch) will return a negative unconfirmed balance instead (which seems like the sane thing to do, but produces an error in this library)

The problem is that rust-electrum-client assumes the former and crashes on receiving a response in the latter format (since the data type serde tries to parse is a u64).

To test this behavior you can take any address with an outgoing, unconfirmed transaction and run the following example:

extern crate bitcoin;
extern crate electrum_client;

use bitcoin::Address;
use electrum_client::{Client, ConfigBuilder, ElectrumApi};
use std::str::FromStr;

fn main() {
    let addr = "1EQDBXLZUPDFiVNZMbsc4a6vkL3rp6haVQ"; // <- change this

    let client1 = Client::new("ssl://electrum.blockstream.info:50002").unwrap();
    let balance1 = client1
        .script_get_balance(&Address::from_str(addr).unwrap().script_pubkey())
        .unwrap();
    eprintln!("blockstream responded: {:?}", balance1);

    let cfg = ConfigBuilder::new().validate_domain(false).build();
    let client2 = Client::from_config("ssl://bcwat.ch:50002", cfg).unwrap();
    let balance2 = client2
        .script_get_balance(&Address::from_str(addr).unwrap().script_pubkey())
        .unwrap();
    eprintln!("bcwatch responded: {:?}", balance2);

    assert_eq!(balance1.unconfirmed, balance2.unconfirmed);
    assert_eq!(balance1.confirmed, balance2.confirmed);
}

It should crash somewhat like this:

blockstream responded: GetBalanceRes { confirmed: 26160000, unconfirmed: 18446744073683391616 }
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: AllAttemptsErrored([JSON(Error("invalid value: integer `-26160000`, expected u64", line: 0, column: 0))])', examples/unconfirmed_error.rs:21:10

The easy fix would be to make the unconfirmed balance a i64 and special casing e.g. any value over 18444644073709551616 (2^64-21*10^14). Maybe @romanz knows what's the right thing to do or how this situation came to be, was blockstream's version just forked before romaz/electrs@f387d038bc500bafaf44e08448aee25ef795a3d1 and nobody actually uses script_get_balance so no once noticed or was there a change in the protocol?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions