@smartledger/bsv 1.5.6-fix1 → 3.1.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 +111 -0
- package/README.md +38 -10
- package/bsv.min.js +8 -8
- package/index.js +11 -0
- package/lib/crypto/ecdsa.js +57 -38
- package/lib/crypto/smartledger_verify.js +42 -11
- package/lib/custom-script-helper.js +249 -0
- package/lib/script/interpreter.js +8 -8
- package/lib/smartminer.js +169 -0
- package/lib/smartutxo.js +200 -0
- package/lib/transaction/transaction.js +39 -0
- package/package.json +30 -5
- package/utilities/README.md +132 -0
- package/utilities/blockchain-state.js +332 -0
- package/utilities/blockchain-state.json +41 -0
- package/utilities/miner-simulator.js +620 -0
- package/utilities/mock-utxo-generator.js +149 -0
- package/utilities/raw-tx-examples.js +213 -0
- package/utilities/success-demo.js +193 -0
- package/utilities/transaction-examples.js +328 -0
- package/utilities/utxo-manager.js +162 -0
- package/utilities/wallet-setup.js +167 -0
- package/utilities/wallet.json +30 -0
- package/utilities/working-signature-demo.js +181 -0
- package/validation_test.js +97 -0
package/index.js
CHANGED
|
@@ -97,5 +97,16 @@ bsv.SmartLedger = {
|
|
|
97
97
|
bsv.SmartVerify = require('./lib/crypto/smartledger_verify')
|
|
98
98
|
bsv.EllipticFixed = require('./lib/crypto/elliptic-fixed')
|
|
99
99
|
|
|
100
|
+
// SmartLedger Development & Testing Tools (Node.js only)
|
|
101
|
+
if (typeof window === 'undefined' && typeof require === 'function') {
|
|
102
|
+
try {
|
|
103
|
+
bsv.SmartUTXO = require('./lib/smartutxo')
|
|
104
|
+
bsv.SmartMiner = require('./lib/smartminer')
|
|
105
|
+
bsv.CustomScriptHelper = require('./lib/custom-script-helper')
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Browser environment - these tools not available
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
// Internal usage, exposed for testing/advanced tweaking
|
|
101
112
|
bsv.Transaction.sighash = require('./lib/transaction/sighash')
|
package/lib/crypto/ecdsa.js
CHANGED
|
@@ -23,7 +23,22 @@ ECDSA.prototype.set = function (obj) {
|
|
|
23
23
|
this.endian = obj.endian || this.endian // the endianness of hashbuf
|
|
24
24
|
this.privkey = obj.privkey || this.privkey
|
|
25
25
|
this.pubkey = obj.pubkey || (this.privkey ? this.privkey.publicKey : this.pubkey)
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
// === SmartLedger Signature Handling ===
|
|
28
|
+
// Auto-parse DER buffers to Signature objects for compatibility
|
|
29
|
+
if (obj.sig) {
|
|
30
|
+
if (Buffer.isBuffer(obj.sig)) {
|
|
31
|
+
// Parse DER buffer to Signature object
|
|
32
|
+
var Signature = require('./signature')
|
|
33
|
+
this.sig = Signature.fromDER(obj.sig)
|
|
34
|
+
} else {
|
|
35
|
+
// Already a Signature object
|
|
36
|
+
this.sig = obj.sig
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
this.sig = this.sig
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
this.k = obj.k || this.k
|
|
28
43
|
this.verified = obj.verified || this.verified
|
|
29
44
|
return this
|
|
@@ -159,53 +174,57 @@ ECDSA.prototype.sigError = function () {
|
|
|
159
174
|
|
|
160
175
|
// === SmartLedger Security Patches ===
|
|
161
176
|
// Apply security validation during cryptographic verification
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
r: this.sig.r,
|
|
166
|
-
s: this.sig.s
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Apply security patches to the copy
|
|
170
|
-
var n = Point.getN()
|
|
177
|
+
var r = this.sig.r
|
|
178
|
+
var s = this.sig.s
|
|
179
|
+
var n = Point.getN()
|
|
171
180
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
181
|
+
try {
|
|
182
|
+
// Fix 1 & 2: Enhanced range validation
|
|
183
|
+
if (r.isZero() || s.isZero() || r.gte(n) || s.gte(n)) {
|
|
175
184
|
return 'r and s not in range'
|
|
176
185
|
}
|
|
177
186
|
|
|
178
|
-
// Fix 3:
|
|
187
|
+
// Fix 3: Handle canonicalization properly
|
|
188
|
+
// For verification, we need to try both the original s and canonical s
|
|
189
|
+
// This handles both pre-canonicalized signatures and non-canonical ones
|
|
179
190
|
var nh = n.shrn(1) // n/2
|
|
180
|
-
|
|
181
|
-
|
|
191
|
+
var canonicalS = s.gt(nh) ? n.sub(s) : s
|
|
192
|
+
|
|
193
|
+
var e = BN.fromBuffer(this.hashbuf, this.endian ? {
|
|
194
|
+
endian: this.endian
|
|
195
|
+
} : undefined)
|
|
196
|
+
|
|
197
|
+
// Try verification with canonical s
|
|
198
|
+
var sinv = canonicalS.invm(n)
|
|
199
|
+
var u1 = sinv.mul(e).umod(n)
|
|
200
|
+
var u2 = sinv.mul(r).umod(n)
|
|
201
|
+
|
|
202
|
+
var p = Point.getG().mulAdd(u1, this.pubkey.point, u2)
|
|
203
|
+
if (p.isInfinity()) {
|
|
204
|
+
return 'p is infinity'
|
|
182
205
|
}
|
|
183
206
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
207
|
+
if (p.getX().umod(n).cmp(r) !== 0) {
|
|
208
|
+
// If canonical verification failed and s was already canonical,
|
|
209
|
+
// try with the non-canonical version (for backwards compatibility)
|
|
210
|
+
if (s.lte(nh)) {
|
|
211
|
+
var nonCanonicalS = n.sub(s)
|
|
212
|
+
sinv = nonCanonicalS.invm(n)
|
|
213
|
+
u1 = sinv.mul(e).umod(n)
|
|
214
|
+
u2 = sinv.mul(r).umod(n)
|
|
215
|
+
|
|
216
|
+
p = Point.getG().mulAdd(u1, this.pubkey.point, u2)
|
|
217
|
+
if (!p.isInfinity() && p.getX().umod(n).cmp(r) === 0) {
|
|
218
|
+
return false // Verification succeeded with non-canonical s
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return 'Invalid signature'
|
|
222
|
+
} else {
|
|
223
|
+
return false // Verification succeeded with canonical s
|
|
224
|
+
}
|
|
187
225
|
} catch (error) {
|
|
188
226
|
return 'Signature security validation failed: ' + error.message
|
|
189
227
|
}
|
|
190
|
-
|
|
191
|
-
var e = BN.fromBuffer(this.hashbuf, this.endian ? {
|
|
192
|
-
endian: this.endian
|
|
193
|
-
} : undefined)
|
|
194
|
-
// Use canonical s for verification
|
|
195
|
-
var sinv = canonicalS.invm(n)
|
|
196
|
-
var u1 = sinv.mul(e).umod(n)
|
|
197
|
-
var u2 = sinv.mul(r).umod(n)
|
|
198
|
-
|
|
199
|
-
var p = Point.getG().mulAdd(u1, this.pubkey.point, u2)
|
|
200
|
-
if (p.isInfinity()) {
|
|
201
|
-
return 'p is infinity'
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (p.getX().umod(n).cmp(r) !== 0) {
|
|
205
|
-
return 'Invalid signature'
|
|
206
|
-
} else {
|
|
207
|
-
return false
|
|
208
|
-
}
|
|
209
228
|
}
|
|
210
229
|
|
|
211
230
|
ECDSA.toLowS = function (s) {
|
|
@@ -16,7 +16,7 @@ const nh = n.shrn(1) // n / 2
|
|
|
16
16
|
/**
|
|
17
17
|
* Hardened signature verification with canonicalization
|
|
18
18
|
* @param {Buffer} msgHash - 32-byte message hash
|
|
19
|
-
* @param {Signature} sig - Signature object with r,s components
|
|
19
|
+
* @param {Signature|Buffer} sig - Signature object with r,s components or DER buffer
|
|
20
20
|
* @param {PublicKey} pubkey - Public key for verification
|
|
21
21
|
* @returns {boolean} - true if signature is valid and canonical
|
|
22
22
|
*/
|
|
@@ -26,13 +26,28 @@ function smartVerify (msgHash, sig, pubkey) {
|
|
|
26
26
|
throw new Error('Invalid message hash: must be 32-byte buffer')
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
if (!sig
|
|
29
|
+
if (!sig) {
|
|
30
|
+
return false
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Parse DER buffer to Signature object if needed
|
|
34
|
+
let sigObj = sig
|
|
35
|
+
if (Buffer.isBuffer(sig)) {
|
|
36
|
+
try {
|
|
37
|
+
const Signature = require('./signature')
|
|
38
|
+
sigObj = Signature.fromDER(sig)
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!sigObj || !sigObj.r || !sigObj.s) {
|
|
30
45
|
return false
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
// Ensure r and s are BN instances
|
|
34
|
-
const r = BN.isBN(
|
|
35
|
-
const s = BN.isBN(
|
|
49
|
+
const r = BN.isBN(sigObj.r) ? sigObj.r : new BN(sigObj.r)
|
|
50
|
+
const s = BN.isBN(sigObj.s) ? sigObj.s : new BN(sigObj.s)
|
|
36
51
|
|
|
37
52
|
// Reject zero values
|
|
38
53
|
if (r.isZero() || s.isZero()) {
|
|
@@ -50,27 +65,43 @@ function smartVerify (msgHash, sig, pubkey) {
|
|
|
50
65
|
canonicalS = n.sub(s)
|
|
51
66
|
}
|
|
52
67
|
|
|
53
|
-
// Create canonicalized signature object
|
|
54
|
-
const
|
|
68
|
+
// Create canonicalized signature object for ECDSA verify
|
|
69
|
+
const Signature = require('./signature')
|
|
70
|
+
const canonicalSig = new Signature({
|
|
55
71
|
r: r,
|
|
56
72
|
s: canonicalS
|
|
57
|
-
}
|
|
73
|
+
})
|
|
58
74
|
|
|
59
|
-
// Use BSV's
|
|
75
|
+
// Use BSV's ECDSA verify with canonical signature object
|
|
60
76
|
return ECDSA.verify(msgHash, canonicalSig, pubkey)
|
|
61
77
|
}
|
|
62
78
|
|
|
63
79
|
/**
|
|
64
80
|
* Check if signature is in canonical form (s <= n/2)
|
|
65
|
-
* @param {Object} sig - Signature with r,s components
|
|
81
|
+
* @param {Object|Buffer} sig - Signature with r,s components or DER buffer
|
|
66
82
|
* @returns {boolean} - true if signature is canonical
|
|
67
83
|
*/
|
|
68
84
|
function isCanonical (sig) {
|
|
69
|
-
if (!sig
|
|
85
|
+
if (!sig) {
|
|
70
86
|
return false
|
|
71
87
|
}
|
|
72
88
|
|
|
73
|
-
|
|
89
|
+
// Parse DER buffer to Signature object if needed
|
|
90
|
+
let sigObj = sig
|
|
91
|
+
if (Buffer.isBuffer(sig)) {
|
|
92
|
+
try {
|
|
93
|
+
const Signature = require('./signature')
|
|
94
|
+
sigObj = Signature.fromDER(sig)
|
|
95
|
+
} catch (e) {
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!sigObj || !sigObj.s) {
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const s = BN.isBN(sigObj.s) ? sigObj.s : new BN(sigObj.s)
|
|
74
105
|
return s.lte(nh)
|
|
75
106
|
}
|
|
76
107
|
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmartLedger BSV Custom Script Helper
|
|
3
|
+
* Simplified API for custom script development
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const bsv = require('../index.js');
|
|
7
|
+
|
|
8
|
+
class CustomScriptHelper {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a signature for any custom script
|
|
12
|
+
* @param {Transaction} transaction - The transaction object
|
|
13
|
+
* @param {PrivateKey} privateKey - Private key to sign with
|
|
14
|
+
* @param {number} inputIndex - Input index (0 for first input)
|
|
15
|
+
* @param {Script} lockingScript - The locking script being spent
|
|
16
|
+
* @param {number} satoshis - Amount in satoshis
|
|
17
|
+
* @param {number} sighashType - Signature hash type
|
|
18
|
+
* @returns {Buffer} Complete signature with sighash type
|
|
19
|
+
*/
|
|
20
|
+
static createSignature(transaction, privateKey, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
21
|
+
sighashType = sighashType || (bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
|
|
22
|
+
|
|
23
|
+
const signature = bsv.Transaction.sighash.sign(
|
|
24
|
+
transaction,
|
|
25
|
+
privateKey,
|
|
26
|
+
sighashType,
|
|
27
|
+
inputIndex,
|
|
28
|
+
lockingScript,
|
|
29
|
+
new bsv.crypto.BN(satoshis)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return Buffer.concat([signature.toDER(), Buffer.from([sighashType])]);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a multi-signature locking script
|
|
37
|
+
* @param {number} m - Required signatures
|
|
38
|
+
* @param {PublicKey[]} publicKeys - Array of public keys
|
|
39
|
+
* @returns {Script} Multi-signature locking script
|
|
40
|
+
*/
|
|
41
|
+
static createMultisigScript(m, publicKeys) {
|
|
42
|
+
let script = new bsv.Script().add(bsv.Opcode[`OP_${m}`]);
|
|
43
|
+
|
|
44
|
+
for (const pubKey of publicKeys) {
|
|
45
|
+
script = script.add(pubKey.toBuffer());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return script
|
|
49
|
+
.add(bsv.Opcode[`OP_${publicKeys.length}`])
|
|
50
|
+
.add(bsv.Opcode.OP_CHECKMULTISIG);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create an unlocking script for multi-signature
|
|
55
|
+
* @param {Buffer[]} signatures - Array of signatures
|
|
56
|
+
* @returns {Script} Unlocking script for multisig
|
|
57
|
+
*/
|
|
58
|
+
static createMultisigUnlocking(signatures) {
|
|
59
|
+
let script = new bsv.Script().add(bsv.Opcode.OP_0); // CHECKMULTISIG bug
|
|
60
|
+
|
|
61
|
+
for (const sig of signatures) {
|
|
62
|
+
script = script.add(sig);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return script;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Create a time-locked script (CHECKLOCKTIMEVERIFY)
|
|
70
|
+
* @param {number} lockTime - Block height or timestamp
|
|
71
|
+
* @param {Script} baseScript - Base script to execute after time lock
|
|
72
|
+
* @returns {Script} Time-locked script
|
|
73
|
+
*/
|
|
74
|
+
static createTimelockScript(lockTime, baseScript) {
|
|
75
|
+
const lockTimeBuffer = Buffer.from(lockTime.toString(16).padStart(8, '0'), 'hex').reverse();
|
|
76
|
+
|
|
77
|
+
return new bsv.Script()
|
|
78
|
+
.add(lockTimeBuffer)
|
|
79
|
+
.add(bsv.Opcode.OP_CHECKLOCKTIMEVERIFY)
|
|
80
|
+
.add(bsv.Opcode.OP_DROP)
|
|
81
|
+
.add(baseScript.toBuffer());
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a conditional script (IF/ELSE/ENDIF)
|
|
86
|
+
* @param {Script} ifScript - Script to execute if condition is true
|
|
87
|
+
* @param {Script} elseScript - Script to execute if condition is false
|
|
88
|
+
* @returns {Script} Conditional script
|
|
89
|
+
*/
|
|
90
|
+
static createConditionalScript(ifScript, elseScript = null) {
|
|
91
|
+
let script = new bsv.Script()
|
|
92
|
+
.add(bsv.Opcode.OP_IF)
|
|
93
|
+
.add(ifScript.toBuffer());
|
|
94
|
+
|
|
95
|
+
if (elseScript) {
|
|
96
|
+
script = script
|
|
97
|
+
.add(bsv.Opcode.OP_ELSE)
|
|
98
|
+
.add(elseScript.toBuffer());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return script.add(bsv.Opcode.OP_ENDIF);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create P2PKH script for a public key
|
|
106
|
+
* @param {PublicKey} publicKey - Public key
|
|
107
|
+
* @returns {Script} P2PKH locking script
|
|
108
|
+
*/
|
|
109
|
+
static createP2PKHScript(publicKey) {
|
|
110
|
+
return new bsv.Script()
|
|
111
|
+
.add(bsv.Opcode.OP_DUP)
|
|
112
|
+
.add(bsv.Opcode.OP_HASH160)
|
|
113
|
+
.add(publicKey.toAddress().hashBuffer)
|
|
114
|
+
.add(bsv.Opcode.OP_EQUALVERIFY)
|
|
115
|
+
.add(bsv.Opcode.OP_CHECKSIG);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create P2PKH unlocking script
|
|
120
|
+
* @param {Buffer} signature - Signature
|
|
121
|
+
* @param {PublicKey} publicKey - Public key
|
|
122
|
+
* @returns {Script} P2PKH unlocking script
|
|
123
|
+
*/
|
|
124
|
+
static createP2PKHUnlocking(signature, publicKey) {
|
|
125
|
+
return new bsv.Script()
|
|
126
|
+
.add(signature)
|
|
127
|
+
.add(publicKey.toBuffer());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get transaction preimage for covenant scripts
|
|
132
|
+
* @param {Transaction} transaction - The transaction
|
|
133
|
+
* @param {number} inputIndex - Input index
|
|
134
|
+
* @param {Script} lockingScript - Locking script
|
|
135
|
+
* @param {number} satoshis - Amount in satoshis
|
|
136
|
+
* @param {number} sighashType - Signature hash type
|
|
137
|
+
* @returns {Buffer} Transaction preimage
|
|
138
|
+
*/
|
|
139
|
+
static getPreimage(transaction, inputIndex, lockingScript, satoshis, sighashType = null) {
|
|
140
|
+
sighashType = sighashType || (bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID);
|
|
141
|
+
|
|
142
|
+
return bsv.Transaction.sighash.sighash(
|
|
143
|
+
transaction,
|
|
144
|
+
sighashType,
|
|
145
|
+
inputIndex,
|
|
146
|
+
lockingScript,
|
|
147
|
+
new bsv.crypto.BN(satoshis)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create a simple data push script
|
|
153
|
+
* @param {Buffer|string} data - Data to push
|
|
154
|
+
* @returns {Script} Data push script
|
|
155
|
+
*/
|
|
156
|
+
static createDataScript(data) {
|
|
157
|
+
if (typeof data === 'string') {
|
|
158
|
+
data = Buffer.from(data, 'utf8');
|
|
159
|
+
}
|
|
160
|
+
return new bsv.Script().add(data);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create OP_RETURN data script
|
|
165
|
+
* @param {Buffer|string} data - Data for OP_RETURN
|
|
166
|
+
* @returns {Script} OP_RETURN script
|
|
167
|
+
*/
|
|
168
|
+
static createOpReturnScript(data) {
|
|
169
|
+
if (typeof data === 'string') {
|
|
170
|
+
data = Buffer.from(data, 'utf8');
|
|
171
|
+
}
|
|
172
|
+
return new bsv.Script()
|
|
173
|
+
.add(bsv.Opcode.OP_FALSE)
|
|
174
|
+
.add(bsv.Opcode.OP_RETURN)
|
|
175
|
+
.add(data);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Validate a transaction with custom scripts
|
|
180
|
+
* @param {Transaction} transaction - Transaction to validate
|
|
181
|
+
* @returns {boolean} True if valid
|
|
182
|
+
*/
|
|
183
|
+
static validateTransaction(transaction) {
|
|
184
|
+
try {
|
|
185
|
+
return transaction.verify();
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error('Transaction validation error:', error.message);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Validate a specific input script
|
|
194
|
+
* @param {Script} unlockingScript - Unlocking script
|
|
195
|
+
* @param {Script} lockingScript - Locking script
|
|
196
|
+
* @param {Transaction} transaction - Transaction
|
|
197
|
+
* @param {number} inputIndex - Input index
|
|
198
|
+
* @returns {boolean} True if script is valid
|
|
199
|
+
*/
|
|
200
|
+
static validateScript(unlockingScript, lockingScript, transaction, inputIndex) {
|
|
201
|
+
try {
|
|
202
|
+
const interpreter = new bsv.Script.Interpreter();
|
|
203
|
+
return interpreter.verify(
|
|
204
|
+
unlockingScript,
|
|
205
|
+
lockingScript,
|
|
206
|
+
transaction,
|
|
207
|
+
inputIndex,
|
|
208
|
+
bsv.Script.Interpreter.SCRIPT_VERIFY_P2SH |
|
|
209
|
+
bsv.Script.Interpreter.SCRIPT_VERIFY_STRICTENC |
|
|
210
|
+
bsv.Script.Interpreter.SCRIPT_ENABLE_SIGHASH_FORKID |
|
|
211
|
+
bsv.Script.Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES |
|
|
212
|
+
bsv.Script.Interpreter.SCRIPT_ENABLE_MONOLITH_OPCODES
|
|
213
|
+
);
|
|
214
|
+
} catch (error) {
|
|
215
|
+
console.error('Script validation error:', error.message);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Create a transaction with ultra-low fees
|
|
222
|
+
* @param {Object[]} utxos - Array of UTXOs
|
|
223
|
+
* @param {Object[]} outputs - Array of outputs
|
|
224
|
+
* @param {number} feePerKb - Fee per KB (default: 10 sats = 0.01 sats/byte)
|
|
225
|
+
* @returns {Transaction} Transaction with ultra-low fees
|
|
226
|
+
*/
|
|
227
|
+
static createLowFeeTransaction(utxos, outputs, feePerKb = 10) {
|
|
228
|
+
let tx = new bsv.Transaction().feePerKb(feePerKb);
|
|
229
|
+
|
|
230
|
+
// Add inputs
|
|
231
|
+
for (const utxo of utxos) {
|
|
232
|
+
tx = tx.from(utxo);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Add outputs
|
|
236
|
+
for (const output of outputs) {
|
|
237
|
+
tx = tx.to(output.address, output.satoshis);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return tx;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Common signature hash types
|
|
245
|
+
CustomScriptHelper.SIGHASH_ALL = bsv.crypto.Signature.SIGHASH_ALL | bsv.crypto.Signature.SIGHASH_FORKID;
|
|
246
|
+
CustomScriptHelper.SIGHASH_NONE = bsv.crypto.Signature.SIGHASH_NONE | bsv.crypto.Signature.SIGHASH_FORKID;
|
|
247
|
+
CustomScriptHelper.SIGHASH_SINGLE = bsv.crypto.Signature.SIGHASH_SINGLE | bsv.crypto.Signature.SIGHASH_FORKID;
|
|
248
|
+
|
|
249
|
+
module.exports = CustomScriptHelper;
|
|
@@ -655,19 +655,21 @@ Interpreter.prototype.step = function () {
|
|
|
655
655
|
switch (opcode) {
|
|
656
656
|
case Opcode.OP_2MUL:
|
|
657
657
|
case Opcode.OP_2DIV:
|
|
658
|
-
|
|
659
|
-
// Disabled opcodes.
|
|
658
|
+
// Permanently disabled opcodes.
|
|
660
659
|
return true
|
|
661
660
|
|
|
662
661
|
case Opcode.OP_INVERT:
|
|
663
662
|
case Opcode.OP_MUL:
|
|
664
663
|
case Opcode.OP_LSHIFT:
|
|
665
664
|
case Opcode.OP_RSHIFT:
|
|
666
|
-
//
|
|
665
|
+
// Magnetic opcodes - still require flag for backwards compatibility
|
|
667
666
|
if ((self.flags & Interpreter.SCRIPT_ENABLE_MAGNETIC_OPCODES) === 0) {
|
|
668
667
|
return true
|
|
669
668
|
}
|
|
670
669
|
break
|
|
670
|
+
|
|
671
|
+
// Monolith opcodes are now enabled by default in SmartLedger BSV
|
|
672
|
+
// These were activated in May 2018 and are part of standard BSV consensus
|
|
671
673
|
case Opcode.OP_DIV:
|
|
672
674
|
case Opcode.OP_MOD:
|
|
673
675
|
case Opcode.OP_SPLIT:
|
|
@@ -677,11 +679,9 @@ Interpreter.prototype.step = function () {
|
|
|
677
679
|
case Opcode.OP_XOR:
|
|
678
680
|
case Opcode.OP_BIN2NUM:
|
|
679
681
|
case Opcode.OP_NUM2BIN:
|
|
680
|
-
//
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
break
|
|
682
|
+
// These opcodes are now always enabled - no flag required
|
|
683
|
+
return false
|
|
684
|
+
|
|
685
685
|
default:
|
|
686
686
|
break
|
|
687
687
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SmartLedger Miner Simulator
|
|
5
|
+
* Provides BSV blockchain mining simulation for testing and development
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Comprehensive BSV Miner Simulator for development and testing
|
|
10
|
+
*/
|
|
11
|
+
class SmartMiner {
|
|
12
|
+
constructor(bsv, options = {}) {
|
|
13
|
+
this.bsv = bsv
|
|
14
|
+
this.options = {
|
|
15
|
+
difficulty: options.difficulty || 1,
|
|
16
|
+
blockTime: options.blockTime || 10000, // 10 seconds for testing
|
|
17
|
+
validateScripts: options.validateScripts !== false, // default true
|
|
18
|
+
logLevel: options.logLevel || 'info',
|
|
19
|
+
...options
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.currentBlock = {
|
|
23
|
+
height: 0,
|
|
24
|
+
transactions: [],
|
|
25
|
+
timestamp: Date.now()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.mempool = []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add a transaction to the mempool for mining
|
|
33
|
+
* @param {Transaction} transaction - BSV transaction object
|
|
34
|
+
* @returns {boolean} - true if accepted, false if rejected
|
|
35
|
+
*/
|
|
36
|
+
acceptTransaction(transaction) {
|
|
37
|
+
try {
|
|
38
|
+
// Validate transaction if script validation is enabled
|
|
39
|
+
if (this.options.validateScripts) {
|
|
40
|
+
const isValid = this.validateTransactionSignatures(transaction)
|
|
41
|
+
if (!isValid) {
|
|
42
|
+
this.log('warn', '❌ Transaction rejected: Invalid signatures')
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Add to mempool
|
|
48
|
+
this.mempool.push(transaction)
|
|
49
|
+
this.log('info', `✅ Transaction accepted into mempool: ${transaction.id || 'unknown'}`)
|
|
50
|
+
return true
|
|
51
|
+
|
|
52
|
+
} catch (error) {
|
|
53
|
+
this.log('error', `❌ Transaction rejected: ${error.message}`)
|
|
54
|
+
return false
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validate transaction signatures using BSV library
|
|
60
|
+
* @param {Transaction} transaction - BSV transaction
|
|
61
|
+
* @returns {boolean} - true if all signatures are valid
|
|
62
|
+
*/
|
|
63
|
+
validateTransactionSignatures(transaction) {
|
|
64
|
+
try {
|
|
65
|
+
// Use BSV's built-in transaction verification
|
|
66
|
+
const result = transaction.verify()
|
|
67
|
+
this.log('debug', `📊 Transaction verification result: ${result}`)
|
|
68
|
+
return result === true
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.log('warn', `⚠️ Signature validation error: ${error.message}`)
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Mine a new block with transactions from mempool
|
|
77
|
+
* @param {number} maxTransactions - Maximum transactions per block
|
|
78
|
+
* @returns {Object} - Mined block object
|
|
79
|
+
*/
|
|
80
|
+
mineBlock(maxTransactions = 10) {
|
|
81
|
+
const transactions = this.mempool.splice(0, maxTransactions)
|
|
82
|
+
|
|
83
|
+
const block = {
|
|
84
|
+
height: this.currentBlock.height + 1,
|
|
85
|
+
timestamp: Date.now(),
|
|
86
|
+
transactions: transactions,
|
|
87
|
+
transactionCount: transactions.length,
|
|
88
|
+
previousBlockHash: this.currentBlock.hash || '0000000000000000000000000000000000000000000000000000000000000000'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Simple block hash simulation
|
|
92
|
+
const blockData = JSON.stringify({
|
|
93
|
+
height: block.height,
|
|
94
|
+
timestamp: block.timestamp,
|
|
95
|
+
previousBlockHash: block.previousBlockHash,
|
|
96
|
+
transactions: transactions.map(tx => tx.id || tx.toString())
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
block.hash = this.bsv.crypto.Hash.sha256(Buffer.from(blockData)).toString('hex')
|
|
100
|
+
|
|
101
|
+
this.currentBlock = block
|
|
102
|
+
|
|
103
|
+
this.log('info', `⛏️ Mined block ${block.height} with ${transactions.length} transactions`)
|
|
104
|
+
this.log('debug', `📦 Block hash: ${block.hash}`)
|
|
105
|
+
|
|
106
|
+
return block
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get mempool status
|
|
111
|
+
* @returns {Object} - Mempool statistics
|
|
112
|
+
*/
|
|
113
|
+
getMempoolStats() {
|
|
114
|
+
return {
|
|
115
|
+
transactionCount: this.mempool.length,
|
|
116
|
+
transactions: this.mempool.map(tx => ({
|
|
117
|
+
id: tx.id || 'unknown',
|
|
118
|
+
size: tx.toBuffer ? tx.toBuffer().length : 0,
|
|
119
|
+
inputs: tx.inputs ? tx.inputs.length : 0,
|
|
120
|
+
outputs: tx.outputs ? tx.outputs.length : 0
|
|
121
|
+
}))
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get current blockchain status
|
|
127
|
+
* @returns {Object} - Blockchain statistics
|
|
128
|
+
*/
|
|
129
|
+
getBlockchainStats() {
|
|
130
|
+
return {
|
|
131
|
+
currentHeight: this.currentBlock.height,
|
|
132
|
+
currentBlockHash: this.currentBlock.hash,
|
|
133
|
+
currentBlockTimestamp: this.currentBlock.timestamp,
|
|
134
|
+
mempoolSize: this.mempool.length,
|
|
135
|
+
difficulty: this.options.difficulty,
|
|
136
|
+
blockTime: this.options.blockTime
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Reset the miner state
|
|
142
|
+
*/
|
|
143
|
+
reset() {
|
|
144
|
+
this.currentBlock = {
|
|
145
|
+
height: 0,
|
|
146
|
+
transactions: [],
|
|
147
|
+
timestamp: Date.now()
|
|
148
|
+
}
|
|
149
|
+
this.mempool = []
|
|
150
|
+
this.log('info', '🔄 Miner reset to genesis state')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Logging utility
|
|
155
|
+
* @param {string} level - Log level
|
|
156
|
+
* @param {string} message - Log message
|
|
157
|
+
*/
|
|
158
|
+
log(level, message) {
|
|
159
|
+
const levels = { error: 0, warn: 1, info: 2, debug: 3 }
|
|
160
|
+
const currentLevel = levels[this.options.logLevel] || 2
|
|
161
|
+
|
|
162
|
+
if (levels[level] <= currentLevel) {
|
|
163
|
+
const timestamp = new Date().toISOString()
|
|
164
|
+
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = SmartMiner
|