@smartledger/bsv 4.4.0 → 4.6.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.
@@ -17,15 +17,18 @@ var Signature = require('../crypto/signature')
17
17
  // SIGHASH_ALL | SIGHASH_FORKID — the BSV default these covenants are built for.
18
18
  var SIGHASH = Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID // 0x41
19
19
 
20
- // Post-Genesis consensus flags (big numbers, OP_CAT/OP_SPLIT/OP_MUL, FORKID,
21
- // CLTV/CSV). LOW_S is enforced: the OP_PUSH_TX grind produces a canonical
22
- // (low-S) in-script signature, so these covenants meet mainnet standardness.
20
+ // Post-Genesis consensus + STANDARD RELAY flags — what mainnet miners actually
21
+ // enforce. Includes SCRIPT_VERIFY_MINIMALDATA (every push must be minimal) and
22
+ // SCRIPT_VERIFY_LOW_S (canonical signatures). Verifying with these locally
23
+ // catches non-relayable covenants BEFORE broadcast — covenants verified here
24
+ // have been deployed and spent on BSV mainnet.
23
25
  function flags () {
24
26
  var I = bsv.Script.Interpreter
25
27
  return I.SCRIPT_VERIFY_P2SH |
26
28
  I.SCRIPT_VERIFY_STRICTENC |
27
29
  I.SCRIPT_VERIFY_DERSIG |
28
30
  I.SCRIPT_VERIFY_LOW_S |
31
+ I.SCRIPT_VERIFY_MINIMALDATA |
29
32
  I.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY |
30
33
  I.SCRIPT_VERIFY_CHECKSEQUENCEVERIFY |
31
34
  I.SCRIPT_ENABLE_SIGHASH_FORKID |
@@ -102,6 +105,13 @@ function p2pkhOutput (addressOrPubKey, satoshis) {
102
105
 
103
106
  /** Minimal little-endian script-number Buffer (push as data to put a number on-stack). */
104
107
  function scriptNum (n) {
108
+ // Minimal on-stack number: 0..16 and -1 use the dedicated opcodes (OP_0..OP_16,
109
+ // OP_1NEGATE) instead of a data push, so covenant scripts stay MINIMALDATA-clean
110
+ // (mainnet relay policy). Larger values use a minimal scriptNum push. The result
111
+ // is passed to Script.add(), which accepts an opcode number or a Buffer.
112
+ if (n === 0) return bsv.Opcode.OP_0
113
+ if (n === -1) return bsv.Opcode.OP_1NEGATE
114
+ if (n >= 1 && n <= 16) return bsv.Opcode['OP_' + n]
105
115
  return new BN(n).toScriptNumBuffer()
106
116
  }
107
117
 
@@ -0,0 +1,71 @@
1
+ 'use strict'
2
+ /**
3
+ * SmartContract.trace — a covenant stack debugger.
4
+ *
5
+ * Step-traces a locking/unlocking pair through Script.Interpreter and records the
6
+ * stack + alt-stack after every opcode, so you can watch an OP_PUSH_TX covenant
7
+ * build its signature and enforce its constraints. Uses the interpreter's
8
+ * stepListener hook. Call SmartContract.enableGenesis() first for OP_PUSH_TX
9
+ * covenants (their preimage element / opcode count exceed pre-Genesis limits).
10
+ */
11
+
12
+ var bsv = require('../..')
13
+ var helpers = require('./covenant_helpers')
14
+
15
+ function opLabel (opcodenum) {
16
+ if (opcodenum === 0) return 'OP_0'
17
+ if (opcodenum >= 1 && opcodenum <= 75) return 'PUSH(' + opcodenum + ')'
18
+ if (opcodenum === 76) return 'OP_PUSHDATA1'
19
+ if (opcodenum === 77) return 'OP_PUSHDATA2'
20
+ if (opcodenum === 78) return 'OP_PUSHDATA4'
21
+ try { return bsv.Opcode.fromNumber(opcodenum).toString() } catch (e) { return '0x' + opcodenum.toString(16) }
22
+ }
23
+
24
+ function hex (buf, max) {
25
+ max = max || 16
26
+ var h = Buffer.from(buf).toString('hex')
27
+ return h.length <= max * 2 ? (h || '0') : h.slice(0, max * 2) + '...(' + buf.length + 'B)'
28
+ }
29
+
30
+ /**
31
+ * @returns {{ ok:boolean, err:string, steps:Array<{phase,op,stack,altstack}> }}
32
+ */
33
+ function trace (unlock, lock, opts) {
34
+ opts = opts || {}
35
+ var interp = new bsv.Script.Interpreter()
36
+ var steps = []
37
+ interp.stepListener = function (step, stack, altstack) {
38
+ steps.push({
39
+ phase: interp.script === lock ? 'lock' : 'unlock',
40
+ op: opLabel(step.opcode && typeof step.opcode.toNumber === 'function' ? step.opcode.toNumber() : step.opcode.num),
41
+ stack: stack.map(function (b) { return hex(b) }),
42
+ altstack: altstack.map(function (b) { return hex(b) })
43
+ })
44
+ }
45
+ var ok = interp.verify(
46
+ unlock, lock,
47
+ opts.tx || new bsv.Transaction(),
48
+ opts.inputIndex || 0,
49
+ typeof opts.flags === 'number' ? opts.flags : helpers.flags(),
50
+ new bsv.crypto.BN(opts.satoshis || 0)
51
+ )
52
+ return { ok: ok, err: interp.errstr || '', steps: steps }
53
+ }
54
+
55
+ /** Pretty-print a trace result to a string. */
56
+ function format (result) {
57
+ var lines = []
58
+ var phase = null
59
+ result.steps.forEach(function (s, i) {
60
+ if (s.phase !== phase) { lines.push('-- ' + s.phase + ' script --'); phase = s.phase }
61
+ var stk = s.stack.length ? s.stack.join(' | ') : '(empty)'
62
+ var alt = s.altstack.length ? ' alt:[' + s.altstack.join(' | ') + ']' : ''
63
+ lines.push(String(i).padStart(3) + ' ' + s.op.padEnd(16) + ' -> [' + stk + ']' + alt)
64
+ })
65
+ lines.push((result.ok ? 'VALID' : 'INVALID') + (result.err ? ' (' + result.err + ')' : ''))
66
+ return lines.join('\n')
67
+ }
68
+
69
+ function print (result) { console.log(format(result)); return result } // eslint-disable-line no-console
70
+
71
+ module.exports = { trace: trace, format: format, print: print, opLabel: opLabel }
@@ -0,0 +1,129 @@
1
+ 'use strict'
2
+ /**
3
+ * SmartContract.policy — a declarative covenant DSL.
4
+ *
5
+ * Describe a spending POLICY and compile it to a verified OP_PUSH_TX locking
6
+ * script, no hand-written opcodes:
7
+ *
8
+ * var c = bsv.SmartContract.policy()
9
+ * .payTo(aliceAddr, 9500) // the spend MUST create this output...
10
+ * .lockUntil(800000) // ...with nLockTime >= 800000
11
+ * .compile()
12
+ * // c.lock -> locking Script
13
+ * // c.outputs -> the required Transaction.Output[]
14
+ * // c.unlock(spendTx, satoshis) -> grinds + returns the preimage unlock Script
15
+ *
16
+ * Each clause compiles to one preimage-field check on top of a single OP_PUSH_TX
17
+ * authentication; clauses AND together. Requires post-Genesis limits at verify
18
+ * time (SmartContract.enableGenesis()).
19
+ */
20
+
21
+ var bsv = require('../..')
22
+ var PushTx = require('./pushtx')
23
+ var PELS = require('./pels')
24
+ var Token = require('./token')
25
+ var helpers = require('./covenant_helpers')
26
+
27
+ var Script = bsv.Script
28
+ var Opcode = bsv.Opcode
29
+ var Transaction = bsv.Transaction
30
+ var n = helpers.scriptNum
31
+
32
+ function toOutput (dest, satoshis) {
33
+ if (dest instanceof Transaction.Output) return dest
34
+ if (dest instanceof Script) return new Transaction.Output({ script: dest, satoshis: satoshis })
35
+ return helpers.p2pkhOutput(dest, satoshis)
36
+ }
37
+
38
+ // Grind the OP_PUSH_TX nonce (nLockTime) from `base` upward until the in-script
39
+ // signature is a clean low-S DER. Grinding from `base` keeps nLockTime within a
40
+ // lockUntil floor instead of fighting it (grind uses nLockTime as its nonce).
41
+ function grindFrom (spendTx, inputIndex, lock, satoshis, base, maxTries) {
42
+ maxTries = maxTries || 50000
43
+ for (var t = 0; t < maxTries; t++) {
44
+ spendTx.nLockTime = base + t
45
+ var pre = helpers.rawPreimage(spendTx, inputIndex, lock, satoshis)
46
+ if (PushTx.sFromPreimage(pre)) return pre
47
+ }
48
+ throw new Error('covenant grind failed after ' + maxTries + ' tries')
49
+ }
50
+
51
+ function Policy () {
52
+ if (!(this instanceof Policy)) return new Policy()
53
+ this._outputs = []
54
+ this._minLockTime = null
55
+ }
56
+
57
+ /** Require the spend to create this output (call once per required output, in order). */
58
+ Policy.prototype.payTo = function (dest, satoshis) {
59
+ this._outputs.push(toOutput(dest, satoshis))
60
+ return this
61
+ }
62
+
63
+ /** Require the spend's nLockTime to be >= height. */
64
+ Policy.prototype.lockUntil = function (height) {
65
+ this._minLockTime = height
66
+ return this
67
+ }
68
+
69
+ /** The exact outputs a valid spend must contain. */
70
+ Policy.prototype.outputs = function () {
71
+ return this._outputs.map(function (o) { return new Transaction.Output(o.toObject()) })
72
+ }
73
+
74
+ Policy.prototype.describe = function () {
75
+ var parts = []
76
+ this._outputs.forEach(function (o, i) {
77
+ parts.push('output[' + i + '] = ' + o.satoshis + ' sat to ' + o.script.toASM().slice(0, 24) + '...')
78
+ })
79
+ if (this._minLockTime !== null) parts.push('nLockTime >= ' + this._minLockTime)
80
+ return 'spend allowed iff: ' + parts.join(' AND ')
81
+ }
82
+
83
+ /** Compile to a locking script + spend helpers. */
84
+ Policy.prototype.compile = function () {
85
+ if (this._outputs.length === 0 && this._minLockTime === null) {
86
+ throw new Error('empty policy: add at least one constraint (payTo / lockUntil)')
87
+ }
88
+ var policy = this
89
+ var s = new Script().add(Opcode.OP_DUP)
90
+ PushTx.pushTxCore(s) // authenticate the preimage
91
+ s.add(Opcode.OP_VERIFY) // stack: [preimage]
92
+
93
+ if (this._outputs.length) {
94
+ var expected = PushTx.hashOutputs(this._outputs)
95
+ s.add(Opcode.OP_DUP)
96
+ PushTx.extractHashOutputs(s)
97
+ s.add(Buffer.from(expected)).add(Opcode.OP_EQUALVERIFY)
98
+ }
99
+
100
+ if (this._minLockTime !== null) {
101
+ s.add(Opcode.OP_DUP)
102
+ s.add(n(8)).add(Opcode.OP_RIGHT).add(n(4)).add(Opcode.OP_LEFT) // last 8, first 4 = nLockTime
103
+ s.add(Opcode.OP_BIN2NUM).add(n(this._minLockTime)).add(Opcode.OP_GREATERTHANOREQUAL).add(Opcode.OP_VERIFY)
104
+ }
105
+
106
+ s.add(Opcode.OP_DROP).add(Opcode.OP_1) // drop preimage; succeed
107
+
108
+ return {
109
+ lock: s,
110
+ outputs: policy.outputs(),
111
+ describe: function () { return policy.describe() },
112
+ unlock: function (spendTx, satoshis, inputIndex) {
113
+ inputIndex = inputIndex || 0
114
+ var base = policy._minLockTime === null ? 0 : policy._minLockTime
115
+ if (policy._minLockTime !== null) {
116
+ spendTx.inputs[inputIndex].sequenceNumber = 0xfffffffe // non-final so locktime applies
117
+ }
118
+ return new Script().add(grindFrom(spendTx, inputIndex, s, satoshis, base))
119
+ }
120
+ }
121
+ }
122
+
123
+ function policy () { return new Policy() }
124
+
125
+ // Terminal shortcuts for the non-composable whole-covenant patterns.
126
+ policy.perpetual = function (fee) { return PELS.pelsCovenant(fee) }
127
+ policy.token = function (fee, ownerHash) { return Token.ownershipToken(fee, ownerHash) }
128
+
129
+ module.exports = { policy: policy, Policy: Policy }
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * SmartContract Module
3
3
  * ===================
4
- *
4
+ *
5
5
  * Production-ready covenant functionality for Bitcoin SV (BSV).
6
6
  * Implements BIP-143 transaction preimage parsing and covenant operations
7
7
  * with comprehensive SIGHASH flag support and error handling.
8
- *
8
+ *
9
9
  * Classes:
10
10
  * - Covenant: Core covenant creation and spending logic
11
11
  * - Preimage: BIP-143 preimage parsing and validation with field extraction
@@ -43,7 +43,20 @@ var SmartContract = {
43
43
  Locks: require('./locks'),
44
44
  PushTx: require('./pushtx'),
45
45
  PELS: require('./pels'),
46
- Token: require('./token')
46
+ Token: require('./token'),
47
+
48
+ // Declarative covenant DSL + stack debugger (v4.5.0)
49
+ DSL: require('./dsl'),
50
+ Policy: require('./dsl').Policy,
51
+ Debugger: require('./debugger')
52
+ }
53
+
54
+ // Declarative spending policy -> verified OP_PUSH_TX covenant (v4.5.0).
55
+ // (Exposes the policy() factory with its .perpetual()/.token() shortcuts.)
56
+ SmartContract.policy = SmartContract.DSL.policy
57
+ // Step-trace a locking/unlocking pair (returns { ok, err, steps }).
58
+ SmartContract.trace = function (unlockingScript, lockingScript, opts) {
59
+ return SmartContract.Debugger.trace(unlockingScript, lockingScript, opts)
47
60
  }
48
61
 
49
62
  // --- Covenant convenience API (v4.2.0) ---
@@ -69,161 +82,161 @@ SmartContract.valueCovenant = function (expectedHashOutputs) {
69
82
  }
70
83
 
71
84
  // Convenience constructor methods
72
- SmartContract.createCovenant = function(privateKey, options) {
85
+ SmartContract.createCovenant = function (privateKey, options) {
73
86
  return new SmartContract.Covenant(privateKey, options)
74
87
  }
75
88
 
76
- SmartContract.extractPreimage = function(preimage, options) {
89
+ SmartContract.extractPreimage = function (preimage, options) {
77
90
  return new SmartContract.Preimage(preimage, options)
78
91
  }
79
92
 
80
- SmartContract.analyzeSIGHASH = function(sighashType) {
93
+ SmartContract.analyzeSIGHASH = function (sighashType) {
81
94
  return new SmartContract.SIGHASH(sighashType)
82
95
  }
83
96
 
84
- SmartContract.buildCovenant = function(privateKey, options) {
97
+ SmartContract.buildCovenant = function (privateKey, options) {
85
98
  return new SmartContract.Builder(privateKey, options)
86
99
  }
87
100
 
88
- SmartContract.createUTXOGenerator = function(options) {
101
+ SmartContract.createUTXOGenerator = function (options) {
89
102
  return new SmartContract.UTXOGenerator(options)
90
103
  }
91
104
 
92
- SmartContract.createTestEnvironment = function(options) {
105
+ SmartContract.createTestEnvironment = function (options) {
93
106
  return SmartContract.UTXOGenerator.createTestEnvironment(options)
94
107
  }
95
108
 
96
- SmartContract.testScript = function(unlocking, locking, options) {
109
+ SmartContract.testScript = function (unlocking, locking, options) {
97
110
  return SmartContract.ScriptTester.test(unlocking, locking, options)
98
111
  }
99
112
 
100
- SmartContract.testCovenant = function(preimageHex, constraints, options) {
113
+ SmartContract.testCovenant = function (preimageHex, constraints, options) {
101
114
  return SmartContract.ScriptTester.testCovenant(preimageHex, constraints, options)
102
115
  }
103
116
 
104
- SmartContract.testFieldExtraction = function(preimageHex, fieldName, options) {
117
+ SmartContract.testFieldExtraction = function (preimageHex, fieldName, options) {
105
118
  return SmartContract.ScriptTester.testFieldExtraction(preimageHex, fieldName, options)
106
119
  }
107
120
 
108
- SmartContract.debugScript = function(config, options) {
121
+ SmartContract.debugScript = function (config, options) {
109
122
  var tester = new SmartContract.ScriptTester(options)
110
123
  return tester.debug(config)
111
124
  }
112
125
 
113
126
  // Educational and utility functions
114
- SmartContract.explainZeroHashes = function() {
127
+ SmartContract.explainZeroHashes = function () {
115
128
  return SmartContract.SIGHASH.explainZeroMystery()
116
129
  }
117
130
 
118
- SmartContract.getAllSIGHASHTypes = function() {
131
+ SmartContract.getAllSIGHASHTypes = function () {
119
132
  return SmartContract.SIGHASH.getAllTypes()
120
133
  }
121
134
 
122
- SmartContract.demonstrateAllSIGHASH = function() {
135
+ SmartContract.demonstrateAllSIGHASH = function () {
123
136
  return SmartContract.SIGHASH.generateAllDemonstrations()
124
137
  }
125
138
 
126
139
  // JavaScript-to-Bitcoin Script covenant generation
127
- SmartContract.createCovenantBuilder = function() {
140
+ SmartContract.createCovenantBuilder = function () {
128
141
  return new SmartContract.CovenantBuilder()
129
142
  }
130
143
 
131
- SmartContract.createValueLockCovenant = function(expectedValue) {
144
+ SmartContract.createValueLockCovenant = function (expectedValue) {
132
145
  return SmartContract.CovenantTemplates.valueLock(expectedValue)
133
146
  }
134
147
 
135
- SmartContract.createHashLockCovenant = function(expectedHash) {
148
+ SmartContract.createHashLockCovenant = function (expectedHash) {
136
149
  return SmartContract.CovenantTemplates.hashLock(expectedHash)
137
150
  }
138
151
 
139
- SmartContract.createComplexValidationCovenant = function(rules) {
152
+ SmartContract.createComplexValidationCovenant = function (rules) {
140
153
  return SmartContract.CovenantTemplates.complexValidation(rules)
141
154
  }
142
155
 
143
156
  // Opcode mapping utilities
144
- SmartContract.getOpcodeMap = function() {
157
+ SmartContract.getOpcodeMap = function () {
145
158
  return SmartContract.OpcodeMap.opcodeMap
146
159
  }
147
160
 
148
- SmartContract.simulateScript = function(operations, initialStack) {
161
+ SmartContract.simulateScript = function (operations, initialStack) {
149
162
  return SmartContract.OpcodeMap.utils.simulate(operations, initialStack)
150
163
  }
151
164
 
152
- SmartContract.createASMFromJS = function(operations) {
165
+ SmartContract.createASMFromJS = function (operations) {
153
166
  return SmartContract.OpcodeMap.utils.createASM(operations)
154
167
  }
155
168
 
156
169
  // Script analysis and conversion utilities
157
- SmartContract.scriptToASM = function(scriptBuffer) {
170
+ SmartContract.scriptToASM = function (scriptBuffer) {
158
171
  return SmartContract.ScriptUtils.scriptToASM(scriptBuffer)
159
172
  }
160
173
 
161
- SmartContract.asmToScript = function(asmString) {
174
+ SmartContract.asmToScript = function (asmString) {
162
175
  return SmartContract.ScriptUtils.asmToScript(asmString)
163
176
  }
164
177
 
165
- SmartContract.asmToHex = function(asmString) {
178
+ SmartContract.asmToHex = function (asmString) {
166
179
  return SmartContract.ScriptUtils.asmToHex(asmString)
167
180
  }
168
181
 
169
- SmartContract.hexToASM = function(hexString) {
182
+ SmartContract.hexToASM = function (hexString) {
170
183
  return SmartContract.ScriptUtils.hexToASM(hexString)
171
184
  }
172
185
 
173
- SmartContract.scriptToHex = function(script) {
186
+ SmartContract.scriptToHex = function (script) {
174
187
  return SmartContract.ScriptUtils.scriptToHex(script)
175
188
  }
176
189
 
177
- SmartContract.validateASM = function(asmString) {
190
+ SmartContract.validateASM = function (asmString) {
178
191
  return SmartContract.ScriptUtils.validateASM(asmString)
179
192
  }
180
193
 
181
- SmartContract.validateScript = function(script) {
194
+ SmartContract.validateScript = function (script) {
182
195
  return SmartContract.ScriptUtils.validateScript(script)
183
196
  }
184
197
 
185
- SmartContract.estimateScriptSize = function(script) {
198
+ SmartContract.estimateScriptSize = function (script) {
186
199
  return SmartContract.ScriptUtils.estimateScriptSize(script)
187
200
  }
188
201
 
189
- SmartContract.optimizeScript = function(script) {
202
+ SmartContract.optimizeScript = function (script) {
190
203
  return SmartContract.ScriptUtils.optimizeScript(script)
191
204
  }
192
205
 
193
- SmartContract.compareScripts = function(scriptA, scriptB) {
206
+ SmartContract.compareScripts = function (scriptA, scriptB) {
194
207
  return SmartContract.ScriptUtils.compareScripts(scriptA, scriptB)
195
208
  }
196
209
 
197
- SmartContract.explainScript = function(script) {
210
+ SmartContract.explainScript = function (script) {
198
211
  return SmartContract.ScriptUtils.explainScript(script)
199
212
  }
200
213
 
201
- SmartContract.scriptMetrics = function(script) {
214
+ SmartContract.scriptMetrics = function (script) {
202
215
  return SmartContract.ScriptUtils.scriptMetrics(script)
203
216
  }
204
217
 
205
- SmartContract.analyzeComplexity = function(script) {
218
+ SmartContract.analyzeComplexity = function (script) {
206
219
  return SmartContract.ScriptUtils.analyzeComplexity(script)
207
220
  }
208
221
 
209
- SmartContract.findOptimizations = function(script) {
222
+ SmartContract.findOptimizations = function (script) {
210
223
  return SmartContract.ScriptUtils.findOptimizations(script)
211
224
  }
212
225
 
213
- SmartContract.covenantToEnglish = function(covenant) {
226
+ SmartContract.covenantToEnglish = function (covenant) {
214
227
  return SmartContract.ScriptUtils.covenantToEnglish(covenant)
215
228
  }
216
229
 
217
- SmartContract.batchTestScripts = function(scripts, options) {
230
+ SmartContract.batchTestScripts = function (scripts, options) {
218
231
  return SmartContract.ScriptUtils.batchTestScripts(scripts, options)
219
232
  }
220
233
 
221
234
  // Debug and analysis tools
222
- SmartContract.examineStack = function(lockingHex, unlockingHex) {
235
+ SmartContract.examineStack = function (lockingHex, unlockingHex) {
223
236
  return SmartContract.StackExaminer.runScript(lockingHex, unlockingHex)
224
237
  }
225
238
 
226
- SmartContract.debugScriptExecution = function(unlockingScript, lockingScript, options) {
239
+ SmartContract.debugScriptExecution = function (unlockingScript, lockingScript, options) {
227
240
  options = options || {}
228
241
  if (options.stepMode) {
229
242
  return SmartContract.ScriptInterpreter.stepThroughScript(
@@ -240,18 +253,18 @@ SmartContract.debugScriptExecution = function(unlockingScript, lockingScript, op
240
253
  }
241
254
  }
242
255
 
243
- SmartContract.parseScript = function(scriptInput) {
256
+ SmartContract.parseScript = function (scriptInput) {
244
257
  return SmartContract.ScriptInterpreter.parseScript(scriptInput)
245
258
  }
246
259
 
247
- SmartContract.printStack = function(stack, altstack) {
260
+ SmartContract.printStack = function (stack, altstack) {
248
261
  return SmartContract.ScriptInterpreter.printStack(stack, altstack)
249
262
  }
250
263
 
251
264
  // Quick covenant creation utility
252
- SmartContract.createQuickCovenant = function(type, params) {
265
+ SmartContract.createQuickCovenant = function (type, params) {
253
266
  const builder = SmartContract.createCovenantBuilder()
254
-
267
+
255
268
  switch (type) {
256
269
  case 'value_lock':
257
270
  return builder
@@ -261,7 +274,7 @@ SmartContract.createQuickCovenant = function(type, params) {
261
274
  .greaterThanOrEqual()
262
275
  .verify()
263
276
  .build()
264
-
277
+
265
278
  case 'hash_lock':
266
279
  return builder
267
280
  .comment(`Hash lock: requires preimage of ${params.hash}`)
@@ -270,7 +283,7 @@ SmartContract.createQuickCovenant = function(type, params) {
270
283
  .push(params.hash)
271
284
  .equalVerify()
272
285
  .build()
273
-
286
+
274
287
  case 'time_lock':
275
288
  return builder
276
289
  .comment(`Time lock: requires nLockTime > ${params.timestamp}`)
@@ -279,7 +292,7 @@ SmartContract.createQuickCovenant = function(type, params) {
279
292
  .greaterThan()
280
293
  .verify()
281
294
  .build()
282
-
295
+
283
296
  case 'multi_condition':
284
297
  let covenant = builder.comment('Multi-condition covenant')
285
298
  params.conditions.forEach(condition => {
@@ -308,20 +321,20 @@ SmartContract.createQuickCovenant = function(type, params) {
308
321
  }
309
322
  })
310
323
  return covenant.build()
311
-
324
+
312
325
  default:
313
326
  throw new Error(`Unknown covenant type: ${type}`)
314
327
  }
315
328
  }
316
329
 
317
330
  // Complete workflow helpers
318
- SmartContract.completeCovenantFlow = function(privateKey, p2pkhUtxo, broadcastCallback) {
331
+ SmartContract.completeCovenantFlow = function (privateKey, p2pkhUtxo, broadcastCallback) {
319
332
  var covenant = new SmartContract.Covenant(privateKey)
320
333
  return covenant.completeFlow(p2pkhUtxo, broadcastCallback)
321
334
  }
322
335
 
323
336
  // Educational utilities
324
- SmartContract.getEducationalResources = function() {
337
+ SmartContract.getEducationalResources = function () {
325
338
  return {
326
339
  zeroHashMystery: SmartContract.SIGHASH.explainZeroMystery(),
327
340
  sighashTypes: SmartContract.SIGHASH.getAllTypes(),
@@ -331,17 +344,17 @@ SmartContract.getEducationalResources = function() {
331
344
 
332
345
  // Utility functions
333
346
  SmartContract.utils = {}
334
- SmartContract.utils.reconstructP2pkhScript = function(address) {
347
+ SmartContract.utils.reconstructP2pkhScript = function (address) {
335
348
  return bsv.Script.buildPublicKeyHashOut(address).toHex()
336
349
  }
337
350
 
338
- SmartContract.utils.createCovenantAddress = function(script) {
351
+ SmartContract.utils.createCovenantAddress = function (script) {
339
352
  // Create P2SH address for covenant script
340
353
  var scriptHash = bsv.crypto.Hash.sha256ripemd160(script.toBuffer())
341
354
  return bsv.Address.fromScriptHash(scriptHash)
342
355
  }
343
356
 
344
- SmartContract.utils.decodeCompactSize = function(buffer, offset) {
357
+ SmartContract.utils.decodeCompactSize = function (buffer, offset) {
345
358
  return SmartContract.Preimage.decodeCompactSize(buffer, offset)
346
359
  }
347
360
 
@@ -380,4 +393,4 @@ SmartContract.features = {
380
393
  SmartContract.interpretScript = SmartContract.debugScriptExecution
381
394
  SmartContract.getScriptMetrics = SmartContract.scriptMetrics
382
395
 
383
- module.exports = SmartContract
396
+ module.exports = SmartContract
@@ -54,8 +54,9 @@ function reverseBytes (script, n) {
54
54
  */
55
55
  function pushTxCore (script) {
56
56
  script.add(Opcode.OP_HASH256) // z = HASH256(preimage), 32B BE
57
- reverseBytes(script, 32) // -> little-endian
58
- script.add(Buffer.from([0x00])).add(Opcode.OP_CAT).add(Opcode.OP_BIN2NUM) // e (positive)
57
+ reverseBytes(script, 32) // -> little-endian = e. The grind guarantees e is
58
+ // already positive and minimally encoded (z[0] in 0x01..0x7f), so NO 0x00 sign
59
+ // byte is appended — keeping the script MINIMALDATA-clean (mainnet-relayable).
59
60
  script.add(gxLe).add(Opcode.OP_ADD) // e + Gx
60
61
  script.add(N_LE).add(Opcode.OP_MOD) // s = (e + Gx) mod n
61
62
  script.add(scriptNum(32)).add(Opcode.OP_NUM2BIN) // s -> 32-byte LE
@@ -64,7 +65,7 @@ function pushTxCore (script) {
64
65
  // r-value inside the DER prefix and the body of the 02||Gx pubkey). Sharing it
65
66
  // via the altstack saves a 32-byte constant vs. embedding Gx twice.
66
67
  script.add(Gx).add(Opcode.OP_DUP) // [s_be, Gx, Gx]
67
- script.add(Buffer.from([0x02])).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // pubkey = 02||Gx
68
+ script.add(Opcode.OP_2).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // pubkey = 02||Gx (OP_2 = minimal push of 0x02)
68
69
  script.add(Opcode.OP_TOALTSTACK) // park pubkey ; [s_be, Gx]
69
70
  script.add(Buffer.from([0x30, 0x44, 0x02, 0x20])).add(Opcode.OP_SWAP).add(Opcode.OP_CAT) // 30440220||Gx
70
71
  script.add(Buffer.from([0x02, 0x20])).add(Opcode.OP_CAT) // ||0220 => DER prefix
@@ -117,6 +118,10 @@ var HALF_N = new BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681
117
118
  */
118
119
  function sFromPreimage (preimage) {
119
120
  var z = Hash.sha256sha256(preimage)
121
+ // Script no longer sign-extends z, so e (= reverse(z) as a number) must be
122
+ // positive AND minimally encoded: its little-endian MSB (= z[0]) in 0x01..0x7f.
123
+ // This keeps the locking script MINIMALDATA-clean (mainnet-relayable).
124
+ if (z[0] < 0x01 || z[0] > 0x7f) return null
120
125
  var s = new BN(z).add(new BN(Gx)).mod(N)
121
126
  if (s.gt(HALF_N)) return null // enforce low-S (canonical / non-malleable)
122
127
  var sBE = s.toBuffer('be', 32)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartledger/bsv",
3
- "version": "4.4.0",
3
+ "version": "4.6.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",
@@ -198,6 +198,7 @@
198
198
  "request": "browser-request"
199
199
  },
200
200
  "dependencies": {
201
+ "@smartledger/bsv": "^4.4.0",
201
202
  "aes-js": "^3.1.2",
202
203
  "bn.js": "=4.11.9",
203
204
  "bs58": "=4.0.1",