Merge pull request #1124 from maraoz/increase/coverage
increase coverage for Input and Output, fix some bugs
This commit is contained in:
commit
281ca5e481
|
@ -24,6 +24,8 @@ var transaction = new Transaction()
|
|||
.sign(privkeySet) // Signs all the inputs it can
|
||||
```
|
||||
|
||||
You can obtain the input and output total amounts of the transaction in satoshis by accessing the fields `inputAmount` and `outputAmount`.
|
||||
|
||||
Now, this could just be serialized to hexadecimal ASCII values (`transaction.serialize()`) and sent over to the bitcoind reference client.
|
||||
|
||||
```bash
|
||||
|
@ -102,7 +104,7 @@ Some methods related to adding inputs are:
|
|||
- `from(utxos)`: same as above, but passing in an array of Unspent Outputs.
|
||||
- `from(utxo, publicKeys, threshold)`: add an input that spends a UTXO with a P2SH output for a Multisig script. The `publicKeys` argument is an array of public keys, and `threshold` is the number of required signatures in the Multisig script.
|
||||
* `addInput`: Performs a series of checks on an input and appends it to the end of the `input` vector and updates the amount of incoming bitcoins of the transaction.
|
||||
* `uncheckedAddInput`: adds an input to the end of the `input` vector and updates the `_inputAmount` without performing any checks.
|
||||
* `uncheckedAddInput`: adds an input to the end of the `input` vector and updates the `inputAmount` without performing any checks.
|
||||
|
||||
### PublicKeyHashInput
|
||||
|
||||
|
@ -131,7 +133,7 @@ The following methods are used to manage signatures for a transaction:
|
|||
|
||||
Outputs can be added by:
|
||||
|
||||
* The `addOutput(output)` method, which pushes an `Output` to the end of the `outputs` property and updates the `_outputAmount`. It also clears signatures (as the hash of the transaction may have changed) and updates the change output.
|
||||
* The `addOutput(output)` method, which pushes an `Output` to the end of the `outputs` property and updates the `outputAmount` field. It also clears signatures (as the hash of the transaction may have changed) and updates the change output.
|
||||
* The `to(address, amount)` method, that adds an output with the script that corresponds to the given address. Builds an output and calls the `addOutput` method.
|
||||
* Specifying a [change address](#Fee_calculation)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var $ = require('../../util/preconditions');
|
||||
var errors = require('../../errors');
|
||||
var BufferWriter = require('../../encoding/bufferwriter');
|
||||
var buffer = require('buffer');
|
||||
|
@ -10,6 +11,9 @@ var Script = require('../../script');
|
|||
var Sighash = require('../sighash');
|
||||
var Output = require('../output');
|
||||
|
||||
|
||||
var DEFAULT_SEQNUMBER = 0xFFFFFFFF;
|
||||
|
||||
function Input(params) {
|
||||
if (!(this instanceof Input)) {
|
||||
return new Input(params);
|
||||
|
@ -19,6 +23,8 @@ function Input(params) {
|
|||
}
|
||||
}
|
||||
|
||||
Input.DEFAULT_SEQNUMBER = DEFAULT_SEQNUMBER;
|
||||
|
||||
Object.defineProperty(Input.prototype, 'script', {
|
||||
configurable: false,
|
||||
writeable: false,
|
||||
|
@ -37,9 +43,10 @@ Input.prototype._fromObject = function(params) {
|
|||
}
|
||||
this.output = params.output ?
|
||||
(params.output instanceof Output ? params.output : new Output(params.output)) : undefined;
|
||||
this.prevTxId = params.prevTxId;
|
||||
this.outputIndex = params.outputIndex;
|
||||
this.sequenceNumber = params.sequenceNumber;
|
||||
this.prevTxId = params.prevTxId || params.txidbuf;
|
||||
this.outputIndex = _.isUndefined(params.outputIndex) ? params.txoutnum : params.outputIndex;
|
||||
this.sequenceNumber = _.isUndefined(params.sequenceNumber) ?
|
||||
(_.isUndefined(params.seqnum) ? DEFAULT_SEQNUMBER : params.seqnum) : params.sequenceNumber;
|
||||
if (_.isUndefined(params.script) && _.isUndefined(params.scriptBuffer)) {
|
||||
throw new errors.Transaction.Input.MissingScript();
|
||||
}
|
||||
|
@ -57,21 +64,19 @@ Input.prototype.toObject = function toObject() {
|
|||
};
|
||||
};
|
||||
|
||||
Input.fromObject = function(obj) {
|
||||
$.checkArgument(_.isObject(obj));
|
||||
var input = new Input();
|
||||
return input._fromObject(obj);
|
||||
};
|
||||
|
||||
Input.prototype.toJSON = function toJSON() {
|
||||
return JSON.stringify(this.toObject());
|
||||
};
|
||||
|
||||
Input.fromJSON = function(json) {
|
||||
if (JSUtil.isValidJSON(json)) {
|
||||
json = JSON.parse(json);
|
||||
}
|
||||
return new Input({
|
||||
output: json.output ? new Output(json.output) : undefined,
|
||||
prevTxId: json.prevTxId || json.txidbuf,
|
||||
outputIndex: _.isUndefined(json.outputIndex) ? json.txoutnum : json.outputIndex,
|
||||
sequenceNumber: json.sequenceNumber || json.seqnum,
|
||||
scriptBuffer: new Script(json.script, 'hex')
|
||||
});
|
||||
$.checkArgument(JSUtil.isValidJSON(json), 'Invalid JSON provided to Input.fromJSON');
|
||||
return Input.fromObject(JSON.parse(json));
|
||||
};
|
||||
|
||||
Input.fromBufferReader = function(br) {
|
||||
|
@ -107,7 +112,7 @@ Input.prototype.setScript = function(script) {
|
|||
this._script = null;
|
||||
this._scriptBuffer = new buffer.Buffer(script);
|
||||
} else {
|
||||
throw new TypeError('Invalid Argument');
|
||||
throw new TypeError('Invalid argument type: script');
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
@ -163,9 +168,7 @@ Input.prototype.isNull = function() {
|
|||
};
|
||||
|
||||
Input.prototype._estimateSize = function() {
|
||||
var bufferWriter = new BufferWriter();
|
||||
this.toBufferWriter(bufferWriter);
|
||||
return bufferWriter.toBuffer().length;
|
||||
return this.toBufferWriter().toBuffer().length;
|
||||
};
|
||||
|
||||
module.exports = Input;
|
||||
|
|
|
@ -77,7 +77,7 @@ Output.fromJSON = function(json) {
|
|||
json = JSON.parse(json);
|
||||
}
|
||||
return new Output({
|
||||
satoshis: json.satoshis || -(-json.valuebn),
|
||||
satoshis: json.satoshis || +json.valuebn,
|
||||
script: new Script(json.script)
|
||||
});
|
||||
};
|
||||
|
@ -93,7 +93,7 @@ Output.prototype.setScript = function(script) {
|
|||
this._scriptBuffer = script;
|
||||
this._script = null;
|
||||
} else {
|
||||
throw new TypeError('Unrecognized Argument');
|
||||
throw new TypeError('Invalid argument type: script');
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
|
|
@ -60,7 +60,6 @@ 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;
|
||||
|
@ -516,7 +515,6 @@ Transaction.prototype._fromNonP2SH = function(utxo) {
|
|||
}),
|
||||
prevTxId: utxo.txId,
|
||||
outputIndex: utxo.outputIndex,
|
||||
sequenceNumber: DEFAULT_SEQNUMBER,
|
||||
script: Script.empty()
|
||||
}));
|
||||
};
|
||||
|
@ -532,7 +530,6 @@ Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) {
|
|||
}),
|
||||
prevTxId: utxo.txId,
|
||||
outputIndex: utxo.outputIndex,
|
||||
sequenceNumber: DEFAULT_SEQNUMBER,
|
||||
script: Script.empty()
|
||||
}, pubkeys, threshold));
|
||||
};
|
||||
|
|
|
@ -30,6 +30,14 @@ describe('Transaction.Input', function() {
|
|||
script: new Script(),
|
||||
satoshis: 1000000
|
||||
};
|
||||
var coinbaseJSON = '{"prevTxId":"0000000000000000000000000000000000000000000000000000000000000000"' +
|
||||
',"outputIndex":4294967295,"script":""}';
|
||||
var otherJSON = '{"txidbuf":"a477af6b2667c29670467e4e0728b685ee07b240235771862318e29ddbe58458"' +
|
||||
',"txoutnum":0,"seqnum":4294967295,"script":"71 0x3044022006553276ec5b885ddf5cc1d7' +
|
||||
'9e1e3dadbb404b60ad4cc00318e215654f13242102200757c17b36e3d0492fb9cf597032e5afbea67a59274e64af' +
|
||||
'5a05d12e5ea2303901 33 0x0223078d2942df62c45621d209fab84ea9a7a23346201b7727b9b45a29c4e76f5e",' +
|
||||
'"output":{"satoshis":100000,"script":"OP_DUP OP_HASH160 20 0x88d9931ea73d60eaf7e5671efc0552b' +
|
||||
'912911f2a OP_EQUALVERIFY OP_CHECKSIG"}}';
|
||||
|
||||
it('has abstract methods: "getSignatures", "isFullySigned", "addSignature", "clearSignatures"', function() {
|
||||
var input = new Input(output);
|
||||
|
@ -41,6 +49,39 @@ describe('Transaction.Input', function() {
|
|||
});
|
||||
it('detects coinbase transactions', function() {
|
||||
new Input(output).isNull().should.equal(false);
|
||||
new Input(coinbase).isNull().should.equal(true);
|
||||
var ci = new Input(coinbase);
|
||||
ci.isNull().should.equal(true);
|
||||
});
|
||||
|
||||
describe('instantiation', function() {
|
||||
it('works without new', function() {
|
||||
var input = Input();
|
||||
should.exist(input);
|
||||
});
|
||||
it('fails with no script info', function() {
|
||||
expect(function() {
|
||||
var input = new Input({});
|
||||
input.toString();
|
||||
}).to.throw('Need a script to create an input');
|
||||
});
|
||||
it('fromJSON should work', function() {
|
||||
var input = Input.fromJSON(coinbaseJSON);
|
||||
var otherInput = Input.fromJSON(otherJSON);
|
||||
should.exist(input);
|
||||
should.exist(otherInput);
|
||||
});
|
||||
it('fromObject should work', function() {
|
||||
var input = Input.fromJSON(coinbaseJSON);
|
||||
var obj = input.toObject();
|
||||
obj.script = new Buffer(obj.script, 'hex');
|
||||
Input.fromObject(obj).should.deep.equal(input);
|
||||
obj.script = 42;
|
||||
Input.fromObject.bind(null, obj).should.throw('Invalid argument type: script');
|
||||
});
|
||||
});
|
||||
|
||||
it('_estimateSize returns correct size', function() {
|
||||
var input = new Input(output);
|
||||
input._estimateSize().should.equal(66);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,10 +17,16 @@ var errors = bitcore.errors;
|
|||
|
||||
describe('Output', function() {
|
||||
|
||||
var output = new Output({satoshis: 0, script: Script.empty()});
|
||||
var output = new Output({
|
||||
satoshis: 0,
|
||||
script: Script.empty()
|
||||
});
|
||||
|
||||
it('can be assigned a satoshi amount in big number', function() {
|
||||
var newOutput = new Output({satoshis: new BN(100), script: Script.empty()});
|
||||
var newOutput = new Output({
|
||||
satoshis: new BN(100),
|
||||
script: Script.empty()
|
||||
});
|
||||
newOutput.satoshis.should.equal(100);
|
||||
});
|
||||
|
||||
|
@ -36,19 +42,48 @@ describe('Output', function() {
|
|||
expectEqualOutputs(output, deserialized);
|
||||
});
|
||||
|
||||
it('roundtrips to/from object', function() {
|
||||
var newOutput = new Output({satoshis: 50, script: new Script().add(0)});
|
||||
var otherOutput = new Output(newOutput.toObject());
|
||||
expectEqualOutputs(newOutput, otherOutput);
|
||||
});
|
||||
|
||||
it('can set a script from a buffer', function() {
|
||||
var newOutput = Output(output);
|
||||
newOutput.setScript(Script().add(0).toBuffer());
|
||||
newOutput.inspect().should.equal('<Output (0 sats) <Script: OP_0>>');
|
||||
});
|
||||
|
||||
|
||||
it('has a inspect property', function() {
|
||||
output.inspect().should.equal('<Output (0 sats) <Script: >>');
|
||||
});
|
||||
|
||||
var output2 = new Output({
|
||||
satoshis: 1100000000,
|
||||
script: new Script('OP_2 21 0x038282263212c609d9ea2a6e3e172de238d8c39' +
|
||||
'cabd5ac1ca10646e23fd5f51508 21 0x038282263212c609d9ea2a6e3e172de23' +
|
||||
'8d8c39cabd5ac1ca10646e23fd5f51508 OP_2 OP_CHECKMULTISIG OP_EQUAL')
|
||||
});
|
||||
|
||||
it('toBufferWriter', function() {
|
||||
output2.toBufferWriter().toBuffer().toString('hex')
|
||||
.should.equal('00ab904100000000485215038282263212c609d9ea2a6e3e172de2' +
|
||||
'38d8c39cabd5ac1ca10646e23fd5f5150815038282263212c609d9ea2a6e3e172d' +
|
||||
'e238d8c39cabd5ac1ca10646e23fd5f5150852ae87');
|
||||
});
|
||||
|
||||
it('roundtrips to/from object', function() {
|
||||
var newOutput = new Output({
|
||||
satoshis: 50,
|
||||
script: new Script().add(0)
|
||||
});
|
||||
var otherOutput = new Output(newOutput.toObject());
|
||||
expectEqualOutputs(newOutput, otherOutput);
|
||||
});
|
||||
|
||||
it('roundtrips to/from JSON', function() {
|
||||
var json = output2.toJSON();
|
||||
var o3 = new Output(json);
|
||||
o3.toJSON().should.equal(json);
|
||||
});
|
||||
|
||||
it('setScript fails with invalid input', function() {
|
||||
var out = new Output(output2.toJSON());
|
||||
out.setScript.bind(out, 45).should.throw('Invalid argument type: script');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue