Added support for signing publickey inputs.
This commit is contained in:
parent
f77da04464
commit
eb077eb3df
|
@ -267,6 +267,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;
|
||||
|
@ -301,12 +306,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
|
||||
*/
|
||||
|
@ -714,6 +724,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.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
module.exports = require('./input');
|
||||
|
||||
module.exports.PublicKey = require('./publickey');
|
||||
module.exports.PublicKeyHash = require('./publickeyhash');
|
||||
module.exports.MultiSigScriptHash = require('./multisigscripthash.js');
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -231,6 +231,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() {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue