@smartledger/bsv 7.12.0 → 7.13.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 +72 -0
- package/README.md +38 -38
- package/bsv-didweb.min.js +6 -6
- package/bsv-gdaf.min.js +58 -58
- package/bsv-ltp.min.js +41 -41
- package/bsv-mnemonic.min.js +8 -8
- package/bsv-smartcontract.min.js +1 -1
- package/bsv.bundle.js +58 -58
- package/bsv.min.js +58 -58
- package/docs/AUDIT_SCOPE.md +18 -19
- 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/index.js +10 -2
- package/lib/crypto/hash.browser.js +13 -5
- package/lib/crypto/hash.js +58 -2
- package/lib/crypto/random.js +72 -15
- package/lib/didweb/index.js +88 -9
- package/lib/encoding/base58.js +67 -3
- package/lib/mnemonic/mnemonic.js +19 -5
- package/lib/mnemonic/pbkdf2.js +35 -2
- package/lib/script/interpreter.js +21 -2
- package/lib/transaction/input/multisig.js +1 -1
- package/lib/transaction/input/multisigscripthash.js +1 -1
- package/lib/transaction/input/publickey.js +1 -1
- package/lib/transaction/input/publickeyhash.js +1 -1
- package/lib/transaction/signature.js +1 -1
- package/lib/util/inherits.js +37 -0
- package/package.json +4 -8
- package/test/crypto/backend_selection.js +126 -0
- package/test/crypto/random.js +73 -0
- package/test/didweb/relationships.js +144 -0
- package/test/transaction/unspentoutput.js +6 -1
- package/version.js +1 -1
package/lib/didweb/index.js
CHANGED
|
@@ -58,6 +58,44 @@ async function generateIssuerKeys(opts) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// The five verification relationships DID Core defines. They exist to be DISTINCT:
|
|
62
|
+
// `authentication` proves who you are right now, `assertionMethod` makes a claim that
|
|
63
|
+
// outlives the session. Keeping them apart is what stops a low-friction login signature
|
|
64
|
+
// from being redeemed as a signed statement.
|
|
65
|
+
//
|
|
66
|
+
// This module used to cross-list every key into both, which erased the distinction and
|
|
67
|
+
// left a caller unable to express "this key may log in but may not make assertions".
|
|
68
|
+
var VERIFICATION_RELATIONSHIPS = [
|
|
69
|
+
'authentication',
|
|
70
|
+
'assertionMethod',
|
|
71
|
+
'keyAgreement',
|
|
72
|
+
'capabilityInvocation',
|
|
73
|
+
'capabilityDelegation'
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
// Preserves the historical output when a key does not say otherwise.
|
|
77
|
+
var DEFAULT_RELATIONSHIPS = ['authentication', 'assertionMethod']
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Resolve the verification relationships for a key descriptor.
|
|
81
|
+
*
|
|
82
|
+
* @param {object} key - `{ kid, jwk, relationships? }`
|
|
83
|
+
* @returns {Array<string>}
|
|
84
|
+
*/
|
|
85
|
+
function relationshipsFor (key) {
|
|
86
|
+
var rels = key.relationships || DEFAULT_RELATIONSHIPS
|
|
87
|
+
if (!Array.isArray(rels) || rels.length === 0) {
|
|
88
|
+
throw new Error('relationships must be a non-empty array of DID Core verification relationships')
|
|
89
|
+
}
|
|
90
|
+
rels.forEach(function (rel) {
|
|
91
|
+
if (VERIFICATION_RELATIONSHIPS.indexOf(rel) === -1) {
|
|
92
|
+
throw new Error('unknown verification relationship: ' + rel +
|
|
93
|
+
' (expected one of ' + VERIFICATION_RELATIONSHIPS.join(', ') + ')')
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
return rels
|
|
97
|
+
}
|
|
98
|
+
|
|
61
99
|
// Build did:web documents (did.json and jwks.json)
|
|
62
100
|
function buildDidWebDocuments(params) {
|
|
63
101
|
if (!params.domain) {
|
|
@@ -68,6 +106,7 @@ function buildDidWebDocuments(params) {
|
|
|
68
106
|
var did = 'did:web:' + domain.replace(/:/g, '%3A')
|
|
69
107
|
|
|
70
108
|
var verificationMethods = []
|
|
109
|
+
var methodRelationships = []
|
|
71
110
|
var publicKeys = []
|
|
72
111
|
|
|
73
112
|
// Add P-256 key if provided
|
|
@@ -79,6 +118,7 @@ function buildDidWebDocuments(params) {
|
|
|
79
118
|
publicKeyJwk: params.p256.jwk
|
|
80
119
|
}
|
|
81
120
|
verificationMethods.push(p256Method)
|
|
121
|
+
methodRelationships.push(relationshipsFor(params.p256))
|
|
82
122
|
publicKeys.push(params.p256.jwk)
|
|
83
123
|
}
|
|
84
124
|
|
|
@@ -91,6 +131,7 @@ function buildDidWebDocuments(params) {
|
|
|
91
131
|
publicKeyJwk: params.k1.jwk
|
|
92
132
|
}
|
|
93
133
|
verificationMethods.push(k1Method)
|
|
134
|
+
methodRelationships.push(relationshipsFor(params.k1))
|
|
94
135
|
publicKeys.push(params.k1.jwk)
|
|
95
136
|
}
|
|
96
137
|
|
|
@@ -105,11 +146,19 @@ function buildDidWebDocuments(params) {
|
|
|
105
146
|
'https://w3id.org/security/suites/jws-2020/v1'
|
|
106
147
|
],
|
|
107
148
|
id: did,
|
|
108
|
-
verificationMethod: verificationMethods
|
|
109
|
-
authentication: verificationMethods.map(function(vm) { return vm.id }),
|
|
110
|
-
assertionMethod: verificationMethods.map(function(vm) { return vm.id })
|
|
149
|
+
verificationMethod: verificationMethods
|
|
111
150
|
}
|
|
112
151
|
|
|
152
|
+
// Scope each key to the relationships it was given. Omitting `relationships`
|
|
153
|
+
// reproduces the previous output exactly (every key in both authentication and
|
|
154
|
+
// assertionMethod), so existing callers are unaffected.
|
|
155
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
156
|
+
var ids = verificationMethods
|
|
157
|
+
.filter(function (vm, i) { return methodRelationships[i].indexOf(rel) !== -1 })
|
|
158
|
+
.map(function (vm) { return vm.id })
|
|
159
|
+
if (ids.length) didDocument[rel] = ids
|
|
160
|
+
})
|
|
161
|
+
|
|
113
162
|
if (params.controllerName) {
|
|
114
163
|
didDocument.controller = params.controllerName
|
|
115
164
|
}
|
|
@@ -144,6 +193,11 @@ function rotateIssuerKey(params) {
|
|
|
144
193
|
publicKeyJwk: params.newKey.jwk
|
|
145
194
|
}
|
|
146
195
|
|
|
196
|
+
// Which relationships this rotation actually replaces. Defaults to both, which is
|
|
197
|
+
// the historical behaviour.
|
|
198
|
+
var rotating = relationshipsFor(params.newKey)
|
|
199
|
+
var current = params.currentDocument || null
|
|
200
|
+
|
|
147
201
|
// Build updated DID Document with new key as primary
|
|
148
202
|
var didDocument = {
|
|
149
203
|
'@context': [
|
|
@@ -151,13 +205,38 @@ function rotateIssuerKey(params) {
|
|
|
151
205
|
'https://w3id.org/security/suites/jws-2020/v1'
|
|
152
206
|
],
|
|
153
207
|
id: did,
|
|
154
|
-
verificationMethod: [newMethod]
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
208
|
+
verificationMethod: [newMethod]
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// A rotation must not silently reach beyond the relationship being rotated.
|
|
212
|
+
// Replacing a compromised `authentication` key used to rewrite `assertionMethod`
|
|
213
|
+
// too, which invalidates every statement the issuer has ever signed. Relationships
|
|
214
|
+
// outside `rotating` are carried across from `currentDocument` untouched; without a
|
|
215
|
+
// `currentDocument` there is nothing to carry, and the result matches the old output.
|
|
216
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
217
|
+
if (rotating.indexOf(rel) !== -1) {
|
|
218
|
+
didDocument[rel] = [newMethod.id]
|
|
219
|
+
} else if (current && Array.isArray(current[rel]) && current[rel].length) {
|
|
220
|
+
didDocument[rel] = current[rel].slice()
|
|
160
221
|
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// Keep the verification methods those carried-over relationships still point at,
|
|
225
|
+
// otherwise the document references key ids it does not define. The rotated-out key
|
|
226
|
+
// drops out here automatically: nothing references it any more.
|
|
227
|
+
if (current && Array.isArray(current.verificationMethod)) {
|
|
228
|
+
var referenced = {}
|
|
229
|
+
VERIFICATION_RELATIONSHIPS.forEach(function (rel) {
|
|
230
|
+
(didDocument[rel] || []).forEach(function (id) { referenced[id] = true })
|
|
231
|
+
})
|
|
232
|
+
current.verificationMethod.forEach(function (vm) {
|
|
233
|
+
if (referenced[vm.id] && vm.id !== newMethod.id) didDocument.verificationMethod.push(vm)
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
didDocument.rotationInfo = {
|
|
238
|
+
rotatedAt: new Date().toISOString(),
|
|
239
|
+
gracePeriodDays: keepOldForDays
|
|
161
240
|
}
|
|
162
241
|
|
|
163
242
|
var jwks = {
|
package/lib/encoding/base58.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
var _ = require('../util/_')
|
|
4
|
-
var bs58 = require('bs58')
|
|
5
4
|
var buffer = require('buffer')
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -11,6 +10,71 @@ var buffer = require('buffer')
|
|
|
11
10
|
*/
|
|
12
11
|
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'.split('')
|
|
13
12
|
|
|
13
|
+
// Reverse lookup for decoding. Built from ALPHABET so the two cannot disagree.
|
|
14
|
+
var ALPHABET_MAP = {}
|
|
15
|
+
for (var _i = 0; _i < ALPHABET.length; _i++) ALPHABET_MAP[ALPHABET[_i]] = _i
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base58 encode/decode, replacing the `bs58` dependency (which also pulled `base-x`).
|
|
19
|
+
*
|
|
20
|
+
* This is the standard big-number-in-base-58 conversion: repeatedly multiply the
|
|
21
|
+
* accumulator by 256 (encode) or 58 (decode) and carry. Two details are where
|
|
22
|
+
* implementations usually go wrong, and both are exercised by the differential test in
|
|
23
|
+
* test/encoding/base58.js:
|
|
24
|
+
*
|
|
25
|
+
* - Leading zero bytes are NOT part of the number — they are significant and each one
|
|
26
|
+
* maps to a literal '1'. Dropping them silently truncates an address.
|
|
27
|
+
* - The carry loops must run to completion before the next digit, or long inputs lose
|
|
28
|
+
* the high-order bytes.
|
|
29
|
+
*
|
|
30
|
+
* These functions encode and decode addresses and WIF private keys, so they were
|
|
31
|
+
* verified by differential test against bs58 across 20,000 random inputs — including
|
|
32
|
+
* every leading-zero-count from 0 to 8 — before the dependency was removed.
|
|
33
|
+
*/
|
|
34
|
+
function base58encode (buf) {
|
|
35
|
+
if (buf.length === 0) return ''
|
|
36
|
+
var digits = [0]
|
|
37
|
+
var i, j, carry
|
|
38
|
+
for (i = 0; i < buf.length; i++) {
|
|
39
|
+
for (j = 0, carry = buf[i]; j < digits.length; j++) {
|
|
40
|
+
carry += digits[j] << 8
|
|
41
|
+
digits[j] = carry % 58
|
|
42
|
+
carry = (carry / 58) | 0
|
|
43
|
+
}
|
|
44
|
+
while (carry > 0) {
|
|
45
|
+
digits.push(carry % 58)
|
|
46
|
+
carry = (carry / 58) | 0
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
var out = ''
|
|
50
|
+
// Each leading zero byte is a significant '1', not part of the number.
|
|
51
|
+
for (i = 0; buf[i] === 0 && i < buf.length - 1; i++) out += ALPHABET[0]
|
|
52
|
+
for (i = digits.length - 1; i >= 0; i--) out += ALPHABET[digits[i]]
|
|
53
|
+
return out
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function base58decode (str) {
|
|
57
|
+
if (str.length === 0) return Buffer.alloc(0)
|
|
58
|
+
var bytes = [0]
|
|
59
|
+
var i, j, carry
|
|
60
|
+
for (i = 0; i < str.length; i++) {
|
|
61
|
+
var value = ALPHABET_MAP[str[i]]
|
|
62
|
+
if (value === undefined) throw new Error('Non-base58 character')
|
|
63
|
+
for (j = 0, carry = value; j < bytes.length; j++) {
|
|
64
|
+
carry += bytes[j] * 58
|
|
65
|
+
bytes[j] = carry & 0xff
|
|
66
|
+
carry >>= 8
|
|
67
|
+
}
|
|
68
|
+
while (carry > 0) {
|
|
69
|
+
bytes.push(carry & 0xff)
|
|
70
|
+
carry >>= 8
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Mirror of the encode side: each leading '1' is a zero byte.
|
|
74
|
+
for (i = 0; str[i] === ALPHABET[0] && i < str.length - 1; i++) bytes.push(0)
|
|
75
|
+
return Buffer.from(bytes.reverse())
|
|
76
|
+
}
|
|
77
|
+
|
|
14
78
|
/**
|
|
15
79
|
* A Base58 object can encode/decoded Base 58, which is used primarily for
|
|
16
80
|
* string-formatted Bitcoin addresses and private keys. Addresses and private
|
|
@@ -54,7 +118,7 @@ Base58.encode = function (buf) {
|
|
|
54
118
|
if (!buffer.Buffer.isBuffer(buf)) {
|
|
55
119
|
throw new Error('Input should be a buffer')
|
|
56
120
|
}
|
|
57
|
-
return
|
|
121
|
+
return base58encode(buf)
|
|
58
122
|
}
|
|
59
123
|
|
|
60
124
|
/**
|
|
@@ -67,7 +131,7 @@ Base58.decode = function (str) {
|
|
|
67
131
|
if (typeof str !== 'string') {
|
|
68
132
|
throw new Error('Input should be a string')
|
|
69
133
|
}
|
|
70
|
-
return
|
|
134
|
+
return base58decode(str)
|
|
71
135
|
}
|
|
72
136
|
|
|
73
137
|
Base58.prototype.fromBuffer = function (buf) {
|
package/lib/mnemonic/mnemonic.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var HDPrivateKey = require('../hdprivatekey')
|
|
4
4
|
var BN = require('../crypto/bn')
|
|
5
|
-
var unorm = require('unorm')
|
|
6
5
|
var _ = require('../util/_')
|
|
7
6
|
|
|
8
7
|
var pbkdf2 = require('./pbkdf2')
|
|
@@ -16,6 +15,21 @@ var Random = require('../crypto/random')
|
|
|
16
15
|
|
|
17
16
|
var $ = require('../util/preconditions')
|
|
18
17
|
|
|
18
|
+
/**
|
|
19
|
+
* NFKD-normalise a mnemonic or passphrase, as BIP-39 requires before PBKDF2.
|
|
20
|
+
*
|
|
21
|
+
* Replaces the `unorm` dependency: `String.prototype.normalize` has been in the language
|
|
22
|
+
* since ES6 and this package requires Node >= 20.19, so the shim was redundant.
|
|
23
|
+
*
|
|
24
|
+
* Non-strings return '' so that `Mnemonic.isValid(garbage)` answers false instead of
|
|
25
|
+
* throwing. unorm was inconsistent about this — `[]` and `{}` returned '' while `['a']`
|
|
26
|
+
* and a Buffer threw — and a validity predicate that fails closed for every non-string
|
|
27
|
+
* is both the safer answer and the coherent one.
|
|
28
|
+
*/
|
|
29
|
+
function nfkd (str) {
|
|
30
|
+
return typeof str === 'string' ? str.normalize('NFKD') : ''
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
/**
|
|
20
34
|
* This is an immutable class that represents a BIP39 Mnemonic code.
|
|
21
35
|
* See BIP39 specification for more info: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
|
@@ -52,7 +66,7 @@ var Mnemonic = function (data, wordlist) {
|
|
|
52
66
|
if (Buffer.isBuffer(data)) {
|
|
53
67
|
seed = data
|
|
54
68
|
} else if (_.isString(data)) {
|
|
55
|
-
phrase =
|
|
69
|
+
phrase = nfkd(data)
|
|
56
70
|
} else if (_.isNumber(data)) {
|
|
57
71
|
ent = data
|
|
58
72
|
} else if (data) {
|
|
@@ -145,7 +159,7 @@ Mnemonic.Words = require('./words')
|
|
|
145
159
|
* @returns {boolean}
|
|
146
160
|
*/
|
|
147
161
|
Mnemonic.isValid = function (mnemonic, wordlist) {
|
|
148
|
-
mnemonic =
|
|
162
|
+
mnemonic = nfkd(mnemonic)
|
|
149
163
|
wordlist = wordlist || Mnemonic._getDictionary(mnemonic)
|
|
150
164
|
|
|
151
165
|
if (!wordlist) {
|
|
@@ -179,7 +193,7 @@ Mnemonic.isValid = function (mnemonic, wordlist) {
|
|
|
179
193
|
* @returns {boolean}
|
|
180
194
|
*/
|
|
181
195
|
Mnemonic._belongsToWordlist = function (mnemonic, wordlist) {
|
|
182
|
-
var words =
|
|
196
|
+
var words = nfkd(mnemonic).split(' ')
|
|
183
197
|
for (var i = 0; i < words.length; i++) {
|
|
184
198
|
var ind = wordlist.indexOf(words[i])
|
|
185
199
|
if (ind < 0) return false
|
|
@@ -217,7 +231,7 @@ Mnemonic._getDictionary = function (mnemonic) {
|
|
|
217
231
|
*/
|
|
218
232
|
Mnemonic.prototype.toSeed = function (passphrase) {
|
|
219
233
|
passphrase = passphrase || ''
|
|
220
|
-
return pbkdf2(
|
|
234
|
+
return pbkdf2(nfkd(this.phrase), nfkd('mnemonic' + passphrase), 2048, 64)
|
|
221
235
|
}
|
|
222
236
|
|
|
223
237
|
/**
|
package/lib/mnemonic/pbkdf2.js
CHANGED
|
@@ -1,2 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Pick the PBKDF2 backend by CAPABILITY, not by `process.browser`. Same reasoning as
|
|
4
|
+
// lib/crypto/hash.js: that flag is undefined in React Native, Deno, Workers and Bun, so
|
|
5
|
+
// all of them took the node branch and required('crypto') — failing outright, or
|
|
6
|
+
// resolving to a partial shim.
|
|
7
|
+
//
|
|
8
|
+
// The node variant needs `createHmac('sha512')`, so that is what is probed, and the
|
|
9
|
+
// result is checked against RFC 4231 test case 1 rather than merely existing. A shim
|
|
10
|
+
// that returns a wrong or stubbed HMAC would otherwise silently corrupt every seed
|
|
11
|
+
// derived from a mnemonic — and unlike a crash, that failure is invisible until funds
|
|
12
|
+
// are missing. The pure-JS fallback is audited and always safe.
|
|
13
|
+
|
|
14
|
+
var RFC4231_CASE1 = '87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854'
|
|
15
|
+
|
|
16
|
+
function nodeHmacIsUsable () {
|
|
17
|
+
var crypto
|
|
18
|
+
try {
|
|
19
|
+
crypto = require('crypto')
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
if (!crypto || typeof crypto.createHmac !== 'function') return false
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
var mac = crypto.createHmac('sha512', Buffer.alloc(20, 0x0b))
|
|
27
|
+
.update('Hi There')
|
|
28
|
+
.digest('hex')
|
|
29
|
+
return mac === RFC4231_CASE1
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = nodeHmacIsUsable() ? require('./pbkdf2.node') : require('./pbkdf2.browser')
|
|
@@ -8,7 +8,26 @@ var BN = require('../crypto/bn')
|
|
|
8
8
|
var Hash = require('../crypto/hash')
|
|
9
9
|
var Signature = require('../crypto/signature')
|
|
10
10
|
var PublicKey = require('../publickey')
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Copy a script stack for the step listener, so a listener that mutates what it is
|
|
13
|
+
* handed cannot corrupt the running evaluation.
|
|
14
|
+
*
|
|
15
|
+
* Replaces the `clone-deep` dependency. The stack is an array of Buffers — every one of
|
|
16
|
+
* the 32 push sites in this file pushes a Buffer — so a shallow map with `Buffer.from`
|
|
17
|
+
* reproduces `cloneDeep(stack, true)` exactly for this input: same bytes, new backing
|
|
18
|
+
* memory, `Buffer.isBuffer` still true, empty buffers preserved.
|
|
19
|
+
*
|
|
20
|
+
* `structuredClone` is NOT a substitute: it returns Uint8Array for a Buffer input, which
|
|
21
|
+
* would silently change the type handed to every existing stepListener.
|
|
22
|
+
*
|
|
23
|
+
* Anything non-Buffer is passed through by reference. Nothing puts such a value on the
|
|
24
|
+
* stack today; if something ever does it will be a primitive, where sharing is harmless.
|
|
25
|
+
*/
|
|
26
|
+
function cloneStack (stack) {
|
|
27
|
+
return stack.map(function (item) {
|
|
28
|
+
return Buffer.isBuffer(item) ? Buffer.from(item) : item
|
|
29
|
+
})
|
|
30
|
+
}
|
|
12
31
|
|
|
13
32
|
/**
|
|
14
33
|
* Bitcoin transactions contain scripts. Each input has a script called the
|
|
@@ -653,7 +672,7 @@ Interpreter.prototype.evaluate = function () {
|
|
|
653
672
|
Interpreter.prototype._callbackStep = function (thisStep) {
|
|
654
673
|
if (typeof this.stepListener === 'function') {
|
|
655
674
|
try {
|
|
656
|
-
this.stepListener(thisStep,
|
|
675
|
+
this.stepListener(thisStep, cloneStack(this.stack), cloneStack(this.altstack))
|
|
657
676
|
} catch (err) {
|
|
658
677
|
console.log(`Error in Step callback:${err}`)
|
|
659
678
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Set up prototypal inheritance between two constructors.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the `inherits` dependency, whose modern branch this reproduces exactly:
|
|
7
|
+
* `ctor.super_` is assigned and `ctor.prototype` is REPLACED with an object whose
|
|
8
|
+
* prototype is `superCtor.prototype`.
|
|
9
|
+
*
|
|
10
|
+
* Two details are load-bearing and must not be "simplified" into
|
|
11
|
+
* `Object.setPrototypeOf(ctor.prototype, superCtor.prototype)`:
|
|
12
|
+
*
|
|
13
|
+
* - `constructor` is redefined as non-enumerable. A plain assignment would make it
|
|
14
|
+
* enumerable, so it would start showing up in `for…in` and in anything that walks
|
|
15
|
+
* own+inherited keys — this library's `toObject`/`toJSON` paths among them.
|
|
16
|
+
* - `ctor.super_` is part of the observable surface; `TransactionSignature` and the
|
|
17
|
+
* four Input subclasses are public classes, so anything reading `.super_` today
|
|
18
|
+
* keeps working.
|
|
19
|
+
*
|
|
20
|
+
* The package also carried an `Object.create`-less fallback for pre-ES5 browsers. That
|
|
21
|
+
* branch is unreachable on Node >= 20.19 and in every browser this library targets.
|
|
22
|
+
*
|
|
23
|
+
* @param {Function} ctor - the subclass constructor
|
|
24
|
+
* @param {Function} superCtor - the superclass constructor
|
|
25
|
+
*/
|
|
26
|
+
module.exports = function inherits (ctor, superCtor) {
|
|
27
|
+
if (!superCtor) return
|
|
28
|
+
ctor.super_ = superCtor
|
|
29
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
|
30
|
+
constructor: {
|
|
31
|
+
value: ctor,
|
|
32
|
+
enumerable: false,
|
|
33
|
+
writable: true,
|
|
34
|
+
configurable: true
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smartledger/bsv",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.13.0",
|
|
4
4
|
"description": "🚀 Complete Bitcoin SV development framework with legally-recognizable DID:web + W3C VC-JWT toolkit, Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF), StatusList2021 revocation, and 16 flexible loading options. Standards-based credentials with ES256/ES256K support, on-chain BSV anchoring, and comprehensive Bitcoin SV API. Perfect for legal tokens, verifiable credentials, DeFi, smart contracts, and secure Bitcoin applications.",
|
|
5
5
|
"author": "SmartLedger Technology <hello@smartledger.technology> (https://smartledger.technology)",
|
|
6
6
|
"homepage": "https://github.com/codenlighten/smartledger-bsv#readme",
|
|
@@ -231,12 +231,7 @@
|
|
|
231
231
|
"@noble/curves": "2.2.0",
|
|
232
232
|
"@noble/hashes": "2.2.0",
|
|
233
233
|
"bn.js": "=4.12.5",
|
|
234
|
-
"
|
|
235
|
-
"clone-deep": "^4.0.1",
|
|
236
|
-
"hash.js": "^1.1.7",
|
|
237
|
-
"inherits": "2.0.4",
|
|
238
|
-
"secrets.js-grempe": "^2.0.0",
|
|
239
|
-
"unorm": "1.6.0"
|
|
234
|
+
"secrets.js-grempe": "^2.0.0"
|
|
240
235
|
},
|
|
241
236
|
"devDependencies": {
|
|
242
237
|
"assert": "^2.1.0",
|
|
@@ -277,7 +272,8 @@
|
|
|
277
272
|
"before",
|
|
278
273
|
"beforeEach",
|
|
279
274
|
"describe",
|
|
280
|
-
"it"
|
|
275
|
+
"it",
|
|
276
|
+
"globalThis"
|
|
281
277
|
],
|
|
282
278
|
"ignore": [
|
|
283
279
|
"dist/**",
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* global describe, it */
|
|
4
|
+
|
|
5
|
+
// The hashing and PBKDF2 backends are chosen by CAPABILITY, not by `process.browser`.
|
|
6
|
+
//
|
|
7
|
+
// `process.browser` is a Browserify-era convention, undefined in React Native, Deno,
|
|
8
|
+
// Cloudflare Workers and Bun. Every one of those took the node branch and
|
|
9
|
+
// require('crypto')'d, which fails outright in React Native or — worse — resolves to a
|
|
10
|
+
// partial shim. rn-nodeify and several RN starter templates register exactly such a
|
|
11
|
+
// shim, so this is a reported failure mode, not a hypothetical one.
|
|
12
|
+
//
|
|
13
|
+
// Each case runs in a child process because it has to intercept module resolution
|
|
14
|
+
// before lib/ is loaded, which cannot be undone safely in-process.
|
|
15
|
+
|
|
16
|
+
require('chai').should()
|
|
17
|
+
var path = require('path')
|
|
18
|
+
var spawnSync = require('child_process').spawnSync
|
|
19
|
+
|
|
20
|
+
var ROOT = path.resolve(__dirname, '../..')
|
|
21
|
+
var SHA256_ABC = 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
|
|
22
|
+
|
|
23
|
+
// Run `body` with Module._load intercepted by `stub`, and print the result of `report`.
|
|
24
|
+
function inChild (stub, report) {
|
|
25
|
+
var src = [
|
|
26
|
+
'var Module = require("module"), orig = Module._load',
|
|
27
|
+
'var real = orig.call(Module, "crypto", module, false)',
|
|
28
|
+
'Module._load = function (r) {',
|
|
29
|
+
' if (r === "crypto") { ' + stub + ' }',
|
|
30
|
+
' return orig.apply(this, arguments)',
|
|
31
|
+
'}',
|
|
32
|
+
// Only the pure-JS backend is loaded for comparison. hash.node.js require()s
|
|
33
|
+
// 'crypto' at module scope, so it is not even loadable when the stub removes it —
|
|
34
|
+
// which is precisely why hash.js must not reach for it unconditionally.
|
|
35
|
+
'var HASH = require(' + JSON.stringify(path.join(ROOT, 'lib/crypto/hash.js')) + ')',
|
|
36
|
+
'var PURE = require(' + JSON.stringify(path.join(ROOT, 'lib/crypto/hash.browser.js')) + ')',
|
|
37
|
+
'console.log(' + report + ')'
|
|
38
|
+
].join('\n')
|
|
39
|
+
var out = spawnSync(process.execPath, ['-e', src], { encoding: 'utf8' })
|
|
40
|
+
if (out.status !== 0) throw new Error('child failed: ' + out.stderr)
|
|
41
|
+
return out.stdout.trim()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe('crypto backend selection', function () {
|
|
45
|
+
this.timeout(30000)
|
|
46
|
+
|
|
47
|
+
it('uses node crypto when it is present and correct', function () {
|
|
48
|
+
// Not the pure-JS module => the node backend was selected.
|
|
49
|
+
inChild('return real', '(HASH !== PURE) + " " + HASH.sha256(Buffer.from("abc")).toString("hex")')
|
|
50
|
+
.should.equal('true ' + SHA256_ABC)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('falls back to the pure-JS backend when there is no crypto module', function () {
|
|
54
|
+
inChild('var e = new Error("no crypto"); e.code = "MODULE_NOT_FOUND"; throw e',
|
|
55
|
+
'(HASH === PURE) + " " + HASH.sha256(Buffer.from("abc")).toString("hex")')
|
|
56
|
+
.should.equal('true ' + SHA256_ABC)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// The important one. A shim that EXISTS but computes the wrong digest must be
|
|
60
|
+
// rejected — trusting it would silently corrupt every hash in the library, and a
|
|
61
|
+
// wrong hash is invisible until it matters.
|
|
62
|
+
it('rejects a crypto shim that returns incorrect digests', function () {
|
|
63
|
+
inChild(
|
|
64
|
+
'return { createHash: function () { return { update: function () { return this }, ' +
|
|
65
|
+
'digest: function () { return "deadbeef" } } } }',
|
|
66
|
+
'(HASH === PURE) + " " + HASH.sha256(Buffer.from("abc")).toString("hex")')
|
|
67
|
+
.should.equal('true ' + SHA256_ABC)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// OpenSSL 3 moved ripemd160 to the legacy provider, so some Node builds have sha256
|
|
71
|
+
// but throw on ripemd160 — and ripemd160 is what addresses are built from.
|
|
72
|
+
it('rejects a crypto build without ripemd160', function () {
|
|
73
|
+
inChild(
|
|
74
|
+
'return Object.assign({}, real, { createHash: function (a) { ' +
|
|
75
|
+
'if (a === "ripemd160") throw new Error("Digest method not supported"); ' +
|
|
76
|
+
'return real.createHash(a) } })',
|
|
77
|
+
'(HASH === PURE) + " " + HASH.sha256ripemd160(Buffer.from("abc")).toString("hex").slice(0, 16)')
|
|
78
|
+
.should.equal('true bb1be98c142444d7')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('does not consult process.browser', function () {
|
|
82
|
+
var src = [
|
|
83
|
+
'process.browser = undefined',
|
|
84
|
+
'var a = require(' + JSON.stringify(path.join(ROOT, 'lib/crypto/hash.js')) + ')',
|
|
85
|
+
'console.log(a.sha256(Buffer.from("abc")).toString("hex"))'
|
|
86
|
+
].join('\n')
|
|
87
|
+
var withoutFlag = spawnSync(process.execPath, ['-e', src], { encoding: 'utf8' })
|
|
88
|
+
var withFlag = spawnSync(process.execPath, ['-e', 'process.browser = true\n' + src.split('\n').slice(1).join('\n')], { encoding: 'utf8' })
|
|
89
|
+
withoutFlag.stdout.trim().should.equal(SHA256_ABC)
|
|
90
|
+
withFlag.stdout.trim().should.equal(SHA256_ABC)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('pbkdf2', function () {
|
|
94
|
+
// BIP-39 English test vector; the seed must not depend on which backend ran.
|
|
95
|
+
var PHRASE = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'
|
|
96
|
+
var SEED = 'c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04'
|
|
97
|
+
|
|
98
|
+
function seedInChild (stub) {
|
|
99
|
+
var src = [
|
|
100
|
+
'var Module = require("module"), orig = Module._load',
|
|
101
|
+
'var real = orig.call(Module, "crypto", module, false)',
|
|
102
|
+
'Module._load = function (r) { if (r === "crypto") { ' + stub + ' } return orig.apply(this, arguments) }',
|
|
103
|
+
'var bsv = require(' + JSON.stringify(ROOT) + ')',
|
|
104
|
+
'console.log(new bsv.Mnemonic(' + JSON.stringify(PHRASE) + ').toSeed("TREZOR").toString("hex"))'
|
|
105
|
+
].join('\n')
|
|
106
|
+
var out = spawnSync(process.execPath, ['-e', src], { encoding: 'utf8' })
|
|
107
|
+
if (out.status !== 0) throw new Error('child failed: ' + out.stderr)
|
|
108
|
+
return out.stdout.trim()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
it('derives the BIP-39 vector with node crypto', function () {
|
|
112
|
+
seedInChild('return real').should.equal(SEED)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('derives the same seed with no crypto module', function () {
|
|
116
|
+
seedInChild('var e = new Error("no crypto"); e.code = "MODULE_NOT_FOUND"; throw e').should.equal(SEED)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('rejects a shim whose HMAC is wrong and still derives the right seed', function () {
|
|
120
|
+
seedInChild(
|
|
121
|
+
'return Object.assign({}, real, { createHmac: function () { return { ' +
|
|
122
|
+
'update: function () { return this }, digest: function () { return Buffer.alloc(64) } } } })'
|
|
123
|
+
).should.equal(SEED)
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
})
|