Merge pull request #1114 from eordano/docs/transactionSerialization
Add more docs on serialization checks
This commit is contained in:
commit
8bc0ec5567
|
@ -142,13 +142,27 @@ There are a series of methods used for serialization:
|
|||
* `toObject`: Returns a plain javascript object with no methods and enough information to fully restore the state of this transaction. Using other serialization methods (except for `toJSON`) will cause a some information to be lost.
|
||||
* `toJSON`: Returns a string with a JSON-encoded version of the output for `toObject`.
|
||||
* `toString` or `uncheckedSerialize`: Returns an hexadecimal serialization of the transaction, in the [serialization format for bitcoin](https://bitcoin.org/en/developer-reference#raw-transaction-format).
|
||||
* `serialize`: Does a series of checks before serializing the transaction:
|
||||
- Check that the fee to be used is not very small or very large
|
||||
- Check for dust outputs
|
||||
* `serialize`: Does a series of checks before serializing the transaction
|
||||
* `inspect`: Returns a string with some information about the transaction (currently a string formated as `<Transaction 000...000>`, that only shows the serialized value of the transaction.
|
||||
* `toBuffer`: Serializes the transaction for sending over the wire in the bitcoin network
|
||||
* `toBufferWriter`: Uses an already existing BufferWriter to copy over the serialized transaction
|
||||
|
||||
## Serialization Checks
|
||||
|
||||
When serializing, the bitcore library performs a series of checks. These can be disabled by providing an object to the `serialize` method with the checks that you'll like to skip.
|
||||
|
||||
* `disableLargeFees` avoids checking that the fee is no more than `Transaction.FEE_PER_KB * Transaction.FEE_SECURITY_MARGIN * size_in_kb`.
|
||||
* `disableSmallFees` avoids checking that the fee is less than `Transaction.FEE_PER_KB * size_in_kb / Transaction.FEE_SECURITY_MARGIN`.
|
||||
* `disableIsFullySigned` does not check if all inputs are fully signed
|
||||
* `disableDustOutputs` does not check for dust outputs being generated
|
||||
* `disableMoreOutputThanInput` avoids checking that the sum of the output amounts is less than or equal to the sum of the amounts for the outputs being spent in the transaction
|
||||
|
||||
These are the current default values in the bitcore library involved on these checks:
|
||||
|
||||
* `Transaction.FEE_PER_KB`: `10000` (satoshis per kilobyte)
|
||||
* `Transaction.FEE_SECURITY_MARGIN`: `15`
|
||||
* `Transaction.DUST_AMOUNT`: `546` (satoshis)
|
||||
|
||||
## Fee calculation
|
||||
|
||||
When outputs' value don't sum up to the same amount that inputs, the difference in bitcoins goes to the miner of the block that includes this transaction. The concept of a "change address" usually is associated with this: an output with an address that can be spent by the creator of the transaction.
|
||||
|
|
|
@ -24,10 +24,6 @@ var PrivateKey = require('../privatekey');
|
|||
var Block = require('../block');
|
||||
var BN = require('../crypto/bn');
|
||||
|
||||
var CURRENT_VERSION = 1;
|
||||
var DEFAULT_NLOCKTIME = 0;
|
||||
var DEFAULT_SEQNUMBER = 0xFFFFFFFF;
|
||||
|
||||
/**
|
||||
* Represents a transaction, a set of inputs and outputs to change ownership of tokens
|
||||
*
|
||||
|
@ -62,6 +58,16 @@ function Transaction(serialized) {
|
|||
}
|
||||
}
|
||||
|
||||
var CURRENT_VERSION = 1;
|
||||
var DEFAULT_NLOCKTIME = 0;
|
||||
var DEFAULT_SEQNUMBER = 0xFFFFFFFF;
|
||||
|
||||
// Minimum amount for an output for it not to be considered a dust output
|
||||
Transaction.DUST_AMOUNT = 546;
|
||||
|
||||
// Margin of error to allow fees in the vecinity of the expected value but doesn't allow a big difference
|
||||
Transaction.FEE_SECURITY_MARGIN = 15;
|
||||
|
||||
// max amount of satoshis in circulation
|
||||
Transaction.MAX_MONEY = 21000000 * 1e8;
|
||||
|
||||
|
@ -71,6 +77,13 @@ Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT = 5e8;
|
|||
// Max value for an unsigned 32 bit value
|
||||
Transaction.NLOCKTIME_MAX_VALUE = 4294967295;
|
||||
|
||||
// Value used for fee estimation (satoshis per kilobyte)
|
||||
Transaction.FEE_PER_KB = 10000;
|
||||
|
||||
// Safe upper bound for change address script size in bytes
|
||||
Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
|
||||
Transaction.MAXIMUM_EXTRA_SIZE = 4 + 9 + 9 + 4;
|
||||
|
||||
/* Constructors and Serialization */
|
||||
|
||||
/**
|
||||
|
@ -140,6 +153,8 @@ Transaction.prototype.uncheckedSerialize = Transaction.prototype.toString = func
|
|||
Transaction.prototype.checkedSerialize = function(opts) {
|
||||
var serializationError = this.getSerializationError(opts);
|
||||
if (serializationError) {
|
||||
serializationError.message += ' Use Transaction#uncheckedSerialize if you want to skip security checks. ' +
|
||||
'See http://bitcore.io/guide/transaction.html#Serialization for more info.'
|
||||
throw serializationError;
|
||||
}
|
||||
return this.uncheckedSerialize();
|
||||
|
@ -179,8 +194,6 @@ Transaction.prototype.getSerializationError = function(opts) {
|
|||
}
|
||||
};
|
||||
|
||||
Transaction.FEE_SECURITY_MARGIN = 15;
|
||||
|
||||
Transaction.prototype._isFeeTooLarge = function() {
|
||||
var fee = this._getUnspentValue();
|
||||
var maximumFee = Math.floor(Transaction.FEE_SECURITY_MARGIN * this._estimateFee());
|
||||
|
@ -201,8 +214,6 @@ Transaction.prototype._missingChange = function() {
|
|||
return !this._changeScript;
|
||||
};
|
||||
|
||||
Transaction.DUST_AMOUNT = 5460;
|
||||
|
||||
Transaction.prototype._hasDustOutputs = function() {
|
||||
var index, output;
|
||||
for (index in this.outputs) {
|
||||
|
@ -712,10 +723,6 @@ Transaction.prototype._clearSignatures = function() {
|
|||
});
|
||||
};
|
||||
|
||||
Transaction.FEE_PER_KB = 10000;
|
||||
// Safe upper bound for change address script
|
||||
Transaction.CHANGE_OUTPUT_MAX_SIZE = 20 + 4 + 34 + 4;
|
||||
|
||||
Transaction._estimateFee = function(size, amountAvailable) {
|
||||
var fee = Math.ceil(size / Transaction.FEE_PER_KB);
|
||||
if (amountAvailable > fee) {
|
||||
|
@ -724,8 +731,6 @@ Transaction._estimateFee = function(size, amountAvailable) {
|
|||
return Math.ceil(size / 1000) * Transaction.FEE_PER_KB;
|
||||
};
|
||||
|
||||
Transaction.MAXIMUM_EXTRA_SIZE = 4 + 9 + 9 + 4;
|
||||
|
||||
Transaction.prototype._estimateSize = function() {
|
||||
var result = Transaction.MAXIMUM_EXTRA_SIZE;
|
||||
_.each(this.inputs, function(input) {
|
||||
|
|
|
@ -322,13 +322,23 @@ describe('Transaction', function() {
|
|||
it('fails if a dust output is created', function() {
|
||||
var transaction = new Transaction()
|
||||
.from(simpleUtxoWith1BTC)
|
||||
.to(toAddress, 1)
|
||||
.to(toAddress, 545)
|
||||
.change(changeAddress)
|
||||
.sign(privateKey);
|
||||
expect(function() {
|
||||
return transaction.serialize();
|
||||
}).to.throw(errors.Transaction.DustOutputs);
|
||||
});
|
||||
it('doesn\'t fail if a dust output is not dust', function() {
|
||||
var transaction = new Transaction()
|
||||
.from(simpleUtxoWith1BTC)
|
||||
.to(toAddress, 546)
|
||||
.change(changeAddress)
|
||||
.sign(privateKey);
|
||||
expect(function() {
|
||||
return transaction.serialize();
|
||||
}).to.not.throw(errors.Transaction.DustOutputs);
|
||||
});
|
||||
it('doesn\'t fail if a dust output is an op_return', function() {
|
||||
var transaction = new Transaction()
|
||||
.from(simpleUtxoWith1BTC)
|
||||
|
|
Loading…
Reference in New Issue