bcoin-org / bcoin

Javascript bitcoin library for node.js and browsers
https://bcoin.io
Other
3.01k stars 811 forks source link

Not getting wallet updated balance after spvnode restart #140

Closed ghost closed 7 years ago

ghost commented 7 years ago

I can't get my wallet balance updated on the spv node. I've already downloaded the full blockchain headers (+300MB): `[info] Node is loaded. mpziU8iqm4Z55M69sByFNX7v4DyAhb7XE7

` Here you can see the address: https://live.blockcypher.com/btc-testnet/address/mpziU8iqm4Z55M69sByFNX7v4DyAhb7XE7/ Code (I've separated the spvcomponents to personalize storage location): ``` const bcoin = require('bcoin').set('testnet'), config = require('./config'); var logger = new bcoin.logger({ level: 'info', color: true }); var spv = new bcoin.spvnode({ prefix: __dirname, logger: logger, db: 'leveldb' }); // SPV chains only store the chain headers. var chain = new bcoin.chain({ db: 'leveldb', location: config.btc_node_location + '/spvchain', spv: true }); var pool = new bcoin.pool({ chain: spv.chain, spv: true, maxPeers: 8 }); var walletdb = new bcoin.walletdb({ db: 'leveldb', location: config.btc_node_location + '/wallet', // master: config.xpub }); spv.chain = chain; spv.pool = pool; spv.walletdb = walletdb; spv.pool.on('error', function(err) { logger.error(err); }); spv.pool.on('tx',function(tx) logger.info('new tx ' + tx); }); spv.open().then(function(){ spv.connect().then(function(){ spv.startSync(); }); spv.walletdb.get('primary').then(function(wallet){ // current balance wallet.getBalance().then(console.log); // print balance when it changes wallet.on('balance', function(balance){ console.log(balance); // print wallet address console.log(wallet.getAddress('base58')); }); // list all receive addresses of the default account wallet.getAccount('default').then(function(account){ var addr, i; for(i = 0; i < account.receiveDepth; i++) { addr = account.deriveReceive(i).getAddress('base58'); console.log(addr); } }); }); }); ``` (Also I'm not getting the tx event fired). Please help. Thanks in advance.
bucko13 commented 7 years ago

I was having a similar issue building out a node and trying to personalize the storage location and I think the way that the node is instantiated, you can't really overwrite with your own chain and pool objects (or at least it wasn't working when I tried that way).

I would recommend just setting the SPVNode as normal with the appropriate config options you want. I believe you can update the location (though not the name) of your databases with the prefix config option. If you've already got the whole chain downloaded, you can find it in the default ~/.bcoin directory and just copy and paste to the custom location, instead of downloading it all again. You can do the same with your walletdb.

I haven't played around with this yet myself but to get the balance to update in your SPVNode you need to add the address to the node's bloom filter so that it knows to look for it. You do this with watchAddress (See SPV Sync example).

I'm pretty new at this myself so not totally confident in my answers, but hopefully it helps put you in the right direction! For reference, this is the sample node I put together (FullNode though)

ghost commented 7 years ago

@Bucko13 thanks for your detailed reply. Now with the code below I'm getting the tx and getting the balance updated in real time, but I don't receive the previous confirmed txs sent while the script was not running. And I could define the wallet location, but not the chain/pool one, for some reason:

edit: and the spvchain and wallet folder are created in the same dir the script is running (see config). @chjj awesome lib, btw!!

const
    bcoin = require('bcoin').set('testnet'),
    config = require('./config');

var logger = new bcoin.logger({
    level: 'debug',
    color: true
});

var spv = new bcoin.spvnode({
    prefix: __dirname,
    logger: logger,
    db: 'leveldb'
});

var walletdb = new bcoin.walletdb({
    db: 'leveldb',
    logger: logger,
    location: config.btc_node_location + '/wallet',
});

spv.walletdb = walletdb;

spv.on('error', function(err) {
    logger.error(err);
});

spv.pool.on('tx', function(tx) {
    console.log('------ new tx!!! :3' + tx);
    spv.walletdb.addTX(tx);
});

spv.open().then(function() {

    spv.connect().then(function() {
        spv.startSync();
    });

    spv.walletdb.get('primary').then(function(wallet) {

        console.log(spv.walletdb.getAccounts().then(console.log));

        // Watch our balance update as we receive transactions.
        wallet.on('balance', function(balance) {
            // Convert satoshis to BTC.
            var btc = bcoin.amount.btc(balance.unconfirmed);
            console.log('Your wallet balance has been updated: %s', btc);
            wallet.getBalance().then(console.log);
        });

        // list all receive addresses of the default account
        wallet.getAccount('default').then(function(account) {
            var addr, i;
            console.log('---------- Receive addresses: ---------');
            for(i = 0; i < account.receiveDepth; i++) {
                addr = account.deriveReceive(i).getAddress('base58');
                console.log(addr);
                // Add our address to the spv filter.
                spv.pool.watchAddress(addr);
            }
            console.log('------------------------------');
        });

    });
});
bucko13 commented 7 years ago

