updated interfase to create: create and createAndSign
This commit is contained in:
parent
eb8763ac9f
commit
aab52ad229
83
README.md
83
README.md
@ -130,54 +130,53 @@ var bitcore = require('bitcore');
|
|||||||
var networks = bitcore.networks;
|
var networks = bitcore.networks;
|
||||||
var Peer = bitcore.Peer;
|
var Peer = bitcore.Peer;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Address = bitcore.Address;
|
|
||||||
var Script = bitcore.Script;
|
|
||||||
var coinUtil = bitcore.util;
|
|
||||||
var PeerManager = require('soop').load('../PeerManager', {
|
var PeerManager = require('soop').load('../PeerManager', {
|
||||||
network: networks.testnet
|
network: networks.testnet
|
||||||
});
|
});
|
||||||
|
|
||||||
var createTx = function() {
|
// this can be get from insight.bitcore.io API o blockchain.info
|
||||||
var TXIN = 'd05f35e0bbc495f6dcab03e599c8f5e32a07cdb4bc76964de201d06a2a7d8265';
|
var utxos = {
|
||||||
var TXIN_N = 0;
|
"unspent": [
|
||||||
var ADDR = 'muHct3YZ9Nd5Pq7uLYYhXRAxeW4EnpcaLz';
|
{
|
||||||
var VAL = '0.001';
|
"address": "n4g2TFaQo8UgedwpkYdcQFF6xE2Ei9Czvy",
|
||||||
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
var txobj = {
|
"scriptPubKey": "76a914fe021bac469a5c49915b2a8ffa7390a9ce5580f988ac",
|
||||||
version: 1,
|
"vout": 1,
|
||||||
lock_time: 0,
|
"amount": 1.0101,
|
||||||
ins: [],
|
"confirmations":7
|
||||||
outs: []
|
},
|
||||||
};
|
{
|
||||||
|
"address": "mhNCT9TwZAGF1tLPpZdqfkTmtBkY282YDW",
|
||||||
var txin = {
|
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2",
|
||||||
s: coinUtil.EMPTY_BUFFER, // Add signature
|
"scriptPubKey": "76a9141448534cb1a1ec44665b0eb2326e570814afe3f188ac",
|
||||||
q: 0xffffffff
|
"vout": 0,
|
||||||
};
|
"confirmations": 1,
|
||||||
|
"amount": 10
|
||||||
var hash = new Buffer(TXIN.split('').reverse(), 'hex');
|
},
|
||||||
var vout = parseInt(TXIN_N);
|
|
||||||
var voutBuf = new Buffer(4);
|
|
||||||
|
|
||||||
voutBuf.writeUInt32LE(vout, 0);
|
|
||||||
txin.o = Buffer.concat([hash, voutBuf]);
|
|
||||||
txobj.ins.push(txin);
|
|
||||||
|
|
||||||
var addr = new Address(ADDR);
|
|
||||||
var script = Script.createPubKeyHashOut(addr.payload());
|
|
||||||
var valueNum = coinUtil.parseValue(VAL);
|
|
||||||
var value = coinUtil.bigIntToValue(valueNum);
|
|
||||||
|
|
||||||
var txout = {
|
|
||||||
v: value,
|
|
||||||
s: script.getBuffer(),
|
|
||||||
};
|
|
||||||
txobj.outs.push(txout);
|
|
||||||
|
|
||||||
return new Transaction(txobj);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//private keys in WIF format (see Transaction.js for other options)
|
||||||
|
var keys = [
|
||||||
|
"cSq7yo4fvsbMyWVN945VUGUWMaSazZPWqBVJZyoGsHmNq6W4HVBV",
|
||||||
|
"cPa87VgwZfowGZYaEenoQeJgRfKW6PhZ1R65EHTkN1K19cSvc92G",
|
||||||
|
"cPQ9DSbBRLva9av5nqeF5AGrh3dsdW8p2E5jS4P8bDWZAoQTeeKB"
|
||||||
|
];
|
||||||
|
|
||||||
|
function createTx() {
|
||||||
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
|
|
||||||
|
var ret = Transaction.createAndSign(utxos, outs, keys);
|
||||||
|
|
||||||
|
/ * create and signing can be done in 2 steps using:
|
||||||
|
* var ret = Transaction.create(utxos,outs);
|
||||||
|
* and later:
|
||||||
|
* ret.tx.sign(ret.tx.selectedUtxos, outs, keys);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return ret.tx.serialize().toString('hex');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
var peerman = new PeerManager();
|
var peerman = new PeerManager();
|
||||||
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
peerman.addPeer(new Peer('127.0.0.1', 18333));
|
||||||
|
|
||||||
|
|||||||
145
Transaction.js
145
Transaction.js
@ -778,10 +778,16 @@ Transaction._sumOutputs = function(outs) {
|
|||||||
return valueOutSat;
|
return valueOutSat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.prepare = function (ins, outs, opts) {
|
/*
|
||||||
opts = opts || {};
|
* createWithFee
|
||||||
|
* Create a TX given ins (selected already), outs, and a FIXED fee
|
||||||
|
* details on the input on .create
|
||||||
|
*/
|
||||||
|
|
||||||
|
Transaction.createWithFee = function (ins, outs, feeSat, opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
feeSat = feeSat || 0;
|
||||||
|
|
||||||
var feeSat = opts.feeSat || (opts.fee? util.parseValue(opts.fee) : FEE_PER_1000B_SAT );
|
|
||||||
var txobj = {};
|
var txobj = {};
|
||||||
txobj.version = 1;
|
txobj.version = 1;
|
||||||
txobj.lock_time = opts.lockTime || 0;
|
txobj.lock_time = opts.lockTime || 0;
|
||||||
@ -981,18 +987,74 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
|||||||
self.ins[i].s = scriptSig.getBuffer();
|
self.ins[i].s = scriptSig.getBuffer();
|
||||||
inputSigned++;
|
inputSigned++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var complete = inputSigned === l;
|
var complete = inputSigned === l;
|
||||||
|
|
||||||
return complete;
|
return complete;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create
|
||||||
|
*
|
||||||
|
* creates a transaction without signing it.
|
||||||
|
*
|
||||||
|
* @utxos
|
||||||
|
* @outs
|
||||||
|
* @opts
|
||||||
|
*
|
||||||
|
* See createAndSign for documentation on the inputs
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* { tx: {}, selectedUtxos: []}
|
||||||
|
* see createAndSign for details
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
Transaction.create = function (utxos, outs, opts) {
|
||||||
|
|
||||||
|
//starting size estimation
|
||||||
|
var size = 500;
|
||||||
|
var opts = opts || {};
|
||||||
|
|
||||||
|
var givenFeeSat;
|
||||||
|
if (opts.fee || opts.feeSat) {
|
||||||
|
givenFeeSat = opts.fee ? opts.fee * util.COIN : opts.feeSat;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedUtxos;
|
||||||
|
do {
|
||||||
|
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
||||||
|
maxSizeK = parseInt(size/1000) + 1;
|
||||||
|
var feeSat = givenFeeSat
|
||||||
|
? givenFeeSat : maxSizeK * FEE_PER_1000B_SAT ;
|
||||||
|
|
||||||
|
var valueOutSat = Transaction
|
||||||
|
._sumOutputs(outs)
|
||||||
|
.add(feeSat);
|
||||||
|
|
||||||
|
selectedUtxos = Transaction
|
||||||
|
.selectUnspent(utxos,valueOutSat / util.COIN, opts.allowUnconfirmed);
|
||||||
|
|
||||||
|
if (!selectedUtxos) {
|
||||||
|
throw new Error(
|
||||||
|
'the given UTXOs dont sum up the given outputs: '
|
||||||
|
+ valueOutSat.toString()
|
||||||
|
+ ' (fee is ' + feeSat
|
||||||
|
+ ' )SAT'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var tx = Transaction.createWithFee(selectedUtxos, outs, feeSat, {
|
||||||
|
remainderAddress: opts.remainderAddress,
|
||||||
|
lockTime: opts.lockTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
size = tx.getSize();
|
||||||
|
} while (size > (maxSizeK+1)*1000 );
|
||||||
|
|
||||||
|
return {tx: tx, selectedUtxos: selectedUtxos};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create
|
* createAndSign
|
||||||
*
|
*
|
||||||
* creates and signs a transaction
|
* creates and signs a transaction
|
||||||
*
|
*
|
||||||
@ -1031,66 +1093,33 @@ Transaction.prototype.sign = function (selectedUtxos, keys, opts) {
|
|||||||
* signhash: SIGHASH_ALL
|
* signhash: SIGHASH_ALL
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
*
|
||||||
|
* Retuns:
|
||||||
|
* {
|
||||||
|
* tx: The new created transaction,
|
||||||
|
* selectedUtxos: The UTXOs selected as inputs for this transaction
|
||||||
|
* }
|
||||||
|
*
|
||||||
* Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given,
|
* Amounts are in BTC. instead of fee and amount; feeSat and amountSat can be given,
|
||||||
* repectively, to provide amounts in satoshis.
|
* repectively, to provide amounts in satoshis.
|
||||||
*
|
*
|
||||||
* If no remainderAddress is given, and there is a remainderAddress
|
* If no remainderAddress is given, and there are remainder coins, the
|
||||||
* first in address will be used. (TODO: is this is reasonable?)
|
* first IN address will be used to return the coins. (TODO: is this is reasonable?)
|
||||||
*
|
*
|
||||||
* if not keys are provided, the transaction will no be signed. .sign can be used to
|
* The Transaction creation is handled in 2 steps:
|
||||||
* sign it later.
|
* .create
|
||||||
*
|
* .selectUnspent
|
||||||
* The Transaction creation is handled in 3 steps:
|
* .createWithFee
|
||||||
* .selectUnspent
|
|
||||||
* .prepare
|
|
||||||
* .sign
|
* .sign
|
||||||
*
|
*
|
||||||
|
* If you need just to create a TX and not sign it, use .create
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Transaction.createAndSign = function (utxos, outs, keys, opts) {
|
||||||
Transaction.create = function (utxos, outs, keys, opts) {
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
|
ret.tx.sign(ret.selectedUtxos, keys);
|
||||||
//starting size estimation
|
return ret;
|
||||||
var size = 500;
|
|
||||||
var opts = opts || {};
|
|
||||||
|
|
||||||
var givenFeeSat;
|
|
||||||
if (opts.fee || opts.feeSat) {
|
|
||||||
givenFeeSat = opts.fee ? opts.fee * util.COIN : opts.feeSat;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
// based on https://en.bitcoin.it/wiki/Transaction_fees
|
|
||||||
maxSizeK = parseInt(size/1000) + 1;
|
|
||||||
var feeSat = givenFeeSat
|
|
||||||
? givenFeeSat : maxSizeK * FEE_PER_1000B_SAT ;
|
|
||||||
|
|
||||||
var valueOutSat = Transaction
|
|
||||||
._sumOutputs(outs)
|
|
||||||
.add(feeSat);
|
|
||||||
|
|
||||||
var selectedUtxos = Transaction
|
|
||||||
.selectUnspent(utxos,valueOutSat / util.COIN, opts.allowUnconfirmed);
|
|
||||||
|
|
||||||
if (!selectedUtxos) {
|
|
||||||
throw new Error(
|
|
||||||
'the given UTXOs dont sum up the given outputs: '
|
|
||||||
+ valueOutSat.toString()
|
|
||||||
+ ' (fee is ' + feeSat
|
|
||||||
+ ' )SAT'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
var tx = Transaction.prepare(selectedUtxos, outs, {
|
|
||||||
feeSat: feeSat,
|
|
||||||
remainderAddress: opts.remainderAddress,
|
|
||||||
lockTime: opts.lockTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
size = tx.getSize();
|
|
||||||
} while (size > (maxSizeK+1)*1000 );
|
|
||||||
|
|
||||||
if (keys) tx.sign(selectedUtxos, keys);
|
|
||||||
return tx;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var TransactionInputsCache = exports.TransactionInputsCache =
|
var TransactionInputsCache = exports.TransactionInputsCache =
|
||||||
|
|||||||
@ -8,195 +8,34 @@ var run = function() {
|
|||||||
var amt = '0.005';
|
var amt = '0.005';
|
||||||
var toAddress = 'myuAQcCc1REUgXGsCTiYhZvPPc3XxZ36G1';
|
var toAddress = 'myuAQcCc1REUgXGsCTiYhZvPPc3XxZ36G1';
|
||||||
var changeAddressString = 'moDz3jEo9q7CxjBDjmb13sL4SKkgo2AACE';
|
var changeAddressString = 'moDz3jEo9q7CxjBDjmb13sL4SKkgo2AACE';
|
||||||
var feeString = '0.0001';
|
|
||||||
|
|
||||||
var safeUnspent = [
|
var utxos = [{
|
||||||
{
|
address: "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
||||||
address: "mqSjTad2TKbPcKQ3Jq4kgCkKatyN44UMgZ",
|
txid: "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||||
hash: "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
vout: 1,
|
||||||
vout: 1,
|
ts: 1394719301,
|
||||||
ts: 1394719301,
|
scriptPubKey: "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
|
||||||
scriptPubKey: "76a9146ce4e1163eb18939b1440c42844d5f0261c0338288ac",
|
amount: 0.01,
|
||||||
amount: 0.01,
|
confirmations: 2
|
||||||
confirmations: 2
|
}];
|
||||||
}
|
|
||||||
]
|
|
||||||
;
|
|
||||||
|
|
||||||
console.log('TX Data: BTC:' + amt + ' => '+ toAddress + ', change To:' + changeAddressString ) ;
|
console.log('TX Data: BTC:' + amt + ' => '+ toAddress + ', change To:' + changeAddressString ) ;
|
||||||
console.log('Unspends:', safeUnspent);
|
console.log('Unspends Outputs:', utxos);
|
||||||
|
|
||||||
var wk = new bitcore.WalletKey({
|
|
||||||
network: bitcore.networks.testnet
|
|
||||||
});
|
|
||||||
wk.fromObj({ priv: priv, });
|
|
||||||
|
|
||||||
var wkObj= wk.storeObj();
|
|
||||||
var keyPairs = [{
|
|
||||||
key: wkObj.priv,
|
|
||||||
address: wkObj.addr,
|
|
||||||
}];
|
|
||||||
console.log('KEY DB IS:', keyPairs);
|
|
||||||
|
|
||||||
var Address = bitcore.Address;
|
|
||||||
var Transaction = bitcore.Transaction;
|
|
||||||
var Script = bitcore.Script;
|
|
||||||
var nets = bitcore.networks;
|
|
||||||
var z = bitcore.bignum(0);
|
|
||||||
var amt = bitcore.util.parseValue(amt);
|
|
||||||
|
|
||||||
if(z.cmp(amt) === 0 )
|
|
||||||
throw "spend amount must be greater than zero";
|
|
||||||
|
|
||||||
if(!changeAddressString)
|
|
||||||
throw "change address was not provided";
|
|
||||||
|
|
||||||
var fee = bitcore.util.parseValue(feeString || '0');
|
|
||||||
var total = bitcore.bignum(0).add(amt).add(fee);
|
|
||||||
var address = new Address(toAddress);
|
|
||||||
var sendTx = new Transaction();
|
|
||||||
var i;
|
|
||||||
|
|
||||||
var unspent = [];
|
|
||||||
var unspentAmt = bitcore.bignum(0);
|
|
||||||
|
|
||||||
|
|
||||||
for(i=0;i<safeUnspent.length;i++) {
|
var outs = [{address:toAddress, amount:amt}];
|
||||||
unspent.push(safeUnspent[i]);
|
var keys = [priv];
|
||||||
|
|
||||||
var amountSatoshiString = new bitcore.bignum(safeUnspent[i].amount * Math.pow(10,8)).toString();
|
var ret = bitcore.Transaction.createAndSign(utxos, outs, keys,
|
||||||
|
{remainderAddress: changeAddressString});
|
||||||
|
|
||||||
unspentAmt = unspentAmt.add(new bitcore.bignum(amountSatoshiString));
|
/* create and signing can be done in 2 steps using:
|
||||||
|
* var ret = Transaction.create(utxos,outs);
|
||||||
|
* and later:
|
||||||
|
* ret.tx.sign(ret.tx.selectedUtxos, outs, keys);
|
||||||
|
*/
|
||||||
|
|
||||||
// If > -1, we have enough to send the requested amount
|
var txHex = ret.tx.serialize().toString('hex');
|
||||||
if(unspentAmt.cmp(total) > -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(unspentAmt.cmp(total) < 0) {
|
|
||||||
throw "you do not have enough bitcoins to send this amount";
|
|
||||||
}
|
|
||||||
|
|
||||||
var txobj = {};
|
|
||||||
txobj.version = 1;
|
|
||||||
txobj.lock_time = 0;
|
|
||||||
txobj.ins = [];
|
|
||||||
txobj.outs = [];
|
|
||||||
|
|
||||||
for(i=0;i<unspent.length;i++) {
|
|
||||||
var txin = {};
|
|
||||||
|
|
||||||
txin.s = bitcore.util.EMPTY_BUFFER;
|
|
||||||
txin.q = 0xffffffff;
|
|
||||||
|
|
||||||
var hash = new bitcore.Buffer(unspent[i].hash, 'hex');
|
|
||||||
var hashReversed = bitcore.buffertools.reverse(hash);
|
|
||||||
var vout = parseInt(unspent[i].vout);
|
|
||||||
var voutBuf = new bitcore.Buffer(4);
|
|
||||||
voutBuf.writeUInt32LE(vout, 0);
|
|
||||||
|
|
||||||
txin.o = bitcore.Buffer.concat([hashReversed, voutBuf]);
|
|
||||||
txobj.ins.push(txin);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This is not used really, but we keep it for the future.
|
|
||||||
var version = address.version();
|
|
||||||
var script;
|
|
||||||
if (version == nets.livenet.addressPubkey || version == nets.testnet.addressPubkey)
|
|
||||||
script = Script.createPubKeyHashOut(address.payload());
|
|
||||||
else if (version == nets.livenet.addressScript || version == nets.testnet.addressScript)
|
|
||||||
script = Script.createP2SH(address.payload());
|
|
||||||
else
|
|
||||||
throw new Error('invalid output address');
|
|
||||||
|
|
||||||
var value = bitcore.util.bigIntToValue(amt);
|
|
||||||
var txout = {
|
|
||||||
v: value,
|
|
||||||
s: script.getBuffer(),
|
|
||||||
};
|
|
||||||
txobj.outs.push(txout);
|
|
||||||
var remainder = unspentAmt.sub(total);
|
|
||||||
|
|
||||||
if(z.cmp(amt) !== 0 ) {
|
|
||||||
var changeAddress = new Address(changeAddressString);
|
|
||||||
var changeValue = bitcore.util.bigIntToValue(remainder);
|
|
||||||
|
|
||||||
// This is not used really, but we keep it for the future.
|
|
||||||
var cversion = changeAddress.version();
|
|
||||||
var cscript;
|
|
||||||
if (cversion == nets.livenet.addressPubkey || cversion == nets.testnet.addressPubkey)
|
|
||||||
cscript = Script.createPubKeyHashOut(changeAddress.payload());
|
|
||||||
else if (cversion == nets.livenet.addressScript || cversion == nets.testnet.addressScript)
|
|
||||||
cscript = Script.createP2SH(changeAddress.payload());
|
|
||||||
else
|
|
||||||
throw new Error('invalid change output address');
|
|
||||||
|
|
||||||
var change = {
|
|
||||||
v: changeValue,
|
|
||||||
s: cscript.getBuffer(),
|
|
||||||
};
|
|
||||||
txobj.outs.push(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
var tx = new Transaction(txobj);
|
|
||||||
var anypay = false;
|
|
||||||
var l = unspent.length;
|
|
||||||
var allFound = l;
|
|
||||||
|
|
||||||
// Here will be the beginning of your signing for loop
|
|
||||||
for(i=0;i < l;i++) {
|
|
||||||
var scriptBuf = new bitcore.Buffer(unspent[i].scriptPubKey, 'hex');
|
|
||||||
|
|
||||||
var s = new Script(scriptBuf);
|
|
||||||
if (s.classify() !== Script.TX_PUBKEYHASH) {
|
|
||||||
throw new Error('input script type '+ s.getRawOutType() +' not supported yet');
|
|
||||||
}
|
|
||||||
var txSigHash = tx.hashForSignature(s, i,
|
|
||||||
anypay ? Transaction.SIGHASH_ANYONECANPAY : Transaction.SIGHASH_ALL);
|
|
||||||
|
|
||||||
|
|
||||||
// txSigHash = bitcore.buffertools.reverse(txSigHash);
|
|
||||||
|
|
||||||
for(var j=0;j<keyPairs.length;j++) {
|
|
||||||
var kp = keyPairs[j];
|
|
||||||
if(kp.address === unspent[i].address) {
|
|
||||||
console.log('SIGNING With...', kp.key); //TODO
|
|
||||||
console.log('HASH TO SIGN: ',bitcore.buffertools.toHex(txSigHash)); //TODO
|
|
||||||
var wKey = new bitcore.WalletKey({network: bitcore.networks.testnet});
|
|
||||||
wKey.fromObj({
|
|
||||||
priv: kp.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('PRIV KEY', bitcore.buffertools.toHex(wKey.privKey.private )); //TODO
|
|
||||||
console.log('PUB KEY', bitcore.buffertools.toHex(wKey.privKey.public )); //TODO
|
|
||||||
|
|
||||||
var sigRaw = wKey.privKey.signSync(txSigHash);
|
|
||||||
console.log('SIGNATURE: ',bitcore.buffertools.toHex(sigRaw)); //TODO
|
|
||||||
|
|
||||||
console.log('VERIFY: ',wKey.privKey.verifySignatureSync(txSigHash, sigRaw)); //TODO
|
|
||||||
|
|
||||||
var sigType = new bitcore.Buffer(1);
|
|
||||||
sigType[0] = anypay ? Transaction.SIGHASH_ANYONECANPAY : Transaction.SIGHASH_ALL;
|
|
||||||
var sig = bitcore.Buffer.concat([sigRaw, sigType]);
|
|
||||||
|
|
||||||
var scriptSig = new Script();
|
|
||||||
scriptSig.chunks.push(sig);
|
|
||||||
scriptSig.chunks.push(wKey.privKey.public);
|
|
||||||
scriptSig.updateBuffer();
|
|
||||||
tx.ins[i].s = scriptSig.getBuffer();
|
|
||||||
allFound--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allFound !== 0) {
|
|
||||||
throw new Error('could not find priv key for some inputs');
|
|
||||||
}
|
|
||||||
|
|
||||||
var txHex = tx.serialize().toString('hex');
|
|
||||||
console.log('TX HEX IS: ', txHex);
|
console.log('TX HEX IS: ', txHex);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -86,8 +86,13 @@ describe('Transaction', function() {
|
|||||||
it('#create should be able to create instance', function() {
|
it('#create should be able to create instance', function() {
|
||||||
var utxos =testdata.dataUnspent;
|
var utxos =testdata.dataUnspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
var tx = Transaction.create(utxos, outs, null, opts);
|
|
||||||
should.exist(tx);
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
|
should.exist(ret.tx);
|
||||||
|
should.exist(ret.selectedUtxos);
|
||||||
|
ret.selectedUtxos.length.should.equal(2);
|
||||||
|
|
||||||
|
var tx = ret.tx;
|
||||||
|
|
||||||
tx.version.should.equal(1);
|
tx.version.should.equal(1);
|
||||||
tx.ins.length.should.equal(2);
|
tx.ins.length.should.equal(2);
|
||||||
@ -105,11 +110,11 @@ describe('Transaction', function() {
|
|||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:80}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:80}];
|
||||||
Transaction
|
Transaction
|
||||||
.create
|
.create
|
||||||
.bind(utxos, outs, null, opts)
|
.bind(utxos, outs, opts)
|
||||||
.should.throw();
|
.should.throw();
|
||||||
|
|
||||||
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.5}];
|
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.5}];
|
||||||
should.exist( Transaction.create(utxos, outs2, null, opts));
|
should.exist( Transaction.create(utxos, outs2, opts));
|
||||||
|
|
||||||
// do not allow unconfirmed
|
// do not allow unconfirmed
|
||||||
Transaction.create.bind(utxos, outs2).should.throw();
|
Transaction.create.bind(utxos, outs2).should.throw();
|
||||||
@ -119,66 +124,96 @@ describe('Transaction', function() {
|
|||||||
it('#create should create same output as bitcoind createrawtransaction ', function() {
|
it('#create should create same output as bitcoind createrawtransaction ', function() {
|
||||||
var utxos =testdata.dataUnspent;
|
var utxos =testdata.dataUnspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
var tx = Transaction.create(utxos, outs, null, opts);
|
var ret = Transaction.create(utxos, outs, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
|
|
||||||
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08,"mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd":0.0299}'
|
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08,"mwZabyZXg8JzUtFX1pkGygsMJjnuqiNhgd":0.0299}'
|
||||||
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0200127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388acb09f2d00000000001976a914b00127584485a7cff0949ef0f6bc5575f06ce00d88ac00000000');
|
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0200127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388acb09f2d00000000001976a914b00127584485a7cff0949ef0f6bc5575f06ce00d88ac00000000');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#create should create same output as bitcoind createrawtransaction wo remainder', function() {
|
||||||
|
var utxos =testdata.dataUnspent;
|
||||||
// no remainder
|
// no remainder
|
||||||
outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
tx = Transaction.create(utxos, outs, null, {fee:0.03} );
|
var ret = Transaction.create(utxos, outs, {fee:0.03} );
|
||||||
|
var tx = ret.tx;
|
||||||
|
|
||||||
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
|
// string output generated from: bitcoind createrawtransaction '[{"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1","vout":1},{"txid":"2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc2","vout":0} ]' '{"mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE":0.08}'
|
||||||
//
|
//
|
||||||
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0100127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388ac00000000');
|
tx.serialize().toString('hex').should.equal('0100000002c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0100000000ffffffffc2cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a0000000000ffffffff0100127a00000000001976a914774e603bafb717bd3f070e68bbcccfd907c77d1388ac00000000');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#sign should sign a tx', function() {
|
it('#createAndSign should sign a tx', function() {
|
||||||
var utxos =testdata.dataUnspentSign.unspent;
|
var utxos =testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
var tx = Transaction.create(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
tx.isComplete().should.equal(true);
|
tx.isComplete().should.equal(true);
|
||||||
tx.ins.length.should.equal(1);
|
tx.ins.length.should.equal(1);
|
||||||
tx.outs.length.should.equal(2);
|
tx.outs.length.should.equal(2);
|
||||||
|
|
||||||
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}];
|
var outs2 = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}];
|
||||||
var tx2 = Transaction.create(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts);
|
var ret2 = Transaction.createAndSign(utxos, outs2, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
|
var tx2 = ret2.tx;
|
||||||
tx2.isComplete().should.equal(true);
|
tx2.isComplete().should.equal(true);
|
||||||
tx2.ins.length.should.equal(3);
|
tx2.ins.length.should.equal(3);
|
||||||
tx2.outs.length.should.equal(2);
|
tx2.outs.length.should.equal(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#sign should sign an incomplete tx ', function() {
|
it('#createAndSign should sign an incomplete tx ', function() {
|
||||||
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
||||||
var utxos =testdata.dataUnspentSign.unspent;
|
var utxos =testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
var tx = Transaction.create(utxos, outs, keys, opts);
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
tx.ins.length.should.equal(1);
|
tx.ins.length.should.equal(1);
|
||||||
tx.outs.length.should.equal(2);
|
tx.outs.length.should.equal(2);
|
||||||
tx.isComplete().should.equal(false);
|
|
||||||
});
|
});
|
||||||
it('#sign should sign a tx in multiple steps', function() {
|
it('#isComplete should return TX signature status', function() {
|
||||||
var utxos = Transaction.selectUnspent(testdata.dataUnspentSign.unspent,13, true);
|
var keys = ['cNpW8B7XPAzCdRR9RBWxZeveSNy3meXgHD8GuhcqUyDuy8ptCDzJ'];
|
||||||
|
var utxos =testdata.dataUnspentSign.unspent;
|
||||||
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.08}];
|
||||||
|
var ret = Transaction.createAndSign(utxos, outs, keys, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.sign(ret.selectedUtxos, testdata.dataUnspentSign.keyStrings);
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#sign should sign a tx in multiple steps (case1)', function() {
|
||||||
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:1.08}];
|
||||||
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
|
var selectedUtxos = ret.selectedUtxos;
|
||||||
|
|
||||||
var tx = Transaction.prepare(utxos, outs, opts);
|
|
||||||
var k1 = testdata.dataUnspentSign.keyStrings.slice(0,1);
|
var k1 = testdata.dataUnspentSign.keyStrings.slice(0,1);
|
||||||
var k23 = testdata.dataUnspentSign.keyStrings.slice(1,3);
|
|
||||||
tx.sign(utxos, k1).should.equal(false);
|
|
||||||
tx.sign(utxos, k23).should.equal(true);
|
|
||||||
|
|
||||||
var tx2 = Transaction.prepare(utxos, outs, opts);
|
tx.isComplete().should.equal(false);
|
||||||
|
|
||||||
|
tx.sign(selectedUtxos, k1).should.equal(false);
|
||||||
|
|
||||||
|
var k23 = testdata.dataUnspentSign.keyStrings.slice(1,3);
|
||||||
|
tx.sign(selectedUtxos, k23).should.equal(true);
|
||||||
|
tx.isComplete().should.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#sign should sign a tx in multiple steps (case2)', function() {
|
||||||
|
var outs = [{address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:16}];
|
||||||
|
var ret = Transaction.create(testdata.dataUnspentSign.unspent, outs, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
|
var selectedUtxos = ret.selectedUtxos;
|
||||||
|
|
||||||
var k1 = testdata.dataUnspentSign.keyStrings.slice(0,1);
|
var k1 = testdata.dataUnspentSign.keyStrings.slice(0,1);
|
||||||
var k2 = testdata.dataUnspentSign.keyStrings.slice(1,2);
|
var k2 = testdata.dataUnspentSign.keyStrings.slice(1,2);
|
||||||
var k3 = testdata.dataUnspentSign.keyStrings.slice(2,3);
|
var k3 = testdata.dataUnspentSign.keyStrings.slice(2,3);
|
||||||
tx2.sign(utxos, k1).should.equal(false);
|
tx.sign(selectedUtxos, k1).should.equal(false);
|
||||||
tx2.sign(utxos, k2).should.equal(false);
|
tx.sign(selectedUtxos, k2).should.equal(false);
|
||||||
tx2.sign(utxos, k3).should.equal(true);
|
tx.sign(selectedUtxos, k3).should.equal(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#create: should generate dynamic fee and readjust (and not) the selected UTXOs', function() {
|
it('#createAndSign: should generate dynamic fee and readjust (and not) the selected UTXOs', function() {
|
||||||
//this cases exceeds the input by 1mbtc AFTEr calculating the dynamic fee,
|
//this cases exceeds the input by 1mbtc AFTEr calculating the dynamic fee,
|
||||||
//so, it should trigger adding a new 10BTC utxo
|
//so, it should trigger adding a new 10BTC utxo
|
||||||
var utxos =testdata.dataUnspentSign.unspent;
|
var utxos =testdata.dataUnspentSign.unspent;
|
||||||
@ -188,7 +223,8 @@ describe('Transaction', function() {
|
|||||||
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx = Transaction.create(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
tx.getSize().should.equal(3560);
|
tx.getSize().should.equal(3560);
|
||||||
|
|
||||||
// ins = 11.0101 BTC (2 inputs: 1.0101 + 10 );
|
// ins = 11.0101 BTC (2 inputs: 1.0101 + 10 );
|
||||||
@ -209,7 +245,8 @@ describe('Transaction', function() {
|
|||||||
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
outs.push({address:'mrPnbY1yKDBsdgbHbS7kJ8GVm8F66hWHLE', amount:0.01});
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx = Transaction.create(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
var ret = Transaction.createAndSign(utxos, outs, testdata.dataUnspentSign.keyStrings, opts);
|
||||||
|
var tx = ret.tx;
|
||||||
tx.getSize().should.equal(3485);
|
tx.getSize().should.equal(3485);
|
||||||
|
|
||||||
// ins = 1.0101 BTC (1 inputs: 1.0101);
|
// ins = 1.0101 BTC (1 inputs: 1.0101);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user