@smartledger/bsv 3.3.2 → 3.3.4
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 +220 -79
- package/README.md +283 -71
- package/bsv-covenant.min.js +26 -3
- package/bsv-gdaf.min.js +11 -9
- package/bsv-ltp.min.js +10 -8
- package/bsv-mnemonic.min.js +4 -4
- package/bsv-script-helper.min.js +2 -2
- package/bsv-security.min.js +3 -24
- package/bsv-shamir.min.js +2 -2
- package/bsv-smartcontract.min.js +10 -8
- package/bsv.bundle.js +9 -9
- package/bsv.min.js +10 -8
- package/build/webpack.bundle.config.js +2 -2
- package/build/webpack.config.js +2 -2
- package/build/webpack.covenant.config.js +2 -2
- package/build/webpack.gdaf.config.js +6 -43
- package/build/webpack.script-helper.config.js +2 -2
- package/build/webpack.security.config.js +2 -2
- package/build/webpack.smartcontract.config.js +2 -2
- package/bundle-entry.js +1 -341
- package/covenant-entry.js +1 -44
- package/demos/README.md +188 -0
- package/{architecture_demo.js → demos/architecture_demo.js} +2 -2
- package/demos/bsv_wallet_demo.js +242 -0
- package/{complete_ltp_demo.js → demos/complete_ltp_demo.js} +1 -1
- package/demos/debug_tools_demo.js +87 -0
- package/demos/demo_features.js +123 -0
- package/demos/easy_interface_demo.js +109 -0
- package/demos/ecies_demo.js +182 -0
- package/demos/gdaf_core_test.js +131 -0
- package/demos/gdaf_demo.js +237 -0
- package/demos/ltp_demo.js +361 -0
- package/demos/ltp_primitives_demo.js +403 -0
- package/demos/message_demo.js +209 -0
- package/demos/preimage_separation_demo.js +383 -0
- package/demos/script_helper_demo.js +289 -0
- package/demos/security_demo.js +287 -0
- package/{shamir_demo.js → demos/shamir_demo.js} +1 -1
- package/{simple_demo.js → demos/simple_demo.js} +1 -1
- package/demos/simple_p2pkh_demo.js +169 -0
- package/demos/simple_utxo_preimage_demo.js +196 -0
- package/demos/smart_contract_demo.html +1347 -0
- package/demos/smart_contract_demo.js +910 -0
- package/demos/utxo_generator_demo.js +244 -0
- package/demos/validation_pipeline_demo.js +155 -0
- package/demos/web3keys.html +740 -0
- package/docs/BUNDLE_UPDATE_SUMMARY.md +40 -0
- package/docs/DOCUMENTATION_REVIEW_REPORT.md +295 -0
- package/docs/FIX_CREATEHMAC_ISSUE.md +91 -0
- package/docs/MODULE_REFERENCE_COMPLETE.md +330 -0
- package/docs/README.md +107 -79
- package/docs/SMARTLEDGER_BSV_USAGE_ANSWERS.md +477 -0
- package/docs/SMARTLEDGER_BSV_USAGE_EXAMPLES.js +372 -0
- package/docs/SMARTLEDGER_BSV_USAGE_GUIDE.md +555 -0
- package/docs/SMART_CONTRACT_DEVELOPMENT_GUIDE.md +1459 -0
- package/docs/advanced/LEGAL_TOKEN_PROTOCOL.md +411 -0
- package/docs/advanced/SMART_CONTRACT_GUIDE.md +1255 -0
- package/docs/advanced/UTXO_MANAGER_GUIDE.md +851 -0
- package/docs/api/LTP.md +334 -0
- package/docs/getting-started/INSTALLATION.md +410 -0
- package/docs/getting-started/QUICK_START.md +180 -0
- package/docs/migration/FROM_BSV_1_5_6.md +260 -0
- package/docs/technical/GDAF_DEVELOPER_INTERFACE.md +187 -0
- package/docs/technical/GDAF_IMPLEMENTATION_COMPLETE.md +190 -0
- package/docs/technical/SHAMIR_INTEGRATION_SUMMARY.md +165 -0
- package/docs/technical/roadmap.md +1250 -0
- package/docs/technical/trust_law.md +142 -0
- package/examples/complete_workflow_demo.js +783 -0
- package/examples/definitive_working_demo.js +261 -0
- package/examples/final_working_contracts.js +338 -0
- package/examples/smart_contract_templates.js +718 -0
- package/examples/working_smart_contracts.js +348 -0
- package/gdaf-entry.js +2 -54
- package/index.js +32 -0
- package/lib/mnemonic/pbkdf2.browser.js +69 -0
- package/lib/mnemonic/pbkdf2.js +2 -68
- package/lib/mnemonic/pbkdf2.node.js +68 -0
- package/ltp-entry.js +2 -92
- package/package.json +21 -8
- package/script-helper-entry.js +1 -49
- package/security-entry.js +1 -70
- package/shamir-entry.js +1 -173
- package/smartcontract-entry.js +1 -133
- package/tests/browser-compatibility/README.md +35 -0
- package/tests/browser-compatibility/test-cdn-vs-local.html +186 -0
- package/tests/browser-compatibility/test-pbkdf2.html +51 -0
- package/tests/test_builtin_verify.js +117 -0
- package/tests/test_debug_integration.js +71 -0
- package/tests/test_ecdsa_little.js +70 -0
- package/tests/test_smartverify_der.js +110 -0
- package/utilities/blockchain-state.js +155 -155
- package/utilities/blockchain-state.json +103293 -5244
- package/utilities/miner-simulator.js +354 -358
- package/utilities/mock-utxo-generator.js +54 -54
- package/utilities/raw-tx-examples.js +120 -122
- package/utilities/success-demo.js +104 -105
- package/utilities/transaction-examples.js +188 -188
- package/utilities/utxo-manager.js +91 -91
- package/utilities/wallet-setup.js +79 -80
- package/utilities/working-signature-demo.js +108 -110
- package/SECURITY.md +0 -75
- package/build/bsv-covenant.min.js +0 -10
- package/build/bsv-script-helper.min.js +0 -10
- package/build/bsv-security.min.js +0 -31
- package/build/bsv-smartcontract.min.js +0 -39
- package/build/bsv.bundle.js +0 -39
- package/build/bsv.min.js +0 -39
- package/validation_test.js +0 -97
- /package/docs/{ADVANCED_COVENANT_DEVELOPMENT.md → advanced/ADVANCED_COVENANT_DEVELOPMENT.md} +0 -0
- /package/docs/{CUSTOM_SCRIPT_DEVELOPMENT.md → advanced/CUSTOM_SCRIPT_DEVELOPMENT.md} +0 -0
- /package/docs/{block.md → api/BLOCKS.md} +0 -0
- /package/docs/{ecies.md → api/ECIES.md} +0 -0
- /package/docs/{networks.md → api/NETWORKS.md} +0 -0
- /package/docs/{script.md → api/SCRIPTS.md} +0 -0
- /package/docs/{transaction.md → api/TRANSACTIONS.md} +0 -0
- /package/docs/{unspentoutput.md → api/UTXO.md} +0 -0
- /package/{test_shamir.js → tests/test_shamir.js} +0 -0
- /package/{test_standalone_shamir.html → tests/test_standalone_shamir.html} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ⛏️ BSV Miner Simulator
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Simulates blockchain miner functionality:
|
|
7
7
|
* - Accepts broadcast transactions
|
|
8
8
|
* - Validates against UTXO set
|
|
@@ -11,451 +11,448 @@
|
|
|
11
11
|
* - Mines blocks (simplified)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const bsv = require('../index.js')
|
|
15
|
-
const fs = require('fs')
|
|
16
|
-
const path = require('path')
|
|
14
|
+
const bsv = require('../index.js')
|
|
15
|
+
// const fs = require('fs') // Currently unused
|
|
16
|
+
// const path = require('path') // Currently unused
|
|
17
17
|
const {
|
|
18
18
|
loadBlockchainState,
|
|
19
19
|
saveBlockchainState,
|
|
20
20
|
getUTXO,
|
|
21
|
-
isUTXOAvailable,
|
|
21
|
+
// isUTXOAvailable, // Currently unused
|
|
22
22
|
spendUTXO,
|
|
23
23
|
addUTXO
|
|
24
|
-
} = require('./blockchain-state')
|
|
24
|
+
} = require('./blockchain-state')
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Transaction validation result
|
|
28
28
|
*/
|
|
29
29
|
class ValidationResult {
|
|
30
|
-
constructor(valid = false, errors = [], warnings = []) {
|
|
31
|
-
this.valid = valid
|
|
32
|
-
this.errors = errors
|
|
33
|
-
this.warnings = warnings
|
|
30
|
+
constructor (valid = false, errors = [], warnings = []) {
|
|
31
|
+
this.valid = valid
|
|
32
|
+
this.errors = errors
|
|
33
|
+
this.warnings = warnings
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
addError(message) {
|
|
37
|
-
this.errors.push(message)
|
|
38
|
-
this.valid = false
|
|
35
|
+
|
|
36
|
+
addError (message) {
|
|
37
|
+
this.errors.push(message)
|
|
38
|
+
this.valid = false
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
addWarning(message) {
|
|
42
|
-
this.warnings.push(message)
|
|
40
|
+
|
|
41
|
+
addWarning (message) {
|
|
42
|
+
this.warnings.push(message)
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Validate transaction inputs against UTXO set
|
|
48
48
|
*/
|
|
49
|
-
function validateTransactionInputs(transaction) {
|
|
50
|
-
console.log('🔍 Validating transaction inputs...')
|
|
51
|
-
|
|
52
|
-
const result = new ValidationResult(true)
|
|
53
|
-
const state = loadBlockchainState()
|
|
54
|
-
|
|
49
|
+
function validateTransactionInputs (transaction) {
|
|
50
|
+
console.log('🔍 Validating transaction inputs...')
|
|
51
|
+
|
|
52
|
+
const result = new ValidationResult(true)
|
|
53
|
+
const state = loadBlockchainState()
|
|
54
|
+
|
|
55
55
|
// Check each input
|
|
56
56
|
for (let i = 0; i < transaction.inputs.length; i++) {
|
|
57
|
-
const input = transaction.inputs[i]
|
|
58
|
-
const prevTxId = input.prevTxId.toString('hex')
|
|
59
|
-
const outputIndex = input.outputIndex
|
|
60
|
-
|
|
61
|
-
console.log(` Input ${i}: ${prevTxId}:${outputIndex}`)
|
|
62
|
-
|
|
57
|
+
const input = transaction.inputs[i]
|
|
58
|
+
const prevTxId = input.prevTxId.toString('hex')
|
|
59
|
+
const outputIndex = input.outputIndex
|
|
60
|
+
|
|
61
|
+
console.log(` Input ${i}: ${prevTxId}:${outputIndex}`)
|
|
62
|
+
|
|
63
63
|
// Check if UTXO exists and is unspent
|
|
64
|
-
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
65
|
-
|
|
64
|
+
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
65
|
+
|
|
66
66
|
if (!utxoResult.exists) {
|
|
67
|
-
result.addError(`Input ${i}: UTXO ${prevTxId}:${outputIndex} does not exist`)
|
|
68
|
-
continue
|
|
67
|
+
result.addError(`Input ${i}: UTXO ${prevTxId}:${outputIndex} does not exist`)
|
|
68
|
+
continue
|
|
69
69
|
}
|
|
70
|
-
|
|
70
|
+
|
|
71
71
|
if (utxoResult.spent) {
|
|
72
|
-
result.addError(`Input ${i}: UTXO ${prevTxId}:${outputIndex} already spent in tx ${utxoResult.utxo.spentInTx}`)
|
|
73
|
-
continue
|
|
72
|
+
result.addError(`Input ${i}: UTXO ${prevTxId}:${outputIndex} already spent in tx ${utxoResult.utxo.spentInTx}`)
|
|
73
|
+
continue
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
console.log(` ✅ UTXO exists: ${utxoResult.utxo.satoshis} satoshis`)
|
|
75
|
+
|
|
76
|
+
console.log(` ✅ UTXO exists: ${utxoResult.utxo.satoshis} satoshis`)
|
|
77
77
|
}
|
|
78
|
-
|
|
79
|
-
return result
|
|
78
|
+
|
|
79
|
+
return result
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Validate transaction signatures using BSV script interpreter
|
|
84
84
|
*/
|
|
85
|
-
function validateTransactionSignatures(transaction) {
|
|
86
|
-
console.log('🔐 Validating transaction signatures with BSV script interpreter...')
|
|
87
|
-
|
|
88
|
-
const result = new ValidationResult(true)
|
|
89
|
-
|
|
85
|
+
function validateTransactionSignatures (transaction) {
|
|
86
|
+
console.log('🔐 Validating transaction signatures with BSV script interpreter...')
|
|
87
|
+
|
|
88
|
+
const result = new ValidationResult(true)
|
|
89
|
+
|
|
90
90
|
try {
|
|
91
91
|
for (let i = 0; i < transaction.inputs.length; i++) {
|
|
92
|
-
const input = transaction.inputs[i]
|
|
93
|
-
const prevTxId = input.prevTxId.toString('hex')
|
|
94
|
-
const outputIndex = input.outputIndex
|
|
95
|
-
|
|
96
|
-
console.log(` Input ${i}: ${prevTxId}:${outputIndex}`)
|
|
97
|
-
|
|
92
|
+
const input = transaction.inputs[i]
|
|
93
|
+
const prevTxId = input.prevTxId.toString('hex')
|
|
94
|
+
const outputIndex = input.outputIndex
|
|
95
|
+
|
|
96
|
+
console.log(` Input ${i}: ${prevTxId}:${outputIndex}`)
|
|
97
|
+
|
|
98
98
|
// Get the UTXO being spent
|
|
99
|
-
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
99
|
+
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
100
100
|
if (!utxoResult.exists) {
|
|
101
|
-
result.addError(`Cannot verify signature for non-existent UTXO ${prevTxId}:${outputIndex}`)
|
|
102
|
-
continue
|
|
101
|
+
result.addError(`Cannot verify signature for non-existent UTXO ${prevTxId}:${outputIndex}`)
|
|
102
|
+
continue
|
|
103
103
|
}
|
|
104
|
-
|
|
105
|
-
const utxo = utxoResult.utxo
|
|
106
|
-
const scriptPubKey = bsv.Script.fromHex(utxo.script)
|
|
107
|
-
const scriptSig = input.script
|
|
108
|
-
|
|
109
|
-
console.log(` 📜 ScriptPubKey: ${scriptPubKey.toHex()}`)
|
|
110
|
-
console.log(` 🔏 ScriptSig: ${scriptSig.toHex()}`)
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
|
|
105
|
+
const utxo = utxoResult.utxo
|
|
106
|
+
const scriptPubKey = bsv.Script.fromHex(utxo.script)
|
|
107
|
+
const scriptSig = input.script
|
|
108
|
+
|
|
109
|
+
console.log(` 📜 ScriptPubKey: ${scriptPubKey.toHex()}`)
|
|
110
|
+
console.log(` 🔏 ScriptSig: ${scriptSig.toHex()}`)
|
|
111
|
+
|
|
112
|
+
// Validate using BSV's built-in transaction verification
|
|
113
|
+
try {
|
|
114
|
+
console.log(' 🔧 Using BSV Transaction.verify() - the gold standard')
|
|
115
|
+
|
|
116
|
+
// Use BSV's own transaction verification - this is what we trust!
|
|
117
|
+
// If the transaction was properly signed, this should pass
|
|
118
|
+
let isValid = false
|
|
119
|
+
|
|
113
120
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
console.log(' ⚠️ BSV verify() error:', verifyError.message);
|
|
125
|
-
// Even if verify() throws, the transaction might still be valid
|
|
126
|
-
// Some BSV versions have strict verification that might fail on valid transactions
|
|
127
|
-
isValid = true; // Trust that it was properly signed if we got this far
|
|
128
|
-
console.log(' 🤷 Defaulting to VALID (transaction was properly constructed)');
|
|
129
|
-
}
|
|
130
|
-
|
|
121
|
+
isValid = transaction.verify()
|
|
122
|
+
console.log(' 🎯 BSV Transaction.verify() result:', isValid)
|
|
123
|
+
} catch (verifyError) {
|
|
124
|
+
console.log(' ⚠️ BSV verify() error:', verifyError.message)
|
|
125
|
+
// Even if verify() throws, the transaction might still be valid
|
|
126
|
+
// Some BSV versions have strict verification that might fail on valid transactions
|
|
127
|
+
isValid = true // Trust that it was properly signed if we got this far
|
|
128
|
+
console.log(' 🤷 Defaulting to VALID (transaction was properly constructed)')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
131
|
if (isValid) {
|
|
132
|
-
console.log(` ✅ Script validation PASSED`)
|
|
132
|
+
console.log(` ✅ Script validation PASSED`)
|
|
133
133
|
} else {
|
|
134
|
-
console.log(` ❌ Script validation FAILED`)
|
|
135
|
-
result.addError(`Input ${i}: Script validation failed - Invalid signature or script`)
|
|
134
|
+
console.log(` ❌ Script validation FAILED`)
|
|
135
|
+
result.addError(`Input ${i}: Script validation failed - Invalid signature or script`)
|
|
136
136
|
}
|
|
137
|
-
|
|
138
137
|
} catch (scriptError) {
|
|
139
|
-
console.log(` ❌ Script interpreter error: ${scriptError.message}`)
|
|
140
|
-
result.addError(`Input ${i}: Script interpreter error - ${scriptError.message}`)
|
|
138
|
+
console.log(` ❌ Script interpreter error: ${scriptError.message}`)
|
|
139
|
+
result.addError(`Input ${i}: Script interpreter error - ${scriptError.message}`)
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
142
|
} catch (error) {
|
|
144
|
-
result.addError(`Signature validation error: ${error.message}`)
|
|
143
|
+
result.addError(`Signature validation error: ${error.message}`)
|
|
145
144
|
}
|
|
146
|
-
|
|
147
|
-
return result
|
|
145
|
+
|
|
146
|
+
return result
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
/**
|
|
151
150
|
* Validate transaction outputs
|
|
152
151
|
*/
|
|
153
|
-
function validateTransactionOutputs(transaction) {
|
|
154
|
-
console.log('📤 Validating transaction outputs...')
|
|
155
|
-
|
|
156
|
-
const result = new ValidationResult(true)
|
|
157
|
-
|
|
152
|
+
function validateTransactionOutputs (transaction) {
|
|
153
|
+
console.log('📤 Validating transaction outputs...')
|
|
154
|
+
|
|
155
|
+
const result = new ValidationResult(true)
|
|
156
|
+
|
|
158
157
|
// Check output values are positive
|
|
159
158
|
for (let i = 0; i < transaction.outputs.length; i++) {
|
|
160
|
-
const output = transaction.outputs[i]
|
|
161
|
-
|
|
159
|
+
const output = transaction.outputs[i]
|
|
160
|
+
|
|
162
161
|
if (output.satoshis <= 0) {
|
|
163
|
-
result.addError(`Output ${i}: Invalid amount ${output.satoshis}`)
|
|
162
|
+
result.addError(`Output ${i}: Invalid amount ${output.satoshis}`)
|
|
164
163
|
}
|
|
165
|
-
|
|
164
|
+
|
|
166
165
|
if (!output.script) {
|
|
167
|
-
result.addError(`Output ${i}: No script provided`)
|
|
166
|
+
result.addError(`Output ${i}: No script provided`)
|
|
168
167
|
}
|
|
169
|
-
|
|
170
|
-
console.log(` Output ${i}: ${output.satoshis} satoshis ✅`)
|
|
168
|
+
|
|
169
|
+
console.log(` Output ${i}: ${output.satoshis} satoshis ✅`)
|
|
171
170
|
}
|
|
172
|
-
|
|
173
|
-
return result
|
|
171
|
+
|
|
172
|
+
return result
|
|
174
173
|
}
|
|
175
174
|
|
|
176
175
|
/**
|
|
177
176
|
* Validate transaction balance (inputs = outputs + fees)
|
|
178
177
|
*/
|
|
179
|
-
function validateTransactionBalance(transaction) {
|
|
180
|
-
console.log('⚖️ Validating transaction balance...')
|
|
181
|
-
|
|
182
|
-
const result = new ValidationResult(true)
|
|
183
|
-
|
|
178
|
+
function validateTransactionBalance (transaction) {
|
|
179
|
+
console.log('⚖️ Validating transaction balance...')
|
|
180
|
+
|
|
181
|
+
const result = new ValidationResult(true)
|
|
182
|
+
|
|
184
183
|
// Calculate input value
|
|
185
|
-
let inputValue = 0
|
|
184
|
+
let inputValue = 0
|
|
186
185
|
for (const input of transaction.inputs) {
|
|
187
|
-
const prevTxId = input.prevTxId.toString('hex')
|
|
188
|
-
const outputIndex = input.outputIndex
|
|
189
|
-
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
190
|
-
|
|
186
|
+
const prevTxId = input.prevTxId.toString('hex')
|
|
187
|
+
const outputIndex = input.outputIndex
|
|
188
|
+
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
189
|
+
|
|
191
190
|
if (utxoResult.exists) {
|
|
192
|
-
inputValue += utxoResult.utxo.satoshis
|
|
191
|
+
inputValue += utxoResult.utxo.satoshis
|
|
193
192
|
}
|
|
194
193
|
}
|
|
195
|
-
|
|
194
|
+
|
|
196
195
|
// Calculate output value
|
|
197
|
-
const outputValue = transaction.outputs.reduce((sum, output) => sum + output.satoshis, 0)
|
|
198
|
-
|
|
199
|
-
const fee = inputValue - outputValue
|
|
200
|
-
|
|
201
|
-
console.log(` Input value: ${inputValue} satoshis`)
|
|
202
|
-
console.log(` Output value: ${outputValue} satoshis`)
|
|
203
|
-
console.log(` Transaction fee: ${fee} satoshis`)
|
|
204
|
-
|
|
196
|
+
const outputValue = transaction.outputs.reduce((sum, output) => sum + output.satoshis, 0)
|
|
197
|
+
|
|
198
|
+
const fee = inputValue - outputValue
|
|
199
|
+
|
|
200
|
+
console.log(` Input value: ${inputValue} satoshis`)
|
|
201
|
+
console.log(` Output value: ${outputValue} satoshis`)
|
|
202
|
+
console.log(` Transaction fee: ${fee} satoshis`)
|
|
203
|
+
|
|
205
204
|
if (fee < 0) {
|
|
206
|
-
result.addError(`Invalid transaction: Outputs (${outputValue}) exceed inputs (${inputValue})`)
|
|
205
|
+
result.addError(`Invalid transaction: Outputs (${outputValue}) exceed inputs (${inputValue})`)
|
|
207
206
|
}
|
|
208
|
-
|
|
207
|
+
|
|
209
208
|
if (fee > 10000) { // Arbitrary high fee warning
|
|
210
|
-
result.addWarning(`High transaction fee: ${fee} satoshis`)
|
|
209
|
+
result.addWarning(`High transaction fee: ${fee} satoshis`)
|
|
211
210
|
}
|
|
212
|
-
|
|
213
|
-
return result
|
|
211
|
+
|
|
212
|
+
return result
|
|
214
213
|
}
|
|
215
214
|
|
|
216
215
|
/**
|
|
217
216
|
* Analyze raw transaction hex and show detailed breakdown
|
|
218
217
|
*/
|
|
219
|
-
function analyzeRawTransactionHex(rawHex) {
|
|
218
|
+
function analyzeRawTransactionHex (rawHex) {
|
|
220
219
|
try {
|
|
221
|
-
let offset = 0
|
|
222
|
-
|
|
220
|
+
let offset = 0
|
|
221
|
+
|
|
223
222
|
// Version (4 bytes)
|
|
224
|
-
const version = rawHex.substr(offset, 8)
|
|
225
|
-
offset += 8
|
|
226
|
-
console.log(` 📄 Version: ${version} (${parseInt(version.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
227
|
-
|
|
223
|
+
const version = rawHex.substr(offset, 8)
|
|
224
|
+
offset += 8
|
|
225
|
+
console.log(` 📄 Version: ${version} (${parseInt(version.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
226
|
+
|
|
228
227
|
// Input count (1+ bytes, varint)
|
|
229
|
-
const inputCount = parseInt(rawHex.substr(offset, 2), 16)
|
|
230
|
-
offset += 2
|
|
231
|
-
console.log(` 🔢 Input count: ${rawHex.substr(offset-2, 2)} (${inputCount} input${inputCount > 1 ? 's' : ''})`)
|
|
232
|
-
|
|
228
|
+
const inputCount = parseInt(rawHex.substr(offset, 2), 16)
|
|
229
|
+
offset += 2
|
|
230
|
+
console.log(` 🔢 Input count: ${rawHex.substr(offset - 2, 2)} (${inputCount} input${inputCount > 1 ? 's' : ''})`)
|
|
231
|
+
|
|
233
232
|
// Inputs
|
|
234
233
|
for (let i = 0; i < inputCount; i++) {
|
|
235
|
-
console.log(` \n 📥 Input ${i}:`)
|
|
236
|
-
|
|
234
|
+
console.log(` \n 📥 Input ${i}:`)
|
|
235
|
+
|
|
237
236
|
// Previous transaction hash (32 bytes)
|
|
238
|
-
const prevTxHash = rawHex.substr(offset, 64)
|
|
239
|
-
offset += 64
|
|
240
|
-
console.log(` 🔗 Prev TX: ${prevTxHash}`)
|
|
241
|
-
|
|
237
|
+
const prevTxHash = rawHex.substr(offset, 64)
|
|
238
|
+
offset += 64
|
|
239
|
+
console.log(` 🔗 Prev TX: ${prevTxHash}`)
|
|
240
|
+
|
|
242
241
|
// Output index (4 bytes)
|
|
243
|
-
const outputIndex = rawHex.substr(offset, 8)
|
|
244
|
-
offset += 8
|
|
245
|
-
console.log(` 📍 Vout: ${outputIndex} (${parseInt(outputIndex.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
246
|
-
|
|
242
|
+
const outputIndex = rawHex.substr(offset, 8)
|
|
243
|
+
offset += 8
|
|
244
|
+
console.log(` 📍 Vout: ${outputIndex} (${parseInt(outputIndex.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
245
|
+
|
|
247
246
|
// Script length (1+ bytes, varint)
|
|
248
|
-
const scriptLen = parseInt(rawHex.substr(offset, 2), 16)
|
|
249
|
-
offset += 2
|
|
250
|
-
console.log(` 📏 Script length: ${rawHex.substr(offset-2, 2)} (${scriptLen} bytes)`)
|
|
251
|
-
|
|
247
|
+
const scriptLen = parseInt(rawHex.substr(offset, 2), 16)
|
|
248
|
+
offset += 2
|
|
249
|
+
console.log(` 📏 Script length: ${rawHex.substr(offset - 2, 2)} (${scriptLen} bytes)`)
|
|
250
|
+
|
|
252
251
|
// Script (scriptLen bytes)
|
|
253
|
-
const script = rawHex.substr(offset, scriptLen * 2)
|
|
254
|
-
offset += scriptLen * 2
|
|
255
|
-
console.log(` 🔐 Script: ${script}`)
|
|
256
|
-
|
|
252
|
+
const script = rawHex.substr(offset, scriptLen * 2)
|
|
253
|
+
offset += scriptLen * 2
|
|
254
|
+
console.log(` 🔐 Script: ${script}`)
|
|
255
|
+
|
|
257
256
|
// Parse script components
|
|
258
257
|
if (script.length > 0) {
|
|
259
|
-
let scriptOffset = 0
|
|
260
|
-
console.log(` 🔍 Script breakdown:`)
|
|
261
|
-
|
|
258
|
+
let scriptOffset = 0
|
|
259
|
+
console.log(` 🔍 Script breakdown:`)
|
|
260
|
+
|
|
262
261
|
// First byte is signature length
|
|
263
|
-
const sigLen = parseInt(script.substr(scriptOffset, 2), 16)
|
|
264
|
-
scriptOffset += 2
|
|
265
|
-
console.log(` 🖊️ Sig length: ${sigLen} bytes`)
|
|
266
|
-
|
|
262
|
+
const sigLen = parseInt(script.substr(scriptOffset, 2), 16)
|
|
263
|
+
scriptOffset += 2
|
|
264
|
+
console.log(` 🖊️ Sig length: ${sigLen} bytes`)
|
|
265
|
+
|
|
267
266
|
// Signature
|
|
268
|
-
const signature = script.substr(scriptOffset, sigLen * 2)
|
|
269
|
-
scriptOffset += sigLen * 2
|
|
270
|
-
console.log(` 🖊️ Signature: ${signature}`)
|
|
271
|
-
|
|
267
|
+
const signature = script.substr(scriptOffset, sigLen * 2)
|
|
268
|
+
scriptOffset += sigLen * 2
|
|
269
|
+
console.log(` 🖊️ Signature: ${signature}`)
|
|
270
|
+
|
|
272
271
|
// Public key length
|
|
273
|
-
const pubKeyLen = parseInt(script.substr(scriptOffset, 2), 16)
|
|
274
|
-
scriptOffset += 2
|
|
275
|
-
console.log(` 🔑 PubKey length: ${pubKeyLen} bytes`)
|
|
276
|
-
|
|
272
|
+
const pubKeyLen = parseInt(script.substr(scriptOffset, 2), 16)
|
|
273
|
+
scriptOffset += 2
|
|
274
|
+
console.log(` 🔑 PubKey length: ${pubKeyLen} bytes`)
|
|
275
|
+
|
|
277
276
|
// Public key
|
|
278
|
-
const pubKey = script.substr(scriptOffset, pubKeyLen * 2)
|
|
279
|
-
scriptOffset += pubKeyLen * 2
|
|
280
|
-
console.log(` 🔑 Public key: ${pubKey}`)
|
|
277
|
+
const pubKey = script.substr(scriptOffset, pubKeyLen * 2)
|
|
278
|
+
scriptOffset += pubKeyLen * 2
|
|
279
|
+
console.log(` 🔑 Public key: ${pubKey}`)
|
|
281
280
|
}
|
|
282
|
-
|
|
281
|
+
|
|
283
282
|
// Sequence (4 bytes)
|
|
284
|
-
const sequence = rawHex.substr(offset, 8)
|
|
285
|
-
offset += 8
|
|
286
|
-
console.log(` ⏰ Sequence: ${sequence}`)
|
|
283
|
+
const sequence = rawHex.substr(offset, 8)
|
|
284
|
+
offset += 8
|
|
285
|
+
console.log(` ⏰ Sequence: ${sequence}`)
|
|
287
286
|
}
|
|
288
|
-
|
|
287
|
+
|
|
289
288
|
// Output count (1+ bytes, varint)
|
|
290
|
-
const outputCount = parseInt(rawHex.substr(offset, 2), 16)
|
|
291
|
-
offset += 2
|
|
292
|
-
console.log(`\n 🔢 Output count: ${rawHex.substr(offset-2, 2)} (${outputCount} output${outputCount > 1 ? 's' : ''})`)
|
|
293
|
-
|
|
289
|
+
const outputCount = parseInt(rawHex.substr(offset, 2), 16)
|
|
290
|
+
offset += 2
|
|
291
|
+
console.log(`\n 🔢 Output count: ${rawHex.substr(offset - 2, 2)} (${outputCount} output${outputCount > 1 ? 's' : ''})`)
|
|
292
|
+
|
|
294
293
|
// Outputs
|
|
295
294
|
for (let i = 0; i < outputCount; i++) {
|
|
296
|
-
console.log(` \n 📤 Output ${i}:`)
|
|
297
|
-
|
|
295
|
+
console.log(` \n 📤 Output ${i}:`)
|
|
296
|
+
|
|
298
297
|
// Value (8 bytes)
|
|
299
|
-
const value = rawHex.substr(offset, 16)
|
|
300
|
-
offset += 16
|
|
301
|
-
const satoshis = parseInt(value.match(/.{2}/g).reverse().join(''), 16)
|
|
302
|
-
console.log(` 💰 Value: ${value} (${satoshis} satoshis)`)
|
|
303
|
-
|
|
298
|
+
const value = rawHex.substr(offset, 16)
|
|
299
|
+
offset += 16
|
|
300
|
+
const satoshis = parseInt(value.match(/.{2}/g).reverse().join(''), 16)
|
|
301
|
+
console.log(` 💰 Value: ${value} (${satoshis} satoshis)`)
|
|
302
|
+
|
|
304
303
|
// Script length (1+ bytes, varint)
|
|
305
|
-
const scriptLen = parseInt(rawHex.substr(offset, 2), 16)
|
|
306
|
-
offset += 2
|
|
307
|
-
console.log(` 📏 Script length: ${rawHex.substr(offset-2, 2)} (${scriptLen} bytes)`)
|
|
308
|
-
|
|
304
|
+
const scriptLen = parseInt(rawHex.substr(offset, 2), 16)
|
|
305
|
+
offset += 2
|
|
306
|
+
console.log(` 📏 Script length: ${rawHex.substr(offset - 2, 2)} (${scriptLen} bytes)`)
|
|
307
|
+
|
|
309
308
|
// Script (scriptLen bytes)
|
|
310
|
-
const script = rawHex.substr(offset, scriptLen * 2)
|
|
311
|
-
offset += scriptLen * 2
|
|
312
|
-
console.log(` 🏠 Script: ${script}`)
|
|
313
|
-
|
|
309
|
+
const script = rawHex.substr(offset, scriptLen * 2)
|
|
310
|
+
offset += scriptLen * 2
|
|
311
|
+
console.log(` 🏠 Script: ${script}`)
|
|
312
|
+
|
|
314
313
|
// Parse P2PKH script
|
|
315
314
|
if (script.length === 50 && script.startsWith('76a914') && script.endsWith('88ac')) {
|
|
316
|
-
const hash160 = script.substr(6, 40)
|
|
317
|
-
console.log(` 🔍 P2PKH script breakdown:`)
|
|
318
|
-
console.log(` 📋 OP_DUP: 76`)
|
|
319
|
-
console.log(` 📋 OP_HASH160: a9`)
|
|
320
|
-
console.log(` 📋 Push 20 bytes: 14`)
|
|
321
|
-
console.log(` 🏠 Address hash160: ${hash160}`)
|
|
322
|
-
console.log(` 📋 OP_EQUALVERIFY: 88`)
|
|
323
|
-
console.log(` 📋 OP_CHECKSIG: ac`)
|
|
315
|
+
const hash160 = script.substr(6, 40)
|
|
316
|
+
console.log(` 🔍 P2PKH script breakdown:`)
|
|
317
|
+
console.log(` 📋 OP_DUP: 76`)
|
|
318
|
+
console.log(` 📋 OP_HASH160: a9`)
|
|
319
|
+
console.log(` 📋 Push 20 bytes: 14`)
|
|
320
|
+
console.log(` 🏠 Address hash160: ${hash160}`)
|
|
321
|
+
console.log(` 📋 OP_EQUALVERIFY: 88`)
|
|
322
|
+
console.log(` 📋 OP_CHECKSIG: ac`)
|
|
324
323
|
}
|
|
325
324
|
}
|
|
326
|
-
|
|
325
|
+
|
|
327
326
|
// Lock time (4 bytes)
|
|
328
|
-
const lockTime = rawHex.substr(offset, 8)
|
|
329
|
-
console.log(`\n 🔒 Lock time: ${lockTime} (${parseInt(lockTime.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
330
|
-
|
|
327
|
+
const lockTime = rawHex.substr(offset, 8)
|
|
328
|
+
console.log(`\n 🔒 Lock time: ${lockTime} (${parseInt(lockTime.match(/.{2}/g).reverse().join(''), 16)})`)
|
|
331
329
|
} catch (error) {
|
|
332
|
-
console.log(` ❌ Error analyzing hex: ${error.message}`)
|
|
330
|
+
console.log(` ❌ Error analyzing hex: ${error.message}`)
|
|
333
331
|
}
|
|
334
332
|
}
|
|
335
333
|
|
|
336
334
|
/**
|
|
337
335
|
* Validate raw transaction hex format
|
|
338
336
|
*/
|
|
339
|
-
function validateTransactionHex(transaction) {
|
|
340
|
-
console.log('🔢 Validating raw transaction hex format...')
|
|
341
|
-
|
|
342
|
-
const result = new ValidationResult(true)
|
|
343
|
-
|
|
337
|
+
function validateTransactionHex (transaction) {
|
|
338
|
+
console.log('🔢 Validating raw transaction hex format...')
|
|
339
|
+
|
|
340
|
+
const result = new ValidationResult(true)
|
|
341
|
+
|
|
344
342
|
try {
|
|
345
343
|
// Get the raw hex of the transaction
|
|
346
|
-
const rawHex = transaction.toString()
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const parsedTx = new bsv.Transaction(rawHex)
|
|
355
|
-
|
|
344
|
+
const rawHex = transaction.toString()
|
|
345
|
+
console.log(` 📦 Raw TX hex length: ${rawHex.length} characters`)
|
|
346
|
+
console.log(` 🔍 TX hex preview: ${rawHex.substring(0, 60)}...`)
|
|
347
|
+
console.log(` 📋 Full raw hex: ${rawHex}`)
|
|
348
|
+
|
|
349
|
+
// Parse and show detailed breakdown
|
|
350
|
+
console.log(`\n 🔬 Raw Hex Breakdown:`)
|
|
351
|
+
analyzeRawTransactionHex(rawHex) // Try to parse the hex back into a transaction object
|
|
352
|
+
const parsedTx = new bsv.Transaction(rawHex)
|
|
353
|
+
|
|
356
354
|
// Verify it matches the original
|
|
357
355
|
if (parsedTx.id !== transaction.id) {
|
|
358
|
-
result.addError(`Transaction hex parsing mismatch: expected ${transaction.id}, got ${parsedTx.id}`)
|
|
356
|
+
result.addError(`Transaction hex parsing mismatch: expected ${transaction.id}, got ${parsedTx.id}`)
|
|
359
357
|
} else {
|
|
360
|
-
console.log(` ✅ Transaction hex is valid and parseable`)
|
|
358
|
+
console.log(` ✅ Transaction hex is valid and parseable`)
|
|
361
359
|
}
|
|
362
|
-
|
|
360
|
+
|
|
363
361
|
// Additional format checks
|
|
364
362
|
if (rawHex.length < 20) {
|
|
365
|
-
result.addError('Transaction hex too short to be valid')
|
|
363
|
+
result.addError('Transaction hex too short to be valid')
|
|
366
364
|
}
|
|
367
|
-
|
|
365
|
+
|
|
368
366
|
if (!/^[0-9a-fA-F]+$/.test(rawHex)) {
|
|
369
|
-
result.addError('Transaction contains invalid hex characters')
|
|
367
|
+
result.addError('Transaction contains invalid hex characters')
|
|
370
368
|
}
|
|
371
|
-
|
|
372
369
|
} catch (error) {
|
|
373
|
-
result.addError(`Invalid transaction hex format: ${error.message}`)
|
|
370
|
+
result.addError(`Invalid transaction hex format: ${error.message}`)
|
|
374
371
|
}
|
|
375
|
-
|
|
376
|
-
return result
|
|
372
|
+
|
|
373
|
+
return result
|
|
377
374
|
}
|
|
378
375
|
|
|
379
376
|
/**
|
|
380
377
|
* Validate entire transaction with comprehensive checks
|
|
381
378
|
*/
|
|
382
|
-
function validateTransaction(transaction) {
|
|
383
|
-
console.log(`\n⛏️ MINER: Validating transaction ${transaction.id}`)
|
|
384
|
-
console.log('═'.repeat(80))
|
|
385
|
-
|
|
379
|
+
function validateTransaction (transaction) {
|
|
380
|
+
console.log(`\n⛏️ MINER: Validating transaction ${transaction.id}`)
|
|
381
|
+
console.log('═'.repeat(80))
|
|
382
|
+
|
|
386
383
|
const results = [
|
|
387
384
|
validateTransactionHex(transaction),
|
|
388
385
|
validateTransactionInputs(transaction),
|
|
389
386
|
validateTransactionSignatures(transaction),
|
|
390
387
|
validateTransactionOutputs(transaction),
|
|
391
388
|
validateTransactionBalance(transaction)
|
|
392
|
-
]
|
|
393
|
-
|
|
389
|
+
]
|
|
390
|
+
|
|
394
391
|
// Combine all results
|
|
395
|
-
const finalResult = new ValidationResult(true)
|
|
396
|
-
|
|
392
|
+
const finalResult = new ValidationResult(true)
|
|
393
|
+
|
|
397
394
|
results.forEach(result => {
|
|
398
395
|
if (!result.valid) {
|
|
399
|
-
finalResult.valid = false
|
|
396
|
+
finalResult.valid = false
|
|
400
397
|
}
|
|
401
|
-
finalResult.errors.push(...result.errors)
|
|
402
|
-
finalResult.warnings.push(...result.warnings)
|
|
403
|
-
})
|
|
404
|
-
|
|
398
|
+
finalResult.errors.push(...result.errors)
|
|
399
|
+
finalResult.warnings.push(...result.warnings)
|
|
400
|
+
})
|
|
401
|
+
|
|
405
402
|
// Display results
|
|
406
403
|
if (finalResult.valid) {
|
|
407
|
-
console.log('\n✅ Transaction is VALID - All checks passed')
|
|
408
|
-
console.log(' 🔢 Raw hex format: ✅')
|
|
409
|
-
console.log(' 🔍 UTXO validation: ✅')
|
|
410
|
-
console.log(' 🔐 Script validation: ✅')
|
|
411
|
-
console.log(' 📤 Output validation: ✅')
|
|
412
|
-
console.log(' ⚖️ Balance validation: ✅')
|
|
404
|
+
console.log('\n✅ Transaction is VALID - All checks passed')
|
|
405
|
+
console.log(' 🔢 Raw hex format: ✅')
|
|
406
|
+
console.log(' 🔍 UTXO validation: ✅')
|
|
407
|
+
console.log(' 🔐 Script validation: ✅')
|
|
408
|
+
console.log(' 📤 Output validation: ✅')
|
|
409
|
+
console.log(' ⚖️ Balance validation: ✅')
|
|
413
410
|
} else {
|
|
414
|
-
console.log('\n❌ Transaction is INVALID')
|
|
411
|
+
console.log('\n❌ Transaction is INVALID')
|
|
415
412
|
}
|
|
416
|
-
|
|
413
|
+
|
|
417
414
|
if (finalResult.errors.length > 0) {
|
|
418
|
-
console.log('\n🚫 Validation Errors:')
|
|
419
|
-
finalResult.errors.forEach(error => console.log(` - ${error}`))
|
|
415
|
+
console.log('\n🚫 Validation Errors:')
|
|
416
|
+
finalResult.errors.forEach(error => console.log(` - ${error}`))
|
|
420
417
|
}
|
|
421
|
-
|
|
418
|
+
|
|
422
419
|
if (finalResult.warnings.length > 0) {
|
|
423
|
-
console.log('\n⚠️ Validation Warnings:')
|
|
424
|
-
finalResult.warnings.forEach(warning => console.log(` - ${warning}`))
|
|
420
|
+
console.log('\n⚠️ Validation Warnings:')
|
|
421
|
+
finalResult.warnings.forEach(warning => console.log(` - ${warning}`))
|
|
425
422
|
}
|
|
426
|
-
|
|
427
|
-
return finalResult
|
|
423
|
+
|
|
424
|
+
return finalResult
|
|
428
425
|
}
|
|
429
426
|
|
|
430
427
|
/**
|
|
431
428
|
* Process a valid transaction (update UTXO set)
|
|
432
429
|
*/
|
|
433
|
-
function processTransaction(transaction) {
|
|
434
|
-
console.log(`\n🔄 Processing transaction ${transaction.id}...`)
|
|
435
|
-
|
|
436
|
-
const state = loadBlockchainState()
|
|
437
|
-
|
|
430
|
+
function processTransaction (transaction) {
|
|
431
|
+
console.log(`\n🔄 Processing transaction ${transaction.id}...`)
|
|
432
|
+
|
|
433
|
+
const state = loadBlockchainState()
|
|
434
|
+
|
|
438
435
|
// Spend input UTXOs
|
|
439
|
-
console.log('❌ Spending input UTXOs:')
|
|
436
|
+
console.log('❌ Spending input UTXOs:')
|
|
440
437
|
for (const input of transaction.inputs) {
|
|
441
|
-
const prevTxId = input.prevTxId.toString('hex')
|
|
442
|
-
const outputIndex = input.outputIndex
|
|
443
|
-
|
|
438
|
+
const prevTxId = input.prevTxId.toString('hex')
|
|
439
|
+
const outputIndex = input.outputIndex
|
|
440
|
+
|
|
444
441
|
try {
|
|
445
|
-
spendUTXO(prevTxId, outputIndex, transaction.id)
|
|
446
|
-
console.log(` - ${prevTxId}:${outputIndex} spent`)
|
|
442
|
+
spendUTXO(prevTxId, outputIndex, transaction.id)
|
|
443
|
+
console.log(` - ${prevTxId}:${outputIndex} spent`)
|
|
447
444
|
} catch (error) {
|
|
448
|
-
console.error(` - Error spending ${prevTxId}:${outputIndex}: ${error.message}`)
|
|
445
|
+
console.error(` - Error spending ${prevTxId}:${outputIndex}: ${error.message}`)
|
|
449
446
|
}
|
|
450
447
|
}
|
|
451
|
-
|
|
448
|
+
|
|
452
449
|
// Create new UTXOs from outputs
|
|
453
|
-
console.log('✅ Creating new UTXOs:')
|
|
450
|
+
console.log('✅ Creating new UTXOs:')
|
|
454
451
|
for (let i = 0; i < transaction.outputs.length; i++) {
|
|
455
|
-
const output = transaction.outputs[i]
|
|
456
|
-
|
|
452
|
+
const output = transaction.outputs[i]
|
|
453
|
+
|
|
457
454
|
try {
|
|
458
|
-
const outputAddress = output.script.toAddress()
|
|
455
|
+
const outputAddress = output.script.toAddress()
|
|
459
456
|
const newUTXO = {
|
|
460
457
|
txid: transaction.id,
|
|
461
458
|
vout: i,
|
|
@@ -464,15 +461,15 @@ function processTransaction(transaction) {
|
|
|
464
461
|
scriptPubKey: output.script.toHex(),
|
|
465
462
|
satoshis: output.satoshis,
|
|
466
463
|
address: outputAddress.toString()
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
addUTXO(newUTXO, outputAddress.toString())
|
|
470
|
-
console.log(` - ${transaction.id}:${i} -> ${outputAddress} (${output.satoshis} sats)`)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
addUTXO(newUTXO, outputAddress.toString())
|
|
467
|
+
console.log(` - ${transaction.id}:${i} -> ${outputAddress} (${output.satoshis} sats)`)
|
|
471
468
|
} catch (error) {
|
|
472
|
-
console.error(` - Error creating output ${i}: ${error.message}`)
|
|
469
|
+
console.error(` - Error creating output ${i}: ${error.message}`)
|
|
473
470
|
}
|
|
474
471
|
}
|
|
475
|
-
|
|
472
|
+
|
|
476
473
|
// Add to transaction history
|
|
477
474
|
state.transactionHistory.push({
|
|
478
475
|
txid: transaction.id,
|
|
@@ -480,132 +477,131 @@ function processTransaction(transaction) {
|
|
|
480
477
|
inputCount: transaction.inputs.length,
|
|
481
478
|
outputCount: transaction.outputs.length,
|
|
482
479
|
fee: calculateTransactionFee(transaction)
|
|
483
|
-
})
|
|
484
|
-
|
|
480
|
+
})
|
|
481
|
+
|
|
485
482
|
// Increment block height (simplified mining)
|
|
486
|
-
state.metadata.blockHeight += 1
|
|
487
|
-
|
|
488
|
-
saveBlockchainState(state)
|
|
489
|
-
|
|
490
|
-
console.log(`✅ Transaction ${transaction.id} processed successfully`)
|
|
491
|
-
console.log(`🏗️ New block height: ${state.metadata.blockHeight}`)
|
|
483
|
+
state.metadata.blockHeight += 1
|
|
484
|
+
|
|
485
|
+
saveBlockchainState(state)
|
|
486
|
+
|
|
487
|
+
console.log(`✅ Transaction ${transaction.id} processed successfully`)
|
|
488
|
+
console.log(`🏗️ New block height: ${state.metadata.blockHeight}`)
|
|
492
489
|
}
|
|
493
490
|
|
|
494
491
|
/**
|
|
495
492
|
* Calculate transaction fee
|
|
496
493
|
*/
|
|
497
|
-
function calculateTransactionFee(transaction) {
|
|
498
|
-
let inputValue = 0
|
|
499
|
-
|
|
494
|
+
function calculateTransactionFee (transaction) {
|
|
495
|
+
let inputValue = 0
|
|
496
|
+
|
|
500
497
|
for (const input of transaction.inputs) {
|
|
501
|
-
const prevTxId = input.prevTxId.toString('hex')
|
|
502
|
-
const outputIndex = input.outputIndex
|
|
503
|
-
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
504
|
-
|
|
498
|
+
const prevTxId = input.prevTxId.toString('hex')
|
|
499
|
+
const outputIndex = input.outputIndex
|
|
500
|
+
const utxoResult = getUTXO(prevTxId, outputIndex)
|
|
501
|
+
|
|
505
502
|
if (utxoResult.exists) {
|
|
506
|
-
inputValue += utxoResult.utxo.satoshis
|
|
503
|
+
inputValue += utxoResult.utxo.satoshis
|
|
507
504
|
}
|
|
508
505
|
}
|
|
509
|
-
|
|
510
|
-
const outputValue = transaction.outputs.reduce((sum, output) => sum + output.satoshis, 0)
|
|
511
|
-
|
|
512
|
-
return inputValue - outputValue
|
|
506
|
+
|
|
507
|
+
const outputValue = transaction.outputs.reduce((sum, output) => sum + output.satoshis, 0)
|
|
508
|
+
|
|
509
|
+
return inputValue - outputValue
|
|
513
510
|
}
|
|
514
511
|
|
|
515
512
|
/**
|
|
516
513
|
* Accept raw transaction hex and validate it
|
|
517
514
|
*/
|
|
518
|
-
function acceptRawTransaction(rawTxHex) {
|
|
519
|
-
console.log('\n📡 BROADCAST: Raw transaction hex received by miner')
|
|
520
|
-
console.log(`Raw hex length: ${rawTxHex.length} characters`)
|
|
521
|
-
|
|
515
|
+
function acceptRawTransaction (rawTxHex) {
|
|
516
|
+
console.log('\n📡 BROADCAST: Raw transaction hex received by miner')
|
|
517
|
+
console.log(`Raw hex length: ${rawTxHex.length} characters`)
|
|
518
|
+
|
|
522
519
|
try {
|
|
523
520
|
// Parse the raw transaction hex
|
|
524
|
-
const transaction = new bsv.Transaction(rawTxHex)
|
|
525
|
-
console.log(`Transaction ID: ${transaction.id}`)
|
|
526
|
-
console.log(`Inputs: ${transaction.inputs.length}`)
|
|
527
|
-
console.log(`Outputs: ${transaction.outputs.length}`)
|
|
528
|
-
|
|
529
|
-
return acceptTransaction(transaction)
|
|
530
|
-
|
|
521
|
+
const transaction = new bsv.Transaction(rawTxHex)
|
|
522
|
+
console.log(`Transaction ID: ${transaction.id}`)
|
|
523
|
+
console.log(`Inputs: ${transaction.inputs.length}`)
|
|
524
|
+
console.log(`Outputs: ${transaction.outputs.length}`)
|
|
525
|
+
|
|
526
|
+
return acceptTransaction(transaction)
|
|
531
527
|
} catch (error) {
|
|
532
|
-
console.log('\n❌ INVALID RAW TRANSACTION')
|
|
533
|
-
console.log(`Parse error: ${error.message}`)
|
|
534
|
-
|
|
528
|
+
console.log('\n❌ INVALID RAW TRANSACTION')
|
|
529
|
+
console.log(`Parse error: ${error.message}`)
|
|
530
|
+
|
|
535
531
|
return {
|
|
536
532
|
accepted: false,
|
|
537
533
|
txid: null,
|
|
538
534
|
errors: [`Failed to parse raw transaction hex: ${error.message}`],
|
|
539
535
|
warnings: []
|
|
540
|
-
}
|
|
536
|
+
}
|
|
541
537
|
}
|
|
542
538
|
}
|
|
543
539
|
|
|
544
540
|
/**
|
|
545
541
|
* Accept and process a broadcast transaction (object or hex)
|
|
546
542
|
*/
|
|
547
|
-
function acceptTransaction(transaction) {
|
|
543
|
+
function acceptTransaction (transaction) {
|
|
548
544
|
// If it's a string, treat as raw hex
|
|
549
545
|
if (typeof transaction === 'string') {
|
|
550
|
-
return acceptRawTransaction(transaction)
|
|
546
|
+
return acceptRawTransaction(transaction)
|
|
551
547
|
}
|
|
552
|
-
|
|
553
|
-
console.log('\n📡 BROADCAST: Transaction object received by miner')
|
|
554
|
-
console.log(`Transaction ID: ${transaction.id}`)
|
|
555
|
-
console.log(`Inputs: ${transaction.inputs.length}`)
|
|
556
|
-
console.log(`Outputs: ${transaction.outputs.length}`)
|
|
557
|
-
|
|
548
|
+
|
|
549
|
+
console.log('\n📡 BROADCAST: Transaction object received by miner')
|
|
550
|
+
console.log(`Transaction ID: ${transaction.id}`)
|
|
551
|
+
console.log(`Inputs: ${transaction.inputs.length}`)
|
|
552
|
+
console.log(`Outputs: ${transaction.outputs.length}`)
|
|
553
|
+
|
|
558
554
|
// Log the raw hex representation
|
|
559
|
-
const rawHex = transaction.toString()
|
|
560
|
-
console.log(`📦 Raw Transaction Hex (${rawHex.length} chars):`)
|
|
561
|
-
console.log(`${rawHex.substring(0, 80)}...`)
|
|
562
|
-
console.log(`...${rawHex.substring(rawHex.length - 80)}`)
|
|
563
|
-
|
|
555
|
+
const rawHex = transaction.toString()
|
|
556
|
+
console.log(`📦 Raw Transaction Hex (${rawHex.length} chars):`)
|
|
557
|
+
console.log(`${rawHex.substring(0, 80)}...`)
|
|
558
|
+
console.log(`...${rawHex.substring(rawHex.length - 80)}`)
|
|
559
|
+
|
|
564
560
|
// Validate transaction
|
|
565
|
-
const validation = validateTransaction(transaction)
|
|
566
|
-
|
|
561
|
+
const validation = validateTransaction(transaction)
|
|
562
|
+
|
|
567
563
|
if (validation.valid) {
|
|
568
564
|
// Process the transaction
|
|
569
|
-
processTransaction(transaction)
|
|
570
|
-
|
|
565
|
+
processTransaction(transaction)
|
|
566
|
+
|
|
571
567
|
return {
|
|
572
568
|
accepted: true,
|
|
573
569
|
txid: transaction.id,
|
|
574
570
|
errors: validation.errors,
|
|
575
571
|
warnings: validation.warnings
|
|
576
|
-
}
|
|
572
|
+
}
|
|
577
573
|
} else {
|
|
578
|
-
console.log(`\n🚫 Transaction ${transaction.id} REJECTED`)
|
|
579
|
-
|
|
574
|
+
console.log(`\n🚫 Transaction ${transaction.id} REJECTED`)
|
|
575
|
+
|
|
580
576
|
return {
|
|
581
577
|
accepted: false,
|
|
582
578
|
txid: transaction.id,
|
|
583
579
|
errors: validation.errors,
|
|
584
580
|
warnings: validation.warnings
|
|
585
|
-
}
|
|
581
|
+
}
|
|
586
582
|
}
|
|
587
583
|
}
|
|
588
584
|
|
|
589
585
|
/**
|
|
590
586
|
* Get mempool status (simplified)
|
|
591
587
|
*/
|
|
592
|
-
function getMempoolStatus() {
|
|
593
|
-
const state = loadBlockchainState()
|
|
594
|
-
|
|
595
|
-
console.log('\n📊 Miner/Mempool Status:')
|
|
596
|
-
console.log('═'.repeat(50))
|
|
597
|
-
console.log(`🏗️ Current Block Height: ${state.metadata.blockHeight}`)
|
|
598
|
-
console.log(`🔄 Transactions Processed: ${state.transactionHistory.length}`)
|
|
599
|
-
console.log(`💰 Total Network Value: ${state.metadata.totalValue} satoshis`)
|
|
600
|
-
console.log(`👛 Active Wallets: ${state.metadata.totalWallets}`)
|
|
601
|
-
console.log(`📦 Available UTXOs: ${state.metadata.totalUTXOs}`)
|
|
602
|
-
|
|
603
|
-
return state
|
|
588
|
+
function getMempoolStatus () {
|
|
589
|
+
const state = loadBlockchainState()
|
|
590
|
+
|
|
591
|
+
console.log('\n📊 Miner/Mempool Status:')
|
|
592
|
+
console.log('═'.repeat(50))
|
|
593
|
+
console.log(`🏗️ Current Block Height: ${state.metadata.blockHeight}`)
|
|
594
|
+
console.log(`🔄 Transactions Processed: ${state.transactionHistory.length}`)
|
|
595
|
+
console.log(`💰 Total Network Value: ${state.metadata.totalValue} satoshis`)
|
|
596
|
+
console.log(`👛 Active Wallets: ${state.metadata.totalWallets}`)
|
|
597
|
+
console.log(`📦 Available UTXOs: ${state.metadata.totalUTXOs}`)
|
|
598
|
+
|
|
599
|
+
return state
|
|
604
600
|
}
|
|
605
601
|
|
|
606
602
|
// If called directly, show mempool status
|
|
607
603
|
if (require.main === module) {
|
|
608
|
-
getMempoolStatus()
|
|
604
|
+
getMempoolStatus()
|
|
609
605
|
}
|
|
610
606
|
|
|
611
607
|
module.exports = {
|
|
@@ -617,4 +613,4 @@ module.exports = {
|
|
|
617
613
|
processTransaction,
|
|
618
614
|
getMempoolStatus,
|
|
619
615
|
ValidationResult
|
|
620
|
-
}
|
|
616
|
+
}
|