Glad that helped you make some progress!

I was running into the same thing in terms of defining the pool, chain, and wallet location. For the full node REST client, I was actually getting db errors when doing the walletdb as you have above so I think what's going in is that the way the node is instantiated you may not be able to set these and have the node load up properly.

For the previous transactions, I'm not sure but you may need to initiate a rescan. The full node seems to do this automatically when it restarts, not sure about SPV though.

ghost commented 7 years ago

@Bucko13 As I understand from the source code the spvnode doesn't sync the wallet db, but I can't find any documentation for doing that. I'll give a try to the REST client.

BluSyn commented 7 years ago

Are you running this in the browser, or in nodejs?

ghost commented 7 years ago

@BluSyn nodejs

chjj commented 7 years ago

@jmliz, you must also call walletdb.addBlock(chainEntry, block.txs) when a block is connected. I recommend using the SPVNode object instead, since it will also handle reorgs and other tricky things.

ghost commented 7 years ago

@chjj thanks a lot! now shows the confirmed balance but the unconfirmed one didn't update and equals the confirmed one.

How can I use the SPVNode to handle all of this automatically, as you suggest? I tried the spvnode script at /bin but it didn't update the wallet records.

Also I noticed that there may be a typo on lib/node/config.js:58:

assert(typeof options.prefix.length > 0);

Shouldn't the typeof be removed?

BluSyn commented 7 years ago

now shows the confirmed balance but the unconfirmed one didn't update and equals the confirmed one.

In bcoin wallet, confirmed = blockchain balance, unconfirmed = blockchain + mempool balance. So this is correct. If unconfirmed and confirmed are equal, this likely means there are no unconfirmed txs. This is different than how other APIs behave, but it was intentional. @chjj We should probably add a note to documentation about this?

ghost commented 7 years ago

@BluSyn that seems counterintuitive to me :| but thanks for the clarification.

ghost commented 7 years ago

@BluSyn @chjj @Bucko13 updated 👏:

const
    config = require('./config'),
    bcoin = require('bcoin').set(config.btc_network);

var logger = new bcoin.logger({
    level: 'debug',
    color: true
});

var options = bcoin.config({
    prefix: config.btc_node_location,
    logger: logger,
    db: 'leveldb'
});

var spv = new bcoin.spvnode(options);

spv.on('connect', function(entry, block) {
    console.log('New block.');
    spv.walletdb.addBlock(entry, block.txs);
});

spv.on('error', function(err) {
    logger.error(err);
});

spv.on('tx', function(tx) {
    console.log('------ New tx. Adding to walletdb...' + tx);
    spv.walletdb.addTX(tx, function(err) {
        if(err) {
            logger.error(err);
        }
    });
});

spv.open().then(function() {

    spv.walletdb.get('primary').then(function(wallet) {

        console.log(spv.walletdb.getAccounts().then(console.log));

        // Watch our balance update as we receive transactions.
        wallet.on('balance', function(balance) {
            // Convert satoshis to BTC.
            var btc = bcoin.amount.btc(balance.unconfirmed);
            console.log('Your wallet balance has been updated: %s', btc);
            wallet.getBalance().then(console.log);
        });

        console.log('wallet state heigt'+spv.walletdb.state.height);

        // list all receive addresses of the default account
        wallet.getAccount('default').then(function(account) {
            var addr, i;
            console.log('---------- Receive addresses: ---------');
            for(i = 0; i < account.receiveDepth; i++) {
                addr = account.deriveReceive(i).getAddress('base58');
                console.log(addr);
                // Add our address to the spv filter.
                spv.pool.watchAddress(addr);
            }
            console.log('------------------------------');
        }).then(function() {
            spv.connect().then(function() {
                spv.startSync();
            });
        });
    });
});
ghost commented 7 years ago

@BluSyn I'm closing this issue since my problem was solved. I'll create an issue for the confirmed=unconfirmed thing you mentioned and I'll made a pull request for the typo I mentioned on node/config.js.

halseth commented 7 years ago

Thanks, I had the exact same problem, this helped :) My problem was that I was creating a wallet with a specific master key each time, instead of getting.

ghost commented 7 years ago

@halseth glad it helped you :)

ghost commented 7 years ago

@halseth wait! there's an error on my last snippet. the addTX returns a promise. It should be:

spvnode.walletdb.addTX(tx).then(function() {
    // ...
})