Skip to content

Commit 22f1d8e

Browse files
authored
Merge forks331 into master (#2)
* add testnet bip49 example * Should be able to deal with incomplete P2SH/P2WSH inputs when allowIncomplete is set * remove redundant baddress.toOutputScript call from tests * set p2sh=true only if redeemScriptType is set * testing/integration/examples: isolate to addresses/transactions examples, use public broadcast endpoints * tests/integration: add BIP32 serialization and multi-child examples * README: re-generate examples list * README: drop Contributors * README: add bech32 * README: move BCoin to Alternative * README: drop insight * README: add helperbit * LICENSE: 2017 too * add from/toBech32 * add Bech32 support to toOutputScript/fromOutputScript * README/tests: add BIP173/BIP16 SegWit address examples * tests: add P2WPK, P2WSH spend example * tests: resist txn-mempool-conflicts * tests/txb: add P2WSH(multisig), incomplete fixture * txbuilder: refactor branches for readability * add witnessPubKeyHash compressed policy * templates/pubkey: only canonical pubkeys to encode * TxBuilder: restrict uncompressed keyPairs for P2WPK and P2WSH * script: use asMinimalOP for ASM/decompile * Fix the integration url's to latest version also some of the urls were broken * add bech32 fixture * Fixed Segwit links The links were redirecting to 404 - I've modified them so they adhere to the beginning of the respective Segwit unit tests. * Fixed some README links Found some more links that were displaced by the Segwit unit tests and fixed them * typescript instructions on README closes: bitcoinjs#815 * README: cleanup typescript help * Add witness is true to signing * Add test for witness = true edge case during multisigning * TransactionBuilder: collect witnessValue as input.value, and match it * Fix txb.__overMaximumFees for segwit * Add test case * Fix absurd fee in fixture * buildstack - don't return op_0 * multisig.input.encodestack - replace OP_0 (permitted by partialSignature) with EMPTY_BUFFER * update CHANGELOG * 3.2.0 * tests: script tests can validate template fixtures too * match scriptHash types 1 for 1, ignore classify order * add fixture to verify input type classification (cherry picked from commit 8f9d8b7) * respond to Jonathan Underwood's comments (cherry picked from commit 8126ca2) * tests/fixtures: amend truncated outputHex * README: add notes about ES5, Node LTS feature tracking * package: rm contributors field, outdated, update wallet estimate * rm bscript circular dependencies * txbuilder: fix canSign returning true for missing witness value * address/txbuilder: require templates to prevent undefined exports * tests: add passing and failing tests for witness*.input.encode/decode * witnessScriptHash: fixed implementation * tests: add failing staged transaction building example bitcoinjs#901 * txbuilder: apply input.value before prepareInput * s/checkP2shInput/checkP2SHInput * 3.2.1 * ECSignature: add toRSBuffer/fromRSBuffer * TxBuilder: add support for RSBuffer type keyPairs and .publicKey * 3.3.0 * tests: txb for TxBuilder, Tx for Transaction * increase max feerate sanity check from 1000 to 2500 * 3.3.1 * opt-in bitcoin-cash support in transaction_builder * TransactionBuilder.fromTransaction & Bitcoin Cash Adds an extra parameter to fromTransaction, which tells the library to expect a value property to be added on each txin which uses bitcoin cash's sighashtype * bitcoin gold support * package: rename to bitcoinforksjs-lib
1 parent 8a07708 commit 22f1d8e

File tree

12 files changed

+700
-14
lines changed

12 files changed

+700
-14
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "bitcoinjs-lib",
3-
"version": "3.3.0",
3+
"version": "3.1.1",
44
"description": "Client-side Bitcoin JavaScript library",
55
"main": "./src/index.js",
66
"engines": {

src/ecsignature.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ ECSignature.fromDER = function (buffer) {
4747
// BIP62: 1 byte hashType flag (only 0x01, 0x02, 0x03, 0x81, 0x82 and 0x83 are allowed)
4848
ECSignature.parseScriptSignature = function (buffer) {
4949
var hashType = buffer.readUInt8(buffer.length - 1)
50-
var hashTypeMod = hashType & ~0x80
50+
var hashTypeMod = hashType & ~0xc0
5151

5252
if (hashTypeMod <= 0x00 || hashTypeMod >= 0x04) throw new Error('Invalid hashType ' + hashType)
5353

@@ -85,7 +85,7 @@ ECSignature.prototype.toRSBuffer = function (buffer, offset) {
8585
}
8686

8787
ECSignature.prototype.toScriptSignature = function (hashType) {
88-
var hashTypeMod = hashType & ~0x80
88+
var hashTypeMod = hashType & ~0xc0
8989
if (hashTypeMod <= 0 || hashTypeMod >= 4) throw new Error('Invalid hashType ' + hashType)
9090

9191
var hashTypeBuffer = Buffer.alloc(1)

src/networks.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
// Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731
33

44
module.exports = {
5+
bitcoingold: {
6+
messagePrefix: '\x18Bitcoin Gold Signed Message:\n',
7+
bip32: {
8+
public: 0x0488b21e,
9+
private: 0x0488ade4
10+
},
11+
pubKeyHash: 0x26,
12+
scriptHash: 0x17,
13+
wif: 0x80
14+
},
515
bitcoin: {
616
messagePrefix: '\x18Bitcoin Signed Message:\n',
717
bech32: 'bc',

src/script.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ function isCanonicalPubKey (buffer) {
185185
}
186186

187187
function isDefinedHashType (hashType) {
188-
var hashTypeMod = hashType & ~0x80
188+
var hashTypeMod = hashType & ~0xc0
189189

190190
// return hashTypeMod > SIGHASH_ALL && hashTypeMod < SIGHASH_SINGLE
191191
return hashTypeMod > 0x00 && hashTypeMod < 0x04

src/transaction.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ Transaction.SIGHASH_ALL = 0x01
3333
Transaction.SIGHASH_NONE = 0x02
3434
Transaction.SIGHASH_SINGLE = 0x03
3535
Transaction.SIGHASH_ANYONECANPAY = 0x80
36+
Transaction.SIGHASH_BITCOINCASHBIP143 = 0x40
3637
Transaction.ADVANCED_TRANSACTION_MARKER = 0x00
3738
Transaction.ADVANCED_TRANSACTION_FLAG = 0x01
39+
Transaction.FORKID_BTG = 0x4F // 79
40+
Transaction.FORKID_BCH = 0x00
3841

3942
var EMPTY_SCRIPT = Buffer.allocUnsafe(0)
4043
var EMPTY_WITNESS = []
@@ -402,6 +405,58 @@ Transaction.prototype.hashForWitnessV0 = function (inIndex, prevOutScript, value
402405
return bcrypto.hash256(tbuffer)
403406
}
404407

408+
/**
409+
* Hash transaction for signing a specific input for Bitcoin Cash.
410+
*/
411+
Transaction.prototype.hashForCashSignature = function (inIndex, prevOutScript, inAmount, hashType) {
412+
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number, types.maybe(types.UInt53)), arguments)
413+
414+
// This function works the way it does because Bitcoin Cash
415+
// uses BIP143 as their replay protection, AND their algo
416+
// includes `forkId | hashType`, AND since their forkId=0,
417+
// this is a NOP, and has no difference to segwit. To support
418+
// other forks, another parameter is required, and a new parameter
419+
// would be required in the hashForWitnessV0 function, or
420+
// it could be broken into two..
421+
422+
// BIP143 sighash activated in BitcoinCash via 0x40 bit
423+
if (hashType & Transaction.SIGHASH_BITCOINCASHBIP143) {
424+
if (types.Null(inAmount)) {
425+
throw new Error('Bitcoin Cash sighash requires value of input to be signed.')
426+
}
427+
return this.hashForWitnessV0(inIndex, prevOutScript, inAmount, hashType)
428+
} else {
429+
return this.hashForSignature(inIndex, prevOutScript, hashType)
430+
}
431+
}
432+
433+
/**
434+
* Hash transaction for signing a specific input for Bitcoin Gold.
435+
*/
436+
Transaction.prototype.hashForGoldSignature = function (inIndex, prevOutScript, inAmount, hashType, sigVersion) {
437+
typeforce(types.tuple(types.UInt32, types.Buffer, /* types.UInt8 */ types.Number, types.maybe(types.UInt53)), arguments)
438+
439+
// Bitcoin Gold also implements segregated witness
440+
// therefore we can pull out the setting of nForkHashType
441+
// and pass it into the functions.
442+
443+
var nForkHashType = hashType
444+
var fUseForkId = (hashType & Transaction.SIGHASH_BITCOINCASHBIP143) > 0
445+
if (fUseForkId) {
446+
nForkHashType |= Transaction.FORKID_BTG << 8
447+
}
448+
449+
// BIP143 sighash activated in BitcoinCash via 0x40 bit
450+
if (sigVersion || fUseForkId) {
451+
if (types.Null(inAmount)) {
452+
throw new Error('Bitcoin Cash sighash requires value of input to be signed.')
453+
}
454+
return this.hashForWitnessV0(inIndex, prevOutScript, inAmount, nForkHashType)
455+
} else {
456+
return this.hashForSignature(inIndex, prevOutScript, nForkHashType)
457+
}
458+
}
459+
405460
Transaction.prototype.getHash = function () {
406461
return bcrypto.hash256(this.__toBuffer(undefined, undefined, false))
407462
}

src/transaction_builder.js

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ function expandInput (scriptSig, witnessStack) {
174174
}
175175

176176
// could be done in expandInput, but requires the original Transaction for hashForSignature
177-
function fixMultisigOrder (input, transaction, vin) {
177+
function fixMultisigOrder (input, transaction, vin, value, forkId) {
178178
if (input.redeemScriptType !== scriptTypes.MULTISIG || !input.redeemScript) return
179179
if (input.pubKeys.length === input.signatures.length) return
180180

@@ -191,7 +191,22 @@ function fixMultisigOrder (input, transaction, vin) {
191191

192192
// TODO: avoid O(n) hashForSignature
193193
var parsed = ECSignature.parseScriptSignature(signature)
194-
var hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType)
194+
var hash
195+
switch (forkId) {
196+
case Transaction.FORKID_BCH:
197+
hash = transaction.hashForCashSignature(vin, input.signScript, value, parsed.hashType)
198+
break
199+
case Transaction.FORKID_BTG:
200+
hash = transaction.hashForGoldSignature(vin, input.signScript, value, parsed.hashType)
201+
break
202+
default:
203+
if (input.witness) {
204+
hash = transaction.hashForWitnessV0(vin, input.signScript, value, parsed.hashType)
205+
} else {
206+
hash = transaction.hashForSignature(vin, input.signScript, parsed.hashType)
207+
}
208+
break
209+
}
195210

196211
// skip if signature does not match pubKey
197212
if (!keyPair.verify(hash, parsed.signature)) return false
@@ -448,7 +463,6 @@ function buildInput (input, allowIncomplete) {
448463
witness.push(input.witnessScript)
449464
scriptType = input.witnessScriptType
450465
}
451-
452466
break
453467
}
454468

@@ -469,12 +483,30 @@ function TransactionBuilder (network, maximumFeeRate) {
469483
this.network = network || networks.bitcoin
470484

471485
// WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth)
472-
this.maximumFeeRate = maximumFeeRate || 1000
486+
this.maximumFeeRate = maximumFeeRate || 2500
473487

474488
this.inputs = []
489+
this.bitcoinCash = false
490+
this.bitcoinGold = false
475491
this.tx = new Transaction()
476492
}
477493

494+
TransactionBuilder.prototype.enableBitcoinCash = function (enable) {
495+
if (typeof enable === 'undefined') {
496+
enable = true
497+
}
498+
499+
this.bitcoinCash = enable
500+
}
501+
502+
TransactionBuilder.prototype.enableBitcoinGold = function (enable) {
503+
if (typeof enable === 'undefined') {
504+
enable = true
505+
}
506+
507+
this.bitcoinGold = enable
508+
}
509+
478510
TransactionBuilder.prototype.setLockTime = function (locktime) {
479511
typeforce(types.UInt32, locktime)
480512

@@ -497,9 +529,17 @@ TransactionBuilder.prototype.setVersion = function (version) {
497529
this.tx.version = version
498530
}
499531

500-
TransactionBuilder.fromTransaction = function (transaction, network) {
532+
TransactionBuilder.fromTransaction = function (transaction, network, forkId) {
501533
var txb = new TransactionBuilder(network)
502534

535+
if (typeof forkId === 'number') {
536+
if (forkId === Transaction.FORKID_BTG) {
537+
txb.enableBitcoinGold(true)
538+
} else if (forkId === Transaction.FORKID_BCH) {
539+
txb.enableBitcoinCash(true)
540+
}
541+
}
542+
503543
// Copy transaction fields
504544
txb.setVersion(transaction.version)
505545
txb.setLockTime(transaction.locktime)
@@ -514,13 +554,14 @@ TransactionBuilder.fromTransaction = function (transaction, network) {
514554
txb.__addInputUnsafe(txIn.hash, txIn.index, {
515555
sequence: txIn.sequence,
516556
script: txIn.script,
517-
witness: txIn.witness
557+
witness: txIn.witness,
558+
value: txIn.value
518559
})
519560
})
520561

521562
// fix some things not possible through the public API
522563
txb.inputs.forEach(function (input, i) {
523-
fixMultisigOrder(input, transaction, i)
564+
fixMultisigOrder(input, transaction, i, input.value, forkId)
524565
})
525566

526567
return txb
@@ -695,10 +736,16 @@ TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashTy
695736

696737
// ready to sign
697738
var signatureHash
698-
if (input.witness) {
699-
signatureHash = this.tx.hashForWitnessV0(vin, input.signScript, input.value, hashType)
739+
if (this.bitcoinGold) {
740+
signatureHash = this.tx.hashForGoldSignature(vin, input.signScript, witnessValue, hashType, input.witness)
741+
} else if (this.bitcoinCash) {
742+
signatureHash = this.tx.hashForCashSignature(vin, input.signScript, witnessValue, hashType)
700743
} else {
701-
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
744+
if (input.witness) {
745+
signatureHash = this.tx.hashForWitnessV0(vin, input.signScript, witnessValue, hashType)
746+
} else {
747+
signatureHash = this.tx.hashForSignature(vin, input.signScript, hashType)
748+
}
702749
}
703750

704751
// enforce in order signing of public keys

test/bitcoincash.test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* global describe, it */
2+
3+
var assert = require('assert')
4+
var bscript = require('../src/script')
5+
var ECPair = require('../src/ecpair')
6+
var NETWORKS = require('../src/networks')
7+
var TransactionBuilder = require('../src/transaction_builder')
8+
var Transaction = require('../src/transaction')
9+
10+
describe('TransactionBuilder', function () {
11+
var network = NETWORKS['testnet']
12+
it('cashtestcase3', function () {
13+
var value = 50 * 1e8
14+
var txid = '40c8a218923f23df3692530fa8e475251c50c7d630dccbdfbd92ba8092f4aa13'
15+
var vout = 0
16+
17+
var wif = 'cTNwkxh7nVByhc3i7BH6eaBFQ4yVs6WvXBGBoA9xdKiorwcYVACc'
18+
var keyPair = ECPair.fromWIF(wif, network)
19+
20+
var pk = keyPair.getPublicKeyBuffer()
21+
var spk = bscript.pubKey.output.encode(pk)
22+
23+
var txb = new TransactionBuilder(network)
24+
txb.addInput(txid, vout, Transaction.DEFAULT_SEQUENCE, spk)
25+
txb.addOutput('mzDktdwPcWwqg8aZkPotx6aYi4mKvDD7ay', value)
26+
txb.enableBitcoinCash(true)
27+
txb.setVersion(2)
28+
29+
var hashType = Transaction.SIGHASH_ALL | Transaction.SIGHASH_BITCOINCASHBIP143
30+
31+
txb.sign(0, keyPair, null, hashType, value)
32+
33+
var tx = txb.build()
34+
var hex = tx.toHex()
35+
assert.equal('020000000113aaf49280ba92bddfcbdc30d6c7501c2575e4a80f539236df233f9218a2c8400000000049483045022100c5874e39da4dd427d35e24792bf31dcd63c25684deec66b426271b4043e21c3002201bfdc0621ad4237e8db05aa6cad69f3d5ab4ae32ebb2048f65b12165da6cc69341ffffffff0100f2052a010000001976a914cd29cc97826c37281ac61301e4d5ed374770585688ac00000000', hex)
36+
})
37+
})

test/bitcoingold.test.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/* global describe, it */
2+
3+
var assert = require('assert')
4+
var bscript = require('../src/script')
5+
var bcrypto = require('../src/crypto')
6+
var ECPair = require('../src/ecpair')
7+
var NETWORKS = require('../src/networks')
8+
var TransactionBuilder = require('../src/transaction_builder')
9+
var Transaction = require('../src/transaction')
10+
11+
describe('TransactionBuilder', function () {
12+
var network = NETWORKS['bitcoingold']
13+
it('goldtestcase', function () {
14+
var value = 50 * 1e8
15+
var txid = '40c8a218923f23df3692530fa8e475251c50c7d630dccbdfbd92ba8092f4aa13'
16+
var vout = 0
17+
18+
var wif = 'L54PmHcjKXi8H6v9cLAJ7DgGJFDpaFpR2YsV2WARieb82dz3QAfr'
19+
var keyPair = ECPair.fromWIF(wif, network)
20+
21+
var pk = bcrypto.hash160(keyPair.getPublicKeyBuffer())
22+
var spk = bscript.pubKeyHash.output.encode(pk)
23+
24+
var txb = new TransactionBuilder(network)
25+
txb.addInput(txid, vout, Transaction.DEFAULT_SEQUENCE, spk)
26+
txb.addOutput('GfEHv6hKvAX8HYfFzabMY2eiYDtC9eViqe', value)
27+
txb.enableBitcoinGold(true)
28+
txb.setVersion(2)
29+
30+
var hashType = Transaction.SIGHASH_ALL | Transaction.SIGHASH_BITCOINCASHBIP143
31+
32+
txb.sign(0, keyPair, null, hashType, value)
33+
34+
var tx = txb.build()
35+
var hex = tx.toHex()
36+
assert.equal('020000000113aaf49280ba92bddfcbdc30d6c7501c2575e4a80f539236df233f9218a2c840000000006b483045022100c594c8e0750b1b6ec4e267b6d6c7098840f86fa9467f8aa452f439c3a72e0cd9022019759d800fffd7fcb78d16468f5693ea07a13da33607e0e8fbb4cdb5967075b441210201ad6a9a15457b162a71f1d5db8fe27ff001abc4ae3a888214f9407cb0da863cffffffff0100f2052a010000001976a914ea95bd5087d3b5f2df279304a46ad827225c4e8688ac00000000', hex)
37+
})
38+
39+
it('goldtestcase_multisig_1', function () {
40+
var value = 50 * 1e8
41+
42+
var txHex = '020000000113aaf49280ba92bddfcbdc30d6c7501c2575e4a80f539236df233f9218a2c840000000009200483045022100b3b4211b8e8babc667dcca0b6f1c1284f191170a38a59bc3b9d7541d68c3c7a002200196267b87a7b80f3f556b3372e5ee6ed19b4b9e802c34916f45bc2b11d2de1a414752210201ad6a9a15457b162a71f1d5db8fe27ff001abc4ae3a888214f9407cb0da863c2103e6533849994cf76a9009447f2ad6dbf84c78e6f5f48fe77cf83cd9a3fe2e30ec52aeffffffff0100f2052a010000001976a914ea95bd5087d3b5f2df279304a46ad827225c4e8688ac00000000'
43+
var tx = Transaction.fromHex(txHex)
44+
tx.ins[0].value = value
45+
46+
var txb = TransactionBuilder.fromTransaction(tx, network, Transaction.FORKID_BTG)
47+
48+
assert.equal(undefined, txb.inputs[0].signatures[0])
49+
assert.equal(
50+
'3045022100b3b4211b8e8babc667dcca0b6f1c1284f191170a38a59bc3b9d7541d68c3c7a002200196267b87a7b80f3f556b3372e5ee6ed19b4b9e802c34916f45bc2b11d2de1a41',
51+
txb.inputs[0].signatures[1].toString('hex')
52+
)
53+
54+
var hex = txb.build().toHex()
55+
assert.equal(txHex, hex)
56+
})
57+
58+
it('goldtestcase_multisig_0', function () {
59+
var value = 50 * 1e8
60+
61+
var txHex = '020000000113aaf49280ba92bddfcbdc30d6c7501c2575e4a80f539236df233f9218a2c840000000009100473044022025cb6ee7a63c7403645be2ed4ffcf9cd41d773ee3ba57a05dc335c4427f647660220323a038daac698efdc700ffa8d90e6641ed9eb4ab82808df0506a9da08863d29414752210201ad6a9a15457b162a71f1d5db8fe27ff001abc4ae3a888214f9407cb0da863c2103e6533849994cf76a9009447f2ad6dbf84c78e6f5f48fe77cf83cd9a3fe2e30ec52aeffffffff0100f2052a010000001976a914ea95bd5087d3b5f2df279304a46ad827225c4e8688ac00000000'
62+
var tx = Transaction.fromHex(txHex)
63+
tx.ins[0].value = value
64+
65+
var txb = TransactionBuilder.fromTransaction(tx, network, Transaction.FORKID_BTG)
66+
67+
assert.equal(
68+
'3044022025cb6ee7a63c7403645be2ed4ffcf9cd41d773ee3ba57a05dc335c4427f647660220323a038daac698efdc700ffa8d90e6641ed9eb4ab82808df0506a9da08863d2941',
69+
txb.inputs[0].signatures[0].toString('hex')
70+
)
71+
assert.equal(undefined, txb.inputs[0].signatures[1])
72+
73+
var hex = txb.build().toHex()
74+
assert.equal(txHex, hex)
75+
})
76+
})

test/integration/_mainnet.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var Blockchain = require('cb-http-client')
2+
var BLOCKTRAIL_API_KEY = process.env.BLOCKTRAIL_API_KEY || 'c0bd8155c66e3fb148bb1664adc1e4dacd872548'
3+
module.exports = new Blockchain('https://api.blocktrail.com/cb/v0.2.1/BTC', { api_key: BLOCKTRAIL_API_KEY })

0 commit comments

Comments
 (0)