How to initiate a transaction with address and privateKey using nodejs

0

I have a list of bitcoin addresses and private keys, I need to write an automation script which checks for the balance in each address and transfer the balance funds to another bitcoin address. My question is how I perform the fund transfer with address and privateKey using nodejs

Thanks in Advance

Mohammed niyas

Posted 2018-09-10T09:06:49.030

Reputation: 11

i.e. "How do I steal bitcoin from accounts I've hacked..."JBaczuk 2018-09-10T13:38:03.707

No, it's not stealing bitcoins. I have the list of accounts and privateKeys that I have generated. I am using these accounts for receiving funds. For privacy reasons, I keep on switching the incoming address and monitor all the address and if any funds deposited on these address I move those funds to my hot wallet address.Mohammed niyas 2018-09-10T15:14:01.410

Oh, ok :). Your use case is exactly why HD wallets (BIP32) were introduced. You can generate many addresses from a single private key and not have to store a list of private keys.JBaczuk 2018-09-10T15:38:54.920

Answers

0

Given a list of bitcoin private keys, first you need to check the balance of the corresponding address, probably best done using a block explorer, e.g. blockchain.info:

Single Address

https://blockchain.info/rawaddr/$bitcoin_address
Address can be base58 or hash160

Optional limit parameter to show n transactions e.g. &limit=50 (Default: 50, Max: 50)
Optional offset parameter to skip the first n transactions e.g. &offset=100 (Page 2 for limit 50)

{
    "hash160":"660d4ef3a743e3e696ad990364e555c271ad504b",
    "address":"1AJbsFZ64EpEfS5UAjAfcUG8pH8Jn3rn1F",
    "n_tx":17,
    "n_unredeemed":2,
    "total_received":1031350000,
    "total_sent":931250000,
    "final_balance":100100000,
    "txs":[--Array of Transactions--]
}

Then, you can use bitcoinjs-lib, for example, to create a raw transaction for each private key:

const alice = bitcoin.ECPair.fromWIF('L1uyy5qTuGrVXrmrsvHWHgVzW9kKdrp27wBC7Vs6nZDTF2BRUVwy')
const txb = new bitcoin.TransactionBuilder()

txb.setVersion(1)
txb.addInput('61d520ccb74288c96bc1a2b20ea1c0d5a704776dd0164a396efec3ea7040349d', 0) // Alice's previous transaction output, has 15000 satoshis
txb.addOutput('1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP', 12000)
// (in)15000 - (out)12000 = (fee)3000, this is the miner fee

txb.sign(0, alice)

For which, you will need unspent outputs to each private key's address.

You will also need to broadcast the raw transaction:

// build and broadcast our RegTest network
regtestUtils.broadcast(txb.build().toHex(), done)

JBaczuk

Posted 2018-09-10T09:06:49.030

Reputation: 6 172

0

I had a similar issue for a store I had where I was accepting bitcoin. I ended up creating a nodejs app that would take the input of just a private key and an address. It would gather all the UTXOs that belong to the private key using a third party API, and then send ALL funds to the address specified. Back then I was using bitcore-lib, but that library is now outdated, I would use bitcoin-js if I re-did it today.

These are the basic functions I used:

app.post("/address", function(req,res){
    var pkey = req.body.pkey;
    var addr = req.body.addy;

    validateAddress(addr, function(isAddyValid){
        if(isAddyValid == 0){
          res.render("pages/index.ejs", {
            outMessage: "Destination address invalid"
          });  
        } else {
            validatePrivateKey(pkey, function(isPkValid){
                if(isPkValid == 0){
                    res.render("pages/index.ejs", {
                    outMessage: "Private Key invalid"
                    });
                } else {
                    convertPK(pkey, function(convertedAddy){
                        getUTXO(convertedAddy, function(result, feeAmt, totalToSend){
                            if(result == 1){
                                res.render("pages/index.ejs", {
                                    outMessage: "No UTXO"
                                });     
                            } else if(result == 2){
                                res.render("pages/index.ejs", {
                                    outMessage: "Source offline"
                                });
                            } else if(result == 3){
                                res.render("pages/index.ejs", {
                                    outMessage: "Insufficient funds to pay fee"
                                });
                            } else {
                                buildTX(result, feeAmt, totalToSend, pkey, addr, function(payloadTx){
                                    pushTX(payloadTx, function(txdone){
                                        if(txdone !== 1){
                                            res.render("pages/index.ejs", {
                                            outMessage: "TX ID: " + txdone
                                            });
                                        } else {
                                            res.render("pages/index.ejs", {
                                            outMessage: "broadcast failed try later"
                                            });
                                        }
                                    });
                                });
                            }
                        });
                    });
                }
            });//end validatePrivateKey
        }
    });//end validate address
}); //end app post

