Skip to content

Commit

Permalink
fix(lib-cash): match bitcore-lib estimateFee fix
Browse files Browse the repository at this point in the history
  • Loading branch information
nitsujlangston committed Feb 16, 2019
1 parent 96eda6f commit 8650345
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 39 deletions.
44 changes: 19 additions & 25 deletions packages/bitcore-lib-cash/lib/transaction/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ Transaction.NLOCKTIME_MAX_VALUE = 4294967295;
// Value used for fee estimation (satoshis per kilobyte)
Transaction.FEE_PER_KB = 100000;

// Value used for fee estimation (satoshis per byte)
Transaction.FEE_PER_BYTE = 1;

// Safe upper bound for change address script size in bytes
Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
Expand Down Expand Up @@ -235,6 +233,14 @@ Transaction.prototype._hasFeeError = function(opts, unspent) {
);
}
}
if (!opts.disableSmallFees) {
var minimumFee = Math.ceil(this._estimateFee() / Transaction.FEE_SECURITY_MARGIN);
if (unspent < minimumFee) {
return new errors.Transaction.FeeError.TooSmall(
'expected more than ' + minimumFee + ' but got ' + unspent
);
}
}
};

Transaction.prototype._missingChange = function() {
Expand Down Expand Up @@ -468,7 +474,7 @@ Transaction.prototype.getLockTime = function() {
};

Transaction.prototype.fromString = function(string) {
this.fromBuffer(new buffer.Buffer(string, 'hex'));
this.fromBuffer(buffer.Buffer.from(string, 'hex'));
};

Transaction.prototype._newTransaction = function() {
Expand Down Expand Up @@ -668,7 +674,6 @@ Transaction.prototype.fee = function(amount) {
* Manually set the fee per KB for this transaction. Beware that this resets all the signatures
* for inputs (in further versions, SIGHASH_SINGLE or SIGHASH_NONE signatures will not
* be reset).
* Takes priority over fee per Byte, for backwards compatibility
*
* @param {number} amount satoshis per KB to be sent
* @return {Transaction} this, for chaining
Expand Down Expand Up @@ -905,14 +910,19 @@ Transaction.prototype.getFee = function() {
/**
* Estimates fee from serialized transaction size in bytes.
*/
Transaction.prototype._estimateFee = function() {
Transaction.prototype._estimateFee = function () {
var estimatedSize = this._estimateSize();
var available = this._getUnspentValue();
if (this._feePerByte && !this._feePerKb) {
return Transaction._estimateFeePerByte(estimatedSize, available, this._feePerByte);
} else {
return Transaction._estimateFeePerKb(estimatedSize, available, this._feePerKb);
var feeRate = this._feePerByte || (this._feePerKb || Transaction.FEE_PER_KB) / 1000;
function getFee(size) {
return size * feeRate;
}
var fee = Math.ceil(getFee(estimatedSize));
var feeWithChange = Math.ceil(getFee(estimatedSize) + getFee(Transaction.CHANGE_OUTPUT_MAX_SIZE));
if (!this._changeScript || available <= feeWithChange) {
return fee;
}
return feeWithChange;
};

Transaction.prototype._getUnspentValue = function() {
Expand All @@ -925,22 +935,6 @@ Transaction.prototype._clearSignatures = function() {
});
};

Transaction._estimateFeePerKb = function(size, amountAvailable, feePerKb) {
var fee = Math.ceil(size / 1000) * (feePerKb || Transaction.FEE_PER_KB);
if (amountAvailable > fee) {
size += Transaction.CHANGE_OUTPUT_MAX_SIZE;
}
return Math.ceil(size / 1000) * (feePerKb || Transaction.FEE_PER_KB);
};

Transaction._estimateFeePerByte = function(size, amountAvailable, feePerByte) {
var fee = size * (feePerByte || Transaction.FEE_PER_BYTE);
if (amountAvailable > fee) {
size += Transaction.CHANGE_OUTPUT_MAX_SIZE;
}
return size * (feePerByte || Transaction.FEE_PER_BYTE);
};

Transaction.prototype._estimateSize = function() {
var result = Transaction.MAXIMUM_EXTRA_SIZE;
_.each(this.inputs, function(input) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bitcore-lib-cash/test/data/tx_creation.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,6 @@
"change", ["HGR6veMqmYQH8uzKvZFqQBV1xkskQxZEYu"],
"sign", ["L2U9m5My3cdyN5qX1PH4B7XstGDZFWwyukdX8gj8vsJ3fkrqArQo", 1],
"sign", ["L4jFVcDaqZCkknP5KQWjCBgiLFxKxRxywNGTucm3jC3ozByZcbZv", 1],
"serialize", "010000000220c24f763536edb05ce8df2a4816d971be4f20b58451d71589db434aca98bfaf00000000fdfd0000473044022024b955f8bf6aaf0741da011e3214eaec7040cd12694303471cefc6ba0cc4ec290220124738015033a465636dec1524a6f956a229e69d31aef6c7a98b2a291f3cfc6701483045022100e6ae6c43240e8a11a6de2d034501c2a366c0ccdf069c7828de0791f05e68e787022028b80bd36c2b2ae63fe7afb491da6c0ce23fbbb982450962c817b20f0bb24075014c695221020483ebb834d91d494a3b649cf0e8f5c9c4fcec5f194ab94341cc99bb440007f2210271ebaeef1c2bf0c1a4772d1391eab03e4d96a6e9b48551ab4e4b0d2983eb452b2103a659828aabe443e2dedabb1db5a22335c5ace5b5b7126998a288d63c99516dd853aeffffffffa0644cd1606e081c59eb65fe69d4a83a3a822da423bc392c91712fb77a192edc00000000fc00483045022100ae7f136cf906dc37d34d5035b8d2001c6a783773b74507ba83080e73e903623f0220023baf7738395268f7097e5586130f682b911fd49b83b265f8fa481f2a6b1ee90146304302201d60f512a8b37663d85c123933053e0354f13d89daf699ca600defa03d4a1dab021f41042b6e4ba30311fc3a68c228c3725f3b0f05a4453ef19408e6a4ae30a2b0014c695221020483ebb834d91d494a3b649cf0e8f5c9c4fcec5f194ab94341cc99bb440007f2210271ebaeef1c2bf0c1a4772d1391eab03e4d96a6e9b48551ab4e4b0d2983eb452b2103a659828aabe443e2dedabb1db5a22335c5ace5b5b7126998a288d63c99516dd853aeffffffff03f04902000000000017a9144de752833233fe69a20064f29b2ca0f6399c8af387007102000000000017a9144de752833233fe69a20064f29b2ca0f6399c8af387ab2f03000000000017a9146c8d8b04c6a1e664b1ec20ec932760760c97688e8700000000"
"serialize", "010000000220c24f763536edb05ce8df2a4816d971be4f20b58451d71589db434aca98bfaf00000000fc004730440220658e8a9a15d05dc72f5ac59823bc82472fa8cdc7853910111dc4717850b4a6ca02201c2e3479704a6581cc8b233fd5533b8f19d093815d60e62b508716ea28010826014730440220054502b9dbf26a962b9b83e719b6c70a55ce6a931d977da0ad26bfcd483d438402205a4ae4193b502592b573bd995d3fa370ef56c6e381ee6356991d14173b0dc396014c695221020483ebb834d91d494a3b649cf0e8f5c9c4fcec5f194ab94341cc99bb440007f2210271ebaeef1c2bf0c1a4772d1391eab03e4d96a6e9b48551ab4e4b0d2983eb452b2103a659828aabe443e2dedabb1db5a22335c5ace5b5b7126998a288d63c99516dd853aeffffffffa0644cd1606e081c59eb65fe69d4a83a3a822da423bc392c91712fb77a192edc00000000fc0047304402207e1a40f986ee26942f780bb2f384d5425b84079149ee659e3ee8d243568cc02102200519928c0c1cd098d296a64316335db5f894f1ddf27453c4dc07e1cc4f4bbf6d014730440220760ef53cf380eeb6683999090577f9b644bf44039f4a887fdd44ccbd9ece72ba02206ad3cf8bb6c1433a203a818eaa43d89c657dc44f9ac514a02fcc295f3beb405e014c695221020483ebb834d91d494a3b649cf0e8f5c9c4fcec5f194ab94341cc99bb440007f2210271ebaeef1c2bf0c1a4772d1391eab03e4d96a6e9b48551ab4e4b0d2983eb452b2103a659828aabe443e2dedabb1db5a22335c5ace5b5b7126998a288d63c99516dd853aeffffffff03f04902000000000017a9144de752833233fe69a20064f29b2ca0f6399c8af387007102000000000017a9144de752833233fe69a20064f29b2ca0f6399c8af38723b203000000000017a9146c8d8b04c6a1e664b1ec20ec932760760c97688e8700000000"
]
]
43 changes: 30 additions & 13 deletions packages/bitcore-lib-cash/test/transaction/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,8 @@ describe('Transaction', function() {
.to(toAddress, 500000)
.change(changeAddress)
.sign(privateKey);

transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.equal(400000);
transaction.outputs[1].satoshis.should.equal(477100);
transaction.outputs[1].script.toString()
.should.equal(Script.fromAddress(changeAddress).toString());
var actual = transaction.getChangeOutput().script.toString();
Expand Down Expand Up @@ -388,10 +387,10 @@ describe('Transaction', function() {
.sign(privateKey);
transaction._estimateSize().should.be.within(1000, 1999);
transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.equal(34000);
transaction.outputs[1].satoshis.should.equal(40464);
});
it('fee per byte (low fee) can be set up manually', function() {
var inputs = _.map(_.range(10), function(i) {
it('fee per byte (low fee) can be set up manually', function () {
var inputs = _.map(_.range(10), function (i) {
var utxo = _.clone(simpleUtxoWith100000Satoshis);
utxo.outputIndex = i;
return utxo;
Expand All @@ -406,8 +405,8 @@ describe('Transaction', function() {
transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.be.within(48001, 49000);
});
it('fee per byte (high fee) can be set up manually', function() {
var inputs = _.map(_.range(10), function(i) {
it('fee per byte (high fee) can be set up manually', function () {
var inputs = _.map(_.range(10), function (i) {
var utxo = _.clone(simpleUtxoWith100000Satoshis);
utxo.outputIndex = i;
return utxo;
Expand All @@ -422,8 +421,8 @@ describe('Transaction', function() {
transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.be.within(46002, 48000);
});
it('fee per byte can be set up manually', function() {
var inputs = _.map(_.range(10), function(i) {
it('fee per byte can be set up manually', function () {
var inputs = _.map(_.range(10), function (i) {
var utxo = _.clone(simpleUtxoWith100000Satoshis);
utxo.outputIndex = i;
return utxo;
Expand All @@ -438,8 +437,8 @@ describe('Transaction', function() {
transaction.outputs.length.should.equal(2);
transaction.outputs[1].satoshis.should.be.within(24013, 37000);
});
it('fee per byte not enough for change', function() {
var inputs = _.map(_.range(10), function(i) {
it('fee per byte not enough for change', function () {
var inputs = _.map(_.range(10), function (i) {
var utxo = _.clone(simpleUtxoWith100000Satoshis);
utxo.outputIndex = i;
return utxo;
Expand All @@ -465,6 +464,16 @@ describe('Transaction', function() {
return transaction.serialize();
}).to.throw(errors.Transaction.InvalidSatoshis);
});
it('if fee is too small, fail serialization', function() {
var transaction = new Transaction({ disableDustOutputs: true })
.from(simpleUtxoWith100000Satoshis)
.to(toAddress, 99999)
.change(changeAddress)
.sign(privateKey);
expect(function() {
return transaction.serialize();
}).to.throw(errors.Transaction.FeeError.TooSmall);
});
it('on second call to sign, change is not recalculated', function() {
var transaction = new Transaction()
.from(simpleUtxoWith100000Satoshis)
Expand Down Expand Up @@ -623,6 +632,14 @@ describe('Transaction', function() {
.sign(privateKey);
}, 'disableLargeFees', errors.Transaction.FeeError.TooLarge
));
it('can skip the check for a fee that is too small', buildSkipTest(
function(transaction) {
return transaction
.fee(1)
.change(changeAddress)
.sign(privateKey);
}, 'disableSmallFees', errors.Transaction.FeeError.TooSmall
));
it('can skip the check that prevents dust outputs', buildSkipTest(
function(transaction) {
return transaction
Expand Down Expand Up @@ -987,7 +1004,7 @@ describe('Transaction', function() {
.change(changeAddress)
.to(toAddress, 1000);
transaction.inputAmount.should.equal(100000000);
transaction.outputAmount.should.equal(99900000);
transaction.outputAmount.should.equal(99977100);
});
it('returns correct values for coinjoin transaction', function() {
// see livenet tx c16467eea05f1f30d50ed6dbc06a38539d9bb15110e4b7dc6653046a3678a718
Expand Down Expand Up @@ -1085,7 +1102,7 @@ describe('Transaction', function() {
tx.outputs.length.should.equal(2);
tx.outputs[0].satoshis.should.equal(10000000);
tx.outputs[0].script.toAddress().toString().should.equal(toAddress);
tx.outputs[1].satoshis.should.equal(89900000);
tx.outputs[1].satoshis.should.equal(89977100);
tx.outputs[1].script.toAddress().toString().should.equal(changeAddress);
});

Expand Down

0 comments on commit 8650345

Please sign in to comment.