Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/ecsignature.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,30 @@ function ECSignature (r, s) {
}

ECSignature.parseCompact = function (buffer) {
if (buffer.length !== 65) throw new Error('Invalid signature length')
typeforce(types.BufferN(65), buffer)

var flagByte = buffer.readUInt8(0) - 27
if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter')

var compressed = !!(flagByte & 4)
var recoveryParam = flagByte & 3

var r = BigInteger.fromBuffer(buffer.slice(1, 33))
var s = BigInteger.fromBuffer(buffer.slice(33))
var signature = ECSignature.fromRSBuffer(buffer.slice(1))

return {
compressed: compressed,
i: recoveryParam,
signature: new ECSignature(r, s)
signature: signature
}
}

ECSignature.fromRSBuffer = function (buffer) {
typeforce(types.BufferN(64), buffer)

var r = BigInteger.fromBuffer(buffer.slice(0, 32))
var s = BigInteger.fromBuffer(buffer.slice(32, 64))
return new ECSignature(r, s)
}

ECSignature.fromDER = function (buffer) {
var decode = bip66.decode(buffer)
var r = BigInteger.fromDERInteger(decode.r)
Expand Down Expand Up @@ -60,9 +66,7 @@ ECSignature.prototype.toCompact = function (i, compressed) {

var buffer = Buffer.alloc(65)
buffer.writeUInt8(i, 0)
this.r.toBuffer(32).copy(buffer, 1)
this.s.toBuffer(32).copy(buffer, 33)

this.toRSBuffer(buffer, 1)
return buffer
}

Expand All @@ -73,6 +77,13 @@ ECSignature.prototype.toDER = function () {
return bip66.encode(r, s)
}

ECSignature.prototype.toRSBuffer = function (buffer, offset) {
buffer = buffer || Buffer.alloc(64)
this.r.toBuffer(32).copy(buffer, offset)
this.s.toBuffer(32).copy(buffer, offset + 32)
return buffer
}

ECSignature.prototype.toScriptSignature = function (hashType) {
var hashTypeMod = hashType & ~0x80
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
Expand Down
13 changes: 9 additions & 4 deletions src/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,8 @@ function canSign (input) {
}

TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) {
if (keyPair.network !== this.network) throw new Error('Inconsistent network')
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== this.network) throw new TypeError('Inconsistent network')
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
hashType = hashType || Transaction.SIGHASH_ALL

Expand All @@ -680,7 +681,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
throw new Error('Inconsistent redeemScript')
}

var kpPubKey = keyPair.getPublicKeyBuffer()
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer()
if (!canSign(input)) {
if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
Expand All @@ -699,14 +700,18 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
} else {
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
}

// enforce in order signing of public keys
var signed = input.pubKeys.some(function (pubKey, i) {
if (!kpPubKey.equals(pubKey)) return false
if (input.signatures[i]) throw new Error('Signature already exists')
if (!keyPair.compressed &&
if (kpPubKey.length !== 33 &&
input.signType === scriptTypes.P2WPKH) throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH')

input.signatures[i] = keyPair.sign(signatureHash).toScriptSignature(hashType)
var signature = keyPair.sign(signatureHash)
if (Buffer.isBuffer(signature)) signature = ECSignature.fromRSBuffer(signature)

input.signatures[i] = signature.toScriptSignature(hashType)
return true
})

Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/ecsignature.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@
"hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62"
},
{
"exception": "Invalid signature length",
"exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 68\\)",
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000"
},
{
"exception": "Invalid signature length",
"exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 59\\)",
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379"
}
],
Expand Down
13 changes: 13 additions & 0 deletions test/transaction_builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,19 @@ describe('TransactionBuilder', function () {
})

describe('sign', function () {
it('supports the alternative abstract interface { publicKey, sign }', function () {
var keyPair = {
publicKey: Buffer.alloc(33, 0x03),
sign: function (hash) { return Buffer.alloc(64) }
}

var txb = new TransactionBuilder()
txb.addInput('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 1)
txb.addOutput('1111111111111111111114oLvT2', 100000)
txb.sign(0, keyPair)
assert.equal(txb.build().toHex(), '0100000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010000002c0930060201000201000121030303030303030303030303030303030303030303030303030303030303030303ffffffff01a0860100000000001976a914000000000000000000000000000000000000000088ac00000000')
})

fixtures.invalid.sign.forEach(function (f) {
it('throws on ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () {
var txb = construct(f, true)
Expand Down