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