@smartledger/bsv 7.9.0 → 7.10.1
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 +103 -0
- package/README.md +38 -38
- package/bsv-covenant.min.js +1 -1
- package/bsv-ecies.min.js +1 -1
- package/bsv-gdaf.min.js +54 -54
- package/bsv-ltp.min.js +58 -85
- package/bsv-message.min.js +2 -2
- package/bsv-mnemonic.min.js +12 -14
- package/bsv-script-helper.min.js +1 -1
- package/bsv-shamir.min.js +14 -16
- package/bsv-smartcontract.min.js +23 -25
- package/bsv.bundle.js +54 -54
- package/bsv.min.js +54 -54
- package/build/esbuild.js +86 -5
- package/docs/MODULE_REFERENCE_COMPLETE.md +27 -27
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +1 -1
- package/docs/getting-started/INSTALLATION.md +23 -23
- package/docs/getting-started/QUICK_START.md +7 -7
- package/docs/migration/FROM_BSV_1_5_6.md +5 -5
- package/index.js +9 -1
- package/lib/covenant/helpers.js +146 -0
- package/lib/covenant/index.js +21 -0
- package/lib/covenant/pushtx.js +243 -0
- package/lib/covenant-interface.js +418 -397
- package/lib/custom-script-helper.js +44 -38
- package/lib/ecies/bitcore-ecies.js +3 -4
- package/lib/ecies/electrum-ecies.js +4 -6
- package/lib/ecies/errors.js +1 -1
- package/lib/gdaf/attestation-signer.js +7 -7
- package/lib/gdaf/attestation-verifier.js +5 -6
- package/lib/gdaf/did-resolver.js +10 -9
- package/lib/gdaf/smartledger-anchor.js +13 -12
- package/lib/gdaf/zk-prover.js +8 -8
- package/lib/ltp/anchor.js +2 -3
- package/lib/ltp/claim.js +2 -3
- package/lib/ltp/index.js +2 -3
- package/lib/ltp/obligation.js +9 -9
- package/lib/ltp/proof.js +7 -7
- package/lib/ltp/registry.js +4 -5
- package/lib/ltp/right.js +11 -11
- package/lib/message/index.js +5 -4
- package/lib/message/message.js +11 -11
- package/lib/mnemonic/errors.js +1 -1
- package/lib/mnemonic/index.js +5 -4
- package/lib/mnemonic/mnemonic.js +22 -10
- package/lib/ordinals/bsv20.js +4 -4
- package/lib/ordinals/inscription.js +8 -12
- package/lib/ordinals/ordlock.js +26 -21
- package/lib/smart_contract/authorizers.js +8 -6
- package/lib/smart_contract/builder.js +32 -26
- package/lib/smart_contract/covenant.js +32 -25
- package/lib/smart_contract/covenant_helpers.js +8 -139
- package/lib/smart_contract/debugger.js +9 -6
- package/lib/smart_contract/dsl.js +5 -6
- package/lib/smart_contract/index.js +11 -8
- package/lib/smart_contract/locks.js +3 -4
- package/lib/smart_contract/opcode_list.js +2 -3
- package/lib/smart_contract/pels.js +4 -5
- package/lib/smart_contract/preimage.js +11 -7
- package/lib/smart_contract/pushtx.js +8 -263
- package/lib/smart_contract/script_interpreter.js +112 -105
- package/lib/smart_contract/script_tester.js +24 -18
- package/lib/smart_contract/script_utils.js +15 -14
- package/lib/smart_contract/sighash.js +23 -15
- package/lib/smart_contract/stack_examiner.js +70 -63
- package/lib/smart_contract/token.js +6 -6
- package/lib/smart_contract/utxo_generator.js +21 -15
- package/lib/smartutxo.js +27 -21
- package/lib/spv/headerchain.js +3 -3
- package/lib/spv/merkleproof.js +3 -3
- package/package.json +4 -2
- package/test/build/bundle_externals.js +116 -0
- package/version.js +1 -1
|
@@ -1,22 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SmartLedger Covenant Interface - Advanced BSV Covenant Development
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Provides high-level abstractions for covenant development while preserving
|
|
5
5
|
* full access to low-level BSV operations. Works alongside existing BSV API.
|
|
6
|
-
*
|
|
6
|
+
*
|
|
7
7
|
* Features:
|
|
8
8
|
* - Simplified covenant creation and validation
|
|
9
|
-
* - Automatic preimage generation and parsing
|
|
9
|
+
* - Automatic preimage generation and parsing
|
|
10
10
|
* - Template-based covenant patterns
|
|
11
11
|
* - Full compatibility with existing bsv library
|
|
12
12
|
* - Granular control when needed
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
var sighash = require('./transaction/sighash')
|
|
16
|
+
var Output = require('./transaction/output')
|
|
17
|
+
var Interpreter = require('./script/interpreter')
|
|
18
|
+
var Signature = require('./crypto/signature')
|
|
19
|
+
var BN = require('./crypto/bn')
|
|
20
|
+
var Transaction = require('./transaction')
|
|
21
|
+
var Opcode = require('./opcode')
|
|
22
|
+
var Script = require('./script')
|
|
16
23
|
|
|
17
24
|
class CovenantInterface {
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Convenience access to the assembled library, for consumers who have a
|
|
27
|
+
* CovenantInterface and want the rest of the namespace.
|
|
28
|
+
*
|
|
29
|
+
* A getter, not a constructor assignment. The previous comment justified the
|
|
30
|
+
* deferral with "this file is reached while index.js is still initializing" —
|
|
31
|
+
* which is not true: nothing in index.js or anywhere under lib/ requires this
|
|
32
|
+
* module, only two files in examples/. It was also never read internally, so an
|
|
33
|
+
* eager require bought nothing and left this as the last package-root require in
|
|
34
|
+
* lib/ after a refactor whose whole purpose was removing them.
|
|
35
|
+
*
|
|
36
|
+
* Resolving on access keeps the property working for anyone using it while making
|
|
37
|
+
* the root require cost nothing for everyone else. `require` is cached.
|
|
38
|
+
*/
|
|
39
|
+
get bsv () {
|
|
40
|
+
return require('../index.js')
|
|
20
41
|
}
|
|
21
42
|
|
|
22
43
|
/**
|
|
@@ -24,59 +45,59 @@ class CovenantInterface {
|
|
|
24
45
|
* @param {Object} config - Transaction configuration
|
|
25
46
|
* @returns {CovenantTransaction} Enhanced transaction object
|
|
26
47
|
*/
|
|
27
|
-
createCovenantTransaction(config) {
|
|
28
|
-
const tx = new
|
|
29
|
-
|
|
48
|
+
createCovenantTransaction (config) {
|
|
49
|
+
const tx = new Transaction()
|
|
50
|
+
|
|
30
51
|
// Add inputs
|
|
31
52
|
if (config.inputs) {
|
|
32
|
-
config.inputs.forEach(input => tx.from(input))
|
|
53
|
+
config.inputs.forEach(input => tx.from(input))
|
|
33
54
|
}
|
|
34
|
-
|
|
55
|
+
|
|
35
56
|
// Add outputs
|
|
36
57
|
if (config.outputs) {
|
|
37
58
|
config.outputs.forEach(output => {
|
|
38
59
|
if (output.script) {
|
|
39
|
-
tx.addOutput(new
|
|
60
|
+
tx.addOutput(new Output({
|
|
40
61
|
script: output.script,
|
|
41
62
|
satoshis: output.satoshis
|
|
42
|
-
}))
|
|
63
|
+
}))
|
|
43
64
|
} else {
|
|
44
|
-
tx.to(output.address, output.satoshis)
|
|
65
|
+
tx.to(output.address, output.satoshis)
|
|
45
66
|
}
|
|
46
|
-
})
|
|
67
|
+
})
|
|
47
68
|
}
|
|
48
|
-
|
|
69
|
+
|
|
49
70
|
// Set fee configuration
|
|
50
71
|
if (config.feePerKb) {
|
|
51
|
-
tx.feePerKb(config.feePerKb)
|
|
72
|
+
tx.feePerKb(config.feePerKb)
|
|
52
73
|
}
|
|
53
|
-
|
|
54
|
-
return new CovenantTransaction(tx, this)
|
|
74
|
+
|
|
75
|
+
return new CovenantTransaction(tx, this)
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
/**
|
|
58
79
|
* Create manual signature using correct BSV API
|
|
59
80
|
* @param {Transaction} transaction - BSV transaction
|
|
60
81
|
* @param {PrivateKey} privateKey - Private key to sign with
|
|
61
|
-
* @param {number} inputIndex - Input index
|
|
82
|
+
* @param {number} inputIndex - Input index
|
|
62
83
|
* @param {Script} lockingScript - Locking script being spent
|
|
63
84
|
* @param {number} satoshis - Amount in satoshis
|
|
64
85
|
* @param {number} sighashType - Signature hash type
|
|
65
86
|
* @returns {Buffer} Complete signature with sighash type
|
|
66
87
|
*/
|
|
67
|
-
createSignature(transaction, privateKey, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
68
|
-
sighashType = sighashType || (
|
|
69
|
-
|
|
70
|
-
const signature =
|
|
88
|
+
createSignature (transaction, privateKey, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
89
|
+
sighashType = sighashType || (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID)
|
|
90
|
+
|
|
91
|
+
const signature = sighash.sign(
|
|
71
92
|
transaction,
|
|
72
93
|
privateKey,
|
|
73
94
|
sighashType,
|
|
74
95
|
inputIndex,
|
|
75
96
|
lockingScript,
|
|
76
|
-
new
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
return Buffer.concat([signature.toDER(), Buffer.from([sighashType])])
|
|
97
|
+
new BN(satoshis)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
return Buffer.concat([signature.toDER(), Buffer.from([sighashType])])
|
|
80
101
|
}
|
|
81
102
|
|
|
82
103
|
/**
|
|
@@ -88,18 +109,18 @@ class CovenantInterface {
|
|
|
88
109
|
* @param {number} sighashType - Signature hash type
|
|
89
110
|
* @returns {CovenantPreimage} Parsed preimage object
|
|
90
111
|
*/
|
|
91
|
-
getPreimage(transaction, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
92
|
-
sighashType = sighashType || (
|
|
93
|
-
|
|
94
|
-
const preimage =
|
|
112
|
+
getPreimage (transaction, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
113
|
+
sighashType = sighashType || (Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID)
|
|
114
|
+
|
|
115
|
+
const preimage = sighash.sighash(
|
|
95
116
|
transaction,
|
|
96
117
|
sighashType,
|
|
97
118
|
inputIndex,
|
|
98
119
|
lockingScript,
|
|
99
|
-
new
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
return new CovenantPreimage(preimage)
|
|
120
|
+
new BN(satoshis)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return new CovenantPreimage(preimage)
|
|
103
124
|
}
|
|
104
125
|
|
|
105
126
|
/**
|
|
@@ -109,73 +130,73 @@ class CovenantInterface {
|
|
|
109
130
|
* @param {PublicKey} authorizedKey - Key authorized for state transitions
|
|
110
131
|
* @returns {Script} State machine covenant script
|
|
111
132
|
*/
|
|
112
|
-
createStateMachine(states, currentState, authorizedKey) {
|
|
113
|
-
const script = new
|
|
114
|
-
|
|
133
|
+
createStateMachine (states, currentState, authorizedKey) {
|
|
134
|
+
const script = new Script()
|
|
135
|
+
|
|
115
136
|
// Check current state
|
|
116
|
-
script.add(Buffer.from([currentState]))
|
|
117
|
-
script.add(
|
|
118
|
-
script.add(
|
|
119
|
-
|
|
137
|
+
script.add(Buffer.from([currentState]))
|
|
138
|
+
script.add(Opcode.OP_EQUAL)
|
|
139
|
+
script.add(Opcode.OP_VERIFY)
|
|
140
|
+
|
|
120
141
|
// Validate state transition logic
|
|
121
142
|
for (let i = 0; i < states.length - 1; i++) {
|
|
122
143
|
if (i === currentState) {
|
|
123
144
|
// Valid next states
|
|
124
145
|
states[i].nextStates.forEach(nextState => {
|
|
125
|
-
script.add(Buffer.from([nextState]))
|
|
126
|
-
script.add(
|
|
127
|
-
script.add(
|
|
146
|
+
script.add(Buffer.from([nextState]))
|
|
147
|
+
script.add(Opcode.OP_EQUAL)
|
|
148
|
+
script.add(Opcode.OP_IF)
|
|
128
149
|
// Add transition logic here
|
|
129
|
-
script.add(
|
|
130
|
-
})
|
|
150
|
+
script.add(Opcode.OP_ENDIF)
|
|
151
|
+
})
|
|
131
152
|
}
|
|
132
153
|
}
|
|
133
|
-
|
|
154
|
+
|
|
134
155
|
// Require authorized signature
|
|
135
|
-
script.add(
|
|
136
|
-
script.add(
|
|
137
|
-
script.add(authorizedKey.toAddress().hashBuffer)
|
|
138
|
-
script.add(
|
|
139
|
-
script.add(
|
|
140
|
-
|
|
141
|
-
return script
|
|
156
|
+
script.add(Opcode.OP_DUP)
|
|
157
|
+
script.add(Opcode.OP_HASH160)
|
|
158
|
+
script.add(authorizedKey.toAddress().hashBuffer)
|
|
159
|
+
script.add(Opcode.OP_EQUALVERIFY)
|
|
160
|
+
script.add(Opcode.OP_CHECKSIG)
|
|
161
|
+
|
|
162
|
+
return script
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
/**
|
|
145
166
|
* Covenant Template: Escrow with Arbitration
|
|
146
167
|
* @param {PublicKey} buyer - Buyer public key
|
|
147
|
-
* @param {PublicKey} seller - Seller public key
|
|
168
|
+
* @param {PublicKey} seller - Seller public key
|
|
148
169
|
* @param {PublicKey} arbitrator - Arbitrator public key
|
|
149
170
|
* @param {number} timelock - Optional timelock for refund
|
|
150
171
|
* @returns {Script} Escrow covenant script
|
|
151
172
|
*/
|
|
152
|
-
createEscrow(buyer, seller, arbitrator, timelock = null) {
|
|
153
|
-
const script = new
|
|
154
|
-
|
|
173
|
+
createEscrow (buyer, seller, arbitrator, timelock = null) {
|
|
174
|
+
const script = new Script()
|
|
175
|
+
|
|
155
176
|
if (timelock) {
|
|
156
177
|
// Time-locked refund path
|
|
157
|
-
script.add(
|
|
158
|
-
script.add(Buffer.from(timelock.toString(16).padStart(8, '0'), 'hex').reverse())
|
|
159
|
-
script.add(
|
|
160
|
-
script.add(
|
|
161
|
-
script.add(buyer.toBuffer())
|
|
162
|
-
script.add(
|
|
163
|
-
script.add(
|
|
178
|
+
script.add(Opcode.OP_IF)
|
|
179
|
+
script.add(Buffer.from(timelock.toString(16).padStart(8, '0'), 'hex').reverse())
|
|
180
|
+
script.add(Opcode.OP_CHECKLOCKTIMEVERIFY)
|
|
181
|
+
script.add(Opcode.OP_DROP)
|
|
182
|
+
script.add(buyer.toBuffer())
|
|
183
|
+
script.add(Opcode.OP_CHECKSIG)
|
|
184
|
+
script.add(Opcode.OP_ELSE)
|
|
164
185
|
}
|
|
165
|
-
|
|
186
|
+
|
|
166
187
|
// 2-of-3 multisig: buyer + seller, buyer + arbitrator, or seller + arbitrator
|
|
167
|
-
script.add(
|
|
168
|
-
script.add(buyer.toBuffer())
|
|
169
|
-
script.add(seller.toBuffer())
|
|
170
|
-
script.add(arbitrator.toBuffer())
|
|
171
|
-
script.add(
|
|
172
|
-
script.add(
|
|
173
|
-
|
|
188
|
+
script.add(Opcode.OP_2)
|
|
189
|
+
script.add(buyer.toBuffer())
|
|
190
|
+
script.add(seller.toBuffer())
|
|
191
|
+
script.add(arbitrator.toBuffer())
|
|
192
|
+
script.add(Opcode.OP_3)
|
|
193
|
+
script.add(Opcode.OP_CHECKMULTISIG)
|
|
194
|
+
|
|
174
195
|
if (timelock) {
|
|
175
|
-
script.add(
|
|
196
|
+
script.add(Opcode.OP_ENDIF)
|
|
176
197
|
}
|
|
177
|
-
|
|
178
|
-
return script
|
|
198
|
+
|
|
199
|
+
return script
|
|
179
200
|
}
|
|
180
201
|
|
|
181
202
|
/**
|
|
@@ -185,30 +206,30 @@ class CovenantInterface {
|
|
|
185
206
|
* @param {Script} additionalRules - Optional additional validation rules
|
|
186
207
|
* @returns {Script} Token covenant script
|
|
187
208
|
*/
|
|
188
|
-
createTokenTransfer(newOwner, minAmount, additionalRules = null) {
|
|
189
|
-
const script = new
|
|
190
|
-
|
|
209
|
+
createTokenTransfer (newOwner, minAmount, additionalRules = null) {
|
|
210
|
+
const script = new Script()
|
|
211
|
+
|
|
191
212
|
// Check minimum amount
|
|
192
213
|
if (minAmount > 0) {
|
|
193
|
-
script.add(
|
|
194
|
-
script.add(Buffer.from(minAmount.toString(16), 'hex'))
|
|
195
|
-
script.add(
|
|
196
|
-
script.add(
|
|
214
|
+
script.add(Opcode.OP_DUP)
|
|
215
|
+
script.add(Buffer.from(minAmount.toString(16), 'hex'))
|
|
216
|
+
script.add(Opcode.OP_GREATERTHANOREQUAL)
|
|
217
|
+
script.add(Opcode.OP_VERIFY)
|
|
197
218
|
}
|
|
198
|
-
|
|
219
|
+
|
|
199
220
|
// Add custom rules
|
|
200
221
|
if (additionalRules) {
|
|
201
|
-
script.add(additionalRules.toBuffer())
|
|
222
|
+
script.add(additionalRules.toBuffer())
|
|
202
223
|
}
|
|
203
|
-
|
|
224
|
+
|
|
204
225
|
// Transfer to new owner
|
|
205
|
-
script.add(
|
|
206
|
-
script.add(
|
|
207
|
-
script.add(newOwner.toAddress().hashBuffer)
|
|
208
|
-
script.add(
|
|
209
|
-
script.add(
|
|
210
|
-
|
|
211
|
-
return script
|
|
226
|
+
script.add(Opcode.OP_DUP)
|
|
227
|
+
script.add(Opcode.OP_HASH160)
|
|
228
|
+
script.add(newOwner.toAddress().hashBuffer)
|
|
229
|
+
script.add(Opcode.OP_EQUALVERIFY)
|
|
230
|
+
script.add(Opcode.OP_CHECKSIG)
|
|
231
|
+
|
|
232
|
+
return script
|
|
212
233
|
}
|
|
213
234
|
|
|
214
235
|
/**
|
|
@@ -219,20 +240,20 @@ class CovenantInterface {
|
|
|
219
240
|
* @param {Script} lockingScript - Locking script
|
|
220
241
|
* @returns {CovenantValidation} Validation result
|
|
221
242
|
*/
|
|
222
|
-
validateCovenant(transaction, inputIndex, unlockingScript, lockingScript) {
|
|
243
|
+
validateCovenant (transaction, inputIndex, unlockingScript, lockingScript) {
|
|
223
244
|
try {
|
|
224
|
-
const interpreter = new
|
|
225
|
-
const flags =
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const isValid = interpreter.verify(unlockingScript, lockingScript, transaction, inputIndex, flags)
|
|
232
|
-
|
|
233
|
-
return new CovenantValidation(isValid, interpreter.errstr || 'Success')
|
|
245
|
+
const interpreter = new Interpreter()
|
|
246
|
+
const flags = Interpreter.SCRIPT_VERIFY_P2SH |
|
|
247
|
+
Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
248
|
+
Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
249
|
+
Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES |
|
|
250
|
+
Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES
|
|
251
|
+
|
|
252
|
+
const isValid = interpreter.verify(unlockingScript, lockingScript, transaction, inputIndex, flags)
|
|
253
|
+
|
|
254
|
+
return new CovenantValidation(isValid, interpreter.errstr || 'Success')
|
|
234
255
|
} catch (error) {
|
|
235
|
-
return new CovenantValidation(false, error.message)
|
|
256
|
+
return new CovenantValidation(false, error.message)
|
|
236
257
|
}
|
|
237
258
|
}
|
|
238
259
|
|
|
@@ -242,20 +263,20 @@ class CovenantInterface {
|
|
|
242
263
|
* @param {Object} params - Covenant parameters
|
|
243
264
|
* @returns {Script} Advanced covenant script
|
|
244
265
|
*/
|
|
245
|
-
createAdvancedCovenant(type, params = {}) {
|
|
266
|
+
createAdvancedCovenant (type, params = {}) {
|
|
246
267
|
const templates = {
|
|
247
268
|
'pushtx': this.createPushtxCovenant,
|
|
248
269
|
'perpetual': this.createPerpetualCovenant,
|
|
249
270
|
'pels': this.createPerpetualCovenant, // PELS is a type of perpetual covenant
|
|
250
271
|
'introspection': this.createIntrospectionCovenant
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const templateFn = templates[type]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const templateFn = templates[type]
|
|
254
275
|
if (!templateFn) {
|
|
255
|
-
throw new Error(`Unknown advanced covenant type: ${type}. Available types: ${Object.keys(templates).join(', ')}`)
|
|
276
|
+
throw new Error(`Unknown advanced covenant type: ${type}. Available types: ${Object.keys(templates).join(', ')}`)
|
|
256
277
|
}
|
|
257
|
-
|
|
258
|
-
return templateFn.call(this, params)
|
|
278
|
+
|
|
279
|
+
return templateFn.call(this, params)
|
|
259
280
|
}
|
|
260
281
|
|
|
261
282
|
/**
|
|
@@ -264,62 +285,62 @@ class CovenantInterface {
|
|
|
264
285
|
* @param {Object} params - PUSHTX parameters
|
|
265
286
|
* @returns {Script} PUSHTX covenant script
|
|
266
287
|
*/
|
|
267
|
-
createPushtxCovenant(params = {}) {
|
|
288
|
+
createPushtxCovenant (params = {}) {
|
|
268
289
|
const {
|
|
269
290
|
publicKey = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
|
|
270
291
|
enforceOutputs = true,
|
|
271
292
|
sighashType = 0x41 // SIGHASH_ALL | SIGHASH_FORKID
|
|
272
|
-
} = params
|
|
273
|
-
|
|
274
|
-
const script = new
|
|
275
|
-
|
|
293
|
+
} = params
|
|
294
|
+
|
|
295
|
+
const script = new Script()
|
|
296
|
+
|
|
276
297
|
// Message construction for covenant validation (items 1-7 from preimage)
|
|
277
|
-
script.add(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
298
|
+
script.add(Opcode.OP_2DUP)
|
|
299
|
+
.add(Opcode.OP_HASH256) // Hash outputs (item 9)
|
|
300
|
+
.add(Opcode.OP_SWAP)
|
|
301
|
+
|
|
281
302
|
// Add sequence number (item 8)
|
|
282
303
|
script.add(Buffer.from('ffffffff', 'hex'))
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
304
|
+
.add(Opcode.OP_CAT)
|
|
305
|
+
.add(Opcode.OP_SWAP)
|
|
306
|
+
.add(Opcode.OP_CAT)
|
|
307
|
+
|
|
287
308
|
// Add locktime (item 10) and sighash flag (item 11)
|
|
288
309
|
script.add(Buffer.from('00000000', 'hex')) // nLockTime
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
310
|
+
.add(Buffer.from('41000000', 'hex')) // sighashType in little-endian
|
|
311
|
+
.add(Opcode.OP_CAT)
|
|
312
|
+
.add(Opcode.OP_CAT)
|
|
313
|
+
|
|
293
314
|
// PUSHTX signature generation (k=a=1 optimization)
|
|
294
|
-
script.add(
|
|
295
|
-
|
|
315
|
+
script.add(Opcode.OP_HASH256)
|
|
316
|
+
|
|
296
317
|
// Add generator point x-coordinate
|
|
297
|
-
const generatorX = '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
|
318
|
+
const generatorX = '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
|
298
319
|
script.add(Buffer.from(generatorX, 'hex'))
|
|
299
|
-
|
|
300
|
-
|
|
320
|
+
.add(Opcode.OP_ADD)
|
|
321
|
+
|
|
301
322
|
// Modular arithmetic with secp256k1 order
|
|
302
|
-
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
323
|
+
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
303
324
|
script.add(Buffer.from(secp256k1Order, 'hex'))
|
|
304
|
-
|
|
305
|
-
|
|
325
|
+
.add(Opcode.OP_MOD)
|
|
326
|
+
|
|
306
327
|
// Convert to canonical DER format
|
|
307
|
-
this._addDERConversion(script)
|
|
308
|
-
|
|
328
|
+
this._addDERConversion(script)
|
|
329
|
+
|
|
309
330
|
// Add sighash flag and public key
|
|
310
331
|
script.add(Buffer.from([sighashType]))
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
332
|
+
.add(Opcode.OP_CAT)
|
|
333
|
+
.add(Buffer.from(publicKey, 'hex'))
|
|
334
|
+
|
|
314
335
|
// Verify signature with OP_CHECKSIGVERIFY
|
|
315
|
-
script.add(
|
|
316
|
-
|
|
336
|
+
script.add(Opcode.OP_CHECKSIGVERIFY)
|
|
337
|
+
|
|
317
338
|
if (enforceOutputs) {
|
|
318
339
|
// Message is now on stack - add output enforcement
|
|
319
|
-
this._addOutputValidation(script, params)
|
|
340
|
+
this._addOutputValidation(script, params)
|
|
320
341
|
}
|
|
321
|
-
|
|
322
|
-
return script
|
|
342
|
+
|
|
343
|
+
return script
|
|
323
344
|
}
|
|
324
345
|
|
|
325
346
|
/**
|
|
@@ -328,70 +349,70 @@ class CovenantInterface {
|
|
|
328
349
|
* @param {Object} params - PELS parameters
|
|
329
350
|
* @returns {Script} PELS covenant script
|
|
330
351
|
*/
|
|
331
|
-
createPerpetualCovenant(params = {}) {
|
|
352
|
+
createPerpetualCovenant (params = {}) {
|
|
332
353
|
const {
|
|
333
354
|
publicKeyHash,
|
|
334
355
|
feeDeduction = 512,
|
|
335
356
|
enforceScript = true,
|
|
336
357
|
enforceValue = true
|
|
337
|
-
} = params
|
|
338
|
-
|
|
358
|
+
} = params
|
|
359
|
+
|
|
339
360
|
if (!publicKeyHash) {
|
|
340
|
-
throw new Error('publicKeyHash required for perpetual covenant')
|
|
361
|
+
throw new Error('publicKeyHash required for perpetual covenant')
|
|
341
362
|
}
|
|
342
|
-
|
|
343
|
-
const script = new
|
|
344
|
-
|
|
363
|
+
|
|
364
|
+
const script = new Script()
|
|
365
|
+
|
|
345
366
|
// Construct message with fee-adjusted output validation
|
|
346
|
-
script.add(
|
|
347
|
-
|
|
348
|
-
|
|
367
|
+
script.add(Opcode.OP_2DUP)
|
|
368
|
+
.add(Opcode.OP_BIN2NUM)
|
|
369
|
+
|
|
349
370
|
// Deduct transaction fee if specified
|
|
350
371
|
if (feeDeduction > 0) {
|
|
351
372
|
script.add(Buffer.from(feeDeduction.toString(16).padStart(4, '0'), 'hex'))
|
|
352
|
-
|
|
373
|
+
.add(Opcode.OP_SUB)
|
|
353
374
|
}
|
|
354
|
-
|
|
375
|
+
|
|
355
376
|
// Convert back to 8-byte little-endian
|
|
356
377
|
script.add(Buffer.from([0x08]))
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
378
|
+
.add(Opcode.OP_NUM2BIN)
|
|
379
|
+
.add(Opcode.OP_SWAP)
|
|
380
|
+
.add(Opcode.OP_CAT)
|
|
381
|
+
|
|
361
382
|
// Hash the new output for message construction
|
|
362
|
-
script.add(
|
|
363
|
-
|
|
383
|
+
script.add(Opcode.OP_HASH256)
|
|
384
|
+
|
|
364
385
|
// Build complete sighash preimage
|
|
365
|
-
this._addPreimageConstruction(script, params)
|
|
366
|
-
|
|
367
|
-
// Generate and verify PUSHTX signature
|
|
368
|
-
this._addPushtxSignature(script, params)
|
|
369
|
-
|
|
386
|
+
this._addPreimageConstruction(script, params)
|
|
387
|
+
|
|
388
|
+
// Generate and verify PUSHTX signature
|
|
389
|
+
this._addPushtxSignature(script, params)
|
|
390
|
+
|
|
370
391
|
// Extract and validate output structure against previous output
|
|
371
392
|
// Skip to value field (104 bytes into preimage)
|
|
372
|
-
script.add(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
393
|
+
script.add(Opcode.OP_SWAP)
|
|
394
|
+
.add(Buffer.from([0x68])) // 104 decimal = 0x68 hex
|
|
395
|
+
.add(Opcode.OP_SPLIT)
|
|
396
|
+
.add(Opcode.OP_NIP)
|
|
397
|
+
.add(Opcode.OP_SWAP)
|
|
398
|
+
.add(Buffer.from([0x08])) // 8 bytes for value
|
|
399
|
+
.add(Opcode.OP_SPLIT)
|
|
400
|
+
.add(Opcode.OP_SWAP)
|
|
401
|
+
.add(Opcode.OP_CAT)
|
|
402
|
+
|
|
382
403
|
// Verify current output matches previous output format
|
|
383
404
|
if (enforceValue || enforceScript) {
|
|
384
|
-
script.add(
|
|
405
|
+
script.add(Opcode.OP_EQUALVERIFY)
|
|
385
406
|
}
|
|
386
|
-
|
|
407
|
+
|
|
387
408
|
// Final P2PKH check for authenticity
|
|
388
|
-
script.add(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return script
|
|
409
|
+
script.add(Opcode.OP_DUP)
|
|
410
|
+
.add(Opcode.OP_HASH160)
|
|
411
|
+
.add(Buffer.from(publicKeyHash, 'hex'))
|
|
412
|
+
.add(Opcode.OP_EQUALVERIFY)
|
|
413
|
+
.add(Opcode.OP_CHECKSIG)
|
|
414
|
+
|
|
415
|
+
return script
|
|
395
416
|
}
|
|
396
417
|
|
|
397
418
|
/**
|
|
@@ -399,152 +420,152 @@ class CovenantInterface {
|
|
|
399
420
|
* @param {Object} params - Introspection parameters
|
|
400
421
|
* @returns {Script} Introspection covenant script
|
|
401
422
|
*/
|
|
402
|
-
createIntrospectionCovenant(params = {}) {
|
|
423
|
+
createIntrospectionCovenant (params = {}) {
|
|
403
424
|
const {
|
|
404
425
|
validateInputs = false,
|
|
405
426
|
validateOutputs = true,
|
|
406
427
|
validateSequence = false,
|
|
407
428
|
validateLocktime = false
|
|
408
|
-
} = params
|
|
409
|
-
|
|
410
|
-
const script = new
|
|
411
|
-
|
|
429
|
+
} = params
|
|
430
|
+
|
|
431
|
+
const script = new Script()
|
|
432
|
+
|
|
412
433
|
// Get preimage for analysis
|
|
413
|
-
this._addPushtxSignature(script, params)
|
|
414
|
-
|
|
434
|
+
this._addPushtxSignature(script, params)
|
|
435
|
+
|
|
415
436
|
// Parse preimage fields for validation
|
|
416
437
|
if (validateInputs) {
|
|
417
|
-
script.add(
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
438
|
+
script.add(Opcode.OP_DUP)
|
|
439
|
+
.add(Buffer.from([0x04, 0x24])) // Skip to hashPrevouts
|
|
440
|
+
.add(Opcode.OP_SPLIT)
|
|
441
|
+
.add(Opcode.OP_DROP)
|
|
421
442
|
// Add input validation logic here
|
|
422
443
|
}
|
|
423
|
-
|
|
444
|
+
|
|
424
445
|
if (validateOutputs) {
|
|
425
446
|
// Extract hashOutputs for validation
|
|
426
|
-
script.add(
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
// Add output validation logic here
|
|
447
|
+
script.add(Opcode.OP_DUP)
|
|
448
|
+
.add(Buffer.from([0x64])) // Skip to hashOutputs position
|
|
449
|
+
.add(Opcode.OP_SPLIT)
|
|
450
|
+
.add(Opcode.OP_NIP)
|
|
451
|
+
.add(Buffer.from([0x20])) // 32 bytes
|
|
452
|
+
.add(Opcode.OP_SPLIT)
|
|
453
|
+
.add(Opcode.OP_DROP)
|
|
454
|
+
// Add output validation logic here
|
|
434
455
|
}
|
|
435
|
-
|
|
436
|
-
return script
|
|
456
|
+
|
|
457
|
+
return script
|
|
437
458
|
}
|
|
438
459
|
|
|
439
460
|
// Helper methods for advanced covenant construction
|
|
440
|
-
|
|
441
|
-
_addDERConversion(script) {
|
|
461
|
+
|
|
462
|
+
_addDERConversion (script) {
|
|
442
463
|
// Convert s value to canonical form (s <= n/2)
|
|
443
|
-
const secp256k1HalfOrder = '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0'
|
|
444
|
-
|
|
445
|
-
script.add(
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
464
|
+
const secp256k1HalfOrder = '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0'
|
|
465
|
+
|
|
466
|
+
script.add(Opcode.OP_DUP)
|
|
467
|
+
.add(Buffer.from(secp256k1HalfOrder, 'hex'))
|
|
468
|
+
.add(Opcode.OP_GREATERTHAN)
|
|
469
|
+
.add(Opcode.OP_IF)
|
|
470
|
+
|
|
450
471
|
// If s > n/2, compute n - s to prevent malleability
|
|
451
|
-
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
472
|
+
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
452
473
|
script.add(Buffer.from(secp256k1Order, 'hex'))
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
script.add(
|
|
457
|
-
|
|
474
|
+
.add(Opcode.OP_SWAP)
|
|
475
|
+
.add(Opcode.OP_SUB)
|
|
476
|
+
|
|
477
|
+
script.add(Opcode.OP_ENDIF)
|
|
478
|
+
|
|
458
479
|
// Add DER sequence formatting
|
|
459
|
-
script.add(
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
480
|
+
script.add(Opcode.OP_SIZE)
|
|
481
|
+
.add(Opcode.OP_DUP)
|
|
482
|
+
.add(Buffer.from([0x24])) // 36 bytes total length
|
|
483
|
+
.add(Opcode.OP_ADD)
|
|
484
|
+
.add(Buffer.from([0x30])) // DER sequence tag
|
|
485
|
+
.add(Opcode.OP_SWAP)
|
|
486
|
+
.add(Opcode.OP_CAT)
|
|
487
|
+
|
|
467
488
|
// Add r component (using generator x-coordinate as r)
|
|
468
489
|
script.add(Buffer.from('022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802', 'hex'))
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
490
|
+
.add(Opcode.OP_CAT)
|
|
491
|
+
.add(Opcode.OP_SWAP)
|
|
492
|
+
.add(Opcode.OP_CAT)
|
|
493
|
+
.add(Opcode.OP_SWAP)
|
|
494
|
+
.add(Opcode.OP_CAT)
|
|
474
495
|
}
|
|
475
|
-
|
|
476
|
-
_addOutputValidation(script, params) {
|
|
477
|
-
const { enforceValue, enforceScript, enforceCount } = params
|
|
478
|
-
|
|
496
|
+
|
|
497
|
+
_addOutputValidation (script, params) {
|
|
498
|
+
const { enforceValue, enforceScript, enforceCount } = params
|
|
499
|
+
|
|
479
500
|
// Output validation after CHECKSIGVERIFY leaves preimage on stack
|
|
480
501
|
if (enforceValue) {
|
|
481
|
-
script.add(
|
|
482
|
-
|
|
483
|
-
|
|
502
|
+
script.add(Opcode.OP_DUP)
|
|
503
|
+
.add(Buffer.from([0x08])) // 8 bytes for value
|
|
504
|
+
.add(Opcode.OP_SPLIT)
|
|
484
505
|
// Add value enforcement logic
|
|
485
506
|
}
|
|
486
|
-
|
|
507
|
+
|
|
487
508
|
if (enforceScript) {
|
|
488
509
|
// Extract and validate locking script
|
|
489
|
-
script.add(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
510
|
+
script.add(Opcode.OP_DUP)
|
|
511
|
+
.add(Opcode.OP_SIZE)
|
|
512
|
+
.add(Buffer.from([0x09])) // Skip value + length byte
|
|
513
|
+
.add(Opcode.OP_SUB)
|
|
514
|
+
.add(Opcode.OP_SPLIT)
|
|
515
|
+
.add(Opcode.OP_NIP)
|
|
495
516
|
}
|
|
496
|
-
|
|
517
|
+
|
|
497
518
|
if (enforceCount) {
|
|
498
519
|
// Validate number of outputs in hashOutputs
|
|
499
520
|
// This requires parsing the actual outputs, not just hash
|
|
500
521
|
}
|
|
501
522
|
}
|
|
502
|
-
|
|
503
|
-
_addPreimageConstruction(script, params) {
|
|
504
|
-
const {
|
|
505
|
-
version = 1,
|
|
506
|
-
locktime = 0,
|
|
523
|
+
|
|
524
|
+
_addPreimageConstruction (script, params) {
|
|
525
|
+
const {
|
|
526
|
+
version = 1,
|
|
527
|
+
locktime = 0,
|
|
507
528
|
sighashType = 0x41,
|
|
508
|
-
sequence = 0xffffffff
|
|
509
|
-
} = params
|
|
510
|
-
|
|
529
|
+
sequence = 0xffffffff
|
|
530
|
+
} = params
|
|
531
|
+
|
|
511
532
|
// Construct fields for complete BIP143 preimage
|
|
512
533
|
// Items 8-11: sequence, hashOutputs, locktime, sighashType
|
|
513
534
|
script.add(Buffer.from(sequence.toString(16).padStart(8, '0'), 'hex'))
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
535
|
+
.add(Opcode.OP_CAT)
|
|
536
|
+
.add(Opcode.OP_SWAP) // hashOutputs already computed
|
|
537
|
+
.add(Opcode.OP_CAT)
|
|
538
|
+
.add(Buffer.from(locktime.toString(16).padStart(8, '0'), 'hex'))
|
|
539
|
+
.add(Opcode.OP_CAT)
|
|
540
|
+
.add(Buffer.from(sighashType.toString(16).padStart(8, '0'), 'hex'))
|
|
541
|
+
.add(Opcode.OP_CAT)
|
|
521
542
|
}
|
|
522
|
-
|
|
523
|
-
_addPushtxSignature(script, params) {
|
|
524
|
-
const {
|
|
543
|
+
|
|
544
|
+
_addPushtxSignature (script, params) {
|
|
545
|
+
const {
|
|
525
546
|
publicKey = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798',
|
|
526
547
|
sighashType = 0x41
|
|
527
|
-
} = params
|
|
528
|
-
|
|
548
|
+
} = params
|
|
549
|
+
|
|
529
550
|
// Generate signature using PUSHTX technique (k=a=1)
|
|
530
|
-
script.add(
|
|
531
|
-
|
|
551
|
+
script.add(Opcode.OP_HASH256)
|
|
552
|
+
|
|
532
553
|
// s = z + Gx mod n (where z = hash256(message), Gx = generator x-coord)
|
|
533
|
-
const generatorX = '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
|
554
|
+
const generatorX = '79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
|
|
534
555
|
script.add(Buffer.from(generatorX, 'hex'))
|
|
535
|
-
|
|
536
|
-
|
|
556
|
+
.add(Opcode.OP_ADD)
|
|
557
|
+
|
|
537
558
|
// Modular reduction with secp256k1 order
|
|
538
|
-
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
559
|
+
const secp256k1Order = 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'
|
|
539
560
|
script.add(Buffer.from(secp256k1Order, 'hex'))
|
|
540
|
-
|
|
541
|
-
|
|
561
|
+
.add(Opcode.OP_MOD)
|
|
562
|
+
|
|
542
563
|
// Convert to DER format and verify
|
|
543
|
-
this._addDERConversion(script)
|
|
564
|
+
this._addDERConversion(script)
|
|
544
565
|
script.add(Buffer.from([sighashType]))
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
566
|
+
.add(Opcode.OP_CAT)
|
|
567
|
+
.add(Buffer.from(publicKey, 'hex'))
|
|
568
|
+
.add(Opcode.OP_CHECKSIGVERIFY)
|
|
548
569
|
}
|
|
549
570
|
}
|
|
550
571
|
|
|
@@ -552,56 +573,56 @@ class CovenantInterface {
|
|
|
552
573
|
* Enhanced transaction with covenant-specific methods
|
|
553
574
|
*/
|
|
554
575
|
class CovenantTransaction {
|
|
555
|
-
constructor(transaction, covenantInterface) {
|
|
556
|
-
this.tx = transaction
|
|
557
|
-
this.covenant = covenantInterface
|
|
558
|
-
this.preimages = new Map()
|
|
576
|
+
constructor (transaction, covenantInterface) {
|
|
577
|
+
this.tx = transaction
|
|
578
|
+
this.covenant = covenantInterface
|
|
579
|
+
this.preimages = new Map()
|
|
559
580
|
}
|
|
560
581
|
|
|
561
582
|
/**
|
|
562
583
|
* Get preimage for specific input with caching
|
|
563
584
|
*/
|
|
564
|
-
getPreimage(inputIndex, lockingScript, satoshis) {
|
|
565
|
-
const key = `${inputIndex}-${lockingScript.toHex()}-${satoshis}
|
|
566
|
-
|
|
585
|
+
getPreimage (inputIndex, lockingScript, satoshis) {
|
|
586
|
+
const key = `${inputIndex}-${lockingScript.toHex()}-${satoshis}`
|
|
587
|
+
|
|
567
588
|
if (!this.preimages.has(key)) {
|
|
568
|
-
const preimage = this.covenant.getPreimage(this.tx, inputIndex, lockingScript, satoshis)
|
|
569
|
-
this.preimages.set(key, preimage)
|
|
589
|
+
const preimage = this.covenant.getPreimage(this.tx, inputIndex, lockingScript, satoshis)
|
|
590
|
+
this.preimages.set(key, preimage)
|
|
570
591
|
}
|
|
571
|
-
|
|
572
|
-
return this.preimages.get(key)
|
|
592
|
+
|
|
593
|
+
return this.preimages.get(key)
|
|
573
594
|
}
|
|
574
595
|
|
|
575
596
|
/**
|
|
576
597
|
* Sign input with covenant-compatible signature
|
|
577
598
|
*/
|
|
578
|
-
signInput(inputIndex, privateKey, lockingScript, satoshis) {
|
|
579
|
-
const signature = this.covenant.createSignature(this.tx, privateKey, inputIndex, lockingScript, satoshis)
|
|
580
|
-
|
|
581
|
-
const unlockingScript = new
|
|
599
|
+
signInput (inputIndex, privateKey, lockingScript, satoshis) {
|
|
600
|
+
const signature = this.covenant.createSignature(this.tx, privateKey, inputIndex, lockingScript, satoshis)
|
|
601
|
+
|
|
602
|
+
const unlockingScript = new Script()
|
|
582
603
|
.add(signature)
|
|
583
|
-
.add(privateKey.publicKey.toBuffer())
|
|
584
|
-
|
|
585
|
-
this.tx.inputs[inputIndex].setScript(unlockingScript)
|
|
586
|
-
return this
|
|
604
|
+
.add(privateKey.publicKey.toBuffer())
|
|
605
|
+
|
|
606
|
+
this.tx.inputs[inputIndex].setScript(unlockingScript)
|
|
607
|
+
return this
|
|
587
608
|
}
|
|
588
609
|
|
|
589
610
|
/**
|
|
590
611
|
* Access underlying BSV transaction
|
|
591
612
|
*/
|
|
592
|
-
getTransaction() {
|
|
593
|
-
return this.tx
|
|
613
|
+
getTransaction () {
|
|
614
|
+
return this.tx
|
|
594
615
|
}
|
|
595
616
|
|
|
596
617
|
/**
|
|
597
618
|
* Verify transaction with enhanced error reporting
|
|
598
619
|
*/
|
|
599
|
-
verify() {
|
|
620
|
+
verify () {
|
|
600
621
|
try {
|
|
601
|
-
return this.tx.verify()
|
|
622
|
+
return this.tx.verify()
|
|
602
623
|
} catch (error) {
|
|
603
|
-
console.error('Transaction validation error:', error.message)
|
|
604
|
-
return false
|
|
624
|
+
console.error('Transaction validation error:', error.message)
|
|
625
|
+
return false
|
|
605
626
|
}
|
|
606
627
|
}
|
|
607
628
|
}
|
|
@@ -610,98 +631,98 @@ class CovenantTransaction {
|
|
|
610
631
|
* Parsed preimage with convenient access methods
|
|
611
632
|
*/
|
|
612
633
|
class CovenantPreimage {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
634
|
+
constructor (preimageHex) {
|
|
635
|
+
this.hex = preimageHex
|
|
636
|
+
this.buffer = Buffer.from(preimageHex, 'hex')
|
|
637
|
+
this.parseFields()
|
|
638
|
+
}
|
|
618
639
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
640
|
+
parseFields () {
|
|
641
|
+
let offset = 0
|
|
642
|
+
|
|
643
|
+
// BIP143 sighash preimage structure (detailed field-by-field parsing)
|
|
644
|
+
// Field 1: nVersion (4 bytes, little-endian)
|
|
645
|
+
this.nVersion = this.buffer.subarray(offset, offset + 4)
|
|
646
|
+
this.nVersionValue = this.buffer.readUInt32LE(offset)
|
|
647
|
+
offset += 4
|
|
648
|
+
|
|
649
|
+
// Field 2: hashPrevouts (32 bytes) - double SHA256 of all input outpoints
|
|
650
|
+
this.hashPrevouts = this.buffer.subarray(offset, offset + 32)
|
|
651
|
+
offset += 32
|
|
652
|
+
|
|
653
|
+
// Field 3: hashSequence (32 bytes) - double SHA256 of all input sequences
|
|
654
|
+
this.hashSequence = this.buffer.subarray(offset, offset + 32)
|
|
655
|
+
offset += 32
|
|
656
|
+
|
|
657
|
+
// Field 4: outpoint (36 bytes) - prevTxId (32) + outputIndex (4, little-endian)
|
|
658
|
+
this.outpoint = this.buffer.subarray(offset, offset + 36)
|
|
659
|
+
this.prevTxId = this.buffer.subarray(offset, offset + 32)
|
|
660
|
+
offset += 32
|
|
661
|
+
this.outputIndex = this.buffer.subarray(offset, offset + 4)
|
|
662
|
+
this.outputIndexValue = this.buffer.readUInt32LE(offset)
|
|
663
|
+
offset += 4
|
|
664
|
+
|
|
665
|
+
// Field 5: scriptCode - variable length with proper parsing
|
|
666
|
+
const scriptLen = this.readVarInt(offset)
|
|
667
|
+
offset += this.getVarIntLength(scriptLen)
|
|
668
|
+
this.scriptCode = this.buffer.subarray(offset, offset + scriptLen)
|
|
669
|
+
offset += scriptLen
|
|
670
|
+
|
|
671
|
+
// Field 6: amount (8 bytes, little-endian) - value of UTXO being spent
|
|
672
|
+
this.amount = this.buffer.subarray(offset, offset + 8)
|
|
673
|
+
this.amountValue = this.buffer.readBigUInt64LE(offset)
|
|
674
|
+
offset += 8
|
|
675
|
+
|
|
676
|
+
// Field 7: nSequence (4 bytes, little-endian)
|
|
677
|
+
this.nSequence = this.buffer.subarray(offset, offset + 4)
|
|
678
|
+
this.nSequenceValue = this.buffer.readUInt32LE(offset)
|
|
679
|
+
offset += 4
|
|
680
|
+
|
|
681
|
+
// Field 8: hashOutputs (32 bytes) - double SHA256 of all outputs
|
|
682
|
+
this.hashOutputs = this.buffer.subarray(offset, offset + 32)
|
|
683
|
+
offset += 32
|
|
684
|
+
|
|
685
|
+
// Field 9: nLockTime (4 bytes, little-endian)
|
|
686
|
+
this.nLockTime = this.buffer.subarray(offset, offset + 4)
|
|
687
|
+
this.nLockTimeValue = this.buffer.readUInt32LE(offset)
|
|
688
|
+
offset += 4
|
|
689
|
+
|
|
690
|
+
// Field 10: sighashType (4 bytes, little-endian)
|
|
691
|
+
this.sighashType = this.buffer.subarray(offset, offset + 4)
|
|
692
|
+
this.sighashTypeValue = this.buffer.readUInt32LE(offset)
|
|
693
|
+
offset += 4
|
|
694
|
+
|
|
695
|
+
// Validate preimage structure (typical 108+ bytes)
|
|
696
|
+
this.isValid = offset === this.buffer.length && this.buffer.length >= 108
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
readVarInt (offset) {
|
|
700
|
+
const first = this.buffer.readUInt8(offset)
|
|
701
|
+
if (first < 0xfd) return first
|
|
702
|
+
if (first === 0xfd) return this.buffer.readUInt16LE(offset + 1)
|
|
703
|
+
if (first === 0xfe) return this.buffer.readUInt32LE(offset + 1)
|
|
704
|
+
if (first === 0xff) return this.buffer.readBigUInt64LE(offset + 1)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
getVarIntLength (value) {
|
|
708
|
+
if (value < 0xfd) return 1
|
|
709
|
+
if (value <= 0xffff) return 3
|
|
710
|
+
if (value <= 0xffffffff) return 5
|
|
711
|
+
return 9
|
|
712
|
+
}
|
|
692
713
|
}
|
|
693
714
|
|
|
694
715
|
/**
|
|
695
716
|
* Covenant validation result
|
|
696
717
|
*/
|
|
697
718
|
class CovenantValidation {
|
|
698
|
-
constructor(isValid, message) {
|
|
699
|
-
this.isValid = isValid
|
|
700
|
-
this.message = message
|
|
719
|
+
constructor (isValid, message) {
|
|
720
|
+
this.isValid = isValid
|
|
721
|
+
this.message = message
|
|
701
722
|
}
|
|
702
723
|
|
|
703
|
-
toString() {
|
|
704
|
-
return `${this.isValid ? 'VALID' : 'INVALID'}: ${this.message}
|
|
724
|
+
toString () {
|
|
725
|
+
return `${this.isValid ? 'VALID' : 'INVALID'}: ${this.message}`
|
|
705
726
|
}
|
|
706
727
|
}
|
|
707
728
|
|
|
@@ -710,4 +731,4 @@ module.exports = {
|
|
|
710
731
|
CovenantTransaction,
|
|
711
732
|
CovenantPreimage,
|
|
712
733
|
CovenantValidation
|
|
713
|
-
}
|
|
734
|
+
}
|