Merge pull request #915 from bitcoinjs/bip32400
BIP32/secp256k1/keyPair preparation
This commit is contained in:
commit
337e6a737a
4 changed files with 43 additions and 14 deletions
|
@ -12,24 +12,30 @@ function ECSignature (r, s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ECSignature.parseCompact = function (buffer) {
|
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
|
var flagByte = buffer.readUInt8(0) - 27
|
||||||
if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter')
|
if (flagByte !== (flagByte & 7)) throw new Error('Invalid signature parameter')
|
||||||
|
|
||||||
var compressed = !!(flagByte & 4)
|
var compressed = !!(flagByte & 4)
|
||||||
var recoveryParam = flagByte & 3
|
var recoveryParam = flagByte & 3
|
||||||
|
var signature = ECSignature.fromRSBuffer(buffer.slice(1))
|
||||||
var r = BigInteger.fromBuffer(buffer.slice(1, 33))
|
|
||||||
var s = BigInteger.fromBuffer(buffer.slice(33))
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
compressed: compressed,
|
compressed: compressed,
|
||||||
i: recoveryParam,
|
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) {
|
ECSignature.fromDER = function (buffer) {
|
||||||
var decode = bip66.decode(buffer)
|
var decode = bip66.decode(buffer)
|
||||||
var r = BigInteger.fromDERInteger(decode.r)
|
var r = BigInteger.fromDERInteger(decode.r)
|
||||||
|
@ -60,9 +66,7 @@ ECSignature.prototype.toCompact = function (i, compressed) {
|
||||||
|
|
||||||
var buffer = Buffer.alloc(65)
|
var buffer = Buffer.alloc(65)
|
||||||
buffer.writeUInt8(i, 0)
|
buffer.writeUInt8(i, 0)
|
||||||
this.r.toBuffer(32).copy(buffer, 1)
|
this.toRSBuffer(buffer, 1)
|
||||||
this.s.toBuffer(32).copy(buffer, 33)
|
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +77,13 @@ ECSignature.prototype.toDER = function () {
|
||||||
return bip66.encode(r, s)
|
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) {
|
ECSignature.prototype.toScriptSignature = function (hashType) {
|
||||||
var hashTypeMod = hashType & ~0x80
|
var hashTypeMod = hashType & ~0x80
|
||||||
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
|
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
|
||||||
|
|
|
@ -667,7 +667,8 @@ function canSign (input) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) {
|
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)
|
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
|
||||||
hashType = hashType || Transaction.SIGHASH_ALL
|
hashType = hashType || Transaction.SIGHASH_ALL
|
||||||
|
|
||||||
|
@ -680,7 +681,7 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
|
||||||
throw new Error('Inconsistent redeemScript')
|
throw new Error('Inconsistent redeemScript')
|
||||||
}
|
}
|
||||||
|
|
||||||
var kpPubKey = keyPair.getPublicKeyBuffer()
|
var kpPubKey = keyPair.publicKey || keyPair.getPublicKeyBuffer()
|
||||||
if (!canSign(input)) {
|
if (!canSign(input)) {
|
||||||
if (witnessValue !== undefined) {
|
if (witnessValue !== undefined) {
|
||||||
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
|
if (input.value !== undefined && input.value !== witnessValue) throw new Error('Input didn\'t match witnessValue')
|
||||||
|
@ -699,14 +700,18 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
|
||||||
} else {
|
} else {
|
||||||
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
|
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// enforce in order signing of public keys
|
// enforce in order signing of public keys
|
||||||
var signed = input.pubKeys.some(function (pubKey, i) {
|
var signed = input.pubKeys.some(function (pubKey, i) {
|
||||||
if (!kpPubKey.equals(pubKey)) return false
|
if (!kpPubKey.equals(pubKey)) return false
|
||||||
if (input.signatures[i]) throw new Error('Signature already exists')
|
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.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
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
4
test/fixtures/ecsignature.json
vendored
4
test/fixtures/ecsignature.json
vendored
|
@ -120,11 +120,11 @@
|
||||||
"hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62"
|
"hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"exception": "Invalid signature length",
|
"exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 68\\)",
|
||||||
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000"
|
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"exception": "Invalid signature length",
|
"exception": "Expected Buffer\\(Length: 65\\), got Buffer\\(Length: 59\\)",
|
||||||
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379"
|
"hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -294,6 +294,19 @@ describe('TransactionBuilder', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sign', 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) {
|
fixtures.invalid.sign.forEach(function (f) {
|
||||||
it('throws on ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () {
|
it('throws on ' + f.exception + (f.description ? ' (' + f.description + ')' : ''), function () {
|
||||||
var txb = construct(f, true)
|
var txb = construct(f, true)
|
||||||
|
|
Loading…
Add table
Reference in a new issue