function validateAddress(output, result){
    addyValue = output.replace(/[^\w\s]/gi, '');
    if(bitcore.Address.isValid(addyValue)){
    result(1);
    } else {
    result(0);    
    };
};

//convert pk to addr
function convertPK(pkeyValue, result){
    var address = new bitcore.PrivateKey(pkeyValue).toAddress();
    result(address);
};

function validatePrivateKey(wif, result){
pkeyValue = wif.replace(/[^\w\s]/gi, '');
        if(bitcore.PrivateKey.isValid(pkeyValue)){
        //private key is valid
        result(1);
        } else {
        result(0);    
        };
};

//get outputs
function getUTXO(address, callback){
    request({
        url: "https://chain.so/api/v2/get_tx_unspent/btc/"+address,
        json: true
    }, function(error, response, body){
        if(!error && response.statusCode == 200){
            if(body.data.txs.length < 1){
                //no utxos
                console.log("no utxo");
                var err = 1;
                callback(err);
            }
            var status = body.status;
            var num = body.data.txs.length;
            var utxos = [];
            var totalSats = 0;  
            var txSize = 44;
                //loop through all UTXOs
                for(i=0;i < num; i++){
            var convertSats = body.data.txs[i].value * 100000000;
            convertSats = parseInt(convertSats);

                    var utxo = {
                    "txId": body.data.txs[i].txid,
                    "outputIndex": body.data.txs[i].output_no,
                    "address": address,
                    "script": body.data.txs[i].script_hex,
                    "satoshis": convertSats
                    };
            utxos.push(utxo);
            totalSats = totalSats + convertSats;
            //calc tx size for fee
            txSize = txSize + 180;
                }; //end utxo loop
            getBestFee(function(bestHourFee){
                var fee = txSize * bestHourFee;
                totalSats = totalSats - fee;
                console.log(totalSats);
                console.log(fee);
                    if(totalSats < 1){
                        //not enough funds to send
                        var err = 3;
                        callback(err);
                    } else {
                        callback(utxos, fee, totalSats);  
                    }
            });            
       } else {
           //err or no response from api
           console.log("no response from api");
            var error = 2;
           callback(error);
       }
    });
};

//build transaction
function buildTX(utxo, fee, total, pkeyValue, output, callback){

        var transaction = new bitcore.Transaction()
        .from(utxo)
        .to(output, total)
        .sign(pkeyValue);

        //payload to push tx
        var txjson = transaction.toString();
        var pload = {
            "tx": txjson
        };
        callback(pload);  
};


//push transaction
function pushTX(pload, callback){
    request({
    url: "https://api.blockcypher.com/v1/btc/main/txs/push",
    method: "POST",
    json: true,
    headers: {"content-type": "application/json"},
    body: pload
    }, function(err, response, body){
            if(err){ 
         //no response or error POST to chainso
             callback(1);    
        } else {
                console.log(JSON.stringify(body));
                completeTxId = body.tx.hash;
                console.log("done");
                callback(completeTxId);
            };                
    });
};

function getBestFee(bestFee){
  var findfee = "https://bitcoinfees.21.co/api/v1/fees/recommended";
    request({
        url: findfee,
        json: true
    }, function(error, response, body){
        if(!body.hourFee){
            //no response from api use 150 sats per byte
            var fee = 150;
            bestFee(fee);
        }
        if(body.hourFee){
            var fee = body.hourFee;
            fee = fee * 0.5;
            fee = Math.ceil(fee);
            bestFee(fee);
        }
    });  
}

If you want to see the entire project here's the repo, it's a simple nodejs express app with a form submission POST option and a GET endpoint /sweep option. Be aware the way I wrote it, it will only work with legacy P2PKH addresses (addresses that start with a 1), no segwit.

https://github.com/coinables/sweepkey

m1xolyd1an

Posted 2018-09-10T09:06:49.030

Reputation: 3 356

Sending private key through API post request body is not a good approach.Mohammed niyas 2018-09-14T09:06:01.387