Privacy improvement by sorting inputs and outputs
See BIP69 for more details: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki
This commit is contained in:
parent
a419a1b037
commit
fb65145ba7
|
@ -905,6 +905,41 @@ Transaction.prototype.removeOutput = function(index) {
|
|||
this._updateChangeOutput();
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort a transaction's inputs and outputs according to BIP69
|
||||
*
|
||||
* @see {https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki}
|
||||
* @return {Transaction} this
|
||||
*/
|
||||
Transaction.prototype.sort = function() {
|
||||
this.sortOutputs(function(outputs) {
|
||||
var copy = Array.prototype.concat.apply([], outputs);
|
||||
copy.sort(function compare(first, second) {
|
||||
if (first.satoshis < second.satoshis) {
|
||||
return -1;
|
||||
} else if (first.satoshis === second.satoshis) {
|
||||
return Buffer.compare(first.script.toBuffer(), second.script.toBuffer());
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
return copy;
|
||||
});
|
||||
this.sortInputs(function(inputs) {
|
||||
var copy = Array.prototype.concat.apply([], inputs);
|
||||
copy.sort(function compare(first, second) {
|
||||
var compareTx = Buffer.compare(first.script.toBuffer(), second.script.toBuffer());
|
||||
if (compareTx === 0) {
|
||||
return first.outputIndex < second.outputIndex ? -1 : 1;
|
||||
} else {
|
||||
return compareTx;
|
||||
}
|
||||
});
|
||||
return copy;
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Randomize this transaction's outputs ordering. The shuffling algorithm is a
|
||||
* version of the Fisher-Yates shuffle, provided by lodash's _.shuffle().
|
||||
|
@ -929,6 +964,20 @@ Transaction.prototype.sortOutputs = function(sortingFunction) {
|
|||
return this._newOutputOrder(outs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sort this transaction's inputs, according to a given sorting function that
|
||||
* takes an array as argument and returns a new array, with the same elements
|
||||
* but with a different order.
|
||||
*
|
||||
* @param {Function} sortingFunction
|
||||
* @return {Transaction} this
|
||||
*/
|
||||
Transaction.prototype.sortInputs = function(sortingFunction) {
|
||||
this.inputs = sortingFunction(this.inputs);
|
||||
this._clearSignatures();
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype._newOutputOrder = function(newOutputs) {
|
||||
var isInvalidSorting = (this.outputs.length !== newOutputs.length ||
|
||||
_.difference(this.outputs, newOutputs).length !== 0);
|
||||
|
|
|
@ -14,6 +14,7 @@ var PrivateKey = bitcore.PrivateKey;
|
|||
var Script = bitcore.Script;
|
||||
var Address = bitcore.Address;
|
||||
var Networks = bitcore.Networks;
|
||||
var Opcode = bitcore.Opcode;
|
||||
var errors = bitcore.errors;
|
||||
|
||||
var transactionVector = require('../data/tx_creation');
|
||||
|
@ -919,6 +920,64 @@ describe('Transaction', function() {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
describe('BIP69 Sorting', function() {
|
||||
|
||||
it('sorts inputs correctly', function() {
|
||||
var from1 = {
|
||||
txId: '0000000000000000000000000000000000000000000000000000000000000000',
|
||||
outputIndex: 0,
|
||||
script: Script.buildPublicKeyHashOut(fromAddress).toString(),
|
||||
satoshis: 100000
|
||||
};
|
||||
var from2 = {
|
||||
txId: '0000000000000000000000000000000000000000000000000000000000000001',
|
||||
outputIndex: 0,
|
||||
script: Script.buildPublicKeyHashOut(fromAddress).toString(),
|
||||
satoshis: 100000
|
||||
};
|
||||
var from3 = {
|
||||
txId: '0000000000000000000000000000000000000000000000000000000000000001',
|
||||
outputIndex: 1,
|
||||
script: Script.buildPublicKeyHashOut(fromAddress).toString(),
|
||||
satoshis: 100000
|
||||
};
|
||||
var tx = new Transaction()
|
||||
.from(from3)
|
||||
.from(from2)
|
||||
.from(from1);
|
||||
tx.sort();
|
||||
tx.inputs[0].prevTxId.toString('hex').should.equal(from1.txId);
|
||||
tx.inputs[1].prevTxId.toString('hex').should.equal(from2.txId);
|
||||
tx.inputs[2].prevTxId.toString('hex').should.equal(from3.txId);
|
||||
tx.inputs[0].outputIndex.should.equal(from1.outputIndex);
|
||||
tx.inputs[1].outputIndex.should.equal(from2.outputIndex);
|
||||
tx.inputs[2].outputIndex.should.equal(from3.outputIndex);
|
||||
});
|
||||
|
||||
it('sorts outputs correctly', function() {
|
||||
var tx = new Transaction()
|
||||
.addOutput(new Transaction.Output({
|
||||
script: new Script().add(Opcode(0)),
|
||||
satoshis: 2
|
||||
}))
|
||||
.addOutput(new Transaction.Output({
|
||||
script: new Script().add(Opcode(1)),
|
||||
satoshis: 2
|
||||
}))
|
||||
.addOutput(new Transaction.Output({
|
||||
script: new Script().add(Opcode(0)),
|
||||
satoshis: 1
|
||||
}));
|
||||
tx.sort();
|
||||
tx.outputs[0].satoshis.should.equal(1);
|
||||
tx.outputs[1].satoshis.should.equal(2);
|
||||
tx.outputs[2].satoshis.should.equal(2);
|
||||
tx.outputs[0].script.toString().should.equal('OP_0');
|
||||
tx.outputs[1].script.toString().should.equal('OP_0');
|
||||
tx.outputs[2].script.toString().should.equal('0x01');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var tx_empty_hex = '01000000000000000000';
|
||||
|
|
Loading…
Reference in New Issue