add different toObj/fromObj fn for networking
This commit is contained in:
parent
c8917fccd6
commit
966818c53a
6 changed files with 233 additions and 116 deletions
|
|
@ -99,14 +99,6 @@ PublicKeyRing.prototype._checkKeys = function() {
|
||||||
throw new Error('dont have required keys yet');
|
throw new Error('dont have required keys yet');
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype._newExtendedPublicKey = function() {
|
|
||||||
return new PrivateKey({
|
|
||||||
networkName: this.network.name
|
|
||||||
})
|
|
||||||
.deriveBIP45Branch()
|
|
||||||
.extendedPublicKeyString();
|
|
||||||
};
|
|
||||||
|
|
||||||
PublicKeyRing.prototype._updateBip = function(index) {
|
PublicKeyRing.prototype._updateBip = function(index) {
|
||||||
var hk = this.copayersHK[index].derive(HDPath.IdBranch);
|
var hk = this.copayersHK[index].derive(HDPath.IdBranch);
|
||||||
this.copayerIds[index] = hk.eckey.public.toString('hex');
|
this.copayerIds[index] = hk.eckey.public.toString('hex');
|
||||||
|
|
@ -125,6 +117,8 @@ PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) {
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) {
|
PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) {
|
||||||
|
preconditions.checkArgument(newEpk);
|
||||||
|
|
||||||
if (this.isComplete())
|
if (this.isComplete())
|
||||||
throw new Error('PKR already has all required key:' + this.totalCopayers);
|
throw new Error('PKR already has all required key:' + this.totalCopayers);
|
||||||
|
|
||||||
|
|
@ -133,10 +127,6 @@ PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) {
|
||||||
throw new Error('PKR already has that key');
|
throw new Error('PKR already has that key');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!newEpk) {
|
|
||||||
newEpk = this._newExtendedPublicKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = this.copayersHK.length;
|
var i = this.copayersHK.length;
|
||||||
var bip = new HK(newEpk);
|
var bip = new HK(newEpk);
|
||||||
this.copayersHK.push(bip);
|
this.copayersHK.push(bip);
|
||||||
|
|
@ -307,6 +297,30 @@ PublicKeyRing.prototype.forPaths = function(paths) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
|
||||||
|
|
||||||
|
var inKeyMap = {}, ret = [];
|
||||||
|
for(var i in pubkeys ){
|
||||||
|
inKeyMap[pubkeys[i]] = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var keys = this.getForPaths(paths);
|
||||||
|
for(var i in keys ){
|
||||||
|
for(var copayerIndex in keys[i] ){
|
||||||
|
var kHex = keys[i][copayerIndex].toString('hex');
|
||||||
|
if (inKeyMap[kHex]) {
|
||||||
|
ret.push(this.copayerIds[copayerIndex]);
|
||||||
|
delete inKeyMap[kHex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(var i in inKeyMap)
|
||||||
|
throw new Error('Pubkey not identified')
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// TODO this could be cached
|
// TODO this could be cached
|
||||||
PublicKeyRing.prototype._addScriptMap = function(map, path) {
|
PublicKeyRing.prototype._addScriptMap = function(map, path) {
|
||||||
var p = HDPath.indexesForPath(path);
|
var p = HDPath.indexesForPath(path);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ var Key = bitcore.Key;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
var preconditions = require('preconditions').instance();
|
var preconditions = require('preconditions').instance();
|
||||||
|
|
||||||
|
var VERSION = 1;
|
||||||
|
var CORE_FIELDS = ['builderObj','inputChainPaths', 'version'];
|
||||||
|
|
||||||
|
|
||||||
function TxProposal(opts) {
|
function TxProposal(opts) {
|
||||||
preconditions.checkArgument(opts);
|
preconditions.checkArgument(opts);
|
||||||
|
|
@ -17,22 +20,26 @@ function TxProposal(opts) {
|
||||||
preconditions.checkArgument(opts.creator,'no creator');
|
preconditions.checkArgument(opts.creator,'no creator');
|
||||||
preconditions.checkArgument(opts.createdTs,'no createdTs');
|
preconditions.checkArgument(opts.createdTs,'no createdTs');
|
||||||
preconditions.checkArgument(opts.builder,'no builder');
|
preconditions.checkArgument(opts.builder,'no builder');
|
||||||
|
preconditions.checkArgument(opts.inputChainPaths,'no inputChainPaths');
|
||||||
|
|
||||||
|
|
||||||
this.creator = opts.creator;
|
|
||||||
this.createdTs = opts.createdTs;
|
|
||||||
this.builder = opts.builder;
|
|
||||||
this.inputChainPaths = opts.inputChainPaths;
|
this.inputChainPaths = opts.inputChainPaths;
|
||||||
|
this.version = opts.version;
|
||||||
|
this.builder = opts.builder;
|
||||||
|
this.createdTs = opts.createdTs;
|
||||||
|
this.createdTs = opts.createdTs;
|
||||||
this._inputSignatures = [];
|
this._inputSignatures = [];
|
||||||
this.seenBy = opts.seenBy || {};
|
|
||||||
|
// CopayerIds
|
||||||
|
this.creator = opts.creator;
|
||||||
this.signedBy = opts.signedBy || {};
|
this.signedBy = opts.signedBy || {};
|
||||||
|
this.seenBy = opts.seenBy || {};
|
||||||
this.rejectedBy = opts.rejectedBy || {};
|
this.rejectedBy = opts.rejectedBy || {};
|
||||||
this.sentTs = opts.sentTs || null;
|
this.sentTs = opts.sentTs || null;
|
||||||
this.sentTxid = opts.sentTxid || null;
|
this.sentTxid = opts.sentTxid || null;
|
||||||
this.comment = opts.comment || null;
|
this.comment = opts.comment || null;
|
||||||
this.readonly = opts.readonly || null;
|
this.readonly = opts.readonly || null;
|
||||||
// this._updateSignedBy();
|
|
||||||
|
this.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
TxProposal.prototype.getId = function() {
|
TxProposal.prototype.getId = function() {
|
||||||
|
|
@ -47,11 +54,24 @@ TxProposal.prototype.toObj = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TxProposal.prototype.setSent = function(sentTxid) {
|
TxProposal.prototype.toObjForNetwork = function() {
|
||||||
this.sentTxid = sentTxid;
|
var o = this.toObj;
|
||||||
this.sentTs = Date.now();
|
|
||||||
|
var newOutput = {};
|
||||||
|
CORE_FIELDS.forEach(function(k){
|
||||||
|
newOutput[k] = o[k];
|
||||||
|
});
|
||||||
|
return newOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.sync = function() {
|
||||||
|
this._check();
|
||||||
|
this._updateSignedBy();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fromObj => from a trusted source
|
||||||
TxProposal.fromObj = function(o, forceOpts) {
|
TxProposal.fromObj = function(o, forceOpts) {
|
||||||
preconditions.checkArgument(o.builderObj);
|
preconditions.checkArgument(o.builderObj);
|
||||||
delete o['builder'];
|
delete o['builder'];
|
||||||
|
|
@ -64,17 +84,24 @@ TxProposal.fromObj = function(o, forceOpts) {
|
||||||
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
||||||
|
// backwards (V0) compatatibility fix.
|
||||||
if (!o.version) {
|
if (!o.version) {
|
||||||
o.builder = new BuilderMockV0(o.builderObj);
|
o.builder = new BuilderMockV0(o.builderObj);
|
||||||
o.readonly = 1;
|
o.readonly = 1;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return new TxProposal(o);
|
||||||
|
};
|
||||||
|
|
||||||
var t = new TxProposal(o);
|
TxProposal.fromObjUntrusted = function(o, forceOpts, senderId) {
|
||||||
t._check();
|
var newInput = {};
|
||||||
t._updateSignedBy();
|
CORE_FIELDS.forEach(function(k){
|
||||||
|
newInput[k] = o[k];
|
||||||
|
});
|
||||||
|
if (newInput.version !== VERSION)
|
||||||
|
throw new Error('Peer using different version');
|
||||||
|
|
||||||
return t;
|
return TxProposal.fromObj(newInput, forceOpts, senderId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,7 +171,8 @@ TxProposal.prototype._updateSignedBy = function() {
|
||||||
if (signatureIndexes.length !== signatureCount)
|
if (signatureIndexes.length !== signatureCount)
|
||||||
throw new Error('Invalid signature');
|
throw new Error('Invalid signature');
|
||||||
this._inputSignatures[i] = signatureIndexes.map(function(i) {
|
this._inputSignatures[i] = signatureIndexes.map(function(i) {
|
||||||
return info.keys[i].toString('hex');
|
var r = info.keys[i].toString('hex');
|
||||||
|
return r;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -184,6 +212,21 @@ TxProposal.prototype.mergeBuilder = function(incoming) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposal.prototype.setSeen = function(copayerId) {
|
||||||
|
if (!this.seenBy[copayerId])
|
||||||
|
this.seenBy[copayerId] = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.setRejected = function(copayerId) {
|
||||||
|
if (!this.rejectedBy[copayerId] && !this.signedBy)
|
||||||
|
this.rejectedBy[copayerId] = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
TxProposal.prototype.setSent = function(sentTxid) {
|
||||||
|
this.sentTxid = sentTxid;
|
||||||
|
this.sentTs = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
/* OTDO
|
/* OTDO
|
||||||
events.push({
|
events.push({
|
||||||
type: 'seen',
|
type: 'seen',
|
||||||
|
|
@ -213,12 +256,11 @@ TxProposal.prototype._allSignatures = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// merge will not merge any metadata.
|
||||||
TxProposal.prototype.merge = function(incoming) {
|
TxProposal.prototype.merge = function(incoming) {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
var newSignatures = [];
|
var newSignatures = [];
|
||||||
|
incoming.sync();
|
||||||
incoming._check();
|
|
||||||
incoming._updateSignedBy();
|
|
||||||
|
|
||||||
var prevInputSignatures = this._allSignatures();
|
var prevInputSignatures = this._allSignatures();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ function TxProposals(opts) {
|
||||||
this.txps = {};
|
this.txps = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fromObj => from a trusted source
|
||||||
TxProposals.fromObj = function(o, forceOpts) {
|
TxProposals.fromObj = function(o, forceOpts) {
|
||||||
var ret = new TxProposals({
|
var ret = new TxProposals({
|
||||||
networkName: o.networkName,
|
networkName: o.networkName,
|
||||||
|
|
@ -60,8 +61,6 @@ TxProposals.prototype.merge = function(inTxp, allowedPubKeys) {
|
||||||
|
|
||||||
var ntxid = inTxp.getId();
|
var ntxid = inTxp.getId();
|
||||||
var ret = {};
|
var ret = {};
|
||||||
ret.events = [];
|
|
||||||
ret.events.hasChanged = false;
|
|
||||||
|
|
||||||
if (myTxps[ntxid]) {
|
if (myTxps[ntxid]) {
|
||||||
var v0 = myTxps[ntxid];
|
var v0 = myTxps[ntxid];
|
||||||
|
|
@ -70,12 +69,7 @@ TxProposals.prototype.merge = function(inTxp, allowedPubKeys) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.txps[ntxid] = inTxp;
|
this.txps[ntxid] = inTxp;
|
||||||
ret.hasChanged = true;
|
ret.new = 1;
|
||||||
ret.events.push({
|
|
||||||
type: 'new',
|
|
||||||
cid: inTxp.creator,
|
|
||||||
tx: ntxid
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
@ -88,22 +82,14 @@ TxProposals.prototype.mergeFromObj = function(txProposalObj, allowedPubKeys, opt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Add a LOCALLY CREATED (trusted) tx proposal
|
// Add a LOCALLY CREATED (trusted) tx proposal
|
||||||
TxProposals.prototype.add = function(data) {
|
TxProposals.prototype.add = function(txp) {
|
||||||
var txp = new TxProposal(data);
|
txp.sync();
|
||||||
var ntxid = txp.getId();
|
var ntxid = txp.getId();
|
||||||
this.txps[ntxid] = txp;
|
this.txps[ntxid] = txp;
|
||||||
return ntxid;
|
return ntxid;
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposals.prototype.setSent = function(ntxid, txid) {
|
|
||||||
//sent TxProposals are local an not broadcasted.
|
|
||||||
this.txps[ntxid].setSent(txid);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
|
TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
|
||||||
var txp = this.txps[ntxid];
|
var txp = this.txps[ntxid];
|
||||||
var i = JSON.parse(JSON.stringify(txp));
|
var i = JSON.parse(JSON.stringify(txp));
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ var Address = bitcore.Address;
|
||||||
|
|
||||||
var HDParams = require('./HDParams');
|
var HDParams = require('./HDParams');
|
||||||
var PublicKeyRing = require('./PublicKeyRing');
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
|
var TxProposal = require('./TxProposal');
|
||||||
var TxProposals = require('./TxProposals');
|
var TxProposals = require('./TxProposals');
|
||||||
var PrivateKey = require('./PrivateKey');
|
var PrivateKey = require('./PrivateKey');
|
||||||
var copayConfig = require('../../../config');
|
var copayConfig = require('../../../config');
|
||||||
|
|
@ -129,11 +130,39 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Wallet.prototype._processProposalEvents = function(mergeInfo) {
|
||||||
|
var ev = [];
|
||||||
|
if (mergeInfo.new) {
|
||||||
|
ev = {
|
||||||
|
type: 'new',
|
||||||
|
cid: senderId
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var i in mergeInfo.newCopayers) {
|
||||||
|
var copayerId = mergeInfo.newCopayers[i];
|
||||||
|
ev.push({
|
||||||
|
type: 'signed',
|
||||||
|
cid: copayerId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev)
|
||||||
|
this.emit('txProposalEvent',ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype._handleTxProposal = function(senderId, data) {
|
Wallet.prototype._handleTxProposal = function(senderId, data) {
|
||||||
this.log('RECV TXPROPOSAL: ', data);
|
this.log('RECV TXPROPOSAL: ', data);
|
||||||
var mergeInfo;
|
var mergeInfo, ntxid;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mergeInfo = this.txProposals.mergeFromObj(data.txProposal, senderId, Wallet.builderOpts);
|
mergeInfo = this.txProposals.mergeFromObj(data.txProposal, senderId, Wallet.builderOpts);
|
||||||
|
mergeInfo.newCopayers=[];
|
||||||
|
for (var i in mergeInfo.newSignatures) {
|
||||||
|
var k = mergeInfo.newSignatures[i];
|
||||||
|
mergeInfo.newCopayers.push(this.getCopayerIdFromPubKey(k));
|
||||||
|
};
|
||||||
|
ntxid = mergeInfo.inTxp.getId();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var corruptEvent = {
|
var corruptEvent = {
|
||||||
type: 'corrupt',
|
type: 'corrupt',
|
||||||
|
|
@ -143,21 +172,36 @@ Wallet.prototype._handleTxProposal = function(senderId, data) {
|
||||||
this.emit('txProposalEvent', corruptEvent);
|
this.emit('txProposalEvent', corruptEvent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.sendSeen(ntxid);
|
||||||
|
|
||||||
var added = this.addSeenToTxProposals();
|
if (mergeInfo.hasChanged)
|
||||||
if (added) {
|
this.sendTxProposal(ntxid);
|
||||||
this.log('### BROADCASTING txProposals with my seenBy updated.');
|
|
||||||
this.sendTxProposal(mergeInfo.inTxp.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emit('txProposalsUpdated');
|
this.emit('txProposalsUpdated');
|
||||||
this.store();
|
this.store();
|
||||||
|
this._processProposalEvents(senderId, mergeInfo);
|
||||||
for (var i = 0; i < mergeInfo.events.length; i++) {
|
|
||||||
this.emit('txProposalEvent', mergeInfo.events[i]);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Wallet.prototype._handleReject = function(senderId, data, isInbound) {
|
||||||
|
this.log('RECV REJECT:', data);
|
||||||
|
// TODO check that has not signed.
|
||||||
|
//
|
||||||
|
this.txProposals.txps[data.ntxid].setRejected(senderId);
|
||||||
|
this.emit('txProposalsUpdated');
|
||||||
|
this.store();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype._handleSeen = function(senderId, data, isInbound) {
|
||||||
|
this.log('RECV SEEN:', data);
|
||||||
|
this.txProposals.txps[data.ntxid].setSeen(senderId);
|
||||||
|
this.emit('txProposalsUpdated');
|
||||||
|
this.store();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype._handleAddressBook = function(senderId, data, isInbound) {
|
Wallet.prototype._handleAddressBook = function(senderId, data, isInbound) {
|
||||||
this.log('RECV ADDRESSBOOK:', data);
|
this.log('RECV ADDRESSBOOK:', data);
|
||||||
var rcv = data.addressBook;
|
var rcv = data.addressBook;
|
||||||
|
|
@ -199,6 +243,10 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
|
||||||
case 'publicKeyRing':
|
case 'publicKeyRing':
|
||||||
this._handlePublicKeyRing(senderId, data, isInbound);
|
this._handlePublicKeyRing(senderId, data, isInbound);
|
||||||
break;
|
break;
|
||||||
|
case 'reject':
|
||||||
|
this._handleReject(senderId, data, isInbound);
|
||||||
|
case 'seen':
|
||||||
|
this._handleReject(senderId, data, isInbound);
|
||||||
case 'txProposal':
|
case 'txProposal':
|
||||||
this._handleTxProposal(senderId, data, isInbound);
|
this._handleTxProposal(senderId, data, isInbound);
|
||||||
break;
|
break;
|
||||||
|
|
@ -381,6 +429,7 @@ Wallet.prototype.toObj = function() {
|
||||||
return walletObj;
|
return walletObj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// fromObj => from a trusted source
|
||||||
Wallet.fromObj = function(o, storage, network, blockchain) {
|
Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||||
opts.addressBook = o.addressBook;
|
opts.addressBook = o.addressBook;
|
||||||
|
|
@ -424,6 +473,26 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.sendSeen = function(ntxid) {
|
||||||
|
preconditions.checkArgument(ntxid);
|
||||||
|
this.log('### SENDING seen: ' + ntxid + ' TO: All');
|
||||||
|
this.send(null, {
|
||||||
|
type: 'seen',
|
||||||
|
ntxid: ntxid,
|
||||||
|
walletId: this.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.sendReject = function(ntxid) {
|
||||||
|
preconditions.checkArgument(ntxid);
|
||||||
|
this.log('### SENDING reject: ' + ntxid + ' TO: All');
|
||||||
|
this.send(null, {
|
||||||
|
type: 'reject',
|
||||||
|
ntxid: ntxid,
|
||||||
|
walletId: this.id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendWalletReady = function(recipients) {
|
Wallet.prototype.sendWalletReady = function(recipients) {
|
||||||
this.log('### SENDING WalletReady TO:', recipients);
|
this.log('### SENDING WalletReady TO:', recipients);
|
||||||
|
|
||||||
|
|
@ -521,7 +590,7 @@ Wallet.prototype.reject = function(ntxid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
txp.rejectedBy[myId] = Date.now();
|
txp.rejectedBy[myId] = Date.now();
|
||||||
this.sendTxProposal(ntxid);
|
this.sendReject(ntxid);
|
||||||
this.store();
|
this.store();
|
||||||
this.emit('txProposalsUpdated');
|
this.emit('txProposalsUpdated');
|
||||||
};
|
};
|
||||||
|
|
@ -534,10 +603,10 @@ Wallet.prototype.sign = function(ntxid, cb) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
var myId = self.getMyCopayerId();
|
var myId = self.getMyCopayerId();
|
||||||
var txp = self.txProposals.txps[ntxid];
|
var txp = self.txProposals.txps[ntxid];
|
||||||
if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) {
|
// if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) {
|
||||||
if (cb) cb(false);
|
// if (cb) cb(false);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var keys = self.privateKey.getForPaths(txp.inputChainPaths);
|
var keys = self.privateKey.getForPaths(txp.inputChainPaths);
|
||||||
|
|
||||||
var b = txp.builder;
|
var b = txp.builder;
|
||||||
|
|
@ -574,7 +643,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
this.blockchain.sendRawTransaction(txHex, function(txid) {
|
this.blockchain.sendRawTransaction(txHex, function(txid) {
|
||||||
self.log('BITCOIND txid:', txid);
|
self.log('BITCOIND txid:', txid);
|
||||||
if (txid) {
|
if (txid) {
|
||||||
self.txProposals.setSent(ntxid, txid);
|
self.txProposals.txps[ntxid].setSent(txid);
|
||||||
self.sendTxProposal(ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
}
|
}
|
||||||
|
|
@ -582,20 +651,20 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.addSeenToTxProposals = function() {
|
// Wallet.prototype.addSeenToTxProposals = function() {
|
||||||
var ret = false;
|
// var ret = false;
|
||||||
var myId = this.getMyCopayerId();
|
// var myId = this.getMyCopayerId();
|
||||||
|
//
|
||||||
for (var k in this.txProposals.txps) {
|
// for (var k in this.txProposals.txps) {
|
||||||
var txp = this.txProposals.txps[k];
|
// var txp = this.txProposals.txps[k];
|
||||||
if (!txp.seenBy[myId]) {
|
// if (!txp.seenBy[myId]) {
|
||||||
|
//
|
||||||
txp.seenBy[myId] = Date.now();
|
// txp.seenBy[myId] = Date.now();
|
||||||
ret = true;
|
// ret = true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return ret;
|
// return ret;
|
||||||
};
|
// };
|
||||||
|
|
||||||
// TODO: remove this method and use getAddressesInfo everywhere
|
// TODO: remove this method and use getAddressesInfo everywhere
|
||||||
Wallet.prototype.getAddresses = function(opts) {
|
Wallet.prototype.getAddresses = function(opts) {
|
||||||
|
|
@ -718,6 +787,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
|
|
||||||
preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName());
|
preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName());
|
||||||
preconditions.checkState(pkr.isComplete());
|
preconditions.checkState(pkr.isComplete());
|
||||||
|
preconditions.checkState(priv);
|
||||||
if (comment) preconditions.checkArgument(comment.length <= 100);
|
if (comment) preconditions.checkArgument(comment.length <= 100);
|
||||||
|
|
||||||
if (!opts.remainderOut) {
|
if (!opts.remainderOut) {
|
||||||
|
|
@ -744,22 +814,23 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
|
|
||||||
b = b.setHashToScriptMap(pkr.getRedeemScriptMap(inputChainPaths));
|
b = b.setHashToScriptMap(pkr.getRedeemScriptMap(inputChainPaths));
|
||||||
|
|
||||||
if (priv) {
|
var keys = priv.getForPaths(inputChainPaths);
|
||||||
var keys = priv.getForPaths(inputChainPaths);
|
var signed = b.sign(keys);
|
||||||
var signed = b.sign(keys);
|
|
||||||
}
|
|
||||||
var myId = this.getMyCopayerId();
|
var myId = this.getMyCopayerId();
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
||||||
var me = {};
|
|
||||||
|
|
||||||
var tx = b.build();
|
var tx = b.build();
|
||||||
if (priv && tx.countInputSignatures(0)) me[myId] = now;
|
if (!tx.countInputSignatures(0))
|
||||||
|
throw new Error ('Could not sign generated tx');
|
||||||
|
|
||||||
|
var me = {};
|
||||||
|
me[myId] = now;
|
||||||
|
|
||||||
var meSeen = {};
|
var meSeen = {};
|
||||||
if (priv) meSeen[myId] = now;
|
if (priv) meSeen[myId] = now;
|
||||||
|
|
||||||
var data = {
|
var ntxid = this.txProposals.add(new TxProposal({
|
||||||
inputChainPaths: inputChainPaths,
|
inputChainPaths: inputChainPaths,
|
||||||
signedBy: me,
|
signedBy: me,
|
||||||
seenBy: meSeen,
|
seenBy: meSeen,
|
||||||
|
|
@ -767,9 +838,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
createdTs: now,
|
createdTs: now,
|
||||||
builder: b,
|
builder: b,
|
||||||
comment: comment
|
comment: comment
|
||||||
};
|
}));
|
||||||
|
|
||||||
var ntxid = this.txProposals.add(data);
|
|
||||||
return ntxid;
|
return ntxid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,8 +182,6 @@ describe('TxProposal', function() {
|
||||||
});
|
});
|
||||||
it('#_updateSignedBy', function() {
|
it('#_updateSignedBy', function() {
|
||||||
var txp = dummyProposal;
|
var txp = dummyProposal;
|
||||||
txp._inputSignatures.should.deep.equal([]);
|
|
||||||
txp._updateSignedBy();
|
|
||||||
txp._inputSignatures.should.deep.equal([[ '03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3' ]]);
|
txp._inputSignatures.should.deep.equal([[ '03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3' ]]);
|
||||||
});
|
});
|
||||||
describe('#_check', function() {
|
describe('#_check', function() {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ try {
|
||||||
}
|
}
|
||||||
var copayConfig = require('../config');
|
var copayConfig = require('../config');
|
||||||
var Wallet = require('../js/models/core/Wallet');
|
var Wallet = require('../js/models/core/Wallet');
|
||||||
var Structure = copay.Structure;
|
var PrivateKey = copay.PrivateKey;
|
||||||
var Storage = require('./mocks/FakeStorage');
|
var Storage = require('./mocks/FakeStorage');
|
||||||
var Network = require('./mocks/FakeNetwork');
|
var Network = require('./mocks/FakeNetwork');
|
||||||
var Blockchain = require('./mocks/FakeBlockchain');
|
var Blockchain = require('./mocks/FakeBlockchain');
|
||||||
|
|
@ -19,22 +19,30 @@ var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
requiredCopayers: 3,
|
||||||
|
totalCopayers: 5,
|
||||||
|
spendUnconfirmed: true,
|
||||||
|
reconnectDelay: 100,
|
||||||
|
networkName: 'testnet',
|
||||||
|
};
|
||||||
|
|
||||||
|
var getNewEpk = function() {
|
||||||
|
return new PrivateKey({
|
||||||
|
networkName: config.networkName,
|
||||||
|
})
|
||||||
|
.deriveBIP45Branch()
|
||||||
|
.extendedPublicKeyString();
|
||||||
|
}
|
||||||
|
|
||||||
var addCopayers = function(w) {
|
var addCopayers = function(w) {
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
w.publicKeyRing.addCopayer();
|
w.publicKeyRing.addCopayer(getNewEpk());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('Wallet model', function() {
|
describe('Wallet model', function() {
|
||||||
|
|
||||||
var config = {
|
|
||||||
requiredCopayers: 3,
|
|
||||||
totalCopayers: 5,
|
|
||||||
spendUnconfirmed: true,
|
|
||||||
reconnectDelay: 100,
|
|
||||||
networkName: 'testnet',
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should fail to create an instance', function() {
|
it('should fail to create an instance', function() {
|
||||||
(function() {
|
(function() {
|
||||||
new Wallet(config)
|
new Wallet(config)
|
||||||
|
|
@ -47,12 +55,11 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var createW = function(netKey, N, conf) {
|
var createW = function(N, conf) {
|
||||||
|
|
||||||
var c = JSON.parse(JSON.stringify(conf || config));
|
var c = JSON.parse(JSON.stringify(conf || config));
|
||||||
if (!N) N = c.totalCopayers;
|
if (!N) N = c.totalCopayers;
|
||||||
|
|
||||||
if (netKey) c.netKey = netKey;
|
|
||||||
var mainPrivateKey = new copay.PrivateKey({
|
var mainPrivateKey = new copay.PrivateKey({
|
||||||
networkName: config.networkName
|
networkName: config.networkName
|
||||||
});
|
});
|
||||||
|
|
@ -148,8 +155,7 @@ describe('Wallet model', function() {
|
||||||
|
|
||||||
var createW2 = function(privateKeys, N, conf) {
|
var createW2 = function(privateKeys, N, conf) {
|
||||||
if (!N) N = 3;
|
if (!N) N = 3;
|
||||||
var netKey = 'T0FbU2JLby0=';
|
var w = createW(N, conf);
|
||||||
var w = createW(netKey, N, conf);
|
|
||||||
should.exist(w);
|
should.exist(w);
|
||||||
|
|
||||||
var pkr = w.publicKeyRing;
|
var pkr = w.publicKeyRing;
|
||||||
|
|
@ -157,9 +163,9 @@ describe('Wallet model', function() {
|
||||||
for (var i = 0; i < N - 1; i++) {
|
for (var i = 0; i < N - 1; i++) {
|
||||||
if (privateKeys) {
|
if (privateKeys) {
|
||||||
var k = privateKeys[i];
|
var k = privateKeys[i];
|
||||||
pkr.addCopayer(k ? k.deriveBIP45Branch().extendedPublicKeyString() : null);
|
pkr.addCopayer(k ? k.deriveBIP45Branch().extendedPublicKeyString() : getNewEpk());
|
||||||
} else {
|
} else {
|
||||||
pkr.addCopayer();
|
pkr.addCopayer(getNewEpk());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,12 +218,12 @@ describe('Wallet model', function() {
|
||||||
|
|
||||||
var t = w.txProposals;
|
var t = w.txProposals;
|
||||||
var txp = t.txps[ntxid];
|
var txp = t.txps[ntxid];
|
||||||
|
Object.keys(txp._inputSignatures).length.should.equal(1);
|
||||||
var tx = txp.builder.build();
|
var tx = txp.builder.build();
|
||||||
should.exist(tx);
|
should.exist(tx);
|
||||||
chai.expect(txp.comment).to.be.null;
|
chai.expect(txp.comment).to.be.null;
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
Object.keys(txp.seenBy).length.should.equal(1);
|
Object.keys(txp.seenBy).length.should.equal(1);
|
||||||
Object.keys(txp.signedBy).length.should.equal(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('#create with comment', function() {
|
it('#create with comment', function() {
|
||||||
|
|
@ -502,7 +508,8 @@ describe('Wallet model', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
var r = w.getRegisteredCopayerIds();
|
var r = w.getRegisteredCopayerIds();
|
||||||
r.length.should.equal(1);
|
r.length.should.equal(1);
|
||||||
w.publicKeyRing.addCopayer();
|
w.publicKeyRing.addCopayer(getNewEpk());
|
||||||
|
|
||||||
r = w.getRegisteredCopayerIds();
|
r = w.getRegisteredCopayerIds();
|
||||||
r.length.should.equal(2);
|
r.length.should.equal(2);
|
||||||
r[0].should.not.equal(r[1]);
|
r[0].should.not.equal(r[1]);
|
||||||
|
|
@ -512,7 +519,7 @@ describe('Wallet model', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
var r = w.getRegisteredPeerIds();
|
var r = w.getRegisteredPeerIds();
|
||||||
r.length.should.equal(1);
|
r.length.should.equal(1);
|
||||||
w.publicKeyRing.addCopayer();
|
w.publicKeyRing.addCopayer(getNewEpk());
|
||||||
r = w.getRegisteredPeerIds();
|
r = w.getRegisteredPeerIds();
|
||||||
r.length.should.equal(2);
|
r.length.should.equal(2);
|
||||||
r[0].should.not.equal(r[1]);
|
r[0].should.not.equal(r[1]);
|
||||||
|
|
@ -642,10 +649,11 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should create & sign transaction from received funds', function(done) {
|
it('should create & sign transaction from received funds', function(done) {
|
||||||
this.timeout(10000);
|
var k2 = new PrivateKey({
|
||||||
var w = cachedCreateW2();
|
networkName: config.networkName
|
||||||
var pk = w.privateKey;
|
});
|
||||||
w.privateKey = null;
|
|
||||||
|
var w = createW2([k2]);
|
||||||
var utxo = createUTXO(w);
|
var utxo = createUTXO(w);
|
||||||
w.blockchain.fixUnspent(utxo);
|
w.blockchain.fixUnspent(utxo);
|
||||||
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
||||||
|
|
@ -654,7 +662,7 @@ describe('Wallet model', function() {
|
||||||
w.getTxProposals()[0].rejectedByUs.should.equal(false);
|
w.getTxProposals()[0].rejectedByUs.should.equal(false);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
w.privateKey = pk;
|
w.privateKey = k2;
|
||||||
w.sign(ntxid, function(success) {
|
w.sign(ntxid, function(success) {
|
||||||
success.should.equal(true);
|
success.should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
@ -1031,9 +1039,9 @@ describe('Wallet model', function() {
|
||||||
e.type.should.equal(result);
|
e.type.should.equal(result);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
var txp = {
|
var txp = {dummy:1};
|
||||||
'txProposal': { dummy: 1}
|
// txp.prototype.getId = function() {return 'aa'};
|
||||||
};
|
var txp = { 'txProposal': txp };
|
||||||
var merge = sinon.stub(w.txProposals, 'mergeFromObj', function() {
|
var merge = sinon.stub(w.txProposals, 'mergeFromObj', function() {
|
||||||
if (shouldThrow) throw new Error();
|
if (shouldThrow) throw new Error();
|
||||||
return {events: [{type:'new'}]};
|
return {events: [{type:'new'}]};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue