@smartledger/bsv 9.0.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +137 -0
- package/README.md +152 -1041
- package/STABILITY.md +134 -0
- package/bsv-gdaf.min.js +53 -48
- package/bsv-ltp.min.js +23 -18
- package/bsv-smartcontract.min.js +18 -18
- package/bsv.bundle.js +53 -48
- package/bsv.min.js +53 -48
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/docs/proposals/10.0.0-package-split.md +131 -0
- package/index.js +5 -0
- package/lib/block/merkleblock.js +19 -5
- package/lib/hdprivatekey.js +15 -0
- package/lib/script/interpreter.js +109 -1
- package/lib/smart_contract/builder.js +14 -0
- package/lib/util/deprecate.js +159 -0
- package/package.json +19 -82
- package/version.js +1 -1
- package/.mocharc.json +0 -5
- package/test/address.js +0 -629
- package/test/block/block.js +0 -239
- package/test/block/blockheader.js +0 -270
- package/test/block/merkleblock.js +0 -207
- package/test/build/bundle_crypto_shim.js +0 -104
- package/test/build/bundle_externals.js +0 -125
- package/test/build/bundle_smoke.js +0 -55
- package/test/build/esbuild_main.js +0 -65
- package/test/build/esm_wrapper.js +0 -97
- package/test/build/exports_resolution.js +0 -84
- package/test/build/version_sync.js +0 -14
- package/test/cli/smoke.js +0 -261
- package/test/consensus/base58-vectors.js +0 -120
- package/test/consensus/sv-script-vectors.js +0 -81
- package/test/consensus/sv-sighash-vectors.js +0 -51
- package/test/consensus/sv-tx-vectors.js +0 -48
- package/test/credentials-test.js +0 -332
- package/test/crypto/backend_selection.js +0 -126
- package/test/crypto/bn.js +0 -168
- package/test/crypto/ecdsa.js +0 -426
- package/test/crypto/elliptic-fixed.js +0 -61
- package/test/crypto/hash.browser.js +0 -121
- package/test/crypto/hash.js +0 -122
- package/test/crypto/point.js +0 -193
- package/test/crypto/random.js +0 -105
- package/test/crypto/security.js +0 -176
- package/test/crypto/shamir.js +0 -177
- package/test/crypto/shamir_rngcap.js +0 -29
- package/test/crypto/signature.js +0 -399
- package/test/data/bip69.json +0 -215
- package/test/data/bitcoin-sv/README.md +0 -170
- package/test/data/bitcoin-sv/base58_encode_decode.json +0 -14
- package/test/data/bitcoin-sv/base58_keys_invalid.json +0 -152
- package/test/data/bitcoin-sv/base58_keys_valid.json +0 -452
- package/test/data/bitcoin-sv/script_tests.json +0 -2591
- package/test/data/bitcoin-sv/sighash.json +0 -1003
- package/test/data/bitcoin-sv/tx_invalid.json +0 -285
- package/test/data/bitcoin-sv/tx_valid.json +0 -367
- package/test/data/bitcoind/base58_keys_invalid.json +0 -152
- package/test/data/bitcoind/base58_keys_valid.json +0 -452
- package/test/data/bitcoind/blocks.json +0 -27
- package/test/data/bitcoind/script_tests.json +0 -2244
- package/test/data/bitcoind/sig_canonical.json +0 -7
- package/test/data/bitcoind/sig_noncanonical.json +0 -22
- package/test/data/bitcoind/tx_invalid.json +0 -177
- package/test/data/bitcoind/tx_valid.json +0 -224
- package/test/data/blk86756-testnet.dat +0 -0
- package/test/data/blk86756-testnet.js +0 -12
- package/test/data/blk86756-testnet.json +0 -684
- package/test/data/brc220-batch-vector.json +0 -129
- package/test/data/ecdsa.json +0 -230
- package/test/data/merkleblocks.js +0 -486
- package/test/data/messages.json +0 -22
- package/test/data/sighash.json +0 -1004
- package/test/data/tx_creation.json +0 -85
- package/test/didweb/relationships.js +0 -144
- package/test/ecies/bitcore-ecies.js +0 -178
- package/test/ecies/electrum-ecies.js +0 -206
- package/test/encoding/base58.js +0 -131
- package/test/encoding/base58check.js +0 -145
- package/test/encoding/bufferreader.js +0 -328
- package/test/encoding/bufferwriter.js +0 -160
- package/test/encoding/varint.js +0 -104
- package/test/gdaf/anchor_no_key_leak.js +0 -146
- package/test/gdaf/anchor_spv.js +0 -163
- package/test/gdaf/canonicalization.js +0 -106
- package/test/gdaf/canonicalize.js +0 -140
- package/test/gdaf/zk_prover.js +0 -204
- package/test/hdkeys.js +0 -365
- package/test/hdprivatekey.js +0 -339
- package/test/hdpublickey.js +0 -293
- package/test/index.js +0 -16
- package/test/ltp/ids.js +0 -74
- package/test/ltp/right.js +0 -198
- package/test/ltp/verify_failclosed.js +0 -134
- package/test/message/message.js +0 -190
- package/test/mnemonic/data/fixtures.json +0 -300
- package/test/mnemonic/mnemonic.js +0 -277
- package/test/mnemonic/mocha.opts +0 -1
- package/test/mnemonic/pbkdf2.test.js +0 -43
- package/test/networks.js +0 -207
- package/test/notaryhash/batch_leaf.js +0 -140
- package/test/notaryhash/batch_vector.js +0 -282
- package/test/notaryhash/certificate.js +0 -249
- package/test/notaryhash/encoding.js +0 -186
- package/test/notaryhash/interop.js +0 -112
- package/test/notaryhash/merkle.js +0 -181
- package/test/notaryhash/script.js +0 -270
- package/test/notaryhash/verify.js +0 -342
- package/test/opcode.js +0 -186
- package/test/ordinals/bsv20.js +0 -337
- package/test/ordinals/inscription.js +0 -329
- package/test/ordinals/ordlock.js +0 -567
- package/test/privatekey.js +0 -540
- package/test/publickey.js +0 -411
- package/test/regressions.js +0 -215
- package/test/script/chronicle.js +0 -543
- package/test/script/defaults.js +0 -160
- package/test/script/genesis_limits.js +0 -203
- package/test/script/interpreter.js +0 -776
- package/test/script/script.js +0 -1259
- package/test/script/string_ops.js +0 -88
- package/test/security/fail_closed_contracts.js +0 -104
- package/test/security/threat_model_coverage.js +0 -31
- package/test/smart_contract/covenants.js +0 -207
- package/test/smart_contract/dsl_debugger.js +0 -92
- package/test/smart_contract/extract_field.js +0 -61
- package/test/smart_contract/nonenforcing_guard.js +0 -44
- package/test/smart_contract/ordinal_transfer.js +0 -71
- package/test/smart_contract/preimage.js +0 -98
- package/test/smart_contract/sighash_marketplace.js +0 -98
- package/test/smart_contract/token_generalized.js +0 -175
- package/test/spv/headerchain.js +0 -86
- package/test/spv/merkleproof.js +0 -133
- package/test/statuslist/failclosed.js +0 -78
- package/test/transaction/deserialize.js +0 -33
- package/test/transaction/input/input.js +0 -92
- package/test/transaction/input/multisig.js +0 -174
- package/test/transaction/input/multisigscripthash.js +0 -111
- package/test/transaction/input/publickey.js +0 -68
- package/test/transaction/input/publickeyhash.js +0 -59
- package/test/transaction/output.js +0 -185
- package/test/transaction/sighash.js +0 -91
- package/test/transaction/signature.js +0 -127
- package/test/transaction/transaction.js +0 -1299
- package/test/transaction/unspentoutput.js +0 -97
- package/test/types/dts_drift.js +0 -124
- package/test/types/surface_honesty.js +0 -102
- package/test/util/id.js +0 -72
- package/test/util/js.js +0 -76
- package/test/util/preconditions.js +0 -79
- package/test/vcjwt/interop.js +0 -126
package/test/crypto/shamir.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it */
|
|
4
|
-
|
|
5
|
-
var assert = require('assert')
|
|
6
|
-
var crypto = require('crypto')
|
|
7
|
-
var Shamir = require('../../lib/crypto/shamir')
|
|
8
|
-
|
|
9
|
-
describe('Shamir Secret Sharing', function () {
|
|
10
|
-
describe('split / combine round-trip', function () {
|
|
11
|
-
it('reconstructs an ASCII secret from exactly threshold shares', function () {
|
|
12
|
-
var shares = Shamir.split('Hello, Bitcoin SV!', 3, 5)
|
|
13
|
-
assert.strictEqual(shares.length, 5)
|
|
14
|
-
var out = Shamir.combine(shares.slice(0, 3))
|
|
15
|
-
assert.strictEqual(out.toString('utf8'), 'Hello, Bitcoin SV!')
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('reconstructs from any threshold-sized subset of shares', function () {
|
|
19
|
-
var secret = crypto.randomBytes(32)
|
|
20
|
-
var shares = Shamir.split(secret, 3, 5)
|
|
21
|
-
// a few different 3-of-5 subsets
|
|
22
|
-
var subsets = [[0, 1, 2], [2, 3, 4], [0, 2, 4], [1, 3, 4]]
|
|
23
|
-
subsets.forEach(function (idx) {
|
|
24
|
-
var pick = idx.map(function (i) { return shares[i] })
|
|
25
|
-
assert.ok(Shamir.combine(pick).equals(secret), 'subset ' + idx)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('reconstructs a 32-byte key and more shares than threshold', function () {
|
|
30
|
-
var key = crypto.randomBytes(32)
|
|
31
|
-
var shares = Shamir.split(key, 4, 7)
|
|
32
|
-
assert.ok(Shamir.combine(shares.slice(0, 4)).equals(key))
|
|
33
|
-
assert.ok(Shamir.combine(shares).equals(key)) // all 7 also works
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('preserves leading zero bytes', function () {
|
|
37
|
-
var secret = Buffer.from('000000deadbeef', 'hex')
|
|
38
|
-
var shares = Shamir.split(secret, 2, 3)
|
|
39
|
-
assert.ok(Shamir.combine(shares.slice(0, 2)).equals(secret))
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
it('handles a single zero byte', function () {
|
|
43
|
-
var shares = Shamir.split(Buffer.from([0]), 2, 2)
|
|
44
|
-
assert.ok(Shamir.combine(shares).equals(Buffer.from([0])))
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('accepts a Buffer secret and returns a Buffer', function () {
|
|
48
|
-
var secret = Buffer.from('binary\x00\xff data', 'binary')
|
|
49
|
-
var shares = Shamir.split(secret, 2, 3)
|
|
50
|
-
var out = Shamir.combine(shares.slice(0, 2))
|
|
51
|
-
assert.ok(Buffer.isBuffer(out))
|
|
52
|
-
assert.ok(out.equals(secret))
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
describe('input validation', function () {
|
|
57
|
-
it('requires a secret', function () {
|
|
58
|
-
assert.throws(function () { Shamir.split(null, 2, 3) }, /Secret is required/)
|
|
59
|
-
})
|
|
60
|
-
it('requires threshold >= 2', function () {
|
|
61
|
-
assert.throws(function () { Shamir.split('x', 1, 3) }, /at least 2/)
|
|
62
|
-
})
|
|
63
|
-
it('requires shares >= threshold', function () {
|
|
64
|
-
assert.throws(function () { Shamir.split('x', 3, 2) }, /at least threshold/)
|
|
65
|
-
})
|
|
66
|
-
it('caps shares/threshold at 255', function () {
|
|
67
|
-
assert.throws(function () { Shamir.split('x', 2, 256) }, /<= 255/)
|
|
68
|
-
})
|
|
69
|
-
it('requires a non-empty shares array to combine', function () {
|
|
70
|
-
assert.throws(function () { Shamir.combine([]) }, /required/)
|
|
71
|
-
})
|
|
72
|
-
it('rejects fewer than threshold shares', function () {
|
|
73
|
-
var shares = Shamir.split('secret', 3, 5)
|
|
74
|
-
assert.throws(function () { Shamir.combine(shares.slice(0, 2)) }, /Insufficient/)
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe('integrity and isolation', function () {
|
|
79
|
-
it('detects a tampered share at combine time', function () {
|
|
80
|
-
// checksum is opt-in (off by default to avoid leaking a hash of the secret);
|
|
81
|
-
// this test exercises the checksum-based detection, so enable it explicitly.
|
|
82
|
-
var shares = Shamir.split(crypto.randomBytes(16), 3, 5, { checksum: true })
|
|
83
|
-
// flip a hex nibble in one share's data
|
|
84
|
-
var s = shares[1]
|
|
85
|
-
var ch = s.share[s.share.length - 1]
|
|
86
|
-
shares[1] = Object.assign({}, s, {
|
|
87
|
-
share: s.share.slice(0, -1) + (ch === '0' ? '1' : '0')
|
|
88
|
-
})
|
|
89
|
-
assert.throws(function () { Shamir.combine(shares.slice(0, 3)) },
|
|
90
|
-
/Integrity check failed|Reconstruction failed/)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it('refuses to mix shares from different splits', function () {
|
|
94
|
-
var secret = crypto.randomBytes(16)
|
|
95
|
-
var a = Shamir.split(secret, 2, 3)
|
|
96
|
-
var b = Shamir.split(secret, 2, 3)
|
|
97
|
-
assert.throws(function () { Shamir.combine([a[0], b[1]]) }, /different splits|splitId/)
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
it('does not embed a secret checksum by default (no leak to sub-threshold holders)', function () {
|
|
101
|
-
var shares = Shamir.split('low-entropy-pin-1234', 2, 3)
|
|
102
|
-
assert.strictEqual(shares[0].checksum, null)
|
|
103
|
-
assert.strictEqual(Shamir.combine(shares.slice(0, 2)).toString('utf8'), 'low-entropy-pin-1234')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('checksum can be disabled', function () {
|
|
107
|
-
var shares = Shamir.split('no-checksum', 2, 3, { checksum: false })
|
|
108
|
-
assert.strictEqual(shares[0].checksum, null)
|
|
109
|
-
assert.strictEqual(Shamir.combine(shares.slice(0, 2)).toString('utf8'), 'no-checksum')
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it('produces a different splitId for each split', function () {
|
|
113
|
-
var a = Shamir.split('x', 2, 3)
|
|
114
|
-
var b = Shamir.split('x', 2, 3)
|
|
115
|
-
assert.notStrictEqual(a[0].splitId, b[0].splitId)
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
describe('verifyShare', function () {
|
|
120
|
-
it('accepts a freshly produced share', function () {
|
|
121
|
-
var shares = Shamir.split('verify me', 2, 3)
|
|
122
|
-
assert.strictEqual(Shamir.verifyShare(shares[0]), true)
|
|
123
|
-
})
|
|
124
|
-
it('rejects non-objects and malformed shares', function () {
|
|
125
|
-
assert.strictEqual(Shamir.verifyShare(null), false)
|
|
126
|
-
assert.strictEqual(Shamir.verifyShare({}), false)
|
|
127
|
-
assert.strictEqual(Shamir.verifyShare({ v: 2, share: 'nothex!!', threshold: 2, shares: 3, id: 1 }), false)
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
describe('generateTestVectors', function () {
|
|
132
|
-
it('round-trips its own vectors', function () {
|
|
133
|
-
var tv = Shamir.generateTestVectors()
|
|
134
|
-
assert.strictEqual(tv.valid, true)
|
|
135
|
-
assert.strictEqual(tv.reconstructed, tv.secret)
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
describe('legacy (<= 4.x) share recovery', function () {
|
|
140
|
-
// A real 2-of-3 share set produced by the old 31-bit-prime implementation
|
|
141
|
-
// for the secret "hi" (0x6869). Kept as a fixed vector so we know old
|
|
142
|
-
// shares remain recoverable.
|
|
143
|
-
function legacyShareSetForHi () {
|
|
144
|
-
// Reconstruct deterministically using the legacy math so the vector
|
|
145
|
-
// is self-consistent: build points for the polynomial f(x)=secretByte
|
|
146
|
-
// with known coefficients is non-trivial; instead we exercise the legacy
|
|
147
|
-
// path by round-tripping through the retained legacy combiner with a
|
|
148
|
-
// hand-built linear (threshold=2) sharing over the legacy prime.
|
|
149
|
-
var prime = 2147483647
|
|
150
|
-
var bytes = [0x68, 0x69] // "hi"
|
|
151
|
-
// f_b(x) = byte_b + a_b * x (a_b chosen fixed, nonzero)
|
|
152
|
-
var a = [12345, 67890]
|
|
153
|
-
function y (b, x) { return ((bytes[b] + a[b] * x) % prime).toString(16) }
|
|
154
|
-
function share (id) {
|
|
155
|
-
return {
|
|
156
|
-
id: id,
|
|
157
|
-
threshold: 2,
|
|
158
|
-
shares: 3,
|
|
159
|
-
length: 2,
|
|
160
|
-
bytes: [{ x: id, y: y(0, id) }, { x: id, y: y(1, id) }]
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return [share(1), share(2), share(3)]
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
it('recovers a secret split by the old implementation', function () {
|
|
167
|
-
var legacy = legacyShareSetForHi()
|
|
168
|
-
var out = Shamir.combine([legacy[0], legacy[2]])
|
|
169
|
-
assert.strictEqual(out.toString('utf8'), 'hi')
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('verifyShare accepts a legacy share', function () {
|
|
173
|
-
var legacy = legacyShareSetForHi()
|
|
174
|
-
assert.strictEqual(Shamir.verifyShare(legacy[0]), true)
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
})
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
/* global describe, it, afterEach */
|
|
4
|
-
|
|
5
|
-
// Regression: the secrets.js RNG callback re-draws on an all-zero draw. A broken
|
|
6
|
-
// or stubbed CSPRNG that returns all-zero bytes forever (e.g. a bundle mocking
|
|
7
|
-
// node crypto to Buffer.alloc) previously made that loop spin forever. The bounded
|
|
8
|
-
// loop now throws a clear error instead of hanging.
|
|
9
|
-
|
|
10
|
-
var assert = require('assert')
|
|
11
|
-
var Shamir = require('../../lib/crypto/shamir')
|
|
12
|
-
var Random = require('../../lib/crypto/random')
|
|
13
|
-
|
|
14
|
-
describe('Shamir RNG degenerate-source cap', function () {
|
|
15
|
-
var orig = Random.getRandomBuffer
|
|
16
|
-
afterEach(function () { Random.getRandomBuffer = orig })
|
|
17
|
-
|
|
18
|
-
it('throws (does not hang) when the CSPRNG is stuck returning all zeros', function () {
|
|
19
|
-
Random.getRandomBuffer = function (n) { return Buffer.alloc(n, 0) }
|
|
20
|
-
assert.throws(function () {
|
|
21
|
-
Shamir.split('secret', 2, 3)
|
|
22
|
-
}, /all-zero|degenerate/)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('still splits/combines normally with a healthy CSPRNG', function () {
|
|
26
|
-
var shares = Shamir.split('round-trip', 2, 3)
|
|
27
|
-
assert.strictEqual(Shamir.combine(shares.slice(0, 2)).toString('utf8'), 'round-trip')
|
|
28
|
-
})
|
|
29
|
-
})
|
package/test/crypto/signature.js
DELETED
|
@@ -1,399 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
var _ = require('../../lib/util/_')
|
|
4
|
-
var should = require('chai').should()
|
|
5
|
-
var expect = require('chai').expect
|
|
6
|
-
var bsv = require('../..')
|
|
7
|
-
var BN = bsv.crypto.BN
|
|
8
|
-
var Signature = bsv.crypto.Signature
|
|
9
|
-
var ECDSA = bsv.crypto.ECDSA
|
|
10
|
-
var Hash = bsv.crypto.Hash
|
|
11
|
-
var PrivateKey = bsv.PrivateKey
|
|
12
|
-
var JSUtil = bsv.util.js
|
|
13
|
-
var Interpreter = bsv.Script.Interpreter
|
|
14
|
-
|
|
15
|
-
var sigCanonical = require('../data/bitcoind/sig_canonical')
|
|
16
|
-
var sigNonCanonical = require('../data/bitcoind/sig_noncanonical')
|
|
17
|
-
|
|
18
|
-
describe('Signature', function () {
|
|
19
|
-
it('should make a blank signature', function () {
|
|
20
|
-
var sig = new Signature()
|
|
21
|
-
should.exist(sig)
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
it('should work with conveniently setting r, s', function () {
|
|
25
|
-
var r = new BN()
|
|
26
|
-
var s = new BN()
|
|
27
|
-
var sig = new Signature(r, s)
|
|
28
|
-
should.exist(sig)
|
|
29
|
-
sig.r.toString().should.equal(r.toString())
|
|
30
|
-
sig.s.toString().should.equal(s.toString())
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
describe('#set', function () {
|
|
34
|
-
it('should set compressed', function () {
|
|
35
|
-
should.exist(Signature().set({
|
|
36
|
-
compressed: true
|
|
37
|
-
}))
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it('should set nhashtype', function () {
|
|
41
|
-
var sig = Signature().set({
|
|
42
|
-
nhashtype: Signature.SIGHASH_ALL
|
|
43
|
-
})
|
|
44
|
-
sig.nhashtype.should.equal(Signature.SIGHASH_ALL)
|
|
45
|
-
sig.set({
|
|
46
|
-
nhashtype: Signature.SIGHASH_ALL | Signature.SIGHASH_ANYONECANPAY
|
|
47
|
-
})
|
|
48
|
-
sig.nhashtype.should.equal(Signature.SIGHASH_ALL | Signature.SIGHASH_ANYONECANPAY)
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
describe('#fromCompact', function () {
|
|
53
|
-
it('should create a signature from a compressed signature', function () {
|
|
54
|
-
var blank = Buffer.alloc(32)
|
|
55
|
-
blank.fill(0)
|
|
56
|
-
var compressed = Buffer.concat([
|
|
57
|
-
Buffer.from([0 + 27 + 4]),
|
|
58
|
-
blank,
|
|
59
|
-
blank
|
|
60
|
-
])
|
|
61
|
-
var sig = Signature.fromCompact(compressed)
|
|
62
|
-
sig.r.cmp(BN.Zero).should.equal(0)
|
|
63
|
-
sig.s.cmp(BN.Zero).should.equal(0)
|
|
64
|
-
sig.compressed.should.equal(true)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should create a signature from an uncompressed signature', function () {
|
|
68
|
-
var sigHexaStr = '1cd5e61ab5bfd0d1450997894cb1a53e917f89d82eb43f06fa96f32c96e061aec12fc1188e8b' +
|
|
69
|
-
'0dc553a2588be2b5b68dbbd7f092894aa3397786e9c769c5348dc6'
|
|
70
|
-
var sig = Signature.fromCompact(Buffer.from(sigHexaStr, 'hex'))
|
|
71
|
-
var r = 'd5e61ab5bfd0d1450997894cb1a53e917f89d82eb43f06fa96f32c96e061aec1'
|
|
72
|
-
var s = '2fc1188e8b0dc553a2588be2b5b68dbbd7f092894aa3397786e9c769c5348dc6'
|
|
73
|
-
sig.r.toString('hex').should.equal(r)
|
|
74
|
-
sig.s.toString('hex').should.equal(s)
|
|
75
|
-
sig.compressed.should.equal(false)
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
describe('#fromDER', function () {
|
|
80
|
-
var buf = Buffer.from('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex')
|
|
81
|
-
|
|
82
|
-
it('should parse this DER format signature', function () {
|
|
83
|
-
var sig = Signature.fromDER(buf)
|
|
84
|
-
sig.r.toBuffer({
|
|
85
|
-
size: 32
|
|
86
|
-
}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277')
|
|
87
|
-
sig.s.toBuffer({
|
|
88
|
-
size: 32
|
|
89
|
-
}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2')
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
describe('#fromString', function () {
|
|
94
|
-
var buf = Buffer.from('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex')
|
|
95
|
-
|
|
96
|
-
it('should parse this DER format signature in hex', function () {
|
|
97
|
-
var sig = Signature.fromString(buf.toString('hex'))
|
|
98
|
-
sig.r.toBuffer({
|
|
99
|
-
size: 32
|
|
100
|
-
}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277')
|
|
101
|
-
sig.s.toBuffer({
|
|
102
|
-
size: 32
|
|
103
|
-
}).toString('hex').should.equal('729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2')
|
|
104
|
-
})
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
describe('#toTxFormat', function () {
|
|
108
|
-
it('should parse this known signature and rebuild it with updated zero-padded sighash types', function () {
|
|
109
|
-
var original = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e7201'
|
|
110
|
-
var buf = Buffer.from(original, 'hex')
|
|
111
|
-
var sig = Signature.fromTxFormat(buf)
|
|
112
|
-
sig.nhashtype.should.equal(Signature.SIGHASH_ALL)
|
|
113
|
-
sig.set({
|
|
114
|
-
nhashtype: Signature.SIGHASH_ALL | Signature.SIGHASH_ANYONECANPAY
|
|
115
|
-
})
|
|
116
|
-
sig.toTxFormat().toString('hex').should.equal(original.slice(0, -2) + '81')
|
|
117
|
-
sig.set({
|
|
118
|
-
nhashtype: Signature.SIGHASH_SINGLE
|
|
119
|
-
})
|
|
120
|
-
sig.toTxFormat().toString('hex').should.equal(original.slice(0, -2) + '03')
|
|
121
|
-
})
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
describe('#fromTxFormat', function () {
|
|
125
|
-
it('should convert from this known tx-format buffer', function () {
|
|
126
|
-
var buf = Buffer.from('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e7201', 'hex')
|
|
127
|
-
var sig = Signature.fromTxFormat(buf)
|
|
128
|
-
sig.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810')
|
|
129
|
-
sig.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130')
|
|
130
|
-
sig.nhashtype.should.equal(Signature.SIGHASH_ALL)
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
it('should parse this known signature and rebuild it', function () {
|
|
134
|
-
var hex = '3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501'
|
|
135
|
-
var buf = Buffer.from(hex, 'hex')
|
|
136
|
-
var sig = Signature.fromTxFormat(buf)
|
|
137
|
-
sig.toTxFormat().toString('hex').should.equal(hex)
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
describe('#parseDER', function () {
|
|
142
|
-
it('should parse this signature generated in node', function () {
|
|
143
|
-
var sighex = '30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72'
|
|
144
|
-
var sig = Buffer.from(sighex, 'hex')
|
|
145
|
-
var parsed = Signature.parseDER(sig)
|
|
146
|
-
parsed.header.should.equal(0x30)
|
|
147
|
-
parsed.length.should.equal(69)
|
|
148
|
-
parsed.rlength.should.equal(33)
|
|
149
|
-
parsed.rneg.should.equal(true)
|
|
150
|
-
parsed.rbuf.toString('hex').should.equal('008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa')
|
|
151
|
-
parsed.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810')
|
|
152
|
-
parsed.slength.should.equal(32)
|
|
153
|
-
parsed.sneg.should.equal(false)
|
|
154
|
-
parsed.sbuf.toString('hex').should.equal('0993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72')
|
|
155
|
-
parsed.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130')
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
it('should parse this 69 byte signature', function () {
|
|
159
|
-
var sighex = '3043021f59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b50302202f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa'
|
|
160
|
-
var sig = Buffer.from(sighex, 'hex')
|
|
161
|
-
var parsed = Signature.parseDER(sig)
|
|
162
|
-
parsed.header.should.equal(0x30)
|
|
163
|
-
parsed.length.should.equal(67)
|
|
164
|
-
parsed.rlength.should.equal(31)
|
|
165
|
-
parsed.rneg.should.equal(false)
|
|
166
|
-
parsed.rbuf.toString('hex').should.equal('59e4705959cc78acbfcf8bd0114e9cc1b389a4287fb33152b73a38c319b503')
|
|
167
|
-
parsed.r.toString().should.equal('158826015856106182499128681792325160381907915189052224498209222621383996675')
|
|
168
|
-
parsed.slength.should.equal(32)
|
|
169
|
-
parsed.sneg.should.equal(false)
|
|
170
|
-
parsed.sbuf.toString('hex').should.equal('2f7428a27284c757e409bf41506183e9e49dfb54d5063796dfa0d403a4deccfa')
|
|
171
|
-
parsed.s.toString().should.equal('21463938592353267769710297084836796652964571266930856168996063301532842380538')
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('should parse this 68 byte signature', function () {
|
|
175
|
-
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51'
|
|
176
|
-
var sig = Buffer.from(sighex, 'hex')
|
|
177
|
-
var parsed = Signature.parseDER(sig)
|
|
178
|
-
parsed.header.should.equal(0x30)
|
|
179
|
-
parsed.length.should.equal(66)
|
|
180
|
-
parsed.rlength.should.equal(30)
|
|
181
|
-
parsed.rneg.should.equal(false)
|
|
182
|
-
parsed.rbuf.toString('hex').should.equal('17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632')
|
|
183
|
-
parsed.r.toString().should.equal('164345250294671732127776123343329699648286106708464198588053542748255794')
|
|
184
|
-
parsed.slength.should.equal(32)
|
|
185
|
-
parsed.sneg.should.equal(false)
|
|
186
|
-
parsed.sbuf.toString('hex').should.equal('61bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a51')
|
|
187
|
-
parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721')
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('should parse this signature from script_valid.json', function () {
|
|
191
|
-
var sighex = '304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef051'
|
|
192
|
-
var sig = Buffer.from(sighex, 'hex')
|
|
193
|
-
var parsed = Signature.parseDER(sig, false)
|
|
194
|
-
should.exist(parsed)
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('reports sneg for a padded s', function () {
|
|
198
|
-
// 02 21 00ab.. — s carries the pad that clears its high bit.
|
|
199
|
-
var sighex = '304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c' +
|
|
200
|
-
'022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef0'
|
|
201
|
-
var parsed = Signature.parseDER(Buffer.from(sighex, 'hex'))
|
|
202
|
-
parsed.sbuf[0].should.equal(0x00)
|
|
203
|
-
parsed.sneg.should.equal(true)
|
|
204
|
-
parsed.rneg.should.equal(false)
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
describe('#parseDER canonicality', function () {
|
|
209
|
-
// A non-canonical INTEGER is a second encoding of the same number, i.e. a
|
|
210
|
-
// malleable signature. Strict (the default, used by fromDER/fromString and
|
|
211
|
-
// so by credential/proof verification) rejects them; non-strict must keep
|
|
212
|
-
// accepting them, because signatures already on chain are not canonical and
|
|
213
|
-
// fromTxFormat has to parse them.
|
|
214
|
-
var cases = [
|
|
215
|
-
{ name: 'r negative (high bit set, unpadded)', hex: '3006' + '0201ff' + '020101', err: /Value of r is negative/ },
|
|
216
|
-
{ name: 's negative (high bit set, unpadded)', hex: '3006' + '020101' + '0201ff', err: /Value of s is negative/ },
|
|
217
|
-
{ name: 'r excessively padded', hex: '3007' + '02020011' + '020101', err: /Value of r is excessively padded/ },
|
|
218
|
-
{ name: 's excessively padded', hex: '3007' + '020101' + '02020011', err: /Value of s is excessively padded/ },
|
|
219
|
-
{ name: 'r zero-length', hex: '3005' + '0200' + '020101', err: /Length of r is zero/ },
|
|
220
|
-
{ name: 's zero-length', hex: '3005' + '020101' + '0200', err: /Length of s is zero/ }
|
|
221
|
-
]
|
|
222
|
-
|
|
223
|
-
cases.forEach(function (c) {
|
|
224
|
-
it('strict rejects ' + c.name, function () {
|
|
225
|
-
expect(function () {
|
|
226
|
-
Signature.parseDER(Buffer.from(c.hex, 'hex'))
|
|
227
|
-
}).to.throw(c.err)
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('non-strict still accepts ' + c.name + ' (chain compatibility)', function () {
|
|
231
|
-
should.exist(Signature.parseDER(Buffer.from(c.hex, 'hex'), false))
|
|
232
|
-
})
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('strict accepts a minimally padded integer', function () {
|
|
236
|
-
// 0x00ff is canonical: the pad is required to clear the high bit.
|
|
237
|
-
should.exist(Signature.parseDER(Buffer.from('3008020200ff020200ff', 'hex')))
|
|
238
|
-
})
|
|
239
|
-
|
|
240
|
-
it('strict accepts a canonical zero integer', function () {
|
|
241
|
-
should.exist(Signature.parseDER(Buffer.from('3006020100020100', 'hex')))
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
it('rejects a malleated re-encoding while the original still verifies', function () {
|
|
245
|
-
var privkey = new PrivateKey()
|
|
246
|
-
var hashbuf = Hash.sha256(Buffer.from('der canonicality'))
|
|
247
|
-
var sig = ECDSA.sign(hashbuf, privkey)
|
|
248
|
-
var der = sig.toDER()
|
|
249
|
-
|
|
250
|
-
ECDSA.verify(hashbuf, Signature.fromDER(der), privkey.toPublicKey()).should.equal(true)
|
|
251
|
-
|
|
252
|
-
// Re-encode r with one pad byte more than DER allows: same r, different
|
|
253
|
-
// bytes. The extra zero goes on top of the canonical encoding, so this is
|
|
254
|
-
// excess padding whether or not r needed a pad to begin with.
|
|
255
|
-
var canonical = function (nbuf) {
|
|
256
|
-
return (nbuf[0] & 0x80) ? Buffer.concat([Buffer.from([0x00]), nbuf]) : nbuf
|
|
257
|
-
}
|
|
258
|
-
var padded = Buffer.concat([Buffer.from([0x00]), canonical(sig.r.toBuffer())])
|
|
259
|
-
var sEnc = canonical(sig.s.toBuffer())
|
|
260
|
-
var body = Buffer.concat([
|
|
261
|
-
Buffer.from([0x02, padded.length]), padded,
|
|
262
|
-
Buffer.from([0x02, sEnc.length]), sEnc
|
|
263
|
-
])
|
|
264
|
-
var malleated = Buffer.concat([Buffer.from([0x30, body.length]), body])
|
|
265
|
-
|
|
266
|
-
malleated.toString('hex').should.not.equal(der.toString('hex'))
|
|
267
|
-
expect(function () {
|
|
268
|
-
Signature.fromDER(malleated)
|
|
269
|
-
}).to.throw(/excessively padded/)
|
|
270
|
-
// The same bytes remain parseable on the non-strict (chain) path.
|
|
271
|
-
Signature.parseDER(malleated, false).r.eq(sig.r).should.equal(true)
|
|
272
|
-
})
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
describe('#toDER', function () {
|
|
276
|
-
it('should convert these known r and s values into a known signature', function () {
|
|
277
|
-
var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810')
|
|
278
|
-
var s = new BN('4331694221846364448463828256391194279133231453999942381442030409253074198130')
|
|
279
|
-
var sig = new Signature({
|
|
280
|
-
r: r,
|
|
281
|
-
s: s
|
|
282
|
-
})
|
|
283
|
-
var der = sig.toDER(r, s)
|
|
284
|
-
der.toString('hex').should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72')
|
|
285
|
-
})
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
describe('#toString', function () {
|
|
289
|
-
it('should convert this signature in to hex DER', function () {
|
|
290
|
-
var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810')
|
|
291
|
-
var s = new BN('4331694221846364448463828256391194279133231453999942381442030409253074198130')
|
|
292
|
-
var sig = new Signature({
|
|
293
|
-
r: r,
|
|
294
|
-
s: s
|
|
295
|
-
})
|
|
296
|
-
var hex = sig.toString()
|
|
297
|
-
hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72')
|
|
298
|
-
})
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
describe('@isTxDER', function () {
|
|
302
|
-
it('should know this is a DER signature', function () {
|
|
303
|
-
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101'
|
|
304
|
-
var sigbuf = Buffer.from(sighex, 'hex')
|
|
305
|
-
Signature.isTxDER(sigbuf).should.equal(true)
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
it('should know this is not a DER signature', function () {
|
|
309
|
-
// for more extensive tests, see the script interpreter
|
|
310
|
-
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101'
|
|
311
|
-
var sigbuf = Buffer.from(sighex, 'hex')
|
|
312
|
-
sigbuf[0] = 0x31
|
|
313
|
-
Signature.isTxDER(sigbuf).should.equal(false)
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
describe('bitcoind fixtures', function () {
|
|
317
|
-
var testSigs = function (set, expected) {
|
|
318
|
-
var i = 0
|
|
319
|
-
set.forEach(function (vector) {
|
|
320
|
-
if (!JSUtil.isHexa(vector)) {
|
|
321
|
-
// non-hex strings are ignored
|
|
322
|
-
return
|
|
323
|
-
}
|
|
324
|
-
it('should be ' + (expected ? '' : 'in') + 'valid for fixture #' + i, function () {
|
|
325
|
-
var sighex = vector
|
|
326
|
-
var interp = Interpreter()
|
|
327
|
-
interp.flags = Interpreter.SCRIPT_VERIFY_DERSIG |
|
|
328
|
-
Interpreter.SCRIPT_VERIFY_STRICTENC
|
|
329
|
-
var result = interp.checkSignatureEncoding(Buffer.from(sighex, 'hex'))
|
|
330
|
-
result.should.equal(expected)
|
|
331
|
-
})
|
|
332
|
-
i++
|
|
333
|
-
})
|
|
334
|
-
}
|
|
335
|
-
testSigs(sigCanonical, true)
|
|
336
|
-
testSigs(sigNonCanonical, false)
|
|
337
|
-
})
|
|
338
|
-
})
|
|
339
|
-
describe('#hasLowS', function () {
|
|
340
|
-
it('should detect high and low S', function () {
|
|
341
|
-
var r = new BN('63173831029936981022572627018246571655303050627048489594159321588908385378810')
|
|
342
|
-
|
|
343
|
-
var sig = new Signature({
|
|
344
|
-
r: r,
|
|
345
|
-
s: new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A1', 'hex')
|
|
346
|
-
})
|
|
347
|
-
sig.hasLowS().should.equal(false)
|
|
348
|
-
|
|
349
|
-
var sig2 = new Signature({
|
|
350
|
-
r: r,
|
|
351
|
-
s: new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0', 'hex')
|
|
352
|
-
})
|
|
353
|
-
sig2.hasLowS().should.equal(true)
|
|
354
|
-
|
|
355
|
-
var sig3 = new Signature({
|
|
356
|
-
r: r,
|
|
357
|
-
s: new BN(1)
|
|
358
|
-
})
|
|
359
|
-
sig3.hasLowS().should.equal(true)
|
|
360
|
-
|
|
361
|
-
var sig4 = new Signature({
|
|
362
|
-
r: r,
|
|
363
|
-
s: new BN(0)
|
|
364
|
-
})
|
|
365
|
-
sig4.hasLowS().should.equal(false)
|
|
366
|
-
})
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
describe('#hasDefinedHashtype', function () {
|
|
370
|
-
it('should reject invalid sighash types and accept valid ones', function () {
|
|
371
|
-
var sig = new Signature()
|
|
372
|
-
sig.hasDefinedHashtype().should.equal(false)
|
|
373
|
-
var testCases = [
|
|
374
|
-
[undefined, false],
|
|
375
|
-
[null, false],
|
|
376
|
-
[0, false],
|
|
377
|
-
[1.1, false],
|
|
378
|
-
[-1, false],
|
|
379
|
-
[-1.1, false],
|
|
380
|
-
['', false],
|
|
381
|
-
['1', false],
|
|
382
|
-
[Signature.SIGHASH_ANYONECANPAY, false],
|
|
383
|
-
[Signature.SIGHASH_ANYONECANPAY | Signature.SIGHASH_ALL, true],
|
|
384
|
-
[Signature.SIGHASH_ANYONECANPAY | Signature.SIGHASH_NONE, true],
|
|
385
|
-
[Signature.SIGHASH_ANYONECANPAY | Signature.SIGHASH_SINGLE, true],
|
|
386
|
-
[Signature.SIGHASH_ALL, true],
|
|
387
|
-
[Signature.SIGHASH_NONE, true],
|
|
388
|
-
[Signature.SIGHASH_SINGLE, true],
|
|
389
|
-
[Signature.SIGHASH_SINGLE + 1, false],
|
|
390
|
-
[(Signature.SIGHASH_ANYONECANPAY | Signature.SIGHASH_SINGLE) + 1, false],
|
|
391
|
-
[(Signature.SIGHASH_ANYONECANPAY | Signature.SIGHASH_ALL) - 1, false]
|
|
392
|
-
]
|
|
393
|
-
_.each(testCases, function (testCase) {
|
|
394
|
-
sig.nhashtype = testCase[0]
|
|
395
|
-
sig.hasDefinedHashtype().should.equal(testCase[1])
|
|
396
|
-
})
|
|
397
|
-
})
|
|
398
|
-
})
|
|
399
|
-
})
|