smartledger-bsv 3.3.5 â 3.4.4
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 +400 -0
- package/README.md +235 -80
- package/SECURITY.md +88 -0
- package/anchor-entry.js +1 -0
- package/bin/cli.js +354 -0
- package/bsv-anchor.min.js +12 -0
- package/bsv-covenant.min.js +8 -8
- package/bsv-didweb.min.js +12 -0
- package/bsv-gdaf.min.js +9 -9
- package/bsv-ltp.min.js +9 -9
- package/bsv-mnemonic.min.js +2 -2
- package/bsv-shamir.min.js +3 -3
- package/bsv-smartcontract.min.js +9 -9
- package/bsv-statuslist.min.js +18 -0
- package/bsv-vcjwt.min.js +12 -0
- package/bsv.bundle.js +9 -9
- package/bsv.d.ts +486 -9
- package/bsv.min.js +8 -8
- package/build/webpack.anchor.config.js +17 -0
- package/build/webpack.didweb.config.js +17 -0
- package/build/webpack.statuslist.config.js +17 -0
- package/build/webpack.vcjwt.config.js +17 -0
- package/didweb-entry.js +1 -0
- package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +2 -2
- package/docs/MODULE_REFERENCE_COMPLETE.md +61 -58
- package/docs/advanced/LEGAL_TOKEN_PROTOCOL.md +3 -3
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +30 -30
- package/docs/getting-started/QUICK_START.md +18 -18
- package/docs/migration/FROM_BSV_1_5_6.md +16 -10
- package/docs/technical/roadmap.md +3 -3
- package/gdaf-entry.js +1 -2
- package/index.js +68 -9
- package/lib/anchor/index.js +102 -0
- package/lib/browser-utxo-manager-es5.js +11 -4
- package/lib/browser-utxo-manager.js +15 -8
- package/lib/didweb/index.js +177 -0
- package/lib/ltp/claim.js +1 -0
- package/lib/ltp/obligation.js +1 -0
- package/lib/ltp/registry.js +2 -0
- package/lib/ltp/right.js +1 -0
- package/lib/smart_contract/covenant.js +10 -1
- package/lib/smartutxo.js +20 -12
- package/lib/statuslist/index.js +164 -0
- package/lib/transaction/transaction.js +8 -1
- package/lib/util/_.js +7 -1
- package/lib/vcjwt/index.js +189 -0
- package/ltp-entry.js +1 -2
- package/package.json +21 -15
- package/statuslist-entry.js +1 -0
- package/utilities/blockchain-state.js +32 -23
- package/vcjwt-entry.js +1 -0
- package/demos/README.md +0 -188
- package/demos/architecture_demo.js +0 -247
- package/demos/browser-test.html +0 -1208
- package/demos/bsv_wallet_demo.js +0 -242
- package/demos/complete_ltp_demo.js +0 -511
- package/demos/debug_tools_demo.js +0 -87
- package/demos/demo_features.js +0 -123
- package/demos/easy_interface_demo.js +0 -109
- package/demos/ecies_demo.js +0 -182
- package/demos/gdaf_core_test.js +0 -131
- package/demos/gdaf_demo.js +0 -237
- package/demos/ltp_demo.js +0 -361
- package/demos/ltp_primitives_demo.js +0 -403
- package/demos/message_demo.js +0 -209
- package/demos/preimage_separation_demo.js +0 -383
- package/demos/script_helper_demo.js +0 -289
- package/demos/security_demo.js +0 -287
- package/demos/shamir_demo.js +0 -121
- package/demos/simple_demo.js +0 -204
- package/demos/simple_p2pkh_demo.js +0 -169
- package/demos/simple_utxo_preimage_demo.js +0 -196
- package/demos/smart_contract_demo.html +0 -1347
- package/demos/smart_contract_demo.js +0 -910
- package/demos/utxo_generator_demo.js +0 -244
- package/demos/validation_pipeline_demo.js +0 -155
- package/demos/web3keys.html +0 -740
- package/examples/README.md +0 -200
- package/examples/basic/transaction-creation.js +0 -534
- package/examples/basic/transaction_signature_api_gap.js +0 -178
- package/examples/complete_workflow_demo.js +0 -783
- package/examples/covenants/advanced_covenant_demo.js +0 -219
- package/examples/covenants/covenant_interface_demo.js +0 -270
- package/examples/covenants/covenant_manual_signature_resolved.js +0 -212
- package/examples/covenants/covenant_signature_template.js +0 -117
- package/examples/covenants2/covenant_bidirectional_example.js +0 -262
- package/examples/covenants2/covenant_utils_demo.js +0 -120
- package/examples/covenants2/preimage_covenant_utils.js +0 -287
- package/examples/covenants2/production_integration.js +0 -256
- package/examples/data/covenant_utxos.json +0 -28
- package/examples/data/utxos.json +0 -26
- package/examples/definitive_working_demo.js +0 -261
- package/examples/final_working_contracts.js +0 -338
- package/examples/preimage/README.md +0 -178
- package/examples/preimage/extract_preimage_bidirectional.js +0 -421
- package/examples/preimage/generate_sample_preimage.js +0 -208
- package/examples/preimage/generate_sighash_examples.js +0 -152
- package/examples/preimage/parse_preimage.js +0 -117
- package/examples/preimage/test_preimage_extractor.js +0 -53
- package/examples/preimage/test_varint_extraction.js +0 -95
- package/examples/scripts/custom_script_helper_example.js +0 -273
- package/examples/scripts/custom_script_signature_test.js +0 -344
- package/examples/scripts/script_interpreter.js +0 -193
- package/examples/smart_contract/complete_workflow_demo.js +0 -343
- package/examples/smart_contract/covenant_builder_demo.js +0 -176
- package/examples/smart_contract/script_testing_integration.js +0 -198
- package/examples/smart_contract_templates.js +0 -718
- package/examples/working_smart_contracts.js +0 -348
- package/lib/smart_contract/test_integration.js +0 -269
- package/tests/browser-compatibility/README.md +0 -35
- package/tests/browser-compatibility/test-cdn-vs-local.html +0 -186
- package/tests/browser-compatibility/test-pbkdf2.html +0 -51
- package/tests/bundle-completeness-test.html +0 -131
- package/tests/bundle-demo.html +0 -476
- package/tests/smartcontract-test.html +0 -239
- package/tests/standalone-modules-test.html +0 -260
- package/tests/test.html +0 -612
- package/tests/test_builtin_verify.js +0 -117
- package/tests/test_debug_integration.js +0 -71
- package/tests/test_ecdsa_little.js +0 -70
- package/tests/test_shamir.js +0 -221
- package/tests/test_smartverify_der.js +0 -110
- package/tests/test_standalone_shamir.html +0 -83
- package/tests/unpkg-demo.html +0 -194
- package/utilities/blockchain-state.json +0 -118565
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* StatusList2021 Module
|
|
5
|
+
* W3C StatusList2021 for credential revocation and suspension
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
var vcjwt = require('../vcjwt')
|
|
9
|
+
var crypto = require('crypto')
|
|
10
|
+
|
|
11
|
+
// Create a new status list
|
|
12
|
+
async function createStatusList(params) {
|
|
13
|
+
if (!params.issuerDid || !params.privateJwk) {
|
|
14
|
+
throw new Error('issuerDid and privateJwk are required')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var listId = params.listId || params.issuerDid + '/status/' + Date.now()
|
|
18
|
+
|
|
19
|
+
// Create a bitstring for 100,000 credentials (default size)
|
|
20
|
+
var listSize = params.listSize || 100000
|
|
21
|
+
var byteSize = Math.ceil(listSize / 8)
|
|
22
|
+
var bitstringBuffer = Buffer.alloc(byteSize, 0)
|
|
23
|
+
|
|
24
|
+
// Compress with gzip
|
|
25
|
+
var zlib = require('zlib')
|
|
26
|
+
var compressed = zlib.gzipSync(bitstringBuffer)
|
|
27
|
+
var encodedCompressed = compressed.toString('base64')
|
|
28
|
+
|
|
29
|
+
// Create StatusList2021 credential
|
|
30
|
+
var statusListCredential = {
|
|
31
|
+
'@context': [
|
|
32
|
+
'https://www.w3.org/2018/credentials/v1',
|
|
33
|
+
'https://w3id.org/vc/status-list/2021/v1'
|
|
34
|
+
],
|
|
35
|
+
type: ['VerifiableCredential', 'StatusList2021Credential'],
|
|
36
|
+
issuer: params.issuerDid,
|
|
37
|
+
issuanceDate: new Date().toISOString(),
|
|
38
|
+
credentialSubject: {
|
|
39
|
+
id: listId,
|
|
40
|
+
type: 'StatusList2021',
|
|
41
|
+
statusPurpose: 'revocation',
|
|
42
|
+
encodedList: encodedCompressed
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Issue as JWT
|
|
47
|
+
var result = await vcjwt.issueVcJwt({
|
|
48
|
+
issuerDid: params.issuerDid,
|
|
49
|
+
subjectId: listId,
|
|
50
|
+
types: ['VerifiableCredential', 'StatusList2021Credential'],
|
|
51
|
+
credentialSubject: statusListCredential.credentialSubject,
|
|
52
|
+
privateJwk: params.privateJwk,
|
|
53
|
+
alg: params.privateJwk.alg || 'ES256'
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
listVcJwt: result.jwt,
|
|
58
|
+
listId: listId
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Update status list (revoke/suspend/activate)
|
|
63
|
+
async function updateStatusList(params) {
|
|
64
|
+
if (!params.listVcJwt || params.index === undefined || !params.status || !params.privateJwk) {
|
|
65
|
+
throw new Error('listVcJwt, index, status, and privateJwk are required')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Decode the existing status list JWT
|
|
69
|
+
var parts = params.listVcJwt.split('.')
|
|
70
|
+
if (parts.length !== 3) {
|
|
71
|
+
throw new Error('Invalid JWT format')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var payload = JSON.parse(vcjwt.base64UrlDecode(parts[1]).toString())
|
|
75
|
+
var encodedList = payload.vc.credentialSubject.encodedList
|
|
76
|
+
|
|
77
|
+
// Decompress
|
|
78
|
+
var zlib = require('zlib')
|
|
79
|
+
var compressed = Buffer.from(encodedList, 'base64')
|
|
80
|
+
var bitstring = zlib.gunzipSync(compressed)
|
|
81
|
+
|
|
82
|
+
// Update the bit at the given index
|
|
83
|
+
var byteIndex = Math.floor(params.index / 8)
|
|
84
|
+
var bitIndex = params.index % 8
|
|
85
|
+
|
|
86
|
+
if (byteIndex >= bitstring.length) {
|
|
87
|
+
throw new Error('Index out of range')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// StatusList2021 uses 2 bits per credential for 4 states
|
|
91
|
+
// For simplicity, we'll use single bit: 0=valid, 1=revoked/suspended
|
|
92
|
+
var statusBit = (params.status === 'revoked' || params.status === 'suspended') ? 1 : 0
|
|
93
|
+
|
|
94
|
+
if (statusBit === 1) {
|
|
95
|
+
bitstring[byteIndex] |= (1 << bitIndex)
|
|
96
|
+
} else {
|
|
97
|
+
bitstring[byteIndex] &= ~(1 << bitIndex)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Recompress
|
|
101
|
+
var recompressed = zlib.gzipSync(bitstring)
|
|
102
|
+
var newEncodedList = recompressed.toString('base64')
|
|
103
|
+
|
|
104
|
+
// Create updated credential
|
|
105
|
+
var updatedCredentialSubject = {
|
|
106
|
+
id: payload.vc.credentialSubject.id,
|
|
107
|
+
type: 'StatusList2021',
|
|
108
|
+
statusPurpose: 'revocation',
|
|
109
|
+
encodedList: newEncodedList
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Re-issue as JWT
|
|
113
|
+
var result = await vcjwt.issueVcJwt({
|
|
114
|
+
issuerDid: payload.iss,
|
|
115
|
+
subjectId: payload.vc.credentialSubject.id,
|
|
116
|
+
types: ['VerifiableCredential', 'StatusList2021Credential'],
|
|
117
|
+
credentialSubject: updatedCredentialSubject,
|
|
118
|
+
privateJwk: params.privateJwk,
|
|
119
|
+
alg: params.privateJwk.alg || 'ES256'
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
listVcJwt: result.jwt
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Get credential status entry
|
|
128
|
+
function getCredentialStatusEntry(params) {
|
|
129
|
+
if (!params.listVcJwt || params.index === undefined) {
|
|
130
|
+
throw new Error('listVcJwt and index are required')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Decode the status list JWT
|
|
134
|
+
var parts = params.listVcJwt.split('.')
|
|
135
|
+
if (parts.length !== 3) {
|
|
136
|
+
throw new Error('Invalid JWT format')
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
var payload = JSON.parse(vcjwt.base64UrlDecode(parts[1]).toString())
|
|
140
|
+
var encodedList = payload.vc.credentialSubject.encodedList
|
|
141
|
+
|
|
142
|
+
// Decompress
|
|
143
|
+
var zlib = require('zlib')
|
|
144
|
+
var compressed = Buffer.from(encodedList, 'base64')
|
|
145
|
+
var bitstring = zlib.gunzipSync(compressed)
|
|
146
|
+
|
|
147
|
+
// Check the bit at the given index
|
|
148
|
+
var byteIndex = Math.floor(params.index / 8)
|
|
149
|
+
var bitIndex = params.index % 8
|
|
150
|
+
|
|
151
|
+
if (byteIndex >= bitstring.length) {
|
|
152
|
+
throw new Error('Index out of range')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
var bit = (bitstring[byteIndex] >> bitIndex) & 1
|
|
156
|
+
|
|
157
|
+
return bit === 1 ? 'revoked' : 'valid'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = {
|
|
161
|
+
createStatusList: createStatusList,
|
|
162
|
+
updateStatusList: updateStatusList,
|
|
163
|
+
getCredentialStatusEntry: getCredentialStatusEntry
|
|
164
|
+
}
|
|
@@ -577,7 +577,7 @@ Transaction.prototype._fromMultisigUtxo = function (utxo, pubkeys, threshold) {
|
|
|
577
577
|
} else if (utxo.script.isScriptHashOut()) {
|
|
578
578
|
Clazz = MultiSigScriptHashInput
|
|
579
579
|
} else {
|
|
580
|
-
throw new
|
|
580
|
+
throw new errors.Transaction.Input.UnsupportedScript(utxo.script.toString())
|
|
581
581
|
}
|
|
582
582
|
this.addInput(new Clazz({
|
|
583
583
|
output: new Output({
|
|
@@ -911,6 +911,13 @@ Transaction.prototype._getUnspentValue = function () {
|
|
|
911
911
|
|
|
912
912
|
Transaction.prototype._clearSignatures = function () {
|
|
913
913
|
_.each(this.inputs, function (input) {
|
|
914
|
+
// Custom-script inputs (bare Input base class â e.g. covenant or other
|
|
915
|
+
// non-standard locking scripts) have no library-managed signatures to
|
|
916
|
+
// clear; the caller owns input.script. Mirror the guard pattern used by
|
|
917
|
+
// Transaction.prototype.isFullySigned / isValidSignature below.
|
|
918
|
+
if (input.clearSignatures === Input.prototype.clearSignatures) {
|
|
919
|
+
return
|
|
920
|
+
}
|
|
914
921
|
input.clearSignatures()
|
|
915
922
|
})
|
|
916
923
|
}
|
package/lib/util/_.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
var Random = require('../crypto/random')
|
|
4
|
+
|
|
3
5
|
var _ = {}
|
|
4
6
|
|
|
5
7
|
_.isArray = t => Array.isArray(t)
|
|
@@ -28,10 +30,14 @@ _.values = o => Object.values(o)
|
|
|
28
30
|
_.filter = (a, f) => a.filter(f)
|
|
29
31
|
_.reduce = (a, f, s) => a.reduce(f, s)
|
|
30
32
|
_.without = (a, n) => a.filter(t => t !== n)
|
|
33
|
+
// CSPRNG-backed Fisher-Yates. Output-order shuffling is a privacy primitive
|
|
34
|
+
// (Transaction.shuffleOutputs); a predictable PRNG defeats the purpose.
|
|
31
35
|
_.shuffle = a => {
|
|
32
36
|
const result = a.slice(0)
|
|
33
37
|
for (let i = result.length - 1; i > 0; i--) {
|
|
34
|
-
const
|
|
38
|
+
const buf = Random.getRandomBuffer(4)
|
|
39
|
+
const r = buf.readUInt32BE(0) / 0x100000000
|
|
40
|
+
const j = Math.floor(r * (i + 1));
|
|
35
41
|
[result[i], result[j]] = [result[j], result[i]]
|
|
36
42
|
}
|
|
37
43
|
return result
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* VC-JWT Module
|
|
5
|
+
* W3C Verifiable Credentials using JWT/JWS
|
|
6
|
+
* Supports ES256 (P-256) and ES256K (secp256k1)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
var crypto = require('crypto')
|
|
10
|
+
|
|
11
|
+
// Base64URL encoding
|
|
12
|
+
function base64UrlEncode(buffer) {
|
|
13
|
+
return buffer.toString('base64')
|
|
14
|
+
.replace(/\+/g, '-')
|
|
15
|
+
.replace(/\//g, '_')
|
|
16
|
+
.replace(/=/g, '')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Base64URL decoding
|
|
20
|
+
function base64UrlDecode(str) {
|
|
21
|
+
str = str.replace(/-/g, '+').replace(/_/g, '/')
|
|
22
|
+
while (str.length % 4) {
|
|
23
|
+
str += '='
|
|
24
|
+
}
|
|
25
|
+
return Buffer.from(str, 'base64')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Issue a Verifiable Credential as JWT
|
|
29
|
+
async function issueVcJwt(params) {
|
|
30
|
+
if (!params.issuerDid || !params.subjectId || !params.credentialSubject || !params.privateJwk) {
|
|
31
|
+
throw new Error('issuerDid, subjectId, credentialSubject, and privateJwk are required')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var alg = params.alg || 'ES256'
|
|
35
|
+
var kid = params.kid || params.privateJwk.kid
|
|
36
|
+
var types = params.types || ['VerifiableCredential']
|
|
37
|
+
var expSeconds = params.expSeconds || (365 * 24 * 60 * 60) // 1 year default
|
|
38
|
+
|
|
39
|
+
var now = Math.floor(Date.now() / 1000)
|
|
40
|
+
var issuedAt = new Date().toISOString()
|
|
41
|
+
|
|
42
|
+
// Build VC payload
|
|
43
|
+
var vcPayload = {
|
|
44
|
+
'@context': [
|
|
45
|
+
'https://www.w3.org/2018/credentials/v1'
|
|
46
|
+
],
|
|
47
|
+
type: types,
|
|
48
|
+
issuer: params.issuerDid,
|
|
49
|
+
issuanceDate: issuedAt,
|
|
50
|
+
credentialSubject: Object.assign({
|
|
51
|
+
id: params.subjectId
|
|
52
|
+
}, params.credentialSubject)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Build JWT claims
|
|
56
|
+
var jwtPayload = {
|
|
57
|
+
iss: params.issuerDid,
|
|
58
|
+
sub: params.subjectId,
|
|
59
|
+
iat: now,
|
|
60
|
+
exp: now + expSeconds,
|
|
61
|
+
vc: vcPayload
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Build JWT header
|
|
65
|
+
var header = {
|
|
66
|
+
alg: alg,
|
|
67
|
+
typ: 'JWT',
|
|
68
|
+
kid: kid
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Encode header and payload
|
|
72
|
+
var headerB64 = base64UrlEncode(Buffer.from(JSON.stringify(header)))
|
|
73
|
+
var payloadB64 = base64UrlEncode(Buffer.from(JSON.stringify(jwtPayload)))
|
|
74
|
+
var signingInput = headerB64 + '.' + payloadB64
|
|
75
|
+
|
|
76
|
+
// Sign with private key
|
|
77
|
+
var privateKey = crypto.createPrivateKey({
|
|
78
|
+
key: params.privateJwk,
|
|
79
|
+
format: 'jwk'
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
var signature
|
|
83
|
+
if (alg === 'ES256') {
|
|
84
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), privateKey)
|
|
85
|
+
} else if (alg === 'ES256K') {
|
|
86
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), privateKey)
|
|
87
|
+
} else {
|
|
88
|
+
throw new Error('Unsupported algorithm: ' + alg)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
var signatureB64 = base64UrlEncode(signature)
|
|
92
|
+
var jwt = signingInput + '.' + signatureB64
|
|
93
|
+
|
|
94
|
+
return { jwt: jwt }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Verify a VC-JWT
|
|
98
|
+
async function verifyVcJwt(jwt, opts) {
|
|
99
|
+
opts = opts || {}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Parse JWT
|
|
103
|
+
var parts = jwt.split('.')
|
|
104
|
+
if (parts.length !== 3) {
|
|
105
|
+
return { valid: false, error: 'Invalid JWT format' }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
var headerB64 = parts[0]
|
|
109
|
+
var payloadB64 = parts[1]
|
|
110
|
+
var signatureB64 = parts[2]
|
|
111
|
+
|
|
112
|
+
var header = JSON.parse(base64UrlDecode(headerB64).toString())
|
|
113
|
+
var payload = JSON.parse(base64UrlDecode(payloadB64).toString())
|
|
114
|
+
var signature = base64UrlDecode(signatureB64)
|
|
115
|
+
|
|
116
|
+
// Check expiration
|
|
117
|
+
var now = Math.floor(Date.now() / 1000)
|
|
118
|
+
var clockTolerance = opts.clockToleranceSec || 60
|
|
119
|
+
|
|
120
|
+
if (payload.exp && payload.exp < (now - clockTolerance)) {
|
|
121
|
+
return { valid: false, error: 'JWT expired', header: header, payload: payload }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check issuer if expected
|
|
125
|
+
if (opts.expectedIssuerDid && payload.iss !== opts.expectedIssuerDid) {
|
|
126
|
+
return { valid: false, error: 'Unexpected issuer', header: header, payload: payload }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Get public key from DID resolver or use default resolver
|
|
130
|
+
var publicKey
|
|
131
|
+
if (opts.didResolver) {
|
|
132
|
+
var resolved = await opts.didResolver(payload.iss)
|
|
133
|
+
if (!resolved || !resolved.jwks || !resolved.jwks.keys) {
|
|
134
|
+
return { valid: false, error: 'Failed to resolve issuer DID', header: header, payload: payload }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Find matching key by kid
|
|
138
|
+
var matchingKey = resolved.jwks.keys.find(function(k) {
|
|
139
|
+
return k.kid === header.kid
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!matchingKey) {
|
|
143
|
+
return { valid: false, error: 'Key not found in JWKS', header: header, payload: payload }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
publicKey = matchingKey
|
|
147
|
+
} else {
|
|
148
|
+
// Without resolver, verification cannot proceed
|
|
149
|
+
return { valid: false, error: 'DID resolver required for verification', header: header, payload: payload }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Verify signature
|
|
153
|
+
var signingInput = headerB64 + '.' + payloadB64
|
|
154
|
+
var pubKey = crypto.createPublicKey({
|
|
155
|
+
key: publicKey,
|
|
156
|
+
format: 'jwk'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
var isValid = crypto.verify(
|
|
160
|
+
'sha256',
|
|
161
|
+
Buffer.from(signingInput),
|
|
162
|
+
pubKey,
|
|
163
|
+
signature
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if (!isValid) {
|
|
167
|
+
return { valid: false, error: 'Invalid signature', header: header, payload: payload }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
valid: true,
|
|
172
|
+
header: header,
|
|
173
|
+
payload: payload
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
} catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
valid: false,
|
|
179
|
+
error: error.message || 'Verification failed'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = {
|
|
185
|
+
issueVcJwt: issueVcJwt,
|
|
186
|
+
verifyVcJwt: verifyVcJwt,
|
|
187
|
+
base64UrlEncode: base64UrlEncode,
|
|
188
|
+
base64UrlDecode: base64UrlDecode
|
|
189
|
+
}
|
package/ltp-entry.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
module.exports = require('./lib/smart_contract');
|
|
1
|
+
module.exports = require('./lib/ltp')
|
package/package.json
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smartledger-bsv",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "đ Complete Bitcoin SV development framework with Legal Token Protocol (LTP), Global Digital Attestation Framework (GDAF),
|
|
3
|
+
"version": "3.4.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",
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/codenlighten/smartledger-bsv/issues"
|
|
9
9
|
},
|
|
10
10
|
"main": "index.js",
|
|
11
|
+
"types": "bsv.d.ts",
|
|
12
|
+
"bin": {
|
|
13
|
+
"smartledger-bsv": "./bin/cli.js"
|
|
14
|
+
},
|
|
11
15
|
"scripts": {
|
|
12
16
|
"lint": "standard",
|
|
13
17
|
"test": "mocha",
|
|
18
|
+
"test:cli": "mocha test/cli/smoke.js --timeout 25000",
|
|
14
19
|
"test:ltp": "node complete_ltp_demo.js",
|
|
15
20
|
"test:ltp-primitives": "node simple_demo.js",
|
|
16
21
|
"test:architecture": "node architecture_demo.js",
|
|
@@ -46,15 +51,20 @@
|
|
|
46
51
|
"build-covenant": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack covenant-entry.js --config build/webpack.covenant.config.js",
|
|
47
52
|
"build-script-helper": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack script-helper-entry.js --config build/webpack.script-helper.config.js",
|
|
48
53
|
"build-security": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack security-entry.js --config build/webpack.security.config.js",
|
|
54
|
+
"build-didweb": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack didweb-entry.js --config build/webpack.didweb.config.js",
|
|
55
|
+
"build-vcjwt": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack vcjwt-entry.js --config build/webpack.vcjwt.config.js",
|
|
56
|
+
"build-statuslist": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack statuslist-entry.js --config build/webpack.statuslist.config.js",
|
|
57
|
+
"build-anchor": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack anchor-entry.js --config build/webpack.anchor.config.js",
|
|
49
58
|
"build-bundle": "NODE_OPTIONS=\"--openssl-legacy-provider\" webpack bundle-entry.js --config build/webpack.bundle.config.js",
|
|
50
59
|
"build": "npm run build-bsv && npm run build-ecies && npm run build-message && npm run build-mnemonic && npm run build-shamir && npm run build-smartcontract",
|
|
51
60
|
"build-specialized": "npm run build-covenant && npm run build-script-helper && npm run build-security",
|
|
61
|
+
"build-credentials": "npm run build-didweb && npm run build-vcjwt && npm run build-statuslist && npm run build-anchor",
|
|
52
62
|
"build-advanced": "npm run build-ltp && npm run build-gdaf",
|
|
53
|
-
"build-all": "npm run build && npm run build-bundle && npm run build-specialized && npm run build-advanced",
|
|
63
|
+
"build-all": "npm run build && npm run build-bundle && npm run build-specialized && npm run build-advanced && npm run build-credentials",
|
|
54
64
|
"test:browser": "echo 'Open tests/standalone-modules-test.html in browser for comprehensive testing'",
|
|
55
65
|
"test:bundle": "echo 'Open tests/bundle-completeness-test.html in browser to verify bundle completeness'",
|
|
56
66
|
"preimage:extract": "node examples/preimage/extract_preimage_bidirectional.js",
|
|
57
|
-
"prepublishOnly": "NODE_OPTIONS=\"--openssl-legacy-provider\" npm run build"
|
|
67
|
+
"prepublishOnly": "NODE_OPTIONS=\"--openssl-legacy-provider\" npm run build-all"
|
|
58
68
|
},
|
|
59
69
|
"unpkg": "bsv.min.js",
|
|
60
70
|
"jsdelivr": "bsv.min.js",
|
|
@@ -62,12 +72,13 @@
|
|
|
62
72
|
"files": [
|
|
63
73
|
"index.js",
|
|
64
74
|
"lib/",
|
|
65
|
-
"utilities
|
|
75
|
+
"utilities/*.js",
|
|
76
|
+
"utilities/README.md",
|
|
77
|
+
"utilities/wallet.json",
|
|
66
78
|
"ecies/",
|
|
67
79
|
"message/",
|
|
68
80
|
"mnemonic/",
|
|
69
81
|
"build/",
|
|
70
|
-
"tests/",
|
|
71
82
|
"*-entry.js",
|
|
72
83
|
"bsv.min.js",
|
|
73
84
|
"bsv.bundle.js",
|
|
@@ -81,17 +92,12 @@
|
|
|
81
92
|
"bsv-covenant.min.js",
|
|
82
93
|
"bsv-script-helper.min.js",
|
|
83
94
|
"bsv-security.min.js",
|
|
95
|
+
"bsv-didweb.min.js",
|
|
96
|
+
"bsv-vcjwt.min.js",
|
|
97
|
+
"bsv-statuslist.min.js",
|
|
98
|
+
"bsv-anchor.min.js",
|
|
84
99
|
"bsv.d.ts",
|
|
85
|
-
"validation_test.js",
|
|
86
|
-
"test_shamir.js",
|
|
87
|
-
"shamir_demo.js",
|
|
88
|
-
"complete_ltp_demo.js",
|
|
89
|
-
"simple_demo.js",
|
|
90
|
-
"architecture_demo.js",
|
|
91
|
-
"test_standalone_shamir.html",
|
|
92
100
|
"docs/",
|
|
93
|
-
"demos/",
|
|
94
|
-
"examples/",
|
|
95
101
|
"LICENSE",
|
|
96
102
|
"README.md",
|
|
97
103
|
"SECURITY.md",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/statuslist')
|
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
* Acts as the "blockchain database" for our miner simulator.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// Set BSV_DEBUG=1 (Node) or window.BSV_DEBUG = true (browser) to surface
|
|
9
|
+
// info/warning narration from this module. Matches the gating pattern
|
|
10
|
+
// used by lib/browser-utxo-manager-es5.js since v3.4.1.
|
|
11
|
+
const debug = function () {
|
|
12
|
+
const enabled = (typeof process !== 'undefined' && process.env && process.env.BSV_DEBUG) ||
|
|
13
|
+
(typeof window !== 'undefined' && window.BSV_DEBUG)
|
|
14
|
+
if (enabled) console.log.apply(console, arguments)
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
// Browser-compatible imports
|
|
9
18
|
let bsv, fs, path, BLOCKCHAIN_STATE_PATH
|
|
10
19
|
|
|
@@ -16,7 +25,7 @@ if (typeof window === 'undefined' && typeof require === 'function') {
|
|
|
16
25
|
path = require('path')
|
|
17
26
|
BLOCKCHAIN_STATE_PATH = path.join(__dirname, 'blockchain-state.json')
|
|
18
27
|
} catch (e) {
|
|
19
|
-
|
|
28
|
+
debug('BlockchainState: Running in browser mode - persistence disabled')
|
|
20
29
|
}
|
|
21
30
|
} else {
|
|
22
31
|
// In browser, try to get bsv from global scope or fallback
|
|
@@ -54,14 +63,14 @@ function loadBlockchainState () {
|
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
if (!fs.existsSync(BLOCKCHAIN_STATE_PATH)) {
|
|
57
|
-
|
|
66
|
+
debug('đ Creating new blockchain state...')
|
|
58
67
|
const initialState = initializeBlockchainState()
|
|
59
68
|
saveBlockchainState(initialState)
|
|
60
69
|
return initialState
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
const state = JSON.parse(fs.readFileSync(BLOCKCHAIN_STATE_PATH, 'utf8'))
|
|
64
|
-
|
|
73
|
+
debug('đ Loaded existing blockchain state')
|
|
65
74
|
return state
|
|
66
75
|
} catch (error) {
|
|
67
76
|
console.error('â Error loading blockchain state:', error.message)
|
|
@@ -79,7 +88,7 @@ function saveBlockchainState (state) {
|
|
|
79
88
|
// Only save to file in Node.js environment
|
|
80
89
|
if (fs && BLOCKCHAIN_STATE_PATH) {
|
|
81
90
|
fs.writeFileSync(BLOCKCHAIN_STATE_PATH, JSON.stringify(state, null, 2))
|
|
82
|
-
|
|
91
|
+
debug('đž Blockchain state saved')
|
|
83
92
|
}
|
|
84
93
|
} catch (error) {
|
|
85
94
|
console.error('â Error saving blockchain state:', error.message)
|
|
@@ -90,12 +99,12 @@ function saveBlockchainState (state) {
|
|
|
90
99
|
* Register a new wallet in the blockchain state
|
|
91
100
|
*/
|
|
92
101
|
function registerWallet (walletAddress, walletData) {
|
|
93
|
-
|
|
102
|
+
debug(`đ Registering wallet: ${walletAddress}`)
|
|
94
103
|
|
|
95
104
|
const state = loadBlockchainState()
|
|
96
105
|
|
|
97
106
|
if (state.wallets[walletAddress]) {
|
|
98
|
-
|
|
107
|
+
debug('âšī¸ Wallet already exists, updating...')
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
state.wallets[walletAddress] = {
|
|
@@ -122,7 +131,7 @@ function registerWallet (walletAddress, walletData) {
|
|
|
122
131
|
|
|
123
132
|
saveBlockchainState(state)
|
|
124
133
|
|
|
125
|
-
|
|
134
|
+
debug(`â
Wallet registered: ${walletAddress}`)
|
|
126
135
|
return state
|
|
127
136
|
}
|
|
128
137
|
|
|
@@ -185,7 +194,7 @@ function spendUTXO (txid, vout, spentInTx) {
|
|
|
185
194
|
|
|
186
195
|
updateBlockchainMetadata(state)
|
|
187
196
|
saveBlockchainState(state)
|
|
188
|
-
|
|
197
|
+
debug(`â UTXO spent: ${utxoKey} in tx ${spentInTx}`)
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
/**
|
|
@@ -197,7 +206,7 @@ function addUTXO (utxo, ownerAddress) {
|
|
|
197
206
|
|
|
198
207
|
// Check if UTXO already exists
|
|
199
208
|
if (state.globalUTXOSet[utxoKey]) {
|
|
200
|
-
|
|
209
|
+
debug(`â ī¸ UTXO ${utxoKey} already exists, skipping`)
|
|
201
210
|
return
|
|
202
211
|
}
|
|
203
212
|
|
|
@@ -229,7 +238,7 @@ function addUTXO (utxo, ownerAddress) {
|
|
|
229
238
|
updateBlockchainMetadata(state)
|
|
230
239
|
saveBlockchainState(state)
|
|
231
240
|
|
|
232
|
-
|
|
241
|
+
debug(`â
UTXO added: ${utxoKey} for ${ownerAddress}`)
|
|
233
242
|
}
|
|
234
243
|
|
|
235
244
|
/**
|
|
@@ -255,18 +264,18 @@ function updateBlockchainMetadata (state) {
|
|
|
255
264
|
function getBlockchainStats () {
|
|
256
265
|
const state = loadBlockchainState()
|
|
257
266
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
267
|
+
debug('đ Blockchain State Statistics:')
|
|
268
|
+
debug('âââââââââââââââââââââââââââââââââââââââââââ')
|
|
269
|
+
debug(`đ Total Wallets: ${state.metadata.totalWallets}`)
|
|
270
|
+
debug(`đ° Total UTXOs: ${state.metadata.totalUTXOs}`)
|
|
271
|
+
debug(`đ Total Value: ${state.metadata.totalValue} satoshis`)
|
|
272
|
+
debug(`đī¸ Block Height: ${state.metadata.blockHeight}`)
|
|
273
|
+
debug(`đ Last Updated: ${state.metadata.lastUpdated}\n`)
|
|
265
274
|
|
|
266
275
|
if (Object.keys(state.wallets).length > 0) {
|
|
267
|
-
|
|
276
|
+
debug('đ Registered Wallets:')
|
|
268
277
|
Object.entries(state.wallets).forEach(([address, wallet]) => {
|
|
269
|
-
|
|
278
|
+
debug(` ${address}: ${wallet.utxos.length} UTXOs, ${wallet.totalValue} sats`)
|
|
270
279
|
})
|
|
271
280
|
}
|
|
272
281
|
|
|
@@ -280,14 +289,14 @@ function importWalletFromFile () {
|
|
|
280
289
|
const walletPath = path.join(__dirname, 'wallet.json')
|
|
281
290
|
|
|
282
291
|
if (!fs.existsSync(walletPath)) {
|
|
283
|
-
|
|
292
|
+
debug('â No wallet.json found to import')
|
|
284
293
|
return false
|
|
285
294
|
}
|
|
286
295
|
|
|
287
296
|
try {
|
|
288
297
|
const walletData = JSON.parse(fs.readFileSync(walletPath, 'utf8'))
|
|
289
298
|
|
|
290
|
-
|
|
299
|
+
debug('đĨ Importing wallet from wallet.json...')
|
|
291
300
|
|
|
292
301
|
const walletInfo = {
|
|
293
302
|
registeredAt: new Date().toISOString(),
|
|
@@ -296,7 +305,7 @@ function importWalletFromFile () {
|
|
|
296
305
|
|
|
297
306
|
registerWallet(walletData.wallet.address, walletInfo)
|
|
298
307
|
|
|
299
|
-
|
|
308
|
+
debug('â
Wallet imported successfully')
|
|
300
309
|
return true
|
|
301
310
|
} catch (error) {
|
|
302
311
|
console.error('â Error importing wallet:', error.message)
|
|
@@ -313,7 +322,7 @@ if (require.main === module) {
|
|
|
313
322
|
} else if (args[0] === 'init') {
|
|
314
323
|
const state = initializeBlockchainState()
|
|
315
324
|
saveBlockchainState(state)
|
|
316
|
-
|
|
325
|
+
debug('đ Initialized new blockchain state')
|
|
317
326
|
}
|
|
318
327
|
|
|
319
328
|
getBlockchainStats()
|
package/vcjwt-entry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/vcjwt')
|