signMessage on message
This commit is contained in:
parent
cdc80e94eb
commit
8339cd7298
7 changed files with 459 additions and 328 deletions
|
|
@ -369,6 +369,8 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
notification.success('Success', 'Transaction proposal created');
|
||||
else if (status == copay.Wallet.TX_SIGNED)
|
||||
notification.success('Success', 'Transaction proposal was signed');
|
||||
else if (status == copay.Wallet.TX_SIGNED_AND_BROADCASTED)
|
||||
notification.success('Success', 'Transaction signed and broadcasted!');
|
||||
else
|
||||
notification.error('Error', 'Unknown error occured');
|
||||
};
|
||||
|
|
@ -378,7 +380,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
|||
$scope.error = $scope.success = null;
|
||||
$scope.loading = true;
|
||||
$rootScope.txAlertCount = 0;
|
||||
w.broadcastTx(ntxid, function(err, txid, status) {
|
||||
w.issueTx(ntxid, function(err, txid, status) {
|
||||
$scope.notifyStatus(status);
|
||||
if (cb) return cb();
|
||||
else $scope.loadTxs();
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ function TxProposal(opts) {
|
|||
this.paymentAckMemo = opts.paymentAckMemo || null;
|
||||
this.paymentProtocolURL = opts.paymentProtocolURL || null;
|
||||
|
||||
// not from obj
|
||||
this._pubkeysForScriptCache = {};
|
||||
this.resetCache();
|
||||
|
||||
// New Tx Proposal
|
||||
if (_.isEmpty(this.seenBy) && opts.creator) {
|
||||
|
|
@ -206,10 +205,122 @@ TxProposal.prototype.isPending = function(maxRejectCount) {
|
|||
return true;
|
||||
};
|
||||
|
||||
TxProposal.prototype._setSigned = function(copayerId) {
|
||||
|
||||
// Sign powns rejected
|
||||
if (this.rejectedBy[copayerId]) {
|
||||
log.info("WARN: a previously rejected transaction was signed by:", copayerId);
|
||||
delete this.rejectedBy[copayerId];
|
||||
}
|
||||
|
||||
this.signedBy[copayerId] = Date.now();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @desc verify signatures of ONE copayer, using an array of signatures for each input
|
||||
*
|
||||
* @param {string[]} signatures, of the same copayer, one for each input
|
||||
* @return {string[]} array for signing pubkeys for each input
|
||||
*/
|
||||
TxProposal.prototype._addSignatureAndVerify = function(signatures) {
|
||||
var self = this;
|
||||
|
||||
var ret = [];
|
||||
var tx = self.builder.build();
|
||||
|
||||
var newScriptSigs = [];
|
||||
_.each(tx.ins, function(input, index) {
|
||||
var scriptSig = new Script(input.s);
|
||||
|
||||
var info = TxProposal.infoFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(info.script, parseInt(index), Transaction.SIGHASH_ALL);
|
||||
var keys = TxProposal.formatKeys(info.keys);
|
||||
var sig = new Buffer(signatures[index], 'hex');
|
||||
|
||||
var hashType = sig[sig.length - 1];
|
||||
if (hashType !== Transaction.SIGHASH_ALL)
|
||||
throw new Error('BADSIG: Invalid signature: Bad hash type');
|
||||
|
||||
var sigRaw = new Buffer(sig.slice(0, sig.length - 1));
|
||||
var signingPubKeyHex = self._verifyOneSignature(keys, sigRaw, txSigHash);
|
||||
if (!signingPubKeyHex)
|
||||
throw new Error('BADSIG: Invalid signatures: invalid for input:' + index);
|
||||
|
||||
// now insert it
|
||||
var keysHex = _.pluck(keys, 'keyHex');
|
||||
var prio = _.indexOf(keysHex, signingPubKeyHex);
|
||||
preconditions.checkState(prio >= 0);
|
||||
|
||||
var currentKeys = self.getSignersPubKeys()[index];
|
||||
|
||||
if (_.indexOf(currentKeys, signingPubKeyHex) >= 0)
|
||||
throw new Error('BADSIG: Already have this signature');
|
||||
|
||||
var currentPrios = _.map(currentKeys, function(key) {
|
||||
var prio = _.indexOf(keysHex, key);
|
||||
preconditions.checkState(prio >= 0);
|
||||
return prio;
|
||||
});
|
||||
|
||||
var insertAt = 0;
|
||||
while ( !_.isUndefined(currentPrios[insertAt]) && prio > currentPrios[insertAt] )
|
||||
insertAt++;
|
||||
|
||||
// Insert it! (1 is OP_0!)
|
||||
scriptSig.chunks.splice(1 + insertAt, 0, sig);
|
||||
scriptSig.updateBuffer();
|
||||
|
||||
|
||||
newScriptSigs.push(scriptSig.buffer);
|
||||
});
|
||||
preconditions.checkState(newScriptSigs.length === tx.ins.length);
|
||||
|
||||
// If we reach here, all signatures are OK, let's update the TX.
|
||||
_.each(tx.ins, function(input, index) {
|
||||
input.s = newScriptSigs[index];
|
||||
|
||||
// TODO just to keep TransactionBuilder
|
||||
self.builder.inputsSigned++;
|
||||
});
|
||||
this.resetCache();
|
||||
};
|
||||
|
||||
TxProposal.prototype.resetCache = function() {
|
||||
this.cache = {
|
||||
pubkeysForScript: {},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* addSignature
|
||||
*
|
||||
* @param {string[]} signatures from *ONE* copayer, one signature for each TX input.
|
||||
* @return {boolean} true = signatures added
|
||||
*/
|
||||
TxProposal.prototype.addSignature = function(copayerId, signatures) {
|
||||
preconditions.checkArgument(_.isArray(signatures));
|
||||
|
||||
if (this.isFullySigned())
|
||||
return false;
|
||||
|
||||
var tx = this.builder.build();
|
||||
preconditions.checkArgument(signatures.length === tx.ins.length, 'Wrong number of signatures given');
|
||||
|
||||
this._addSignatureAndVerify(signatures);
|
||||
this._setSigned(copayerId);
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* getSignersPubKey
|
||||
* @desc get Pubkeys of signers, for each input
|
||||
* @desc get Pubkeys of signers, for each input. this is CPU intensive
|
||||
*
|
||||
* @return {string[][]} array of hashes for signing pubkeys for each input
|
||||
*/
|
||||
|
|
@ -219,34 +330,36 @@ TxProposal.prototype.getSignersPubKeys = function(forceUpdate) {
|
|||
|
||||
var signersPubKey = [];
|
||||
|
||||
if (!self._signersPubKey || forceUpdate) {
|
||||
if (!self.cache.signersPubKey || forceUpdate) {
|
||||
|
||||
log.debug('PERFORMANCE WARN: Verifying *all* TX signatures:', self.getId());
|
||||
|
||||
var tx = self.builder.build();
|
||||
_.each(tx.ins, function(input, index) {
|
||||
|
||||
if (!self._pubkeysForScriptCache[input.s]) {
|
||||
if (!self.cache.pubkeysForScript[input.s]) {
|
||||
var scriptSig = new Script(input.s);
|
||||
var signatureCount = scriptSig.countSignatures();
|
||||
|
||||
var info = TxProposal._infoFromRedeemScript(scriptSig);
|
||||
var info = TxProposal.infoFromRedeemScript(scriptSig);
|
||||
var txSigHash = tx.hashForSignature(info.script, parseInt(index), Transaction.SIGHASH_ALL);
|
||||
var inputSignersPubKey = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash);
|
||||
var inputSignersPubKey = self.verifySignatures(info.keys, scriptSig, txSigHash);
|
||||
|
||||
// Does scriptSig has strings that are not signatures?
|
||||
if (inputSignersPubKey.length !== signatureCount)
|
||||
throw new Error('Invalid signature');
|
||||
|
||||
self._pubkeysForScriptCache[input.s] = inputSignersPubKey;
|
||||
self.cache.pubkeysForScript[input.s] = inputSignersPubKey;
|
||||
}
|
||||
|
||||
signersPubKey[index] = self._pubkeysForScriptCache[input.s];
|
||||
signersPubKey[index] = self.cache.pubkeysForScript[input.s];
|
||||
});
|
||||
self._signersPubKey = signersPubKey;
|
||||
self.cache.signersPubKey = signersPubKey;
|
||||
} else {
|
||||
log.debug('Using signatures verification cache')
|
||||
}
|
||||
|
||||
return self._signersPubKey;
|
||||
return self.cache.signersPubKey;
|
||||
};
|
||||
|
||||
TxProposal.prototype.getId = function() {
|
||||
|
|
@ -261,7 +374,7 @@ TxProposal.prototype.getId = function() {
|
|||
TxProposal.prototype.toObj = function() {
|
||||
var o = JSON.parse(JSON.stringify(this));
|
||||
delete o['builder'];
|
||||
delete o['_pubkeysForScriptCache'];
|
||||
delete o['cache'];
|
||||
o.builderObj = this.builder.toObj();
|
||||
return o;
|
||||
};
|
||||
|
|
@ -279,8 +392,6 @@ TxProposal.fromObj = function(o, forceOpts) {
|
|||
preconditions.checkArgument(o.builderObj);
|
||||
delete o['builder'];
|
||||
forceOpts = forceOpts || {};
|
||||
var builderClass = forceOpts.transactionBuilderClass || TransactionBuilder;
|
||||
|
||||
o.builderObj.opts = o.builderObj.opts || {};
|
||||
|
||||
// force opts is requested.
|
||||
|
|
@ -295,7 +406,7 @@ TxProposal.fromObj = function(o, forceOpts) {
|
|||
}
|
||||
|
||||
try {
|
||||
o.builder = builderClass.fromObj(o.builderObj);
|
||||
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
||||
} catch (e) {
|
||||
throw new Error(e);
|
||||
return null;
|
||||
|
|
@ -317,7 +428,7 @@ TxProposal.prototype.toObjTrim = function() {
|
|||
return TxProposal._trim(this.toObj());
|
||||
};
|
||||
|
||||
TxProposal._formatKeys = function(keys) {
|
||||
TxProposal.formatKeys = function(keys) {
|
||||
var ret = [];
|
||||
for (var i in keys) {
|
||||
if (!Buffer.isBuffer(keys[i]))
|
||||
|
|
@ -333,33 +444,66 @@ TxProposal._formatKeys = function(keys) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
TxProposal._verifySignatures = function(inKeys, scriptSig, txSigHash) {
|
||||
|
||||
/**
|
||||
* @desc Verify a single signature, for a given hash, tested against a given list of public keys.
|
||||
* @param keys
|
||||
* @param sigRaw
|
||||
* @param txSigHash
|
||||
* @return {string?} on valid signature, return the signing public key hex representation
|
||||
*/
|
||||
TxProposal.prototype._verifyOneSignature = function(keys, sigRaw, txSigHash) {
|
||||
preconditions.checkArgument(Buffer.isBuffer(txSigHash));
|
||||
preconditions.checkArgument(Buffer.isBuffer(sigRaw));
|
||||
preconditions.checkArgument(_.isArray(keys));
|
||||
preconditions.checkArgument(keys[0].keyObj);
|
||||
|
||||
var signingKey = _.find(keys, function(key) {
|
||||
var ret = false;
|
||||
try {
|
||||
ret = key.keyObj.verifySignatureSync(txSigHash, sigRaw);
|
||||
} catch (e) {};
|
||||
return ret;
|
||||
});
|
||||
|
||||
return signingKey ? signingKey.keyHex : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @desc verify transaction signatures
|
||||
*
|
||||
* @param inKeys
|
||||
* @param scriptSig
|
||||
* @param txSigHash
|
||||
* @return {string[]} signing pubkeys, in order of apperance
|
||||
*/
|
||||
TxProposal.prototype.verifySignatures = function(inKeys, scriptSig, txSigHash) {
|
||||
preconditions.checkArgument(Buffer.isBuffer(txSigHash));
|
||||
preconditions.checkArgument(inKeys);
|
||||
preconditions.checkState(Buffer.isBuffer(inKeys[0]));
|
||||
var self = this;
|
||||
|
||||
if (scriptSig.chunks[0] !== 0)
|
||||
throw new Error('Invalid scriptSig');
|
||||
|
||||
var keys = TxProposal._formatKeys(inKeys);
|
||||
var keys = TxProposal.formatKeys(inKeys);
|
||||
var ret = [];
|
||||
for (var i = 1; i <= scriptSig.countSignatures(); i++) {
|
||||
var chunk = scriptSig.chunks[i];
|
||||
log.debug('\t Verifying CHUNK:', i);
|
||||
var sigRaw = new Buffer(chunk.slice(0, chunk.length - 1));
|
||||
|
||||
log.debug('\t Verifying CHUNK:', i);
|
||||
for (var j in keys) {
|
||||
var k = keys[j];
|
||||
if (k.keyObj.verifySignatureSync(txSigHash, sigRaw)) {
|
||||
ret.push(k.keyHex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
var signingPubKeyHex = self._verifyOneSignature(keys, sigRaw, txSigHash);
|
||||
if (!signingPubKeyHex)
|
||||
throw new Error('Found a signature that is invalid');
|
||||
|
||||
ret.push(signingPubKeyHex);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
TxProposal._infoFromRedeemScript = function(s) {
|
||||
TxProposal.infoFromRedeemScript = function(s) {
|
||||
var redeemScript = new Script(s.chunks[s.chunks.length - 1]);
|
||||
if (!redeemScript)
|
||||
throw new Error('Bad scriptSig (no redeemscript)');
|
||||
|
|
@ -455,10 +599,8 @@ TxProposal.prototype.setCopayers = function(pubkeyToCopayerMap) {
|
|||
this.seenBy[this.creator] = this.createdTs = Date.now();
|
||||
}
|
||||
|
||||
//Ended. Update this.
|
||||
for (var i in newCopayer) {
|
||||
this.signedBy[i] = newCopayer[i];
|
||||
}
|
||||
//Ended. Update this
|
||||
_.extend(this.signedBy, newCopayer);
|
||||
|
||||
// signedBy has preference over rejectedBy
|
||||
for (var i in this.signedBy) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,11 @@ TxProposals.prototype.add = function(txp) {
|
|||
};
|
||||
|
||||
|
||||
TxProposals.prototype.exist = function(ntxid) {
|
||||
return this.txps[ntxid] ? true : false;
|
||||
};
|
||||
|
||||
|
||||
TxProposals.prototype.get = function(ntxid) {
|
||||
var ret = this.txps[ntxid];
|
||||
if (!ret)
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ inherits(Wallet, events.EventEmitter);
|
|||
Wallet.TX_BROADCASTED = 'txBroadcasted';
|
||||
Wallet.TX_PROPOSAL_SENT = 'txProposalSent';
|
||||
Wallet.TX_SIGNED = 'txSigned';
|
||||
Wallet.TX_SIGNED_AND_BROADCASTED = 'txSignedAndBroadcasted';
|
||||
|
||||
Wallet.prototype.emitAndKeepAlive = function(args) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
|
@ -131,7 +132,7 @@ Wallet.prototype.emitAndKeepAlive = function(args) {
|
|||
};
|
||||
|
||||
/**
|
||||
* @desc Fixed & Forced TransactionBuilder options, for genereration transactions.
|
||||
* @desc Fixed & Forced TransactionBuilder options, for genererating transactions.
|
||||
*
|
||||
* @static
|
||||
* @property lockTime null
|
||||
|
|
@ -144,7 +145,6 @@ Wallet.builderOpts = {
|
|||
signhash: bitcore.Transaction.SIGHASH_ALL,
|
||||
fee: undefined,
|
||||
feeSat: undefined,
|
||||
builderClass: undefined,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -467,8 +467,6 @@ Wallet.prototype._processTxProposalPayPro = function(txp, cb) {
|
|||
};
|
||||
|
||||
/**
|
||||
* _processIncomingTxProposal
|
||||
*
|
||||
* @desc Process an NEW incoming transaction proposal. Runs safety and sanity checks on it.
|
||||
*
|
||||
* @param mergeInfo Proposals merge information, as returned by TxProposals.merge
|
||||
|
|
@ -490,6 +488,12 @@ Wallet.prototype._processIncomingNewTxProposal = function(txp, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
/* only for stubbing */
|
||||
Wallet.prototype._txProposalFromUntrustedObj = function(data, opts) {
|
||||
return TxProposal.fromUntrustedObj(data, opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* @desc
|
||||
* Handles a NEW 'TXPROPOSAL' network message
|
||||
|
|
@ -500,27 +504,31 @@ Wallet.prototype._processIncomingNewTxProposal = function(txp, cb) {
|
|||
* @emits txProposalsUpdated
|
||||
*/
|
||||
Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||
preconditions.checkArgument(data.txProposal);
|
||||
var self = this;
|
||||
var incomingTx = TxProposal.fromUntrustedObj(data.txProposal, Wallet.builderOpts);
|
||||
var incomingNtxid = incomingTx.getId();
|
||||
|
||||
try {
|
||||
var localTx = this.txProposals.get(incomingNtxid);
|
||||
} catch (e) {};
|
||||
var incomingTx = self._txProposalFromUntrustedObj(data.txProposal, Wallet.builderOpts);
|
||||
var incomingNtxid = incomingTx.getId();
|
||||
} catch (e) {
|
||||
log.warn(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (localTx) {
|
||||
log.debug('Ignoring existing tx Proposal:' + incomingNtxid);
|
||||
if (this.txProposals.exist(incomingNtxid)) {
|
||||
log.warn('Ignoring existing tx Proposal:' + incomingNtxid);
|
||||
return;
|
||||
}
|
||||
|
||||
self._processIncomingNewTxProposal(incomingTx, function(err) {
|
||||
if (err) {
|
||||
log.info('Corrupt TX proposal received from:', senderId, err.toString());
|
||||
log.warn('Corrupt TX proposal received from:', senderId, err.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
var keyMap = self._getPubkeyToCopayerMap(incomingTx);
|
||||
incomingTx.setCopayers(keyMap);
|
||||
|
||||
var pubkeyToCopayerMap = self._getPubkeyToCopayerMap(incomingTx);
|
||||
incomingTx.setCopayers(pubkeyToCopayerMap);
|
||||
|
||||
self.txProposals.add(incomingTx);
|
||||
self.emitAndKeepAlive('txProposalEvent', {
|
||||
|
|
@ -531,7 +539,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
|||
};
|
||||
|
||||
|
||||
Wallet.prototype._onSignatures = function(senderId, data) {
|
||||
Wallet.prototype._onSignature = function(senderId, data) {
|
||||
var self = this;
|
||||
try {
|
||||
var localTx = this.txProposals.get(data.ntxid);
|
||||
|
|
@ -539,15 +547,12 @@ Wallet.prototype._onSignatures = function(senderId, data) {
|
|||
log.info('Ignoring signature for unknown tx Proposal:' + data.ntxid);
|
||||
return;
|
||||
};
|
||||
|
||||
var keyMap = self._getPubkeyToCopayerMap(locaTx);
|
||||
|
||||
// TODO look senderIdin keyMap
|
||||
localTx.addSignature(pubkeys, data.signature, keyMap);
|
||||
|
||||
this.emitAndKeepAlive('txProposalEvent', {
|
||||
type: 'signed',
|
||||
cId: senderId,
|
||||
localTx.addSignature(senderId, data.signatures);
|
||||
self.issueTxIfComplete(data.ntxid, function(err, txid) {
|
||||
self.emitAndKeepAlive('txProposalEvent', {
|
||||
type: txid ? 'signedAndBroadcasted' : 'signed',
|
||||
cId: senderId,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -1259,10 +1264,13 @@ Wallet.prototype.sendSignature = function(ntxid) {
|
|||
preconditions.checkArgument(ntxid);
|
||||
|
||||
var txp = this.txProposals.get(ntxid);
|
||||
var signatures = txp.getMySignatures();
|
||||
preconditions.checkState(signatures && signatures.length);
|
||||
|
||||
this._sendToPeers(null, {
|
||||
type: 'signature',
|
||||
signatures: txp.getMySignatures(),
|
||||
ntxid: ntxid,
|
||||
signatures: signatures,
|
||||
walletId: this.id,
|
||||
});
|
||||
};
|
||||
|
|
@ -1517,6 +1525,16 @@ Wallet.prototype.sign = function(ntxid) {
|
|||
return true;
|
||||
};
|
||||
|
||||
Wallet.prototype.issueTxIfComplete = function(ntxid, cb) {
|
||||
var txp = this.txProposals.get(ntxid);
|
||||
var tx = txp.builder.build();
|
||||
if (tx.isComplete()) {
|
||||
this.issueTx(ntxid, cb);
|
||||
} else {
|
||||
return cb();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -1531,13 +1549,13 @@ Wallet.prototype.sign = function(ntxid) {
|
|||
*/
|
||||
Wallet.prototype.signAndSend = function(ntxid, cb) {
|
||||
if (this.sign(ntxid)) {
|
||||
var txp = this.txProposals.get(ntxid);
|
||||
this.sendSignature(ntxid);
|
||||
if (txp.isFullySigned()) {
|
||||
return this.broadcastTx(ntxid, cb);
|
||||
} else {
|
||||
return cb(null, ntxid, Wallet.TX_SIGNED);
|
||||
}
|
||||
this.issueTxIfComplete(ntxid, function(err, txid, status) {
|
||||
if (!txid)
|
||||
return cb(null, ntxid, Wallet.TX_SIGNED);
|
||||
else
|
||||
return cb(null, ntxid, Wallet.TX_SIGNED_AND_BROADCASTED);
|
||||
});
|
||||
} else {
|
||||
return cb(new Error('Could not sign the proposal'));
|
||||
}
|
||||
|
|
@ -1552,13 +1570,12 @@ Wallet.prototype.signAndSend = function(ntxid, cb) {
|
|||
* @param cb
|
||||
* @return {undefined}
|
||||
*/
|
||||
Wallet.prototype._doBroadcastTx = function(ntxid, cb) {
|
||||
Wallet.prototype.broadcastToBitcoinNetwork = function(ntxid, cb) {
|
||||
var self = this;
|
||||
var txp = this.txProposals.get(ntxid);
|
||||
var tx = txp.builder.build();
|
||||
|
||||
if (!tx.isComplete())
|
||||
throw new Error('Tx is not complete. Can not broadcast');
|
||||
var tx = txp.builder.build();
|
||||
preconditions.checkState(tx.isComplete(), 'tx is not complete');
|
||||
|
||||
var txHex = tx.serialize().toString('hex');
|
||||
|
||||
|
|
@ -1575,7 +1592,7 @@ Wallet.prototype._doBroadcastTx = function(ntxid, cb) {
|
|||
return cb(err, txid);
|
||||
});
|
||||
} else {
|
||||
log.info('Wallet:' + self.getName() + ' broadcasted a TX. BITCOIND txid:', txid);
|
||||
log.info('Wallet:' + self.getName() + ' broadcasted a TX! TXID:', txid);
|
||||
return cb(null, txid);
|
||||
}
|
||||
});
|
||||
|
|
@ -1590,10 +1607,10 @@ Wallet.prototype._doBroadcastTx = function(ntxid, cb) {
|
|||
* @param {string} txid - the transaction id on the blockchain
|
||||
* @param {signCallback} cb
|
||||
*/
|
||||
Wallet.prototype.broadcastTx = function(ntxid, cb) {
|
||||
Wallet.prototype.issueTx = function(ntxid, cb) {
|
||||
var self = this;
|
||||
|
||||
self._doBroadcastTx(ntxid, function(err, txid) {
|
||||
self.broadcastToBitcoinNetwork(ntxid, function(err, txid) {
|
||||
if (err) return cb(err);
|
||||
preconditions.checkState(txid);
|
||||
|
||||
|
|
@ -1610,8 +1627,6 @@ Wallet.prototype.broadcastTx = function(ntxid, cb) {
|
|||
self.onPayProPaymentAck(ntxid, data);
|
||||
});
|
||||
}
|
||||
|
||||
self.sendTxProposal(ntxid);
|
||||
self.emitAndKeepAlive('txProposalsUpdated');
|
||||
return cb(null, txid, Wallet.TX_BROADCASTED);
|
||||
});
|
||||
|
|
@ -2183,7 +2198,7 @@ Wallet.prototype.spend = function(opts, cb) {
|
|||
self.sendIndexes();
|
||||
// Needs only one signature? Broadcast it!
|
||||
if (!self.requiresMultipleSignatures()) {
|
||||
self.broadcastTx(ntxid, cb);
|
||||
self.issueTx(ntxid, cb);
|
||||
} else {
|
||||
self.sendTxProposal(ntxid);
|
||||
self.emitAndKeepAlive('txProposalsUpdated');
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ angular.module('copayApp.services')
|
|||
notification.info('[' + name + '] Transaction Signed',
|
||||
$filter('translate')('A transaction was signed by') + ' ' + user);
|
||||
break;
|
||||
case 'signedAndBroadcasted':
|
||||
notification.info('[' + name + '] Transaction Approved',
|
||||
$filter('translate')('A transaction was signed and broadcasted by') + ' ' + user);
|
||||
break;
|
||||
case 'rejected':
|
||||
notification.info('[' + name + '] Transaction Rejected',
|
||||
$filter('translate')('A transaction was rejected by') + ' ' + user);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue