smartledger-bsv 3.1.0 ā 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.
- package/CHANGELOG.md +123 -1
- package/README.md +233 -277
- package/bsv.bundle.js +39 -0
- package/bsv.min.js +8 -8
- package/docs/ADVANCED_COVENANT_DEVELOPMENT.md +533 -0
- package/docs/COVENANT_DEVELOPMENT_RESOLVED.md +169 -0
- package/docs/CUSTOM_SCRIPT_DEVELOPMENT.md +320 -0
- package/docs/README.md +201 -0
- package/docs/block.md +46 -0
- package/docs/ecies.md +102 -0
- package/docs/index.md +104 -0
- package/docs/nchain.md +958 -0
- package/docs/networks.md +55 -0
- package/docs/preimage.md +126 -0
- package/docs/script.md +139 -0
- package/docs/transaction.md +174 -0
- package/docs/unspentoutput.md +32 -0
- package/examples/README.md +200 -0
- package/examples/basic/transaction-creation.js +534 -0
- package/examples/basic/transaction_signature_api_gap.js +178 -0
- package/examples/covenants/advanced_covenant_demo.js +219 -0
- package/examples/covenants/covenant_interface_demo.js +270 -0
- package/examples/covenants/covenant_manual_signature_resolved.js +212 -0
- package/examples/covenants/covenant_signature_template.js +117 -0
- package/examples/covenants2/covenant_bidirectional_example.js +262 -0
- package/examples/covenants2/covenant_utils_demo.js +120 -0
- package/examples/covenants2/preimage_covenant_utils.js +287 -0
- package/examples/covenants2/production_integration.js +256 -0
- package/examples/data/covenant_utxos.json +28 -0
- package/examples/data/utxos.json +26 -0
- package/examples/preimage/README.md +178 -0
- package/examples/preimage/extract_preimage_bidirectional.js +421 -0
- package/examples/preimage/generate_sample_preimage.js +208 -0
- package/examples/preimage/generate_sighash_examples.js +152 -0
- package/examples/preimage/parse_preimage.js +117 -0
- package/examples/preimage/test_preimage_extractor.js +53 -0
- package/examples/preimage/test_varint_extraction.js +95 -0
- package/examples/scripts/custom_script_helper_example.js +273 -0
- package/examples/scripts/custom_script_signature_test.js +344 -0
- package/examples/scripts/script_interpreter.js +193 -0
- package/examples/smart_contract/complete_workflow_demo.js +343 -0
- package/examples/smart_contract/covenant_builder_demo.js +176 -0
- package/examples/smart_contract/script_testing_integration.js +198 -0
- package/index.js +3 -0
- package/lib/covenant-interface.js +713 -0
- package/lib/opcode.js +14 -7
- package/lib/smart_contract/API_REFERENCE.md +754 -0
- package/lib/smart_contract/DOCUMENTATION_SUMMARY.md +201 -0
- package/lib/smart_contract/EXAMPLES.md +751 -0
- package/lib/smart_contract/QUICK_START.md +549 -0
- package/lib/smart_contract/README.md +395 -0
- package/lib/smart_contract/builder.js +452 -0
- package/lib/smart_contract/covenant.js +336 -0
- package/lib/smart_contract/covenant_builder.js +512 -0
- package/lib/smart_contract/index.js +311 -0
- package/lib/smart_contract/opcode_list.js +30 -0
- package/lib/smart_contract/opcode_map.js +1174 -0
- package/lib/smart_contract/opcodes.md +1173 -0
- package/lib/smart_contract/preimage.js +903 -0
- package/lib/smart_contract/script_tester.js +487 -0
- package/lib/smart_contract/script_utils.js +609 -0
- package/lib/smart_contract/sighash.js +310 -0
- package/lib/smart_contract/smartledger-opcode_review.md +70 -0
- package/lib/smart_contract/test_integration.js +269 -0
- package/lib/smart_contract/utxo_generator.js +367 -0
- package/package.json +43 -10
- package/utilities/blockchain-state.json +20478 -3
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* covenant_builder.js
|
|
3
|
+
* ===================
|
|
4
|
+
*
|
|
5
|
+
* High-level JavaScript API for building Bitcoin SV covenant scripts.
|
|
6
|
+
* This module allows you to write covenant logic in JavaScript and automatically
|
|
7
|
+
* generates the corresponding Bitcoin Script ASM and hex.
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - JavaScript-to-Bitcoin Script translation
|
|
11
|
+
* - Preimage field extraction utilities
|
|
12
|
+
* - Conditional logic builders
|
|
13
|
+
* - Arithmetic and comparison operations
|
|
14
|
+
* - Data manipulation functions
|
|
15
|
+
* - Template-based covenant patterns
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
'use strict'
|
|
19
|
+
|
|
20
|
+
const { opcodeMap, scriptNum, utils } = require('./opcode_map')
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* CovenantBuilder Class - High-level covenant construction
|
|
24
|
+
*/
|
|
25
|
+
class CovenantBuilder {
|
|
26
|
+
constructor() {
|
|
27
|
+
this.operations = []
|
|
28
|
+
this.comments = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add a comment for documentation
|
|
33
|
+
*/
|
|
34
|
+
comment(text) {
|
|
35
|
+
this.comments.push(`// ${text}`)
|
|
36
|
+
return this
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Push a value onto the stack
|
|
41
|
+
*/
|
|
42
|
+
push(value) {
|
|
43
|
+
if (typeof value === 'number') {
|
|
44
|
+
if (value >= -1 && value <= 16) {
|
|
45
|
+
if (value === -1) {
|
|
46
|
+
this.operations.push('OP_1NEGATE')
|
|
47
|
+
} else if (value === 0) {
|
|
48
|
+
this.operations.push('OP_0')
|
|
49
|
+
} else {
|
|
50
|
+
this.operations.push(`OP_${value}`)
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
// For larger numbers, push encoded bytes
|
|
54
|
+
const encoded = scriptNum.encode(value)
|
|
55
|
+
this.operations.push(encoded.toString('hex'))
|
|
56
|
+
}
|
|
57
|
+
} else if (typeof value === 'string') {
|
|
58
|
+
// Assume hex string
|
|
59
|
+
this.operations.push(value)
|
|
60
|
+
} else if (Buffer.isBuffer(value)) {
|
|
61
|
+
this.operations.push(value.toString('hex'))
|
|
62
|
+
} else {
|
|
63
|
+
throw new Error(`Invalid value type: ${typeof value}`)
|
|
64
|
+
}
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Stack manipulation operations
|
|
70
|
+
*/
|
|
71
|
+
dup() { this.operations.push('OP_DUP'); return this }
|
|
72
|
+
drop() { this.operations.push('OP_DROP'); return this }
|
|
73
|
+
swap() { this.operations.push('OP_SWAP'); return this }
|
|
74
|
+
over() { this.operations.push('OP_OVER'); return this }
|
|
75
|
+
rot() { this.operations.push('OP_ROT'); return this }
|
|
76
|
+
pick(n) { this.push(n); this.operations.push('OP_PICK'); return this }
|
|
77
|
+
roll(n) { this.push(n); this.operations.push('OP_ROLL'); return this }
|
|
78
|
+
depth() { this.operations.push('OP_DEPTH'); return this }
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Arithmetic operations
|
|
82
|
+
*/
|
|
83
|
+
add() { this.operations.push('OP_ADD'); return this }
|
|
84
|
+
sub() { this.operations.push('OP_SUB'); return this }
|
|
85
|
+
mul() { this.operations.push('OP_MUL'); return this }
|
|
86
|
+
div() { this.operations.push('OP_DIV'); return this }
|
|
87
|
+
mod() { this.operations.push('OP_MOD'); return this }
|
|
88
|
+
negate() { this.operations.push('OP_NEGATE'); return this }
|
|
89
|
+
abs() { this.operations.push('OP_ABS'); return this }
|
|
90
|
+
min() { this.operations.push('OP_MIN'); return this }
|
|
91
|
+
max() { this.operations.push('OP_MAX'); return this }
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Comparison operations
|
|
95
|
+
*/
|
|
96
|
+
equal() { this.operations.push('OP_EQUAL'); return this }
|
|
97
|
+
equalVerify() { this.operations.push('OP_EQUALVERIFY'); return this }
|
|
98
|
+
numEqual() { this.operations.push('OP_NUMEQUAL'); return this }
|
|
99
|
+
numNotEqual() { this.operations.push('OP_NUMNOTEQUAL'); return this }
|
|
100
|
+
lessThan() { this.operations.push('OP_LESSTHAN'); return this }
|
|
101
|
+
greaterThan() { this.operations.push('OP_GREATERTHAN'); return this }
|
|
102
|
+
lessThanOrEqual() { this.operations.push('OP_LESSTHANOREQUAL'); return this }
|
|
103
|
+
greaterThanOrEqual() { this.operations.push('OP_GREATERTHANOREQUAL'); return this }
|
|
104
|
+
within() { this.operations.push('OP_WITHIN'); return this }
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Logical operations
|
|
108
|
+
*/
|
|
109
|
+
not() { this.operations.push('OP_NOT'); return this }
|
|
110
|
+
boolAnd() { this.operations.push('OP_BOOLAND'); return this }
|
|
111
|
+
boolOr() { this.operations.push('OP_BOOLOR'); return this }
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Bitwise operations
|
|
115
|
+
*/
|
|
116
|
+
and() { this.operations.push('OP_AND'); return this }
|
|
117
|
+
or() { this.operations.push('OP_OR'); return this }
|
|
118
|
+
xor() { this.operations.push('OP_XOR'); return this }
|
|
119
|
+
invert() { this.operations.push('OP_INVERT'); return this }
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Data manipulation operations
|
|
123
|
+
*/
|
|
124
|
+
cat() { this.operations.push('OP_CAT'); return this }
|
|
125
|
+
split() { this.operations.push('OP_SPLIT'); return this }
|
|
126
|
+
size() { this.operations.push('OP_SIZE'); return this }
|
|
127
|
+
left(n) { this.push(n); this.operations.push('OP_LEFT'); return this }
|
|
128
|
+
right(n) { this.push(n); this.operations.push('OP_RIGHT'); return this }
|
|
129
|
+
substr(start, length) {
|
|
130
|
+
this.push(length).push(start)
|
|
131
|
+
this.operations.push('OP_SUBSTR')
|
|
132
|
+
return this
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Cryptographic operations
|
|
137
|
+
*/
|
|
138
|
+
sha256() { this.operations.push('OP_SHA256'); return this }
|
|
139
|
+
hash256() { this.operations.push('OP_HASH256'); return this }
|
|
140
|
+
hash160() { this.operations.push('OP_HASH160'); return this }
|
|
141
|
+
ripemd160() { this.operations.push('OP_RIPEMD160'); return this }
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Control flow operations
|
|
145
|
+
*/
|
|
146
|
+
verify() { this.operations.push('OP_VERIFY'); return this }
|
|
147
|
+
return() { this.operations.push('OP_RETURN'); return this }
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Preimage field extraction utilities
|
|
151
|
+
*/
|
|
152
|
+
extractField(fieldName) {
|
|
153
|
+
this.comment(`Extract ${fieldName} field from preimage`)
|
|
154
|
+
|
|
155
|
+
const fieldMappings = {
|
|
156
|
+
'nVersion': { strategy: 'LEFT', offset: 0, length: 4 },
|
|
157
|
+
'hashPrevouts': { strategy: 'LEFT', offset: 4, length: 32 },
|
|
158
|
+
'hashSequence': { strategy: 'LEFT', offset: 36, length: 32 },
|
|
159
|
+
'outpoint': { strategy: 'LEFT', offset: 68, length: 36 },
|
|
160
|
+
'scriptLen': { strategy: 'DYNAMIC', position: 'after_outpoint' },
|
|
161
|
+
'scriptCode': { strategy: 'DYNAMIC', variable_length: true },
|
|
162
|
+
'value': { strategy: 'RIGHT', offsetFromEnd: 52, length: 8 },
|
|
163
|
+
'nSequence': { strategy: 'RIGHT', offsetFromEnd: 44, length: 4 },
|
|
164
|
+
'hashOutputs': { strategy: 'RIGHT', offsetFromEnd: 40, length: 32 },
|
|
165
|
+
'nLocktime': { strategy: 'RIGHT', offsetFromEnd: 8, length: 4 },
|
|
166
|
+
'sighashType': { strategy: 'RIGHT', offsetFromEnd: 4, length: 4 }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const field = fieldMappings[fieldName]
|
|
170
|
+
if (!field) {
|
|
171
|
+
throw new Error(`Unknown field: ${fieldName}`)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (field.strategy === 'LEFT') {
|
|
175
|
+
// LEFT extraction: split at offset, take left part, then extract field
|
|
176
|
+
this.push(field.offset + field.length)
|
|
177
|
+
.split()
|
|
178
|
+
.drop() // Drop right part
|
|
179
|
+
.push(field.offset)
|
|
180
|
+
.split()
|
|
181
|
+
.swap()
|
|
182
|
+
.drop() // Drop left padding, keep field
|
|
183
|
+
} else if (field.strategy === 'RIGHT') {
|
|
184
|
+
// RIGHT extraction: calculate split position from end
|
|
185
|
+
this.size()
|
|
186
|
+
.push(field.offsetFromEnd)
|
|
187
|
+
.sub()
|
|
188
|
+
.split()
|
|
189
|
+
.drop() // Drop left part
|
|
190
|
+
.push(field.length)
|
|
191
|
+
.split()
|
|
192
|
+
.drop() // Drop remaining, keep field
|
|
193
|
+
} else if (field.strategy === 'DYNAMIC') {
|
|
194
|
+
if (fieldName === 'scriptLen') {
|
|
195
|
+
// Extract scriptLen (varint after outpoint)
|
|
196
|
+
this.push(104) // Fixed left part (4+32+32+36)
|
|
197
|
+
.split()
|
|
198
|
+
.drop() // Drop left part
|
|
199
|
+
// For now, assume 1-byte varint
|
|
200
|
+
.push(1)
|
|
201
|
+
.split()
|
|
202
|
+
.swap()
|
|
203
|
+
.drop() // Keep just the length byte
|
|
204
|
+
} else {
|
|
205
|
+
throw new Error(`Dynamic extraction for ${fieldName} not implemented`)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return this
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Preimage validation patterns
|
|
214
|
+
*/
|
|
215
|
+
validateField(fieldName, expectedValue) {
|
|
216
|
+
this.comment(`Validate ${fieldName} equals expected value`)
|
|
217
|
+
this.extractField(fieldName)
|
|
218
|
+
this.push(expectedValue)
|
|
219
|
+
this.equalVerify()
|
|
220
|
+
return this
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Range validation
|
|
225
|
+
*/
|
|
226
|
+
validateRange(fieldName, min, max) {
|
|
227
|
+
this.comment(`Validate ${fieldName} is within range [${min}, ${max})`)
|
|
228
|
+
this.extractField(fieldName)
|
|
229
|
+
// Convert to number for comparison
|
|
230
|
+
// Stack: [field_value]
|
|
231
|
+
this.dup()
|
|
232
|
+
this.push(min)
|
|
233
|
+
this.greaterThanOrEqual()
|
|
234
|
+
this.verify() // Ensure >= min
|
|
235
|
+
|
|
236
|
+
this.push(max)
|
|
237
|
+
this.lessThan()
|
|
238
|
+
this.verify() // Ensure < max
|
|
239
|
+
|
|
240
|
+
return this
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Multi-field validation
|
|
245
|
+
*/
|
|
246
|
+
validateFields(fieldRules) {
|
|
247
|
+
this.comment('Multi-field validation')
|
|
248
|
+
Object.entries(fieldRules).forEach(([fieldName, rule]) => {
|
|
249
|
+
if (rule.equals) {
|
|
250
|
+
this.validateField(fieldName, rule.equals)
|
|
251
|
+
} else if (rule.min !== undefined || rule.max !== undefined) {
|
|
252
|
+
this.validateRange(fieldName, rule.min || 0, rule.max || Number.MAX_SAFE_INTEGER)
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
return this
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Conditional execution helpers
|
|
260
|
+
*/
|
|
261
|
+
if(condition) {
|
|
262
|
+
// This is a simplified IF - real implementation would need parser state
|
|
263
|
+
this.comment('Begin IF block')
|
|
264
|
+
if (typeof condition === 'function') {
|
|
265
|
+
condition(this)
|
|
266
|
+
}
|
|
267
|
+
this.operations.push('OP_IF')
|
|
268
|
+
return this
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
else() {
|
|
272
|
+
this.comment('ELSE block')
|
|
273
|
+
this.operations.push('OP_ELSE')
|
|
274
|
+
return this
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
endif() {
|
|
278
|
+
this.comment('End IF block')
|
|
279
|
+
this.operations.push('OP_ENDIF')
|
|
280
|
+
return this
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build the final script
|
|
285
|
+
*/
|
|
286
|
+
build() {
|
|
287
|
+
const asm = this.operations.join(' ')
|
|
288
|
+
const cleanedASM = this._cleanASM(asm)
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
operations: [...this.operations],
|
|
292
|
+
comments: [...this.comments],
|
|
293
|
+
asm: asm,
|
|
294
|
+
cleanedASM: cleanedASM,
|
|
295
|
+
hex: this._asmToHex(cleanedASM),
|
|
296
|
+
size: cleanedASM.split(' ').length
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Clean ASM for Bitcoin Script execution
|
|
302
|
+
*/
|
|
303
|
+
_cleanASM(asm) {
|
|
304
|
+
return asm.split(' ').map(token => {
|
|
305
|
+
// Convert decimal numbers to hex representation
|
|
306
|
+
if (/^\d+$/.test(token)) {
|
|
307
|
+
const num = parseInt(token)
|
|
308
|
+
if (num >= 1 && num <= 75) {
|
|
309
|
+
// Direct push of 1-75 bytes
|
|
310
|
+
return num.toString(16).padStart(2, '0')
|
|
311
|
+
} else {
|
|
312
|
+
// Encode as script number
|
|
313
|
+
return scriptNum.encode(num).toString('hex')
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return token
|
|
317
|
+
}).join(' ')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Convert ASM to hex (simplified)
|
|
322
|
+
*/
|
|
323
|
+
_asmToHex(asm) {
|
|
324
|
+
// This is a simplified implementation
|
|
325
|
+
// Real implementation would use BSV library
|
|
326
|
+
return asm.split(' ').map(token => {
|
|
327
|
+
if (opcodeMap[token]) {
|
|
328
|
+
return opcodeMap[token].code.toString(16).padStart(2, '0')
|
|
329
|
+
} else if (/^[0-9a-fA-F]+$/.test(token)) {
|
|
330
|
+
return token
|
|
331
|
+
} else {
|
|
332
|
+
return '00' // Unknown
|
|
333
|
+
}
|
|
334
|
+
}).join('')
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Simulate execution
|
|
339
|
+
*/
|
|
340
|
+
simulate(initialStack = []) {
|
|
341
|
+
return utils.simulate(this.operations, initialStack)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Generate documentation
|
|
346
|
+
*/
|
|
347
|
+
document() {
|
|
348
|
+
const built = this.build()
|
|
349
|
+
return {
|
|
350
|
+
title: 'Covenant Script Documentation',
|
|
351
|
+
operations: built.operations.length,
|
|
352
|
+
size: built.size,
|
|
353
|
+
asm: built.cleanedASM,
|
|
354
|
+
comments: this.comments,
|
|
355
|
+
structure: this._analyzeStructure()
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
_analyzeStructure() {
|
|
360
|
+
const structure = {
|
|
361
|
+
stack_operations: 0,
|
|
362
|
+
arithmetic: 0,
|
|
363
|
+
comparisons: 0,
|
|
364
|
+
crypto: 0,
|
|
365
|
+
data_manipulation: 0,
|
|
366
|
+
flow_control: 0
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
this.operations.forEach(op => {
|
|
370
|
+
const opInfo = opcodeMap[op]
|
|
371
|
+
if (opInfo) {
|
|
372
|
+
switch (opInfo.category) {
|
|
373
|
+
case 'stack': structure.stack_operations++; break
|
|
374
|
+
case 'arithmetic': structure.arithmetic++; break
|
|
375
|
+
case 'bitwise': structure.comparisons++; break
|
|
376
|
+
case 'crypto': structure.crypto++; break
|
|
377
|
+
case 'data': structure.data_manipulation++; break
|
|
378
|
+
case 'flow_control': structure.flow_control++; break
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
return structure
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Factory functions for common covenant patterns
|
|
389
|
+
*/
|
|
390
|
+
const CovenantTemplates = {
|
|
391
|
+
/**
|
|
392
|
+
* Value lock covenant - ensures output value matches expected amount
|
|
393
|
+
*/
|
|
394
|
+
valueLock: (expectedValue) => {
|
|
395
|
+
return new CovenantBuilder()
|
|
396
|
+
.comment('Value Lock Covenant')
|
|
397
|
+
.comment(`Expected value: ${expectedValue}`)
|
|
398
|
+
.validateField('value', expectedValue)
|
|
399
|
+
.push(1) // Success
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Hash lock covenant - requires preimage that hashes to expected value
|
|
404
|
+
*/
|
|
405
|
+
hashLock: (expectedHash) => {
|
|
406
|
+
return new CovenantBuilder()
|
|
407
|
+
.comment('Hash Lock Covenant')
|
|
408
|
+
.comment(`Expected hash: ${expectedHash}`)
|
|
409
|
+
.sha256()
|
|
410
|
+
.push(expectedHash)
|
|
411
|
+
.equalVerify()
|
|
412
|
+
.push(1) // Success
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Multi-signature covenant with field validation
|
|
417
|
+
*/
|
|
418
|
+
multiSigWithValidation: (requiredSigs, pubkeys, fieldRules) => {
|
|
419
|
+
const builder = new CovenantBuilder()
|
|
420
|
+
.comment('Multi-Signature Covenant with Field Validation')
|
|
421
|
+
.comment(`Required signatures: ${requiredSigs}`)
|
|
422
|
+
|
|
423
|
+
// Validate fields first
|
|
424
|
+
builder.validateFields(fieldRules)
|
|
425
|
+
|
|
426
|
+
// Then require signatures (simplified)
|
|
427
|
+
builder.comment('Signature validation (placeholder)')
|
|
428
|
+
.push(1) // Success placeholder
|
|
429
|
+
|
|
430
|
+
return builder
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Time-locked covenant
|
|
435
|
+
*/
|
|
436
|
+
timeLock: (locktime) => {
|
|
437
|
+
return new CovenantBuilder()
|
|
438
|
+
.comment('Time Lock Covenant')
|
|
439
|
+
.comment(`Locktime: ${locktime}`)
|
|
440
|
+
.validateField('nLocktime', locktime)
|
|
441
|
+
.push(1) // Success
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Complex validation covenant
|
|
446
|
+
*/
|
|
447
|
+
complexValidation: (rules) => {
|
|
448
|
+
const builder = new CovenantBuilder()
|
|
449
|
+
.comment('Complex Validation Covenant')
|
|
450
|
+
|
|
451
|
+
// Value range validation
|
|
452
|
+
if (rules.valueRange) {
|
|
453
|
+
builder.validateRange('value', rules.valueRange.min, rules.valueRange.max)
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Specific field validations
|
|
457
|
+
if (rules.fields) {
|
|
458
|
+
builder.validateFields(rules.fields)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Hash validation
|
|
462
|
+
if (rules.hashValidation) {
|
|
463
|
+
builder.sha256()
|
|
464
|
+
.push(rules.hashValidation.expectedHash)
|
|
465
|
+
.equalVerify()
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
builder.push(1) // Success
|
|
469
|
+
return builder
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
module.exports = {
|
|
474
|
+
CovenantBuilder,
|
|
475
|
+
CovenantTemplates
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// CLI demonstration
|
|
479
|
+
if (require.main === module) {
|
|
480
|
+
console.log("šļø Covenant Builder Demonstration")
|
|
481
|
+
console.log("==================================")
|
|
482
|
+
|
|
483
|
+
// Example 1: Simple value lock
|
|
484
|
+
console.log("\nš Example 1: Value Lock Covenant")
|
|
485
|
+
const valueLock = CovenantTemplates.valueLock('50c3000000000000')
|
|
486
|
+
const built = valueLock.build()
|
|
487
|
+
console.log("ASM:", built.cleanedASM)
|
|
488
|
+
console.log("Size:", built.size, "operations")
|
|
489
|
+
|
|
490
|
+
// Example 2: Custom covenant
|
|
491
|
+
console.log("\nš Example 2: Custom Arithmetic Covenant")
|
|
492
|
+
const custom = new CovenantBuilder()
|
|
493
|
+
.comment('Validate that value field equals 5 + 3')
|
|
494
|
+
.extractField('value')
|
|
495
|
+
.push(5)
|
|
496
|
+
.push(3)
|
|
497
|
+
.add()
|
|
498
|
+
.numEqual()
|
|
499
|
+
.verify()
|
|
500
|
+
.push(1)
|
|
501
|
+
|
|
502
|
+
const customBuilt = custom.build()
|
|
503
|
+
console.log("Operations:", customBuilt.operations.length)
|
|
504
|
+
console.log("ASM:", customBuilt.cleanedASM)
|
|
505
|
+
|
|
506
|
+
// Example 3: Documentation
|
|
507
|
+
console.log("\nš Example 3: Documentation Generation")
|
|
508
|
+
const docs = custom.document()
|
|
509
|
+
console.log("Structure analysis:", docs.structure)
|
|
510
|
+
console.log("Comments:")
|
|
511
|
+
docs.comments.forEach(comment => console.log(" " + comment))
|
|
512
|
+
}
|