check tx already exists based on foreign id

This commit is contained in:
Ivan Socolsky 2016-07-26 11:01:14 -03:00
parent 49929942e7
commit 04fdc090bd
No known key found for this signature in database
GPG Key ID: FAECE6A05FAA4F56
2 changed files with 84 additions and 65 deletions

View File

@ -23,6 +23,7 @@ var errors = {
NOT_AUTHORIZED: 'Not authorized', NOT_AUTHORIZED: 'Not authorized',
TOO_MANY_KEYS: 'Too many keys registered', TOO_MANY_KEYS: 'Too many keys registered',
TX_ALREADY_BROADCASTED: 'The transaction proposal is already broadcasted', TX_ALREADY_BROADCASTED: 'The transaction proposal is already broadcasted',
TX_ALREADY_EXISTS: 'A transaction proposal with the same id already exists',
TX_CANNOT_CREATE: 'Cannot create TX proposal during backoff time', TX_CANNOT_CREATE: 'Cannot create TX proposal during backoff time',
TX_CANNOT_REMOVE: 'Cannot remove this tx proposal during locktime', TX_CANNOT_REMOVE: 'Cannot remove this tx proposal during locktime',
TX_MAX_SIZE_EXCEEDED: 'TX exceeds maximum allowed size', TX_MAX_SIZE_EXCEEDED: 'TX exceeds maximum allowed size',

View File

@ -1934,6 +1934,7 @@ WalletService.prototype._validateAndSanitizeTxOpts = function(wallet, opts, cb)
/** /**
* Creates a new transaction proposal. * Creates a new transaction proposal.
* @param {Object} opts * @param {Object} opts
* @param {string} opts.txProposalId - Optional. If provided it will be used as this TX proposal ID. Should be unique in the scope of the wallet.
* @param {Array} opts.outputs - List of outputs. * @param {Array} opts.outputs - List of outputs.
* @param {string} opts.outputs[].toAddress - Destination address. * @param {string} opts.outputs[].toAddress - Destination address.
* @param {number} opts.outputs[].amount - Amount to transfer in satoshi. * @param {number} opts.outputs[].amount - Amount to transfer in satoshi.
@ -1973,73 +1974,90 @@ WalletService.prototype.createTx = function(opts, cb) {
} }
}; };
function checkTxpAlreadyExists(txProposalId, cb) {
if (!txProposalId) return cb();
self.storage.fetchTx(self.walletId, txProposalId, function(err, txp) {
if (err || !txp) return cb(err);
if (txp.status == 'temporary') {
return cb(null, txp);
} else {
return cb(Errors.TX_ALREADY_EXISTS);
}
});
};
self._runLocked(cb, function(cb) { self._runLocked(cb, function(cb) {
var wallet, txp, changeAddress; var txp, changeAddress;
async.series([ self.getWallet({}, function(err, wallet) {
function(next) {
self.getWallet({}, function(err, w) {
if (err) return next(err);
if (!w.isComplete()) return next(Errors.WALLET_NOT_COMPLETE);
wallet = w;
next();
});
},
function(next) {
self._validateAndSanitizeTxOpts(wallet, opts, next);
},
function(next) {
self._canCreateTx(function(err, canCreate) {
if (err) return next(err);
if (!canCreate) return next(Errors.TX_CANNOT_CREATE);
next();
});
},
function(next) {
if (opts.sendMax) return next();
getChangeAddress(wallet, function(err, address) {
if (err) return next(err);
changeAddress = address;
next();
});
},
function(next) {
var txOpts = {
walletId: self.walletId,
creatorId: self.copayerId,
outputs: opts.outputs,
message: opts.message,
changeAddress: changeAddress,
feePerKb: opts.feePerKb,
payProUrl: opts.payProUrl,
walletM: wallet.m,
walletN: wallet.n,
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos,
validateOutputs: !opts.validateOutputs,
addressType: wallet.addressType,
customData: opts.customData,
inputs: opts.inputs,
fee: opts.inputs && !_.isNumber(opts.feePerKb) ? opts.fee : null,
noShuffleOutputs: opts.noShuffleOutputs
};
txp = Model.TxProposal.create(txOpts);
next();
},
function(next) {
self._selectTxInputs(txp, opts.utxosToExclude, next);
},
function(next) {
if (!changeAddress || opts.dryRun) return next();
self.storage.storeAddressAndWallet(wallet, txp.changeAddress, next);
},
function(next) {
if (opts.dryRun) return next();
self.storage.storeTx(wallet.id, txp, next);
},
], function(err) {
if (err) return cb(err); if (err) return cb(err);
return cb(null, txp); if (!wallet.isComplete()) return cb(Errors.WALLET_NOT_COMPLETE);
checkTxpAlreadyExists(opts.txProposalId, function(err, txp) {
if (err) return cb(err);
if (txp) return cb(null, txp);
async.series([
function(next) {
self._validateAndSanitizeTxOpts(wallet, opts, next);
},
function(next) {
self._canCreateTx(function(err, canCreate) {
if (err) return next(err);
if (!canCreate) return next(Errors.TX_CANNOT_CREATE);
next();
});
},
function(next) {
if (opts.sendMax) return next();
getChangeAddress(wallet, function(err, address) {
if (err) return next(err);
changeAddress = address;
next();
});
},
function(next) {
var txOpts = {
id: opts.txProposalId,
walletId: self.walletId,
creatorId: self.copayerId,
outputs: opts.outputs,
message: opts.message,
changeAddress: changeAddress,
feePerKb: opts.feePerKb,
payProUrl: opts.payProUrl,
walletM: wallet.m,
walletN: wallet.n,
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos,
validateOutputs: !opts.validateOutputs,
addressType: wallet.addressType,
customData: opts.customData,
inputs: opts.inputs,
fee: opts.inputs && !_.isNumber(opts.feePerKb) ? opts.fee : null,
noShuffleOutputs: opts.noShuffleOutputs
};
txp = Model.TxProposal.create(txOpts);
next();
},
function(next) {
self._selectTxInputs(txp, opts.utxosToExclude, next);
},
function(next) {
if (!changeAddress || opts.dryRun) return next();
self.storage.storeAddressAndWallet(wallet, txp.changeAddress, next);
},
function(next) {
if (opts.dryRun) return next();
self.storage.storeTx(wallet.id, txp, next);
},
], function(err) {
if (err && err !== 'break') return cb(err);
return cb(null, txp);
});
});
}); });
}); });
}; };