0

When a user registers on my web app, a HD wallet is generated and a webhook is instantiated on the blockcypher API to listen to unconfirmed and single confirmation transactions. When an unconfirmed transaction is detected, and event is emitted to a route on my express app from the blockcypher website and the user is notified that their transaction has been received. When the transaction has one confirmation, I am attempting to move the money from their wallet to a main, hot wallet. However I'm getting an error when I try to broadcast the raw tx.

ERR When broadcasting the TX

Here is the code I have. What does this error mean and how can I fix it. Additionally, if there are alternate methods of achieving the same outcome. Would be very open to hearing / trying them out.

Here is the code that listens to and signs the transactions.

    // UNCONFIRMED TRANSACTION
    api.post('/callbacks/new-tx', async function(req, res) {
    console.log('POST: Callback received from blockcypher');
    console.log(req.body);
    fs.appendFile('Deposits.txt', 'Unconfirmed Deposit\n' + JSON.stringify(req.body) + '\n');

    // FIND OWNER OF ADDRESS
    User.findOne({'keyPairs.address': req.body.addresses[0]}, function(err, user) {
        // UPDATE BALANCE OF USER
        // user.balance += req.body.outputs[0].value;

        // CREATE TRANSACTION MODEL OF EVENT (RECORD OF TRANSACTION TO DB)
        var transaction = new Transaction({
            to: req.body.addresses[0],
            from: req.body.addresses[1],
            change: req.body.addresses[2],
            amount: req.body.outputs[0].value,
            time: moment().format('MMMM Do YYYY, h:mm:ss a'),
        });

        transaction.save((err) => {
            // HANDLE ERROR
            if (err) {return 'rest in pieces'}

            io.emit('notify', {
                user_id: user._id, 
                title: 'Deposit Received',
                message: 'We\'ve recieved your deposit of ' + req.body.outputs.value + 'BTC. Your balance will be updated after 1 confirmation.',
                color: 'success',
            })
        })
    });


    // RESPOND TO API 
    res.status(200).send('ok');
});

