mirror of https://github.com/BTCPrivate/copay.git
txpropal MERGE working!
This commit is contained in:
parent
c5b1fca910
commit
81be01d197
|
@ -201,11 +201,19 @@ PublicKeyRing.prototype.getAddress = function (index, isChange) {
|
||||||
|
|
||||||
var script = this.getRedeemScript(index,isChange);
|
var script = this.getRedeemScript(index,isChange);
|
||||||
var hash = coinUtil.sha256ripe160(script.getBuffer());
|
var hash = coinUtil.sha256ripe160(script.getBuffer());
|
||||||
var version = this.network.addressScript;
|
var version = this.network.P2SHVersion;
|
||||||
var addr = new Address(version, hash);
|
var addr = new Address(version, hash);
|
||||||
return addr.as('base58');
|
return addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PublicKeyRing.prototype.getScriptPubKeyHex = function (index, isChange) {
|
||||||
|
this._checkIndexRange(index, isChange);
|
||||||
|
var addr = this.getAddress(index,isChange);
|
||||||
|
return Script.createP2SH(addr.payload()).getBuffer().toString('hex');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//generate a new address, update index.
|
//generate a new address, update index.
|
||||||
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
PublicKeyRing.prototype.generateAddress = function(isChange) {
|
||||||
|
|
||||||
|
@ -237,11 +245,11 @@ PublicKeyRing.prototype.getRedeemScriptMap = function () {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
|
|
||||||
for (var i=0; i<this.changeAddressIndex; i++) {
|
for (var i=0; i<this.changeAddressIndex; i++) {
|
||||||
ret[this.getAddress(i,true)] = this.getRedeemScript(i,true);
|
ret[this.getAddress(i,true)] = this.getRedeemScript(i,true).getBuffer().toString('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i=0; i<this.addressIndex; i++) {
|
for (var i=0; i<this.addressIndex; i++) {
|
||||||
ret[this.getAddress(i)] = this.getRedeemScript(i);
|
ret[this.getAddress(i)] = this.getRedeemScript(i).getBuffer().toString('hex');
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
|
|
||||||
var imports = require('soop').imports();
|
var imports = require('soop').imports();
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var coinUtil = bitcore.util;
|
var util = bitcore.util;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Builder = bitcore.TransactionBuilder;
|
var Builder = bitcore.TransactionBuilder;
|
||||||
|
var Script = bitcore.Script;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
||||||
var Storage = imports.Storage || require('./Storage');
|
var Storage = imports.Storage || require('./Storage');
|
||||||
|
@ -25,17 +26,17 @@ function TxProposals(opts) {
|
||||||
this.network = opts.networkName === 'livenet' ?
|
this.network = opts.networkName === 'livenet' ?
|
||||||
bitcore.networks.livenet : bitcore.networks.testnet;
|
bitcore.networks.livenet : bitcore.networks.testnet;
|
||||||
this.publicKeyRing = opts.publicKeyRing;
|
this.publicKeyRing = opts.publicKeyRing;
|
||||||
this.txs = [];
|
this.txps = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
TxProposals.fromObj = function(o) {
|
TxProposals.fromObj = function(o) {
|
||||||
var ret = new TxProposals({
|
var ret = new TxProposals({
|
||||||
networkName: o.networkName,
|
networkName: o.networkName,
|
||||||
});
|
});
|
||||||
o.txs.forEach(function(t) {
|
o.txps.forEach(function(t) {
|
||||||
var tx = new Transaction;
|
var tx = new Transaction;
|
||||||
tx.parse(t.txHex);
|
tx.parse(t.txHex);
|
||||||
ret.txs.push({
|
ret.txps.push({
|
||||||
seenBy: t.seenBy,
|
seenBy: t.seenBy,
|
||||||
signedBy: t.signedBy,
|
signedBy: t.signedBy,
|
||||||
tx: tx,
|
tx: tx,
|
||||||
|
@ -46,7 +47,7 @@ TxProposals.fromObj = function(o) {
|
||||||
|
|
||||||
TxProposals.prototype.toObj = function() {
|
TxProposals.prototype.toObj = function() {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
this.txs.forEach(function(t) {
|
this.txps.forEach(function(t) {
|
||||||
ret.push({
|
ret.push({
|
||||||
seenBy: t.seenBy,
|
seenBy: t.seenBy,
|
||||||
signedBy: t.signedBy,
|
signedBy: t.signedBy,
|
||||||
|
@ -54,20 +55,205 @@ TxProposals.prototype.toObj = function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
txs: ret,
|
txps: ret,
|
||||||
networkName: this.network.name,
|
networkName: this.network.name,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.create = function(toAddress, amountSat, utxos, priv) {
|
|
||||||
|
TxProposals.prototype._getNormHash = function(txps) {
|
||||||
|
var ret = {};
|
||||||
|
txps.forEach(function(txp) {
|
||||||
|
var hash = txp.tx.getNormalizedHash().toString('hex');
|
||||||
|
ret[hash]=txp;
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype._startMerge = function(myTxps, theirTxps) {
|
||||||
|
var fromUs=0, fromTheirs=0, merged =0;
|
||||||
|
var toMerge = {}, ready=[];
|
||||||
|
|
||||||
|
Object.keys(theirTxps).forEach(function(hash) {
|
||||||
|
if (!myTxps[hash]) {
|
||||||
|
ready.push(theirTxps[hash]); // only in theirs;
|
||||||
|
fromTheirs++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toMerge[hash]=theirTxps[hash]; // need Merging
|
||||||
|
merged++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(myTxps).forEach(function(hash) {
|
||||||
|
if(!toMerge[hash]) {
|
||||||
|
ready.push(myTxps[hash]); // only in myTxps;
|
||||||
|
fromUs++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
stats: {
|
||||||
|
fromUs: fromUs,
|
||||||
|
fromTheirs: fromTheirs,
|
||||||
|
merged: merged,
|
||||||
|
},
|
||||||
|
ready: ready,
|
||||||
|
toMerge: toMerge,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype._mergeMetadata = function(myTxps, theirTxps, mergeInfo) {
|
||||||
|
|
||||||
|
var toMerge = mergeInfo.toMerge;
|
||||||
|
|
||||||
|
Object.keys(toMerge).forEach(function(hash) {
|
||||||
|
var v0 = myTxps[hash];
|
||||||
|
var v1 = toMerge[hash];
|
||||||
|
|
||||||
|
Object.keys(v1.seenBy).forEach(function(k) {
|
||||||
|
v0.seenBy[k] = v1.seenBy[k];
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(v1.signedBy).forEach(function(k) {
|
||||||
|
v0.signedBy[k] = v1.signedBy[k];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposals.prototype._chunkIsEmpty = function(chunk) {
|
||||||
|
return chunk === 0 || // when serializing and back, EMPTY_BUFFER becomes 0
|
||||||
|
buffertools.compare(chunk, util.EMPTY_BUFFER) === 0;
|
||||||
|
};
|
||||||
|
//
|
||||||
|
// function d(s,l) {
|
||||||
|
// console.log('DUMP'); //TODO
|
||||||
|
// for (var i=0; i<l; i++) {
|
||||||
|
// var k = s.chunks[i];
|
||||||
|
// console.log("0:", i, Buffer.isBuffer(k)? k.toString('hex'):k);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// this assumes that the same signature can not be v0 / v1 (which shouldnt be!)
|
||||||
|
TxProposals.prototype._mergeInputSig = function(s0buf, s1buf) {
|
||||||
|
if (buffertools.compare(s0buf,s1buf) === 0) {
|
||||||
|
console.log('BUFFERS .s MATCH'); //TODO
|
||||||
|
return s0buf;
|
||||||
|
}
|
||||||
|
// Is multisig?
|
||||||
|
var s0 = new Script(s0buf);
|
||||||
|
var s1 = new Script(s1buf);
|
||||||
|
var l0 = s0.chunks.length;
|
||||||
|
var l1 = s1.chunks.length;
|
||||||
|
var s0map = {};
|
||||||
|
|
||||||
|
if (l0 && l1 && l0 !== l1)
|
||||||
|
throw new Error('TX sig types mismatch in merge');
|
||||||
|
|
||||||
|
if (l0) {
|
||||||
|
for (var i=0; i<l0; i++) {
|
||||||
|
if (!this._chunkIsEmpty(s0.chunks[i]))
|
||||||
|
s0map[s0.chunks[i]] = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var diff = [];
|
||||||
|
for (var i=0; i<l1; i++) {
|
||||||
|
if ( !this._chunkIsEmpty(s1.chunks[i]) && !s0map[s1.chunks[i]]) {
|
||||||
|
diff.push(s1.chunks[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!diff) {
|
||||||
|
console.log('[TxProposals.js.155] NO DIFF FOUND'); //TODO
|
||||||
|
return s0.getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptySlots = [];
|
||||||
|
for (var i=1; i<l0; i++) {
|
||||||
|
if (this._chunkIsEmpty(s0.chunks[i])) {
|
||||||
|
emptySlots.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emptySlots.length<diff.length)
|
||||||
|
throw new Error('no enough empty slots to merge Txs');
|
||||||
|
|
||||||
|
for (var i=0; i<diff.length; i++) {
|
||||||
|
s0.chunks[emptySlots[i]] = diff[i];
|
||||||
|
}
|
||||||
|
s0.updateBuffer();
|
||||||
|
return s0.getBuffer();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return s1.getBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype._mergeSignatures = function(myTxps, theirTxps, mergeInfo) {
|
||||||
|
var self = this;
|
||||||
|
var toMerge = mergeInfo.toMerge;
|
||||||
|
|
||||||
|
Object.keys(toMerge).forEach(function(hash) {
|
||||||
|
var v0 = myTxps[hash].tx;
|
||||||
|
var v1 = toMerge[hash].tx;
|
||||||
|
|
||||||
|
var l = v0.ins.length;
|
||||||
|
if (l !== v1.ins.length)
|
||||||
|
throw new Error('TX in length mismatch in merge');
|
||||||
|
|
||||||
|
for(var i=0; i<l; i++) {
|
||||||
|
var i0 = v0.ins[i];
|
||||||
|
var i1 = v1.ins[i];
|
||||||
|
|
||||||
|
if (i0.q !== i1.q)
|
||||||
|
throw new Error('TX sequence ins mismatch in merge. Input:',i);
|
||||||
|
|
||||||
|
if (buffertools.compare(i0.o,i1.o) !== 0)
|
||||||
|
throw new Error('TX .o in mismatch in merge. Input:',i);
|
||||||
|
|
||||||
|
|
||||||
|
i0.s=self._mergeInputSig(i0.s,i1.s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TxProposals.prototype.merge = function(t) {
|
||||||
|
if (this.network.name !== t.network.name)
|
||||||
|
throw new Error('network mismatch');
|
||||||
|
|
||||||
|
var res = [];
|
||||||
|
|
||||||
|
var myTxps = this._getNormHash(this.txps);
|
||||||
|
var theirTxps = this._getNormHash(t.txps);
|
||||||
|
|
||||||
|
var mergeInfo = this._startMerge(myTxps, theirTxps);
|
||||||
|
this._mergeMetadata(myTxps, theirTxps, mergeInfo);
|
||||||
|
this._mergeSignatures(myTxps, theirTxps, mergeInfo);
|
||||||
|
|
||||||
|
Object.keys(mergeInfo.toMerge).forEach(function(hash) {
|
||||||
|
mergeInfo.ready.push(myTxps[hash]);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.txps=mergeInfo.ready;
|
||||||
|
return mergeInfo.stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.create = function(toAddress, amountSat, utxos, priv, opts) {
|
||||||
var pkr = this.publicKeyRing;
|
var pkr = this.publicKeyRing;
|
||||||
|
opts = opts || {};
|
||||||
|
|
||||||
if (! pkr.isComplete() ) {
|
if (! pkr.isComplete() ) {
|
||||||
throw new Error('publicKeyRing is not complete');
|
throw new Error('publicKeyRing is not complete');
|
||||||
}
|
}
|
||||||
|
|
||||||
var opts = {
|
var opts = {
|
||||||
remainderOut: { address: pkr.generateAddress(true) }
|
remainderOut: opts.remainderOut || { address: pkr.generateAddress(true).toString() }
|
||||||
};
|
};
|
||||||
|
|
||||||
var b = new Builder(opts)
|
var b = new Builder(opts)
|
||||||
|
@ -76,20 +262,21 @@ TxProposals.prototype.create = function(toAddress, amountSat, utxos, priv) {
|
||||||
.setOutputs([{address: toAddress, amountSat: amountSat}])
|
.setOutputs([{address: toAddress, amountSat: amountSat}])
|
||||||
;
|
;
|
||||||
|
|
||||||
|
var signRet;
|
||||||
if (priv) {
|
if (priv) {
|
||||||
//console.log('*** SIGNING IDX:', pkr.addressIndex, pkr.changeAddressIndex);
|
|
||||||
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
b.sign( priv.getAll(pkr.addressIndex, pkr.changeAddressIndex) );
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx = b.build();
|
var tx = b.build();
|
||||||
var me = {};
|
|
||||||
if (priv)
|
|
||||||
me[priv.id] = Date.now();
|
|
||||||
|
|
||||||
this.txs.push(
|
var me = {};
|
||||||
|
|
||||||
|
if (priv) me[priv.id] = Date.now();
|
||||||
|
|
||||||
|
this.txps.push(
|
||||||
new TxProposal({
|
new TxProposal({
|
||||||
signedBy: me,
|
signedBy: priv && b.signaturesAdded ? me : {},
|
||||||
seenBy: me,
|
seenBy: priv ? me : {},
|
||||||
tx: tx,
|
tx: tx,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -100,14 +100,13 @@ describe('PublicKeyRing model', function() {
|
||||||
|
|
||||||
for(var isChange=0; isChange<2; isChange++) {
|
for(var isChange=0; isChange<2; isChange++) {
|
||||||
for(var i=0; i<5; i++) {
|
for(var i=0; i<5; i++) {
|
||||||
var addr = w.generateAddress(isChange);
|
var a = w.generateAddress(isChange);
|
||||||
var a = new Address(addr);
|
|
||||||
a.isValid().should.equal(true);
|
a.isValid().should.equal(true);
|
||||||
a.isScript().should.equal(true);
|
a.isScript().should.equal(true);
|
||||||
a.network().name.should.equal('livenet');
|
a.network().name.should.equal('livenet');
|
||||||
if (i>1) {
|
if (i>1) {
|
||||||
w.getAddress(i-1,isChange).should
|
w.getAddress(i-1,isChange).toString().should
|
||||||
.not.equal(w.getAddress(i-2,isChange));
|
.not.equal(w.getAddress(i-2,isChange).toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +127,7 @@ describe('PublicKeyRing model', function() {
|
||||||
var as = w.getAddresses();
|
var as = w.getAddresses();
|
||||||
as.length.should.equal(12);
|
as.length.should.equal(12);
|
||||||
for(var j in as) {
|
for(var j in as) {
|
||||||
var a = new Address(as[j]);
|
var a = as[j];
|
||||||
a.isValid().should.equal(true);
|
a.isValid().should.equal(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,8 @@ var WalletKey = bitcore.WalletKey;
|
||||||
var Key = bitcore.Key;
|
var Key = bitcore.Key;
|
||||||
var BIP32 = bitcore.BIP32;
|
var BIP32 = bitcore.BIP32;
|
||||||
var bignum = bitcore.bignum;
|
var bignum = bitcore.bignum;
|
||||||
|
var Script = bitcore.Script;
|
||||||
|
var util = bitcore.util;
|
||||||
var networks = bitcore.networks;
|
var networks = bitcore.networks;
|
||||||
var copay = copay || require('../copay');
|
var copay = copay || require('../copay');
|
||||||
var fakeStorage = copay.FakeStorage;
|
var fakeStorage = copay.FakeStorage;
|
||||||
|
@ -32,7 +34,7 @@ var unspentTest = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
var createW = function (bip32s) {
|
var createPKR = function (bip32s) {
|
||||||
var w = new PublicKeyRing(config);
|
var w = new PublicKeyRing(config);
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
|
|
||||||
|
@ -66,16 +68,16 @@ describe('TxProposals model', function() {
|
||||||
w.network.name.should.equal('livenet');
|
w.network.name.should.equal('livenet');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#create', function () {
|
it('#create, no signing', function () {
|
||||||
var w = new TxProposals({
|
var w = new TxProposals({
|
||||||
networkName: config.networkName,
|
networkName: config.networkName,
|
||||||
publicKeyRing: createW(),
|
publicKeyRing: createPKR(),
|
||||||
});
|
});
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
w.network.name.should.equal('livenet');
|
w.network.name.should.equal('livenet');
|
||||||
|
|
||||||
unspentTest[0].address = w.publicKeyRing.getAddress(1, true);
|
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
|
||||||
unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(1, true).getBuffer();
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
|
||||||
|
|
||||||
var tx = w.create(
|
var tx = w.create(
|
||||||
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
@ -84,21 +86,49 @@ describe('TxProposals model', function() {
|
||||||
);
|
);
|
||||||
should.exist(tx);
|
should.exist(tx);
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#create, signing with wrong key', function () {
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR(),
|
||||||
|
});
|
||||||
|
should.exist(w);
|
||||||
|
w.network.name.should.equal('livenet');
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var tx = w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
should.exist(tx);
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('#create. Signing with derivate keys', function () {
|
it('#create. Signing with derivate keys', function () {
|
||||||
|
|
||||||
var priv = new PrivateKey(config);
|
var priv = new PrivateKey(config);
|
||||||
var w = new TxProposals({
|
var w = new TxProposals({
|
||||||
networkName: config.networkName,
|
networkName: config.networkName,
|
||||||
publicKeyRing: createW([priv.getBIP32()]),
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
});
|
});
|
||||||
|
|
||||||
var ts = Date.now();
|
var ts = Date.now();
|
||||||
for (var isChange=0; isChange<2; isChange++) {
|
for (var isChange=0; isChange<2; isChange++) {
|
||||||
for (var index=0; index<3; index++) {
|
for (var index=0; index<3; index++) {
|
||||||
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange);
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer();
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
var tx = w.create(
|
var tx = w.create(
|
||||||
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
bignum('123456789'),
|
bignum('123456789'),
|
||||||
|
@ -107,25 +137,307 @@ describe('TxProposals model', function() {
|
||||||
);
|
);
|
||||||
should.exist(tx);
|
should.exist(tx);
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
|
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
|
||||||
(w.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('#toObj #fromObj roundtrip', function () {
|
|
||||||
|
it('#merge with self', function () {
|
||||||
|
|
||||||
var priv = new PrivateKey(config);
|
var priv = new PrivateKey(config);
|
||||||
var w = new TxProposals({
|
var w = new TxProposals({
|
||||||
networkName: config.networkName,
|
networkName: config.networkName,
|
||||||
publicKeyRing: createW([priv.getBIP32()]),
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
});
|
});
|
||||||
var ts = Date.now();
|
var ts = Date.now();
|
||||||
var isChange=0;
|
var isChange=0;
|
||||||
var index=0;
|
var index=0;
|
||||||
|
|
||||||
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange);
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
unspentTest[0].scriptPubKey = w.publicKeyRing.getRedeemScript(index, isChange).getBuffer();
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
var tx = w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv
|
||||||
|
);
|
||||||
|
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 1', function () {
|
||||||
|
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32()]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: w.publicKeyRing,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
w2.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w2.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var _dumpChunks = function (scriptSig, label) {
|
||||||
|
console.log('## DUMP: ' + label + ' ##');
|
||||||
|
for(var i=0; i<scriptSig.chunks.length; i++) {
|
||||||
|
console.log('\tCHUNK ', i, scriptSig.chunks[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 2', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv3 = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32(), priv2.getBIP32()]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv3,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
|
Object.keys(w.txps[0].signedBy).length.should.equal(0);
|
||||||
|
Object.keys(w.txps[0].seenBy).length.should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
w2.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w2.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w3 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w3.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
w3.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w3.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
(w3.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w3.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w3);
|
||||||
|
w.txps.length.should.equal(1);
|
||||||
|
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#merge, merge signatures case 3', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var priv2 = new PrivateKey(config);
|
||||||
|
var priv3 = new PrivateKey(config);
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
var pkr = createPKR([priv.getBIP32(), priv2.getBIP32(), priv3.getBIP32() ]);
|
||||||
|
var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }};
|
||||||
|
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w2 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w2.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv2,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
w2.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w2.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w2.txps[0].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
(w2.txps[0].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
|
var w3 = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: pkr,
|
||||||
|
});
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
|
w3.create(
|
||||||
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
|
bignum('123456789'),
|
||||||
|
unspentTest,
|
||||||
|
priv3,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
w3.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w3.txps[0].tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
(w3.txps[0].signedBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
(w3.txps[0].seenBy[priv3.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
w.merge(w2);
|
||||||
|
w.txps[0].tx.isComplete().should.equal(false);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
|
|
||||||
|
w.merge(w3);
|
||||||
|
w.txps[0].tx.isComplete().should.equal(true);
|
||||||
|
w.txps[0].tx.countInputMissingSignatures(0).should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('#toObj #fromObj roundtrip', function () {
|
||||||
|
|
||||||
|
var priv = new PrivateKey(config);
|
||||||
|
var w = new TxProposals({
|
||||||
|
networkName: config.networkName,
|
||||||
|
publicKeyRing: createPKR([priv.getBIP32()]),
|
||||||
|
});
|
||||||
|
var ts = Date.now();
|
||||||
|
var isChange=0;
|
||||||
|
var index=0;
|
||||||
|
|
||||||
|
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange).toString();
|
||||||
|
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange);
|
||||||
var tx = w.create(
|
var tx = w.create(
|
||||||
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
bignum('123456789'),
|
bignum('123456789'),
|
||||||
|
@ -134,23 +446,24 @@ describe('TxProposals model', function() {
|
||||||
);
|
);
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var o = w.toObj();
|
var o = w.toObj();
|
||||||
should.exist(o);
|
should.exist(o);
|
||||||
o.txs.length.should.equal(1);
|
o.txps.length.should.equal(1);
|
||||||
should.exist(o.txs[0].txHex);
|
should.exist(o.txps[0].txHex);
|
||||||
should.exist(o.txs[0].signedBy);
|
should.exist(o.txps[0].signedBy);
|
||||||
should.exist(o.txs[0].seenBy);
|
should.exist(o.txps[0].seenBy);
|
||||||
should.exist(o.txs[0].signedBy[priv.id]);
|
should.exist(o.txps[0].signedBy[priv.id]);
|
||||||
|
|
||||||
var w2 = TxProposals.fromObj(o);
|
var w2 = TxProposals.fromObj(o);
|
||||||
var tx2 = w2.txs[0].tx;
|
var tx2 = w2.txps[0].tx;
|
||||||
tx2.isComplete().should.equal(false);
|
tx2.isComplete().should.equal(false);
|
||||||
tx2.countInputMissingSignatures(0).should.equal(2);
|
tx2.countInputMissingSignatures(0).should.equal(2);
|
||||||
(w2.txs[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[0].signedBy[priv.id] - ts > 0).should.equal(true);
|
||||||
(w2.txs[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[0].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue