@smartledger/bsv 3.1.1 → 3.2.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +123 -1
  2. package/README.md +233 -277
  3. package/bsv.bundle.js +39 -0
  4. package/bsv.min.js +8 -8
  5. package/docs/ADVANCED_COVENANT_DEVELOPMENT.md +533 -0
  6. package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +169 -0
  7. package/docs/CUSTOM_SCRIPT_DEVELOPMENT.md +320 -0
  8. package/docs/README.md +201 -0
  9. package/docs/block.md +46 -0
  10. package/docs/ecies.md +102 -0
  11. package/docs/index.md +104 -0
  12. package/docs/nchain.md +958 -0
  13. package/docs/networks.md +55 -0
  14. package/docs/preimage.md +126 -0
  15. package/docs/script.md +139 -0
  16. package/docs/transaction.md +174 -0
  17. package/docs/unspentoutput.md +32 -0
  18. package/examples/README.md +200 -0
  19. package/examples/basic/transaction-creation.js +534 -0
  20. package/examples/basic/transaction_signature_api_gap.js +178 -0
  21. package/examples/covenants/advanced_covenant_demo.js +219 -0
  22. package/examples/covenants/covenant_interface_demo.js +270 -0
  23. package/examples/covenants/covenant_manual_signature_resolved.js +212 -0
  24. package/examples/covenants/covenant_signature_template.js +117 -0
  25. package/examples/covenants2/covenant_bidirectional_example.js +262 -0
  26. package/examples/covenants2/covenant_utils_demo.js +120 -0
  27. package/examples/covenants2/preimage_covenant_utils.js +287 -0
  28. package/examples/covenants2/production_integration.js +256 -0
  29. package/examples/data/covenant_utxos.json +28 -0
  30. package/examples/data/utxos.json +26 -0
  31. package/examples/preimage/README.md +178 -0
  32. package/examples/preimage/extract_preimage_bidirectional.js +421 -0
  33. package/examples/preimage/generate_sample_preimage.js +208 -0
  34. package/examples/preimage/generate_sighash_examples.js +152 -0
  35. package/examples/preimage/parse_preimage.js +117 -0
  36. package/examples/preimage/test_preimage_extractor.js +53 -0
  37. package/examples/preimage/test_varint_extraction.js +95 -0
  38. package/examples/scripts/custom_script_helper_example.js +273 -0
  39. package/examples/scripts/custom_script_signature_test.js +344 -0
  40. package/examples/scripts/script_interpreter.js +193 -0
  41. package/examples/smart_contract/complete_workflow_demo.js +343 -0
  42. package/examples/smart_contract/covenant_builder_demo.js +176 -0
  43. package/examples/smart_contract/script_testing_integration.js +198 -0
  44. package/index.js +3 -0
  45. package/lib/covenant-interface.js +713 -0
  46. package/lib/opcode.js +14 -7
  47. package/lib/smart_contract/API_REFERENCE.md +754 -0
  48. package/lib/smart_contract/DOCUMENTATION_SUMMARY.md +201 -0
  49. package/lib/smart_contract/EXAMPLES.md +751 -0
  50. package/lib/smart_contract/QUICK_START.md +549 -0
  51. package/lib/smart_contract/README.md +395 -0
  52. package/lib/smart_contract/builder.js +452 -0
  53. package/lib/smart_contract/covenant.js +336 -0
  54. package/lib/smart_contract/covenant_builder.js +512 -0
  55. package/lib/smart_contract/index.js +311 -0
  56. package/lib/smart_contract/opcode_list.js +30 -0
  57. package/lib/smart_contract/opcode_map.js +1174 -0
  58. package/lib/smart_contract/opcodes.md +1173 -0
  59. package/lib/smart_contract/preimage.js +903 -0
  60. package/lib/smart_contract/script_tester.js +487 -0
  61. package/lib/smart_contract/script_utils.js +609 -0
  62. package/lib/smart_contract/sighash.js +310 -0
  63. package/lib/smart_contract/smartledger-opcode_review.md +70 -0
  64. package/lib/smart_contract/test_integration.js +269 -0
  65. package/lib/smart_contract/utxo_generator.js +367 -0
  66. package/package.json +43 -10
  67. package/utilities/blockchain-state.json +20478 -3
@@ -0,0 +1,336 @@
1
+ /**
2
+ * SmartContract.Covenant Class
3
+ * ============================
4
+ *
5
+ * Production-ready covenant framework with:
6
+ * - Complete P2PKH → Covenant → Spending workflow
7
+ * - Local UTXO storage and portfolio management
8
+ * - WhatsOnChain API integration ready
9
+ * - BIP-143 preimage validation
10
+ * - Script.Interpreter compliance
11
+ *
12
+ * Based on examples/covenants2/preimage_covenant_utils.js
13
+ */
14
+
15
+ 'use strict'
16
+
17
+ var bsv = require('../..')
18
+ var path = require('path')
19
+
20
+ // Optional fs dependency for Node.js environments
21
+ var fs
22
+ try {
23
+ fs = require('fs')
24
+ } catch (e) {
25
+ // Browser environment - fs operations will be disabled
26
+ fs = null
27
+ }
28
+
29
+ /**
30
+ * Covenant Class - Advanced covenant management
31
+ * @param {PrivateKey} privateKey - Private key for covenant operations
32
+ * @param {Object} options - Configuration options
33
+ */
34
+ function Covenant(privateKey, options) {
35
+ if (!(this instanceof Covenant)) {
36
+ return new Covenant(privateKey, options)
37
+ }
38
+
39
+ this.privateKey = privateKey
40
+ this.publicKey = privateKey.publicKey
41
+ this.address = privateKey.toAddress()
42
+
43
+ options = options || {}
44
+ this.storageDir = options.storageDir || (typeof process !== 'undefined' ? path.join(process.cwd(), '.bsv-covenants') : '.bsv-covenants')
45
+ this.covenantUtxoPath = path.join(this.storageDir, 'covenant_utxos.json')
46
+
47
+ this.sighashType = bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID
48
+
49
+ // Ensure storage directory exists (Node.js only)
50
+ if (fs) {
51
+ this._ensureStorageDir()
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Create covenant from P2PKH UTXO
57
+ * @param {Object} utxo - P2PKH UTXO object
58
+ * @returns {Object} Creation result with transaction and covenant UTXO
59
+ */
60
+ Covenant.prototype.createFromP2PKH = function(utxo) {
61
+ // Reconstruct P2PKH script if not provided
62
+ if (!utxo.script) {
63
+ utxo.script = Covenant.reconstructP2pkhScript(this.address)
64
+ }
65
+
66
+ // Create covenant creation transaction
67
+ var creationTx = new bsv.Transaction()
68
+ .from({
69
+ txId: utxo.txid,
70
+ outputIndex: utxo.vout,
71
+ script: utxo.script,
72
+ satoshis: utxo.satoshis
73
+ })
74
+ .to(this.address, utxo.satoshis - 1000) // 1000 sat fee
75
+
76
+ // Get preimage for covenant creation
77
+ var p2pkhScript = bsv.Script.fromHex(utxo.script)
78
+ var creationPreimage = bsv.Transaction.sighash.sighashPreimage(
79
+ creationTx,
80
+ this.sighashType,
81
+ 0,
82
+ p2pkhScript,
83
+ new bsv.crypto.BN(utxo.satoshis)
84
+ )
85
+
86
+ var preimageHash = bsv.crypto.Hash.sha256sha256(creationPreimage)
87
+
88
+ // Build covenant locking script
89
+ var covenantLockingScript = new bsv.Script()
90
+ .add('OP_DUP')
91
+ .add('OP_HASH256')
92
+ .add(preimageHash)
93
+ .add('OP_EQUALVERIFY')
94
+ .add('OP_DROP') // Stack management
95
+ .add(this.publicKey.toBuffer())
96
+ .add('OP_CHECKSIG')
97
+
98
+ // Replace output script with covenant
99
+ creationTx.outputs[0].setScript(covenantLockingScript)
100
+
101
+ // Sign creation transaction (against P2PKH)
102
+ creationTx.sign(this.privateKey)
103
+
104
+ var covenantUtxo = {
105
+ txid: creationTx.id,
106
+ vout: 0,
107
+ satoshis: utxo.satoshis - 1000,
108
+ script: covenantLockingScript.toHex(),
109
+ scriptPubKey: covenantLockingScript.toHex(),
110
+ preimageHash: preimageHash.toString('hex'),
111
+ originalPreimage: creationPreimage.toString('hex'),
112
+ status: 'local',
113
+ createdAt: new Date().toISOString(),
114
+ type: 'preimage_covenant'
115
+ }
116
+
117
+ return {
118
+ transaction: creationTx,
119
+ covenantUtxo: covenantUtxo
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Create spending transaction for covenant UTXO
125
+ * @param {Object} covenantUtxo - Covenant UTXO to spend
126
+ * @param {Address|string} outputAddress - Output address (optional)
127
+ * @param {number} outputSatoshis - Output amount (optional)
128
+ * @returns {Transaction} Spending transaction
129
+ */
130
+ Covenant.prototype.createSpendingTx = function(covenantUtxo, outputAddress, outputSatoshis) {
131
+ var outputAddr = outputAddress || this.address
132
+ var outputSats = outputSatoshis || (covenantUtxo.satoshis - 500) // 500 sat fee
133
+
134
+ // Create spending transaction - manual input for custom scripts
135
+ var spendingTx = new bsv.Transaction()
136
+
137
+ // Manually add input for custom covenant script
138
+ spendingTx.addInput(new bsv.Transaction.Input({
139
+ prevTxId: covenantUtxo.txid,
140
+ outputIndex: covenantUtxo.vout,
141
+ script: bsv.Script.empty() // Will be set after signing
142
+ }), bsv.Script.fromHex(covenantUtxo.script), covenantUtxo.satoshis)
143
+
144
+ // Add output
145
+ spendingTx.to(outputAddr, outputSats)
146
+
147
+ var covenantScript = bsv.Script.fromHex(covenantUtxo.script)
148
+
149
+ // Create signature against covenant script
150
+ var covenantSignature = bsv.Transaction.sighash.sign(
151
+ spendingTx,
152
+ this.privateKey,
153
+ this.sighashType,
154
+ 0,
155
+ covenantScript,
156
+ new bsv.crypto.BN(covenantUtxo.satoshis)
157
+ )
158
+
159
+ var fullSignature = Buffer.concat([
160
+ covenantSignature.toDER(),
161
+ Buffer.from([this.sighashType])
162
+ ])
163
+
164
+ // Use original preimage from covenant creation
165
+ var originalPreimage = Buffer.from(covenantUtxo.originalPreimage, 'hex')
166
+
167
+ // Create unlocking script
168
+ var unlockingScript = new bsv.Script()
169
+ .add(fullSignature)
170
+ .add(originalPreimage)
171
+
172
+ spendingTx.inputs[0].setScript(unlockingScript)
173
+
174
+ return spendingTx
175
+ }
176
+
177
+ /**
178
+ * Validate covenant transaction using Script.Interpreter
179
+ * @param {Transaction} spendingTx - Transaction to validate
180
+ * @param {Object} covenantUtxo - Original covenant UTXO
181
+ * @returns {Object} Validation result
182
+ */
183
+ Covenant.prototype.validate = function(spendingTx, covenantUtxo) {
184
+ var interpreter = new bsv.Script.Interpreter()
185
+ var flags = bsv.Script.Interpreter.SCRIPT_VERIFY_P2SH |
186
+ bsv.Script.Interpreter.SCRIPT_VERIFY_STRICTENC |
187
+ bsv.Script.Interpreter.SCRIPT_VERIFY_DERSIG |
188
+ bsv.Script.Interpreter.SCRIPT_VERIFY_LOW_S |
189
+ bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID
190
+
191
+ var unlockingScript = spendingTx.inputs[0].script
192
+ var lockingScript = bsv.Script.fromHex(covenantUtxo.script)
193
+
194
+ var result = interpreter.verify(
195
+ unlockingScript,
196
+ lockingScript,
197
+ spendingTx,
198
+ 0,
199
+ flags,
200
+ new bsv.crypto.BN(covenantUtxo.satoshis)
201
+ )
202
+
203
+ return {
204
+ valid: result,
205
+ error: result ? null : interpreter.errstr
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Save covenant UTXO to local storage
211
+ * @param {Object} covenantUtxo - Covenant UTXO to save
212
+ */
213
+ Covenant.prototype.save = function(covenantUtxo) {
214
+ if (!fs) {
215
+ console.warn('File system operations not available in browser environment')
216
+ return covenantUtxo
217
+ }
218
+
219
+ var covenantUtxos = this.list()
220
+ covenantUtxos.push(covenantUtxo)
221
+ fs.writeFileSync(this.covenantUtxoPath, JSON.stringify(covenantUtxos, null, 2))
222
+ return covenantUtxo
223
+ }
224
+
225
+ /**
226
+ * Load covenant UTXO from storage
227
+ * @param {string} txid - Transaction ID (optional, loads latest if not provided)
228
+ * @returns {Object} Covenant UTXO
229
+ */
230
+ Covenant.prototype.load = function(txid) {
231
+ var covenantUtxos = this.list()
232
+
233
+ if (txid) {
234
+ var utxo = covenantUtxos.find(function(u) { return u.txid === txid })
235
+ if (!utxo) {
236
+ throw new Error('Covenant UTXO with TXID ' + txid + ' not found')
237
+ }
238
+ return utxo
239
+ } else {
240
+ var utxo = covenantUtxos.find(function(u) { return u.type === 'preimage_covenant' })
241
+ if (!utxo) {
242
+ throw new Error('No preimage covenant UTXO found')
243
+ }
244
+ return utxo
245
+ }
246
+ }
247
+
248
+ /**
249
+ * List all stored covenant UTXOs
250
+ * @returns {Array} Array of covenant UTXOs
251
+ */
252
+ Covenant.prototype.list = function() {
253
+ if (!fs.existsSync(this.covenantUtxoPath)) {
254
+ return []
255
+ }
256
+ return JSON.parse(fs.readFileSync(this.covenantUtxoPath, 'utf8'))
257
+ }
258
+
259
+ /**
260
+ * Get covenant portfolio statistics
261
+ * @returns {Object} Portfolio statistics
262
+ */
263
+ Covenant.prototype.getPortfolio = function() {
264
+ var covenants = this.list()
265
+
266
+ var portfolio = {
267
+ total: covenants.length,
268
+ totalValue: covenants.reduce(function(sum, c) { return sum + c.satoshis }, 0),
269
+ byStatus: {},
270
+ recent: covenants.slice(-5) // Last 5 created
271
+ }
272
+
273
+ // Group by status
274
+ covenants.forEach(function(covenant) {
275
+ var status = covenant.status || 'local'
276
+ if (!portfolio.byStatus[status]) {
277
+ portfolio.byStatus[status] = { count: 0, value: 0 }
278
+ }
279
+ portfolio.byStatus[status].count++
280
+ portfolio.byStatus[status].value += covenant.satoshis
281
+ })
282
+
283
+ return portfolio
284
+ }
285
+
286
+ /**
287
+ * Complete covenant flow: P2PKH → Covenant → Spending
288
+ * @param {Object} p2pkhUtxo - P2PKH UTXO to convert
289
+ * @param {Function} broadcastCallback - Optional broadcast callback
290
+ * @returns {Object} Complete flow result
291
+ */
292
+ Covenant.prototype.completeFlow = function(p2pkhUtxo, broadcastCallback) {
293
+ // Phase 1: Create covenant from P2PKH
294
+ var creation = this.createFromP2PKH(p2pkhUtxo)
295
+ var covenantUtxo = this.save(creation.covenantUtxo)
296
+
297
+ // Optional: Broadcast creation transaction
298
+ if (broadcastCallback) {
299
+ broadcastCallback(creation.transaction, 'creation')
300
+ }
301
+
302
+ // Phase 2: Spend covenant UTXO
303
+ var spendingTx = this.createSpendingTx(covenantUtxo)
304
+ var validation = this.validate(spendingTx, covenantUtxo)
305
+
306
+ // Optional: Broadcast spending transaction
307
+ if (broadcastCallback && validation.valid) {
308
+ broadcastCallback(spendingTx, 'spending')
309
+ }
310
+
311
+ return {
312
+ creationTx: creation.transaction,
313
+ spendingTx: spendingTx,
314
+ covenantUtxo: covenantUtxo,
315
+ validation: validation,
316
+ success: validation.valid
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Private helper methods
322
+ */
323
+ Covenant.prototype._ensureStorageDir = function() {
324
+ if (!fs.existsSync(this.storageDir)) {
325
+ fs.mkdirSync(this.storageDir, { recursive: true })
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Static utility methods
331
+ */
332
+ Covenant.reconstructP2pkhScript = function(address) {
333
+ return bsv.Script.buildPublicKeyHashOut(address).toHex()
334
+ }
335
+
336
+ module.exports = Covenant