// 1 CONFIRMATION
api.post('/callbacks/confirmed', async function(req, res) {
    console.log('confirmed blockcypher api');
    console.log(req.body);
    fs.appendFile('Deposits.txt', 'Confirmed Deposit\n' + JSON.stringify(req.body) + '\n');
    // NEED TO CHECK STRUCTURE OF CALLBACK RESPONSE

    let webhook_id;
    let address = req.body.addresses[1];

    User.findOne({'keyPairs.address' : address}, async function(err, user) {
        var keypair = await WalletService.createKeyPair(user.accountIndex);
        user.btc_address = keypair.address;
        user.btc_s = keypair.secret;

        // SEND COINS TO MAIN WALLET
        async function send() {
            let max; 
            let main_wallet = config.master_address;
            let transaction = new bitcore.Transaction();

            WalletService.getAddressUtxo(address).then(utxo => {
                for(let i=0; i<utxo.length; i++) {
                    transaction.from(utxo[i])
                }

            // set full wallet balance
            WalletService.getAddressBalance(address)
                .then(balance => {
                    max = balance.final_balance;

                    transaction.to(main_wallet, max);
                    transaction.change(keypair.address);

                    let priv_key = new Promise((resolve, reject) => {
                        user.keyPairs.forEach(kp => {
                            if (kp.address == address) {
                                resolve(kp);
                            }
                        })
                    })

                    priv_key.then(kp => {
                        axios.get('https://bitcoinfees.earn.com/api/v1/fees/recommended')
                            .then(fee => {
                                var f = fee.data; 
                                var finalFee = (transaction.toString().length / 2) * f.hourFee;

                                transaction.to(main_wallet, max-finalFee);
                                transaction.fee(finalFee);
                                transaction.sign(kp.secret);

                                // EMIT TRANSACTION!!!!!!!!!
                                axios.post('https://api.blockcypher.com/v1/btc/test3/txs/push', JSON.stringify({
                                    tx: transaction.toString(),
                                })).then(res => {
                                    console.log('Transaction Successfully Sent');
                                    console.log('Response: ');
                                    console.log(res.data);
                                })

                                /*
                                    NOTICE!
                                    Remember that when a user deposits to their HD wallet, they pay fee to send.
                                    Once balance is confirmed, we need to move money to our main wallet.
                                    Moving this money will incur another fee. So add the full amount to users
                                    balance and send the amount MINUS the fee to our big daddy wallet. 
                                */

                                // on success
                                user.balance += bitcore.Unit.fromSatoshis(max).toMilis();

                                io.emit('increase-balance', {
                                    id: user._id,
                                    amount: bitcore.Unit.fromSatoshis(max).toMilis(),
                                });

                                io.emit('notify', {
                                    user_id: user._id,
                                    color: 'success',
                                    message: 'Deposit has been confirmed. Your balance has been updated'
                                });
                                // ======================================
                            })
                    })
                })
            })
        } await send(); 
        // ========================

        user.save((err) => {
            console.log('new keypair generated and saved for' + user.username);

            // unconfirmed hook
            var unconfirmed_hook = {
                "event": "unconfirmed-tx",
                "address": address, 
                "url": config.callback_address + "/new-tx" 
            }

            // confirmed hook
            var confirmed_hook = {
                "event": "confirmed-tx",
                "address": address, 
                "url": config.callback_address + "/confirmed" 
            }

            // delete old webhook
            axios({
                method: 'DELETE',
                url: 'https://api.blockcypher.com/v1/btc/test3/hooks/' + webhook_id,
            });

            // create new webhooks

            // create unconfirmed_hook
            axios({
                method: 'POST', 
                url: 'https://api.blockcypher.com/v1/btc/test3/hooks?token=' + config.blockcypher_token, 
                data: JSON.stringify(unconfirmed_hook)
            }).then(res => {
                console.log('*NEW* Webhook Unconfirmed Webhook Created')
            })

            axios({
                method: 'POST', 
                url: 'https://api.blockcypher.com/v1/btc/test3/hooks?token=' + config.blockcypher_token, 
                data: JSON.stringify(confirmed_hook),
            }).then(res => {
                console.log('*NEW* Webhook Confirmed Webhook Created')
            })
        });
    });
});

pls help. thanks.

Lorza
  • 459
  • 8
  • 23
  • I would suggest adding some additional `console.log` methods to compare your `vin` transactions and generated `vout` transactions. The error seems to be pretty straightforward as it says the *sum of **inputs** is less than the sum of **outputs***. Blockchain transactions are all just proof of an amount moving, so the problem could be either the input you are using is not enough to cover the output you are expecting, or the txfee was not taken into the math eq – Pixxl Dec 15 '18 at 07:54
  • If you could provide the `rawtransaction` you've generated and are attempting to sign/submit that would be helpful as well. – Pixxl Dec 15 '18 at 07:55
  • Hey, sorry for the delay, busy few days. Here's the rawtx 0100000001896ab75fa96301f95fd2f86bda3e0f8284714ef1a788a201e0ac9f5698159e20000000006a47304402207df2ebe1d9c6864c05571e515b7701c4ab6f14e13002484c8fea220c5320fbf5022051e29462a5b8c139b551ec4b66bc6a3b0093754b8d45fb7c8b0d6772fcbb0498012103850a664b27a1d4d066c4cd6b79df1c5a55e86f8c608e0510077b03229e7f7290ffffffff0261180000000000001976a914d22ac447895abd2ed9523042a407c3a85db3fb9188ac0d170000000000001976a914d22ac447895abd2ed9523042a407c3a85db3fb9188ac00000000 – Lorza Dec 17 '18 at 23:04
  • 1
    No worries, I fixed the issue. Very silly mistake which I could not see for the life of me. – Lorza Dec 18 '18 at 03:23

1 Answers1

1

Recipient was being included twice in the outputs.

Lorza
  • 459
  • 8
  • 23