add paypro checks

This commit is contained in:
Matias Alejo Garcia 2014-11-21 11:06:47 -03:00
parent 30c04c615f
commit 32f281fb82
5 changed files with 221 additions and 351 deletions

View File

@ -41,6 +41,38 @@ function TxProposal(opts) {
this._sync();
}
TxProposal.prototype._checkPayPro = function() {
if (!this.merchant) return;
console.log('[TxProposal.js.46]',
this.paymentProtocolURL , this.merchant.request_url);
if (this.paymentProtocolURL !== this.merchant.request_url)
throw new Error('PayPro: Mismatch on Payment URLs');
if (!this.merchant.outs || this.merchant.outs.length !== 1)
throw new Error('PayPro: Unsopported number of outputs');
if (this.merchant.expires < (this.getSent() || Date.now()/1000.) )
throw new Error('PayPro: Request expired');
if (!this.merchant.total || !this.merchant.outs[0].amountSatStr || !this.merchant.outs[0].address)
throw new Error('PayPro: Missing amount');
if (this.builder.vanilla.outs.length != 1)
throw new Error('PayPro: Wrong outs in Tx');
var ppOut = this.merchant.outs[0];
var txOut = this.builder.vanilla.outs[0];
if (ppOut.address !== txOut.address)
throw new Error('PayPro: Wrong out address in Tx');
if (ppOut.amountSatStr !== txOut.amountSatStr)
throw new Error('PayPro: Wrong amount in Tx');
};
TxProposal.prototype._check = function() {
@ -61,6 +93,23 @@ TxProposal.prototype._check = function() {
if (hashType && hashType !== Transaction.SIGHASH_ALL)
throw new Error('Invalid tx proposal: bad signatures');
});
this._checkPayPro();
};
TxProposal.prototype.trimForStorage = function() {
// TODO (remove builder / builderObj. utxos, etc)
//
return this;
};
TxProposal.prototype.addMerchantData = function(merchantData) {
var m = _.clone(merchantData);
// remove unneeded data
m.raw = m.pr.pki_data = m.pr.signature = undefined;
this.merchant = m;
this._checkPayPro();
};
TxProposal.prototype.rejectCount = function() {
@ -101,7 +150,6 @@ TxProposal.prototype._sync = function() {
return this;
}
TxProposal.prototype.getId = function() {
preconditions.checkState(this.builder);
return this.builder.build().getNormalizedHash().toString('hex');
@ -248,11 +296,14 @@ TxProposal.prototype.setRejected = function(copayerId) {
if (!this.rejectedBy[copayerId])
this.rejectedBy[copayerId] = Date.now();
return this;
};
TxProposal.prototype.setSent = function(sentTxid) {
this.sentTxid = sentTxid;
this.sentTs = Date.now();
return this;
};
TxProposal.prototype.getSent = function() {

View File

@ -474,8 +474,14 @@ Wallet.prototype._processTxProposalPayPro = function(mergeInfo, cb) {
log.info('Received a Payment Protocol TX Proposal');
self.fetchPaymentTx(txp.paymentProtocolURL, function(err, merchantData) {
if (err) return cb(err);
txp.merchant = merchantData;
return cb();
try {
txp.addMerchantData(merchantData);
} catch (e) {
log.error(e);
err = 'BADPAYPRO: ' + e.toString();
}
return cb(err);
});
};
@ -1435,14 +1441,7 @@ Wallet.prototype.sign = function(ntxid) {
var myId = this.getMyCopayerId();
var txp = this.txProposals.get(ntxid);
// If this is a payment protocol request,
// ensure it hasn't been tampered with.
if (!this.verifyPaymentRequest(ntxid)) {
throw new Error('Bad payment request');
}
var before = txp.countSignatures();
var keys = this.privateKey.getForPaths(txp.inputChainPaths);
txp.builder.sign(keys);
@ -1491,7 +1490,8 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
if (txid) {
log.debug('Wallet:' + self.getName() + ' Broadcasted TX. BITCOIND txid:', txid);
self.txProposals.get(ntxid).setSent(txid);
var txp = self.txProposals.get(ntxid);
txp.setSent(txid);
self.sendTxProposal(ntxid);
self.emitAndKeepAlive('txProposalsUpdated');
return cb(txid);
@ -1676,11 +1676,9 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
untrusted: !trust.caTrusted,
selfSigned: trust.selfSigned
},
expires: expires,
request_url: options.uri,
total: bignum('0', 10).toString(10),
// Expose so other copayers can verify signature
// and identity, not to mention data.
raw: pr.serialize().toString('hex')
};
return this.getUnspent(function(err, safeUnspent, unspent) {
@ -1694,9 +1692,7 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
} catch (e) {
var msg = e.message || '';
if (msg.indexOf('not enough unspent tx outputs to fulfill')) {
var sat = /(\d+)/.exec(msg)[1];
e = new Error('No unspent outputs available.');
e.amount = sat;
return cb(e);
}
}
@ -1815,21 +1811,21 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
}
var postInfo = {
method: 'POST',
url: txp.merchant.pr.pd.payment_url,
headers: {
// BIP-71
'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE,
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE
// XHR does not allow these:
// 'Content-Length': (pay.byteLength || pay.length) + '',
// 'Content-Transfer-Encoding': 'binary'
},
// Technically how this should be done via XHR (used to
// be the ArrayBuffer, now you send the View instead).
data: view,
responseType: 'arraybuffer'
};
method: 'POST',
url: txp.merchant.pr.pd.payment_url,
headers: {
// BIP-71
'Accept': PayPro.PAYMENT_ACK_CONTENT_TYPE,
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE
// XHR does not allow these:
// 'Content-Length': (pay.byteLength || pay.length) + '',
// 'Content-Transfer-Encoding': 'binary'
},
// Technically how this should be done via XHR (used to
// be the ArrayBuffer, now you send the View instead).
data: view,
responseType: 'arraybuffer'
};
return Wallet.request(postInfo)
.success(function(data, status, headers, config) {
@ -1940,7 +1936,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
merchantData.total = bignum(merchantData.total, 10);
var outs = [];
var outs = {};
merchantData.pr.pd.outputs.forEach(function(output) {
var amount = output.amount;
@ -1964,13 +1960,11 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
var network = merchantData.pr.pd.network === 'main' ? 'livenet' : 'testnet';
var addr = bitcore.Address.fromScriptPubKey(new bitcore.Script(s), network);
outs.push({
address: addr[0].toString(),
amountSatStr: bignum.fromBuffer(v, {
endian: 'big',
size: 1
}).toString(10)
});
var a = addr[0].toString();
outs[a] = bignum.fromBuffer(v, {
endian: 'big',
size: 1
}).add(outs[a] || bignum(0));
merchantData.total = merchantData.total.add(bignum.fromBuffer(v, {
endian: 'big',
@ -1978,11 +1972,18 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
}));
});
if (Object.keys(outs) > 1)
throw new Error('PayPro: Unsupported outputs');
merchantData.outs = outs;
merchantData.total = merchantData.total.toString(10);
var b = new Builder(opts)
.setUnspent(unspent)
.setOutputs(outs);
.setOutputs({
address: _.keys(outs)[0],
amountSatStr: _.values(outs)[0].toString(10),
});
merchantData.pr.pd.outputs.forEach(function(output, i) {
var script = {
@ -2030,6 +2031,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
var meSeen = {};
if (priv) meSeen[myId] = now;
console.log('[Wallet.js.2043]', options, merchantData); //TODO
var ntxid = this.txProposals.add(new TxProposal({
inputChainPaths: inputChainPaths,
signedBy: me,
@ -2045,159 +2047,6 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
return ntxid;
};
/**
* @desc Verifies a PaymentRequest sent by another peer
* This essentially ensures that a copayer hasn't tampered with a
* PaymentRequest message from a payment server. It verifies the signature
* based on the cert, and checks to ensure the desired outputs are the same as
* the ones on the tx proposal.
* @TODO: Document better
*/
Wallet.prototype.verifyPaymentRequest = function(ntxid) {
if (!ntxid) return false;
var txp = _.isObject(ntxid) ? ntxid : this.txProposals.get(ntxid);
// If we're not a payment protocol proposal, ignore.
if (!txp.merchant) return true;
// The copayer didn't send us the raw payment request, unverifiable.
if (!txp.merchant.raw) return false;
// var tx = txp.builder.tx;
var tx = txp.builder.build();
var data = new Buffer(txp.merchant.raw, 'hex');
data = PayPro.PaymentRequest.decode(data);
var pr = new PayPro();
pr = pr.makePaymentRequest(data);
// Verify the signature so we know this is the real request.
var trust = pr.verify(true);
if (!trust.verified) {
// Signature does not match cert. It may have
// been modified by an untrustworthy person.
// We should not sign this transaction proposal!
return false;
}
var details = pr.get('serialized_payment_details');
details = PayPro.PaymentDetails.decode(details);
var pd = new PayPro();
pd = pd.makePaymentDetails(details);
var outputs = pd.get('outputs');
if (tx.outs.length < outputs.length) {
// Outputs do not and cannot match.
return false;
}
// Figure out whether the user is supposed
// to decide the value of the outputs.
var undecided = false;
var total = bignum('0', 10);
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var amount = output.get('amount');
// big endian
var v = new Buffer(8);
v[0] = (amount.high >> 24) & 0xff;
v[1] = (amount.high >> 16) & 0xff;
v[2] = (amount.high >> 8) & 0xff;
v[3] = (amount.high >> 0) & 0xff;
v[4] = (amount.low >> 24) & 0xff;
v[5] = (amount.low >> 16) & 0xff;
v[6] = (amount.low >> 8) & 0xff;
v[7] = (amount.low >> 0) & 0xff;
total = total.add(bignum.fromBuffer(v, {
endian: 'big',
size: 1
}));
}
if (+total.toString(10) === 0) {
undecided = true;
}
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var amount = output.get('amount');
var script = {
offset: output.get('script').offset,
limit: output.get('script').limit,
buffer: new Buffer(new Uint8Array(output.get('script').buffer))
};
// Expected value
// little endian (keep this LE to compare with tx output value)
var ev = new Buffer(8);
ev[0] = (amount.low >> 0) & 0xff;
ev[1] = (amount.low >> 8) & 0xff;
ev[2] = (amount.low >> 16) & 0xff;
ev[3] = (amount.low >> 24) & 0xff;
ev[4] = (amount.high >> 0) & 0xff;
ev[5] = (amount.high >> 8) & 0xff;
ev[6] = (amount.high >> 16) & 0xff;
ev[7] = (amount.high >> 24) & 0xff;
// Expected script
var es = script.buffer.slice(script.offset, script.limit);
// Actual value
var av = tx.outs[i].v;
// Actual script
var as = tx.outs[i].s;
// XXX allow changing of script as long as address is same
// var as = es;
// XXX allow changing of script as long as address is same
// var network = pd.get('network') === 'main' ? 'livenet' : 'testnet';
// var es = bitcore.Address.fromScriptPubKey(new bitcore.Script(es), network)[0];
// var as = bitcore.Address.fromScriptPubKey(new bitcore.Script(tx.outs[i].s), network)[0];
if (undecided) {
av = ev = new Buffer([0]);
}
// Make sure the tx's output script and values match the payment request's.
if (av.toString('hex') !== ev.toString('hex') || as.toString('hex') !== es.toString('hex')) {
// Verifiable outputs do not match outputs of merchant
// data. We should not sign this transaction proposal!
return false;
}
// Checking the merchant data itself isn't technically
// necessary as long as we check the transaction, but
// we can do it for good measure.
var ro = txp.merchant.pr.pd.outputs[i];
// Actual value
// little endian (keep this LE to compare with the ev above)
var av = new Buffer(8);
av[0] = (ro.amount.low >> 0) & 0xff;
av[1] = (ro.amount.low >> 8) & 0xff;
av[2] = (ro.amount.low >> 16) & 0xff;
av[3] = (ro.amount.low >> 24) & 0xff;
av[4] = (ro.amount.high >> 0) & 0xff;
av[5] = (ro.amount.high >> 8) & 0xff;
av[6] = (ro.amount.high >> 16) & 0xff;
av[7] = (ro.amount.high >> 24) & 0xff;
// Actual script
var as = new Buffer(ro.script.buffer, 'hex')
.slice(ro.script.offset, ro.script.limit);
if (av.toString('hex') !== ev.toString('hex') || as.toString('hex') !== es.toString('hex')) {
return false;
}
}
return true;
};
/**
* @desc Mark that a user has seen a given TxProposal
* @return {boolean} true if the internal state has changed
@ -2243,7 +2092,7 @@ Wallet.prototype.subscribeToAddresses = function() {
var addrInfo = this.publicKeyRing.getAddressesInfo();
this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr'));
log.debug('Subscribed to ' + addrInfo.length + ' addresses');
log.debug('Subscribed to ' + addrInfo.length + ' addresses');
};
/**

View File

@ -788,132 +788,6 @@ describe('PayPro (in Wallet) model', function() {
});
});
it('#try to sign a tampered payment request (raw)', function(done) {
var w = createWallet();
should.exist(w);
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
var commentText = 'Hello, server. I\'d like to make a payment.';
w.createPaymentTx({
uri: address,
memo: commentText
}, function(err, ntxid, merchantData) {
should.equal(err, null);
should.exist(ntxid);
should.exist(merchantData);
// Tamper with payment request in its raw form:
var data = new Buffer(merchantData.raw, 'hex');
data = PayPro.PaymentRequest.decode(data);
var pr = new PayPro();
pr = pr.makePaymentRequest(data);
var details = pr.get('serialized_payment_details');
details = PayPro.PaymentDetails.decode(details);
var pd = new PayPro();
pd = pd.makePaymentDetails(details);
var outputs = pd.get('outputs');
outputs[outputs.length - 1].set('amount', 1000000000);
pd.set('outputs', outputs);
pr.set('serialized_payment_details', pd.serialize());
merchantData.raw = pr.serialize().toString('hex');
var myId = w.getMyCopayerId();
var txp = w.txProposals.get(ntxid);
should.exist(txp);
should.exist(txp.signedBy[myId]);
should.not.exist(txp.rejectedBy[myId]);
w.verifyPaymentRequest(ntxid).should.equal(false);
return done();
});
});
it('#try to sign a tampered payment request (abstract)', function(done) {
var w = createWallet();
should.exist(w);
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
var commentText = 'Hello, server. I\'d like to make a payment.';
w.createPaymentTx({
uri: address,
memo: commentText
}, function(err, ntxid, merchantData) {
should.equal(err, null);
should.exist(ntxid);
should.exist(merchantData);
// Tamper with payment request in its abstract form:
var outputs = merchantData.pr.pd.outputs;
var output = outputs[outputs.length - 1];
var amount = output.amount;
amount.low = 2;
var myId = w.getMyCopayerId();
var txp = w.txProposals.get(ntxid);
should.exist(txp);
should.exist(txp.signedBy[myId]);
should.not.exist(txp.rejectedBy[myId]);
w.verifyPaymentRequest(ntxid).should.equal(false);
return done();
});
});
it('#try to sign a tampered txp tx (abstract)', function(done) {
var w = createWallet();
should.exist(w);
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
var commentText = 'Hello, server. I\'d like to make a payment.';
w.createPaymentTx({
uri: address,
memo: commentText
}, function(err, ntxid, merchantData) {
should.equal(err, null);
should.exist(ntxid);
should.exist(merchantData);
// Tamper with payment request in its abstract form:
var txp = w.txProposals.get(ntxid);
var tx = txp.builder.tx || txp.builder.build();
tx.outs[0].v = new Buffer([2, 0, 0, 0, 0, 0, 0, 0]);
var myId = w.getMyCopayerId();
var txp = w.txProposals.get(ntxid);
should.exist(txp);
should.exist(txp.signedBy[myId]);
should.not.exist(txp.rejectedBy[myId]);
w.verifyPaymentRequest(ntxid).should.equal(false);
return done();
});
});
it('#sign an untampered payment request', function(done) {
var w = createWallet();
should.exist(w);
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
var commentText = 'Hello, server. I\'d like to make a payment.';
w.createPaymentTx({
uri: address,
memo: commentText
}, function(err, ntxid, merchantData) {
should.equal(err, null);
should.exist(ntxid);
should.exist(merchantData);
var myId = w.getMyCopayerId();
var txp = w.txProposals.get(ntxid);
should.exist(txp);
should.exist(txp.signedBy[myId]);
should.not.exist(txp.rejectedBy[myId]);
w.verifyPaymentRequest(ntxid).should.equal(true);
return done();
});
});
it('#close payment server', function(done) {
server.close(function() {
return done();

View File

@ -11,12 +11,12 @@ var networks = bitcore.networks;
var FakeBuilder = requireMock('FakeBuilder');
var TxProposal = copay.TxProposal;
var dummyProposal = new TxProposal({
creator: 1,
var dummyProposal = function() { return new TxProposal({
creator: 'creator',
createdTs: 1,
builder: new FakeBuilder(),
inputChainPaths: ['m/1'],
});
})};
var someKeys = ["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5", "022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"];
@ -208,8 +208,8 @@ describe('TxProposal', function() {
});
it('#_verifyScriptSig, two signatures', function() {
// Data taken from bitcore's TransactionBuilder test
var txp = dummyProposal;
var tx = dummyProposal.builder.build();
var txp = dummyProposal();
var tx = dummyProposal().builder.build();
var ret = TxProposal._verifySignatures(pubkeys, validScriptSig, tx.hashForSignature());
ret.should.deep.equal(['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']);
});
@ -223,13 +223,13 @@ describe('TxProposal', function() {
Buffer.isBuffer(info.script.getBuffer()).should.equal(true);
});
it('#_updateSignedBy', function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp._inputSigners.should.deep.equal([
['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']
]);
});
describe('#_check', function() {
var txp = dummyProposal;
var txp = dummyProposal();
var backup = txp.builder.tx.ins;
it('OK', function() {
@ -272,8 +272,96 @@ describe('TxProposal', function() {
txp.builder.tx.ins[0].s = backup;
});
});
describe('#_checkPayPro', function() {
var txp, md;
beforeEach(function() {
txp = dummyProposal();
txp.paymentProtocolURL = '123';
md = {
request_url: '123',
pr: {
pd: {
expires: 123,
memo: 'memo',
},
},
total: '1230',
outs: [{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
amountSatStr: "123"
}],
expires: 92345678900,
};
});
it('OK no merchant data', function() {
txp._checkPayPro();
});
it('OK merchant data', function() {
txp.addMerchantData(md);
});
it('NOK URL', function() {
txp.paymentProtocolURL = '1234';
(function() {
txp.addMerchantData(md);
}).should.throw('Mismatch');
});
it('NOK OUTS', function() {
md.outs = [];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK OUTS (case 2)', function() {
md.outs = [{}, {}];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK OUTS (case 3)', function() {
md.outs = [{}, {}];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK Amount', function() {
md.total = undefined;
(function() {
txp.addMerchantData(md);
}).should.throw('amount');
});
it('NOK Outs case 4', function() {
md.outs[0].address = 'aaa';
(function() {
txp.addMerchantData(md);
}).should.throw('address');
});
it('NOK Outs case 5', function() {
md.outs[0].amountSatStr = '432';
(function() {
txp.addMerchantData(md);
}).should.throw('amount');
});
it('NOK Expired', function() {
md.expires = 1;
(function() {
txp.addMerchantData(md);
}).should.throw('expired');
});
it('OK Expired but sent', function() {
md.expires = 2;
txp.sentTs = 1;
txp.addMerchantData(md);
});
});
describe('#merge', function() {
var txp = dummyProposal;
var txp = dummyProposal();
var backup = txp.builder.tx.ins;
it('with self', function() {
var hasChanged = txp.merge(txp);
@ -307,7 +395,7 @@ describe('TxProposal', function() {
});
describe('#setCopayers', function() {
it("should fails if Tx has no creator", function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.signedBy = {
'hugo': 1
};
@ -319,7 +407,7 @@ describe('TxProposal', function() {
}).should.throw('no creator');
});
it("should fails if Tx is not signed by creator", function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.creator = 'creator';
txp.signedBy = {
'hugo': 1
@ -336,7 +424,7 @@ describe('TxProposal', function() {
it("should fails if Tx has unmapped signatures", function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.creator = 'creator';
txp.signedBy = {
creator: 1
@ -353,7 +441,7 @@ describe('TxProposal', function() {
// This was disabled. Unnecessary to check this.
it.skip("should be signed by sender", function() {
var txp = dummyProposal;
var txp = dummyProposal();
var ts = Date.now();
txp._inputSigners = [
['pk1', 'pk0']
@ -372,7 +460,7 @@ describe('TxProposal', function() {
it("should set signedBy (trivial case)", function() {
var txp = dummyProposal;
var txp = dummyProposal();
var ts = Date.now();
txp._inputSigners = [
['pk1', 'pk0']
@ -390,7 +478,7 @@ describe('TxProposal', function() {
txp.signedBy['creator'].should.gte(ts);
});
it("should assign creator", function() {
var txp = dummyProposal;
var txp = dummyProposal();
var ts = Date.now();
txp._inputSigners = [
['pk0']
@ -409,7 +497,7 @@ describe('TxProposal', function() {
txp.seenBy['creator'].should.equal(txp.createdTs);
})
it("New tx should have only 1 signature", function() {
var txp = dummyProposal;
var txp = dummyProposal();
var ts = Date.now();
txp.signedBy = {};
delete txp['creator'];
@ -431,7 +519,7 @@ describe('TxProposal', function() {
})
it("if signed, should not change ts", function() {
var txp = dummyProposal;
var txp = dummyProposal();
var ts = Date.now();
txp._inputSigners = [
['pk0', 'pk1']
@ -456,25 +544,25 @@ describe('TxProposal', function() {
describe('micelaneous functions', function() {
it('should report rejectCount', function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.rejectCount().should.equal(0);
txp.setRejected(['juan'])
txp.rejectCount().should.equal(1);
});
it('should report isPending 1', function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.rejectedBy = [];
txp.sentTxid = 1;
txp.isPending(3).should.equal(false);
});
it('should report isPending 2', function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.rejectedBy = [];
txp.sentTxid = null;
txp.isPending(3).should.equal(true);
});
it('should report isPending 3', function() {
var txp = dummyProposal;
var txp = dummyProposal();
txp.rejectedBy = [1, 2, 3, 4];
txp.sentTxid = null;
txp.isPending(3).should.equal(false);

View File

@ -2,10 +2,12 @@
var bitcore = bitcore || require('bitcore');
var Script = bitcore.Script;
var VALID_SCRIPTSIG_BUF = new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae','hex');
var VALID_SCRIPTSIG_BUF = new Buffer('0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae', 'hex');
function Tx() {
this.ins = [{s: VALID_SCRIPTSIG_BUF }];
this.ins = [{
s: VALID_SCRIPTSIG_BUF
}];
};
Tx.prototype.getHashType = function() {
@ -23,26 +25,32 @@ function FakeBuilder() {
this.test = 1;
this.tx = new Tx();
this.signhash = 1;
this.inputMap = [{ address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
this.inputMap = [{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
scriptPubKey: new Script(new Buffer('a914dc0623476aefb049066b09b0147a022e6eb8429187', 'hex')),
scriptType: 4,
i: 0 }];
i: 0
}];
this.vanilla = {
scriptSig: [VALID_SCRIPTSIG_BUF],
}
this.vanilla = {
scriptSig: [VALID_SCRIPTSIG_BUF],
outs: [{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
amountSatStr: '123',
}]
}
}
FakeBuilder.prototype.merge = function() {
};
FakeBuilder.prototype.merge = function() {};
FakeBuilder.prototype.build = function() {
FakeBuilder.prototype.build = function() {
return this.tx;
};
FakeBuilder.prototype.toObj = function() {
FakeBuilder.prototype.toObj = function() {
return this;
};
FakeBuilder.VALID_SCRIPTSIG_BUF = VALID_SCRIPTSIG_BUF;