Merge pull request #1301 from braydonf/pubkey

Added support for signing publickey inputs.
This commit is contained in:
Patrick Nagurny 2015-07-30 10:21:44 -04:00
commit 7c6df33ab7
7 changed files with 210 additions and 7 deletions

View File

@ -327,6 +327,11 @@ Script.prototype.isPublicKeyHashIn = function() {
return false;
};
Script.prototype.getPublicKey = function() {
$.checkState(this.isPublicKeyOut(), 'Can\'t retreive PublicKey from a non-PK output');
return this.chunks[0].buf;
};
Script.prototype.getPublicKeyHash = function() {
$.checkState(this.isPublicKeyHashOut(), 'Can\'t retrieve PublicKeyHash from a non-PKH output');
return this.chunks[2].buf;
@ -361,12 +366,17 @@ Script.prototype.isPublicKeyOut = function() {
* @returns {boolean} if this is a pay to public key input script
*/
Script.prototype.isPublicKeyIn = function() {
return this.chunks.length === 1 &&
BufferUtil.isBuffer(this.chunks[0].buf) &&
this.chunks[0].buf.length === 0x47;
if (this.chunks.length === 1) {
var signatureBuf = this.chunks[0].buf;
if (signatureBuf &&
signatureBuf.length &&
signatureBuf[0] === 0x30) {
return true;
}
}
return false;
};
/**
* @returns {boolean} if this is a p2sh output script
*/
@ -775,6 +785,26 @@ Script.buildScriptHashOut = function(script) {
return s;
};
/**
* Builds a scriptSig (a script for an input) that signs a public key output script.
*
* @param {Signature|Buffer} signature - a Signature object, or the signature in DER cannonical encoding
* @param {number=} sigtype - the type of the signature (defaults to SIGHASH_ALL)
*/
Script.buildPublicKeyIn = function(signature, sigtype) {
$.checkArgument(signature instanceof Signature || BufferUtil.isBuffer(signature));
$.checkArgument(_.isUndefined(sigtype) || _.isNumber(sigtype));
if (signature instanceof Signature) {
signature = signature.toBuffer();
}
var script = new Script();
script.add(BufferUtil.concat([
signature,
BufferUtil.integerAsSingleByteBuffer(sigtype || Signature.SIGHASH_ALL)
]));
return script;
};
/**
* Builds a scriptSig (a script for an input) that signs a public key hash
* output script.

View File

@ -1,4 +1,5 @@
module.exports = require('./input');
module.exports.PublicKey = require('./publickey');
module.exports.PublicKeyHash = require('./publickeyhash');
module.exports.MultiSigScriptHash = require('./multisigscripthash.js');

View File

@ -0,0 +1,89 @@
'use strict';
var inherits = require('inherits');
var $ = require('../../util/preconditions');
var BufferUtil = require('../../util/buffer');
var Input = require('./input');
var Output = require('../output');
var Sighash = require('../sighash');
var Script = require('../../script');
var Signature = require('../../crypto/signature');
var TransactionSignature = require('../signature');
/**
* Represents a special kind of input of PayToPublicKey kind.
* @constructor
*/
function PublicKeyInput() {
Input.apply(this, arguments);
}
inherits(PublicKeyInput, Input);
/**
* @param {Transaction} transaction - the transaction to be signed
* @param {PrivateKey} privateKey - the private key with which to sign the transaction
* @param {number} index - the index of the input in the transaction input vector
* @param {number=} sigtype - the type of signature, defaults to Signature.SIGHASH_ALL
* @return {Array} of objects that can be
*/
PublicKeyInput.prototype.getSignatures = function(transaction, privateKey, index, sigtype) {
$.checkState(this.output instanceof Output);
sigtype = sigtype || Signature.SIGHASH_ALL;
var publicKey = privateKey.toPublicKey();
if (publicKey.toString() === this.output.script.getPublicKey().toString('hex')) {
return [new TransactionSignature({
publicKey: publicKey,
prevTxId: this.prevTxId,
outputIndex: this.outputIndex,
inputIndex: index,
signature: Sighash.sign(transaction, privateKey, sigtype, index, this.output.script),
sigtype: sigtype
})];
}
return [];
};
/**
* Add the provided signature
*
* @param {Object} signature
* @param {PublicKey} signature.publicKey
* @param {Signature} signature.signature
* @param {number=} signature.sigtype
* @return {PublicKeyInput} this, for chaining
*/
PublicKeyInput.prototype.addSignature = function(transaction, signature) {
$.checkState(this.isValidSignature(transaction, signature), 'Signature is invalid');
this.setScript(Script.buildPublicKeyIn(
signature.signature.toDER(),
signature.sigtype
));
return this;
};
/**
* Clear the input's signature
* @return {PublicKeyHashInput} this, for chaining
*/
PublicKeyInput.prototype.clearSignatures = function() {
this.setScript(Script.empty());
return this;
};
/**
* Query whether the input is signed
* @return {boolean}
*/
PublicKeyInput.prototype.isFullySigned = function() {
return this.script.isPublicKeyIn();
};
PublicKeyInput.SCRIPT_MAX_SIZE = 73; // sigsize (1 + 72)
PublicKeyInput.prototype._estimateSize = function() {
return PublicKeyInput.SCRIPT_MAX_SIZE;
};
module.exports = PublicKeyInput;

View File

@ -17,6 +17,7 @@ var Address = require('../address');
var UnspentOutput = require('./unspentoutput');
var Input = require('./input');
var PublicKeyHashInput = Input.PublicKeyHash;
var PublicKeyInput = Input.PublicKey;
var MultiSigScriptHashInput = Input.MultiSigScriptHash;
var Output = require('./output');
var Script = require('../script');
@ -538,6 +539,8 @@ Transaction.prototype._fromNonP2SH = function(utxo) {
utxo = new UnspentOutput(utxo);
if (utxo.script.isPublicKeyHashOut()) {
clazz = PublicKeyHashInput;
} else if (utxo.script.isPublicKeyOut()) {
clazz = PublicKeyInput;
} else {
clazz = Input;
}

View File

@ -249,6 +249,16 @@ describe('Script', function() {
});
});
describe('#isPublicKeyIn', function() {
it('correctly identify scriptSig as a public key in', function() {
// from txid: 5c85ed63469aa9971b5d01063dbb8bcdafd412b2f51a3d24abf2e310c028bbf8
// and input index: 5
var scriptBuffer = new Buffer('483045022050eb59c79435c051f45003d9f82865c8e4df5699d7722e77113ef8cadbd92109022100d4ab233e070070eb8e0e62e3d2d2eb9474a5bf135c9eda32755acb0875a6c20601', 'hex');
var script = bitcore.Script.fromBuffer(scriptBuffer);
script.isPublicKeyIn().should.equal(true);
});
});
describe('#isPublicKeyHashIn', function() {
it('should identify this known pubkeyhashin (uncompressed pubkey version)', function() {

View File

@ -0,0 +1,71 @@
'use strict';
var should = require('chai').should();
var bitcore = require('../../..');
var Transaction = bitcore.Transaction;
var PrivateKey = bitcore.PrivateKey;
describe('PublicKeyInput', function() {
var utxo = {
txid: '7f3b688cb224ed83e12d9454145c26ac913687086a0a62f2ae0bc10934a4030f',
vout: 0,
address: 'n4McBrSkw42eYGX5YMACGpkGUJKL3jVSbo',
scriptPubKey: '2103c9594cb2ebfebcb0cfd29eacd40ba012606a197beef76f0269ed8c101e56ceddac',
amount: 50,
confirmations: 104,
spendable: true
};
var privateKey = PrivateKey.fromWIF('cQ7tSSQDEwaxg9usnnP1Aztqvm9nCQVfNWz9kU2rdocDjknF2vd6');
var address = privateKey.toAddress();
utxo.address.should.equal(address.toString());
var destKey = new PrivateKey();
it('will correctly sign a publickey out transaction', function() {
var tx = new Transaction();
tx.from(utxo);
tx.to(destKey.toAddress(), 10000);
tx.sign(privateKey);
tx.inputs[0].script.toBuffer().length.should.be.above(0);
});
it('count can count missing signatures', function() {
var tx = new Transaction();
tx.from(utxo);
tx.to(destKey.toAddress(), 10000);
var input = tx.inputs[0];
input.isFullySigned().should.equal(false);
tx.sign(privateKey);
input.isFullySigned().should.equal(true);
});
it('it\'s size can be estimated', function() {
var tx = new Transaction();
tx.from(utxo);
tx.to(destKey.toAddress(), 10000);
var input = tx.inputs[0];
input._estimateSize().should.equal(73);
});
it('it\'s signature can be removed', function() {
var tx = new Transaction();
tx.from(utxo);
tx.to(destKey.toAddress(), 10000);
var input = tx.inputs[0];
tx.sign(privateKey);
input.isFullySigned().should.equal(true);
input.clearSignatures();
input.isFullySigned().should.equal(false);
});
it('returns an empty array if private key mismatches', function() {
var tx = new Transaction();
tx.from(utxo);
tx.to(destKey.toAddress(), 10000);
var input = tx.inputs[0];
var signatures = input.getSignatures(tx, new PrivateKey(), 0);
signatures.length.should.equal(0);
});
});

View File

@ -58,8 +58,7 @@ describe('PublicKeyHashInput', function() {
.from(output)
.to(address, 1000000);
var input = transaction.inputs[0];
input.getSignatures(transaction, new PrivateKey(), 0);
input.isFullySigned().should.equal(false);
var signatures = input.getSignatures(transaction, new PrivateKey(), 0);
signatures.length.should.equal(0);
});
});