jaelis-node 1.10.0 → 2.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.
Files changed (173) hide show
  1. package/README.md +156 -439
  2. package/bin/jaelis-node.js +83 -504
  3. package/lib/index.js +31 -2840
  4. package/lib/node.js +309 -0
  5. package/lib/rpc.js +315 -0
  6. package/lib/storage.js +198 -0
  7. package/lib/sync.js +366 -0
  8. package/package.json +19 -53
  9. package/config/default.json +0 -74
  10. package/config/mainnet.json +0 -30
  11. package/config/testnet.json +0 -26
  12. package/lib/JAELIS-VM/lib/adapters/evm-adapter.js +0 -454
  13. package/lib/JAELIS-VM/lib/adapters/index.js +0 -411
  14. package/lib/JAELIS-VM/lib/adapters/svm-adapter.js +0 -457
  15. package/lib/JAELIS-VM/lib/compiler/jir-compiler.js +0 -1097
  16. package/lib/JAELIS-VM/lib/execution/engine.js +0 -1183
  17. package/lib/JAELIS-VM/lib/index.js +0 -440
  18. package/lib/JAELIS-VM/lib/integration/jaelis-integration.js +0 -543
  19. package/lib/JAELIS-VM/lib/serialization/serializer.js +0 -819
  20. package/lib/JAELIS-VM/lib/state/state-manager.js +0 -1116
  21. package/lib/JAELIS-VM/lib/translator/bytecode-translator.js +0 -1222
  22. package/lib/JAELIS-VM/lib/unified/cross-chain-deploy.js +0 -1678
  23. package/lib/JAELIS-VM/lib/unified/cross-chain-state.js +0 -836
  24. package/lib/JAELIS-VM/lib/unified/dynamic-contracts.js +0 -1127
  25. package/lib/JAELIS-VM/lib/unified/index.js +0 -456
  26. package/lib/JAELIS-VM/lib/unified/jaelis-abi.js +0 -1150
  27. package/lib/JAELIS-VM/lib/unified/unified-compiler.js +0 -1350
  28. package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds +0 -12
  29. package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds.cmd +0 -17
  30. package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds.ps1 +0 -28
  31. package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds +0 -12
  32. package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds.cmd +0 -17
  33. package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds.ps1 +0 -28
  34. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages +0 -12
  35. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional +0 -12
  36. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional.cmd +0 -17
  37. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional.ps1 +0 -28
  38. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test +0 -12
  39. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test.cmd +0 -17
  40. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test.ps1 +0 -28
  41. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages.cmd +0 -17
  42. package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages.ps1 +0 -28
  43. package/lib/JAELIS-VM/node_modules/.package-lock.json +0 -127
  44. package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/README.md +0 -1
  45. package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/index.js +0 -0
  46. package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/node.abi115.node +0 -0
  47. package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/node.napi.node +0 -0
  48. package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/package.json +0 -17
  49. package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/README.md +0 -1
  50. package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/index.js +0 -0
  51. package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/node.abi115.node +0 -0
  52. package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/node.napi.node +0 -0
  53. package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/package.json +0 -17
  54. package/lib/JAELIS-VM/node_modules/cbor-extract/LICENSE +0 -21
  55. package/lib/JAELIS-VM/node_modules/cbor-extract/README.md +0 -5
  56. package/lib/JAELIS-VM/node_modules/cbor-extract/bin/download-prebuilds.js +0 -11
  57. package/lib/JAELIS-VM/node_modules/cbor-extract/binding.gyp +0 -60
  58. package/lib/JAELIS-VM/node_modules/cbor-extract/index.js +0 -1
  59. package/lib/JAELIS-VM/node_modules/cbor-extract/package.json +0 -50
  60. package/lib/JAELIS-VM/node_modules/cbor-extract/src/extract.cpp +0 -198
  61. package/lib/JAELIS-VM/node_modules/cbor-x/LICENSE +0 -21
  62. package/lib/JAELIS-VM/node_modules/cbor-x/README.md +0 -380
  63. package/lib/JAELIS-VM/node_modules/cbor-x/SECURITY.md +0 -11
  64. package/lib/JAELIS-VM/node_modules/cbor-x/benchmark.md +0 -73
  65. package/lib/JAELIS-VM/node_modules/cbor-x/browser.js +0 -11
  66. package/lib/JAELIS-VM/node_modules/cbor-x/decode.d.ts +0 -2
  67. package/lib/JAELIS-VM/node_modules/cbor-x/decode.js +0 -1300
  68. package/lib/JAELIS-VM/node_modules/cbor-x/dist/decode-no-eval.cjs +0 -1244
  69. package/lib/JAELIS-VM/node_modules/cbor-x/dist/decode-no-eval.cjs.map +0 -1
  70. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.cjs +0 -2509
  71. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.cjs.map +0 -1
  72. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.min.js +0 -2
  73. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.min.js.map +0 -1
  74. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.js +0 -2508
  75. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.js.map +0 -1
  76. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.min.js +0 -2
  77. package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.min.js.map +0 -1
  78. package/lib/JAELIS-VM/node_modules/cbor-x/dist/node.cjs +0 -2629
  79. package/lib/JAELIS-VM/node_modules/cbor-x/dist/node.cjs.map +0 -1
  80. package/lib/JAELIS-VM/node_modules/cbor-x/dist/test.js +0 -3343
  81. package/lib/JAELIS-VM/node_modules/cbor-x/dist/test.js.map +0 -1
  82. package/lib/JAELIS-VM/node_modules/cbor-x/encode.d.ts +0 -1
  83. package/lib/JAELIS-VM/node_modules/cbor-x/encode.js +0 -1231
  84. package/lib/JAELIS-VM/node_modules/cbor-x/index.d.ts +0 -79
  85. package/lib/JAELIS-VM/node_modules/cbor-x/index.js +0 -3
  86. package/lib/JAELIS-VM/node_modules/cbor-x/iterators.js +0 -85
  87. package/lib/JAELIS-VM/node_modules/cbor-x/node-index.js +0 -24
  88. package/lib/JAELIS-VM/node_modules/cbor-x/package.json +0 -94
  89. package/lib/JAELIS-VM/node_modules/cbor-x/rollup.config.js +0 -88
  90. package/lib/JAELIS-VM/node_modules/cbor-x/stream.js +0 -61
  91. package/lib/JAELIS-VM/node_modules/cbor-x/webpack.config.js +0 -19
  92. package/lib/JAELIS-VM/node_modules/detect-libc/LICENSE +0 -201
  93. package/lib/JAELIS-VM/node_modules/detect-libc/README.md +0 -163
  94. package/lib/JAELIS-VM/node_modules/detect-libc/index.d.ts +0 -14
  95. package/lib/JAELIS-VM/node_modules/detect-libc/lib/detect-libc.js +0 -313
  96. package/lib/JAELIS-VM/node_modules/detect-libc/lib/elf.js +0 -39
  97. package/lib/JAELIS-VM/node_modules/detect-libc/lib/filesystem.js +0 -51
  98. package/lib/JAELIS-VM/node_modules/detect-libc/lib/process.js +0 -24
  99. package/lib/JAELIS-VM/node_modules/detect-libc/package.json +0 -44
  100. package/lib/JAELIS-VM/node_modules/msgpackr/LICENSE +0 -21
  101. package/lib/JAELIS-VM/node_modules/msgpackr/README.md +0 -372
  102. package/lib/JAELIS-VM/node_modules/msgpackr/SECURITY.md +0 -11
  103. package/lib/JAELIS-VM/node_modules/msgpackr/benchmark.md +0 -67
  104. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.cjs +0 -2407
  105. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.cjs.map +0 -1
  106. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.min.js +0 -2
  107. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.min.js.map +0 -1
  108. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.js +0 -2406
  109. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.js.map +0 -1
  110. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.min.js +0 -2
  111. package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.min.js.map +0 -1
  112. package/lib/JAELIS-VM/node_modules/msgpackr/dist/node.cjs +0 -3320
  113. package/lib/JAELIS-VM/node_modules/msgpackr/dist/node.cjs.map +0 -1
  114. package/lib/JAELIS-VM/node_modules/msgpackr/dist/test.js +0 -4540
  115. package/lib/JAELIS-VM/node_modules/msgpackr/dist/test.js.map +0 -1
  116. package/lib/JAELIS-VM/node_modules/msgpackr/dist/unpack-no-eval.cjs +0 -1250
  117. package/lib/JAELIS-VM/node_modules/msgpackr/dist/unpack-no-eval.cjs.map +0 -1
  118. package/lib/JAELIS-VM/node_modules/msgpackr/index.d.cts +0 -91
  119. package/lib/JAELIS-VM/node_modules/msgpackr/index.d.ts +0 -91
  120. package/lib/JAELIS-VM/node_modules/msgpackr/index.js +0 -5
  121. package/lib/JAELIS-VM/node_modules/msgpackr/iterators.js +0 -87
  122. package/lib/JAELIS-VM/node_modules/msgpackr/node-index.js +0 -25
  123. package/lib/JAELIS-VM/node_modules/msgpackr/pack.d.cts +0 -1
  124. package/lib/JAELIS-VM/node_modules/msgpackr/pack.d.ts +0 -1
  125. package/lib/JAELIS-VM/node_modules/msgpackr/pack.js +0 -1141
  126. package/lib/JAELIS-VM/node_modules/msgpackr/package.json +0 -104
  127. package/lib/JAELIS-VM/node_modules/msgpackr/rollup.config.js +0 -88
  128. package/lib/JAELIS-VM/node_modules/msgpackr/stream.js +0 -57
  129. package/lib/JAELIS-VM/node_modules/msgpackr/struct.js +0 -815
  130. package/lib/JAELIS-VM/node_modules/msgpackr/test-worker.js +0 -3
  131. package/lib/JAELIS-VM/node_modules/msgpackr/unpack.d.cts +0 -2
  132. package/lib/JAELIS-VM/node_modules/msgpackr/unpack.d.ts +0 -2
  133. package/lib/JAELIS-VM/node_modules/msgpackr/unpack.js +0 -1221
  134. package/lib/JAELIS-VM/node_modules/msgpackr-extract/LICENSE +0 -21
  135. package/lib/JAELIS-VM/node_modules/msgpackr-extract/README.md +0 -5
  136. package/lib/JAELIS-VM/node_modules/msgpackr-extract/bin/download-prebuilds.js +0 -13
  137. package/lib/JAELIS-VM/node_modules/msgpackr-extract/binding.gyp +0 -63
  138. package/lib/JAELIS-VM/node_modules/msgpackr-extract/index.js +0 -1
  139. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages +0 -12
  140. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional +0 -12
  141. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional.cmd +0 -17
  142. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional.ps1 +0 -28
  143. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test +0 -12
  144. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test.cmd +0 -17
  145. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test.ps1 +0 -28
  146. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages.cmd +0 -17
  147. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages.ps1 +0 -28
  148. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/LICENSE +0 -21
  149. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/README.md +0 -58
  150. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/bin.js +0 -82
  151. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/build-test.js +0 -19
  152. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/index.js +0 -6
  153. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/node-gyp-build.js +0 -236
  154. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/optional.js +0 -7
  155. package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/package.json +0 -32
  156. package/lib/JAELIS-VM/node_modules/msgpackr-extract/package.json +0 -50
  157. package/lib/JAELIS-VM/node_modules/msgpackr-extract/src/extract.cpp +0 -274
  158. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/LICENSE +0 -21
  159. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/README.md +0 -58
  160. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/bin.js +0 -77
  161. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/build-test.js +0 -19
  162. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/index.js +0 -224
  163. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/optional.js +0 -7
  164. package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/package.json +0 -32
  165. package/lib/JAELIS-VM/package-lock.json +0 -284
  166. package/lib/JAELIS-VM/package.json +0 -38
  167. package/lib/JAELIS-VM/test/comprehensive.test.js +0 -267
  168. package/lib/JAELIS-VM/test/cross-chain-test.js +0 -470
  169. package/lib/JAELIS-VM/test/unified-vm-test.js +0 -459
  170. package/lib/JAELIS-VM/test/unified.test.js +0 -166
  171. package/lib/JAELIS-VM/test/vm.test.js +0 -599
  172. package/lib/settlement-server.js +0 -999
  173. package/lib/vm/index.js +0 -397
package/lib/index.js CHANGED
@@ -1,2863 +1,54 @@
1
1
  /**
2
- * JAELIS Node Library
3
- * Core node implementation for the JAELIS blockchain
2
+ * JAELIS External Node
4
3
  *
5
- * This module provides the JaelisNode class which wraps the core blockchain
6
- * implementation for easy deployment as a standalone node.
4
+ * A lightweight node that connects to the JAELIS network, syncs blocks,
5
+ * and exposes local RPC for dApps. Like running geth light mode.
7
6
  *
8
- * ═══════════════════════════════════════════════════════════════════════════════
9
- * RPC ARCHITECTURE (v1.4.0+)
10
- * ═══════════════════════════════════════════════════════════════════════════════
7
+ * Architecture:
8
+ * - WebSocket connection to JAELIS main network
9
+ * - Local LevelDB for block/state storage
10
+ * - JSON-RPC server for local dApp access
11
+ * - NO internal JAELIS code - just network protocol
11
12
  *
12
- * LAYER 1 - NATIVE JAELIS (PRIMARY):
13
- * jaelis_* Native JAELIS methods - rich responses, zero-gas semantics
14
- *
15
- * LAYER 2 - COMPATIBILITY (PROXIES):
16
- * eth_* Ethereum JSON-RPC compat (MetaMask, ethers.js, web3.js)
17
- * solana_* Solana-compatible methods (Rust/SPL developers)
18
- * move_* Move-compatible methods (Aptos/Sui developers)
19
- * ton_* TON-compatible methods (FunC/Telegram developers)
20
- *
21
- * All compatibility methods PROXY to native jaelis_* implementations.
22
- * This follows Solana/Cosmos/Polkadot patterns for multi-ecosystem support.
23
- * ═══════════════════════════════════════════════════════════════════════════════
24
- *
25
- * @version 1.9.0
26
- * @author JAELIS Foundation
27
- */
28
-
29
- const path = require('path');
30
- const fs = require('fs');
31
- const crypto = require('crypto');
32
- const EventEmitter = require('events');
33
-
34
- // ═══════════════════════════════════════════════════════════════════════════
35
- // MULTI-CHAIN ADDRESS UTILITIES
36
- // Supports: EVM (0x...), Solana (base58), Bitcoin (P2PKH/bech32), etc.
37
- // JAELIS accepts ANY chain's address format - TRUE multi-chain!
38
- // ═══════════════════════════════════════════════════════════════════════════
39
-
40
- const ADDRESS_FORMATS = {
41
- // EVM chains (Ethereum, Polygon, Arbitrum, etc.)
42
- evm: {
43
- pattern: /^0x[a-fA-F0-9]{40}$/,
44
- name: 'EVM (Ethereum, Polygon, etc.)',
45
- example: '0x742d35Cc6634C0532925a3b844Bc9e7595f...'
46
- },
47
- // Solana (base58)
48
- solana: {
49
- pattern: /^[1-9A-HJ-NP-Za-km-z]{32,44}$/,
50
- name: 'Solana',
51
- example: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZ...'
52
- },
53
- // Bitcoin P2PKH (starts with 1 or 3)
54
- bitcoin_legacy: {
55
- pattern: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
56
- name: 'Bitcoin (Legacy)',
57
- example: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2'
58
- },
59
- // Bitcoin bech32 (starts with bc1)
60
- bitcoin_bech32: {
61
- pattern: /^bc1[a-z0-9]{39,59}$/,
62
- name: 'Bitcoin (SegWit)',
63
- example: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq'
64
- },
65
- // TON (base64url)
66
- ton: {
67
- pattern: /^[A-Za-z0-9_-]{48}$/,
68
- name: 'TON',
69
- example: 'EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N'
70
- },
71
- // Aptos/Sui (hex with 0x, 64 chars)
72
- move: {
73
- pattern: /^0x[a-fA-F0-9]{64}$/,
74
- name: 'Move (Aptos/Sui)',
75
- example: '0x1234567890abcdef...'
76
- },
77
- // JAELIS native (same as EVM for now, but could be extended)
78
- jaelis: {
79
- pattern: /^0x[a-fA-F0-9]{40}$/,
80
- name: 'JAELIS Native',
81
- example: '0x742d35Cc6634C0532925a3b844Bc9e7595f...'
82
- }
83
- };
84
-
85
- /**
86
- * Detect the chain type from an address
87
- */
88
- function detectAddressType(address) {
89
- if (!address || typeof address !== 'string') {
90
- return null;
91
- }
92
-
93
- // Check each format
94
- for (const [type, config] of Object.entries(ADDRESS_FORMATS)) {
95
- if (config.pattern.test(address)) {
96
- return { type, ...config };
97
- }
98
- }
99
-
100
- return null;
101
- }
102
-
103
- /**
104
- * Validate any chain address
13
+ * @version 2.0.0
14
+ * @license MIT
105
15
  */
106
- function validateAddress(address) {
107
- const detected = detectAddressType(address);
108
- if (!detected) {
109
- return {
110
- valid: false,
111
- error: 'Unknown address format',
112
- supportedFormats: Object.keys(ADDRESS_FORMATS)
113
- };
114
- }
115
- return {
116
- valid: true,
117
- type: detected.type,
118
- name: detected.name
119
- };
120
- }
121
-
122
- /**
123
- * Convert any address to canonical form for internal storage
124
- * EVM addresses are lowercased, others kept as-is
125
- */
126
- function toCanonicalAddress(address) {
127
- const detected = detectAddressType(address);
128
- if (!detected) {
129
- throw new Error('Invalid address format');
130
- }
131
-
132
- // EVM addresses are case-insensitive, normalize to lowercase
133
- if (detected.type === 'evm' || detected.type === 'jaelis') {
134
- return address.toLowerCase();
135
- }
136
-
137
- // Other formats are case-sensitive
138
- return address;
139
- }
140
16
 
141
- // ═══════════════════════════════════════════════════════════════════════════
142
- // NODE WALLET CONFIGURATION (Like Geth --suggested-fee-recipient)
143
- // Stores the node operator's wallet for receiving rewards
144
- // NO STAKING REQUIRED - JAELIS doesn't hold your funds!
145
- // ═══════════════════════════════════════════════════════════════════════════
146
-
147
- class NodeWalletConfig {
148
- constructor(dataDir) {
149
- this.dataDir = dataDir;
150
- this.configPath = path.join(dataDir, 'node-wallet.json');
151
- this.config = null;
152
- }
153
-
154
- /**
155
- * Load wallet configuration from disk
156
- */
157
- load() {
158
- try {
159
- if (fs.existsSync(this.configPath)) {
160
- const data = fs.readFileSync(this.configPath, 'utf8');
161
- this.config = JSON.parse(data);
162
- return this.config;
163
- }
164
- } catch (e) {
165
- console.warn('[WALLET] Failed to load wallet config:', e.message);
166
- }
167
- return null;
168
- }
169
-
170
- /**
171
- * Save wallet configuration to disk
172
- */
173
- save() {
174
- try {
175
- // Ensure directory exists
176
- if (!fs.existsSync(this.dataDir)) {
177
- fs.mkdirSync(this.dataDir, { recursive: true });
178
- }
179
- fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
180
- return true;
181
- } catch (e) {
182
- console.error('[WALLET] Failed to save wallet config:', e.message);
183
- return false;
184
- }
185
- }
186
-
187
- /**
188
- * Set the reward recipient wallet (ANY chain address!)
189
- * Like Geth's --suggested-fee-recipient
190
- */
191
- setRewardRecipient(address) {
192
- const validation = validateAddress(address);
193
- if (!validation.valid) {
194
- throw new Error(`Invalid address: ${validation.error}`);
195
- }
196
-
197
- if (!this.config) {
198
- this.config = { createdAt: Date.now() };
199
- }
200
-
201
- this.config.rewardRecipient = {
202
- address: toCanonicalAddress(address),
203
- originalFormat: address,
204
- type: validation.type,
205
- typeName: validation.name,
206
- setAt: Date.now()
207
- };
208
-
209
- this.save();
210
- console.log(`[WALLET] Reward recipient set: ${address} (${validation.name})`);
211
- return this.config.rewardRecipient;
212
- }
213
-
214
- /**
215
- * Get the reward recipient address
216
- */
217
- getRewardRecipient() {
218
- if (!this.config) this.load();
219
- return this.config?.rewardRecipient || null;
220
- }
221
-
222
- /**
223
- * Add an additional wallet (for multi-wallet nodes)
224
- */
225
- addWallet(name, address, options = {}) {
226
- const validation = validateAddress(address);
227
- if (!validation.valid) {
228
- throw new Error(`Invalid address: ${validation.error}`);
229
- }
230
-
231
- if (!this.config) {
232
- this.config = { createdAt: Date.now() };
233
- }
234
-
235
- if (!this.config.wallets) {
236
- this.config.wallets = [];
237
- }
238
-
239
- // Check for duplicates
240
- const existing = this.config.wallets.find(w =>
241
- toCanonicalAddress(w.address) === toCanonicalAddress(address)
242
- );
243
- if (existing) {
244
- throw new Error(`Wallet already exists: ${existing.name}`);
245
- }
246
-
247
- const wallet = {
248
- name,
249
- address: toCanonicalAddress(address),
250
- originalFormat: address,
251
- type: validation.type,
252
- typeName: validation.name,
253
- purpose: options.purpose || 'general',
254
- addedAt: Date.now()
255
- };
256
-
257
- this.config.wallets.push(wallet);
258
- this.save();
259
- console.log(`[WALLET] Added wallet: ${name} (${validation.name})`);
260
- return wallet;
261
- }
262
-
263
- /**
264
- * Remove a wallet by name or address
265
- */
266
- removeWallet(nameOrAddress) {
267
- if (!this.config?.wallets) {
268
- throw new Error('No wallets configured');
269
- }
270
-
271
- const canonical = nameOrAddress.startsWith('0x') || nameOrAddress.startsWith('bc1')
272
- ? toCanonicalAddress(nameOrAddress)
273
- : null;
274
-
275
- const index = this.config.wallets.findIndex(w =>
276
- w.name === nameOrAddress ||
277
- (canonical && toCanonicalAddress(w.address) === canonical)
278
- );
279
-
280
- if (index === -1) {
281
- throw new Error(`Wallet not found: ${nameOrAddress}`);
282
- }
283
-
284
- const removed = this.config.wallets.splice(index, 1)[0];
285
- this.save();
286
- console.log(`[WALLET] Removed wallet: ${removed.name}`);
287
- return removed;
288
- }
289
-
290
- /**
291
- * List all configured wallets
292
- */
293
- listWallets() {
294
- if (!this.config) this.load();
295
- return {
296
- rewardRecipient: this.config?.rewardRecipient || null,
297
- wallets: this.config?.wallets || []
298
- };
299
- }
300
-
301
- /**
302
- * Get node identity (generates one if not exists)
303
- * Like Solana's identity keypair
304
- */
305
- getNodeIdentity() {
306
- if (!this.config) this.load();
307
- if (!this.config) {
308
- this.config = { createdAt: Date.now() };
309
- }
310
-
311
- if (!this.config.nodeIdentity) {
312
- // Generate a node identity (not a wallet, just an ID)
313
- const nodeId = crypto.randomBytes(32).toString('hex');
314
- this.config.nodeIdentity = {
315
- id: nodeId,
316
- publicKey: '0x' + crypto.createHash('sha256').update(nodeId).digest('hex').slice(0, 40),
317
- createdAt: Date.now()
318
- };
319
- this.save();
320
- console.log(`[WALLET] Generated node identity: ${this.config.nodeIdentity.publicKey}`);
321
- }
322
-
323
- return this.config.nodeIdentity;
324
- }
17
+ 'use strict';
325
18
 
326
- /**
327
- * Get full configuration
328
- */
329
- getConfig() {
330
- if (!this.config) this.load();
331
- return this.config;
332
- }
333
- }
19
+ const { JaelisNode } = require('./node');
20
+ const { SyncManager } = require('./sync');
21
+ const { RPCServer } = require('./rpc');
22
+ const { Storage } = require('./storage');
334
23
 
335
- // JAELIS Network Endpoints - users connect here automatically
336
- // Bootstrap nodes use libp2p multiaddr format: /dns4/host/tcp/port/p2p/PEER_ID
337
- // External nodes connect via DNS which resolves to the main node's public IP
338
- //
339
- // IMPORTANT: Peer ID is required for libp2p dialing!
340
- // The peer ID is generated on first run and persisted to disk.
341
- // Main node logs bootstrap info on startup - update this when it changes.
342
- const JAELIS_NETWORKS = {
24
+ // Network configurations (public endpoints only)
25
+ const NETWORKS = Object.freeze({
343
26
  testnet: {
344
27
  name: 'JAELIS Testnet',
345
28
  chainId: 4545,
346
29
  symbol: 'tJAELIS',
347
- rpcUrl: 'https://rpc.jaelis.io',
348
- wsUrl: 'wss://rpc.jaelis.io/ws',
349
- explorerUrl: 'https://explorer.jaelis.io',
350
- // Testnet uses port 30304 (mainnet uses 30303)
351
- // Peer ID will be added after main node first run
352
- // For now, P2P will fallback to RPC sync until peer ID is configured
353
- bootstrapNodes: [
354
- // Format: /dns4/rpc.jaelis.io/tcp/30304/p2p/PEER_ID
355
- // The main node outputs this on startup - update here when available
356
- ]
30
+ rpcEndpoint: 'https://rpc.jaelis.io',
31
+ wsEndpoint: 'wss://rpc.jaelis.io/ws',
32
+ explorerUrl: 'https://explorer.jaelis.io'
357
33
  },
358
34
  mainnet: {
359
35
  name: 'JAELIS Mainnet',
360
36
  chainId: 4547,
361
37
  symbol: 'JAELIS',
362
- rpcUrl: 'https://mainnet.jaelis.io',
363
- wsUrl: 'wss://mainnet.jaelis.io/ws',
364
- explorerUrl: 'https://explorer.jaelis.io',
365
- bootstrapNodes: [],
366
- status: 'coming-soon'
367
- }
368
- };
369
-
370
- /**
371
- * JaelisNode - Main node class for running a JAELIS blockchain node
372
- */
373
- class JaelisNode extends EventEmitter {
374
- constructor(options = {}) {
375
- super();
376
-
377
- // Get network config - defaults to testnet
378
- const networkName = options.network || 'testnet';
379
- const networkConfig = JAELIS_NETWORKS[networkName] || JAELIS_NETWORKS.testnet;
380
-
381
- this.options = {
382
- network: networkName,
383
- networkConfig: networkConfig,
384
- chainId: networkConfig.chainId,
385
- rpcPort: options.rpcPort || 8545,
386
- rpcHost: options.rpcHost || '0.0.0.0',
387
- p2pPort: options.p2pPort || 30303,
388
- dataDir: options.dataDir || './jaelis-data',
389
- syncMode: options.syncMode || 'full',
390
- enableRpc: options.enableRpc !== false,
391
- enableP2p: options.enableP2p !== false,
392
- bootstrapNodes: options.bootstrapNodes || networkConfig.bootstrapNodes,
393
- maxPeers: options.maxPeers || 50,
394
- // Remote RPC to sync from
395
- remoteRpc: options.remoteRpc || networkConfig.rpcUrl,
396
- ...options
397
- };
398
-
399
- this.blockchain = null;
400
- this.network = null;
401
- this.rpcServer = null;
402
- this.isRunning = false;
403
- this.startTime = null;
404
- this.peerCount = 0;
405
-
406
- // Initialize wallet configuration (like Geth's keystore)
407
- this.walletConfig = new NodeWalletConfig(this.options.dataDir);
408
- this.walletConfig.load();
409
-
410
- // Set reward recipient if provided via options (like --suggested-fee-recipient)
411
- if (options.rewardRecipient) {
412
- this.walletConfig.setRewardRecipient(options.rewardRecipient);
413
- }
414
- }
415
-
416
- /**
417
- * Get the wallet configuration manager
418
- */
419
- getWalletConfig() {
420
- return this.walletConfig;
421
- }
422
-
423
- /**
424
- * Set the reward recipient address (ANY chain format!)
425
- * Like Geth's --suggested-fee-recipient
426
- */
427
- setRewardRecipient(address) {
428
- return this.walletConfig.setRewardRecipient(address);
429
- }
430
-
431
- /**
432
- * Get the reward recipient address
433
- */
434
- getRewardRecipient() {
435
- return this.walletConfig.getRewardRecipient();
436
- }
437
-
438
- /**
439
- * Add a wallet to this node
440
- */
441
- addWallet(name, address, options = {}) {
442
- return this.walletConfig.addWallet(name, address, options);
443
- }
444
-
445
- /**
446
- * Remove a wallet from this node
447
- */
448
- removeWallet(nameOrAddress) {
449
- return this.walletConfig.removeWallet(nameOrAddress);
450
- }
451
-
452
- /**
453
- * List all configured wallets
454
- */
455
- listWallets() {
456
- return this.walletConfig.listWallets();
457
- }
458
-
459
- /**
460
- * Get node identity
461
- */
462
- getNodeIdentity() {
463
- return this.walletConfig.getNodeIdentity();
464
- }
465
-
466
- /**
467
- * Start the node
468
- */
469
- async start() {
470
- if (this.isRunning) {
471
- throw new Error('Node is already running');
472
- }
473
-
474
- console.log(`[JAELIS] Starting node on ${this.options.network}...`);
475
-
476
- // Ensure data directory exists
477
- if (!fs.existsSync(this.options.dataDir)) {
478
- fs.mkdirSync(this.options.dataDir, { recursive: true });
479
- }
480
-
481
- try {
482
- // Try to load from JAELIS core
483
- await this._loadCore();
484
-
485
- // Initialize blockchain
486
- await this._initBlockchain();
487
-
488
- // Start P2P networking
489
- if (this.options.enableP2p) {
490
- await this._initNetwork();
491
- }
492
-
493
- // Start RPC server
494
- if (this.options.enableRpc) {
495
- await this._initRpcServer();
496
- }
497
-
498
- // Start Settlement Server for cross-chain deployments
499
- await this._initSettlementServer();
500
-
501
- this.isRunning = true;
502
- this.startTime = Date.now();
503
-
504
- // Register with the JAELIS network for tracking
505
- await this._registerWithNetwork();
506
-
507
- // Start heartbeat to keep registration alive
508
- this._startHeartbeat();
509
-
510
- this.emit('started', {
511
- network: this.options.network,
512
- chainId: this.options.chainId,
513
- rpcPort: this.options.rpcPort,
514
- p2pPort: this.options.p2pPort
515
- });
516
-
517
- console.log(`[JAELIS] Node started successfully`);
518
-
519
- // Check for updates (non-blocking)
520
- this._checkForUpdates();
521
-
522
- } catch (error) {
523
- console.error(`[JAELIS] Failed to start node: ${error.message}`);
524
- throw error;
525
- }
526
- }
527
-
528
- /**
529
- * Stop the node
530
- */
531
- async stop() {
532
- if (!this.isRunning) {
533
- return;
534
- }
535
-
536
- console.log('[JAELIS] Stopping node...');
537
-
538
- // Stop heartbeat first
539
- this._stopHeartbeat();
540
-
541
- // Stop Settlement server
542
- if (this.settlementServer) {
543
- await this._stopSettlementServer();
544
- }
545
-
546
- // Stop RPC server
547
- if (this.rpcServer) {
548
- await this._stopRpcServer();
549
- }
550
-
551
- // Stop P2P network
552
- if (this.network) {
553
- await this._stopNetwork();
554
- }
555
-
556
- // Save blockchain state
557
- if (this.blockchain) {
558
- await this._saveState();
559
- }
560
-
561
- this.isRunning = false;
562
- this.emit('stopped');
563
-
564
- console.log('[JAELIS] Node stopped');
565
- }
566
-
567
- /**
568
- * Initialize node modules - connects to JAELIS network
569
- */
570
- async _loadCore() {
571
- console.log(`[JAELIS] Connecting to ${this.options.networkConfig.name}...`);
572
- console.log(`[JAELIS] RPC: ${this.options.remoteRpc}`);
573
-
574
- // Use embedded light client that syncs from remote RPC
575
- this.JaelisBlockchain = EmbeddedBlockchain;
576
- this.JaelisNetwork = EmbeddedNetwork;
577
- this.JaelisRpcServer = EmbeddedRpcServer;
578
- this.JaelisStorage = EmbeddedStorage;
579
- }
580
-
581
- /**
582
- * Initialize blockchain
583
- */
584
- async _initBlockchain() {
585
- console.log('[JAELIS] Initializing blockchain...');
586
-
587
- const storagePath = path.join(this.options.dataDir, 'blockchain');
588
-
589
- if (this.JaelisBlockchain) {
590
- this.blockchain = new this.JaelisBlockchain({
591
- chainId: this.options.chainId,
592
- dataDir: storagePath,
593
- network: this.options.network
594
- });
595
-
596
- if (this.blockchain.initialize) {
597
- await this.blockchain.initialize();
598
- }
599
- }
600
-
601
- console.log(`[JAELIS] Blockchain initialized (Chain ID: ${this.options.chainId})`);
602
- }
603
-
604
- /**
605
- * Initialize P2P network
606
- */
607
- async _initNetwork() {
608
- console.log('[JAELIS] Initializing P2P network...');
609
-
610
- if (this.JaelisNetwork) {
611
- this.network = new this.JaelisNetwork({
612
- port: this.options.p2pPort,
613
- maxPeers: this.options.maxPeers,
614
- bootstrapNodes: this.options.bootstrapNodes,
615
- blockchain: this.blockchain,
616
- remoteRpc: this.options.remoteRpc
617
- });
618
-
619
- if (this.network.start) {
620
- await this.network.start();
621
- }
622
-
623
- // Track peer count
624
- if (this.network.on) {
625
- this.network.on('peer:connect', () => {
626
- this.peerCount++;
627
- this.emit('peer:connect', this.peerCount);
628
- });
629
-
630
- this.network.on('peer:disconnect', () => {
631
- this.peerCount = Math.max(0, this.peerCount - 1);
632
- this.emit('peer:disconnect', this.peerCount);
633
- });
634
- }
635
- }
636
-
637
- console.log(`[JAELIS] P2P network started on port ${this.options.p2pPort}`);
638
- }
639
-
640
- /**
641
- * Initialize RPC server
642
- */
643
- async _initRpcServer() {
644
- console.log('[JAELIS] Initializing RPC server...');
645
-
646
- if (this.JaelisRpcServer) {
647
- // Pass network reference for sync status reporting
648
- this.rpcServer = new this.JaelisRpcServer(this.blockchain, this.options.rpcPort, this.network);
649
-
650
- if (this.rpcServer.start) {
651
- await this.rpcServer.start();
652
- } else if (this.rpcServer.listen) {
653
- await this.rpcServer.listen(this.options.rpcPort, this.options.rpcHost);
654
- }
655
- }
656
-
657
- console.log(`[JAELIS] RPC server started on http://${this.options.rpcHost}:${this.options.rpcPort}`);
658
- }
659
-
660
- /**
661
- * Initialize Settlement Server for cross-chain deployments
662
- *
663
- * The Settlement Server handles propagating JAELIS contract deployments
664
- * to external chains. NO GAS - NO FEES - NO SANDBOX - NO WALLS!
665
- */
666
- async _initSettlementServer() {
667
- console.log('[JAELIS] Initializing Settlement Server...');
668
-
669
- try {
670
- // Load the settlement server module
671
- const settlementPath = path.join(__dirname, 'settlement-server.js');
672
-
673
- if (fs.existsSync(settlementPath)) {
674
- const { SettlementServer } = require(settlementPath);
675
-
676
- this.settlementServer = new SettlementServer({
677
- port: this.options.settlementPort || 3847,
678
- host: this.options.rpcHost || '0.0.0.0'
679
- });
680
-
681
- await this.settlementServer.start();
682
-
683
- console.log(`[JAELIS] Settlement Server started on port ${this.options.settlementPort || 3847}`);
684
- console.log('[JAELIS] Cross-chain deployments: ENABLED');
685
- console.log('[JAELIS] NO GAS - NO FEES - NO SANDBOX - NO WALLS!');
686
- } else {
687
- console.log('[JAELIS] Settlement Server not found, cross-chain disabled');
688
- }
689
- } catch (error) {
690
- // Don't fail node startup if settlement server fails
691
- console.error('[JAELIS] Settlement Server error:', error.message);
692
- console.log('[JAELIS] Continuing without cross-chain support...');
693
- }
694
- }
695
-
696
- /**
697
- * Stop Settlement Server
698
- */
699
- async _stopSettlementServer() {
700
- if (this.settlementServer && this.settlementServer.stop) {
701
- await this.settlementServer.stop();
702
- console.log('[JAELIS] Settlement Server stopped');
703
- }
704
- }
705
-
706
- /**
707
- * Stop RPC server
708
- */
709
- async _stopRpcServer() {
710
- if (this.rpcServer && this.rpcServer.stop) {
711
- await this.rpcServer.stop();
712
- }
713
- }
714
-
715
- /**
716
- * Stop P2P network
717
- */
718
- async _stopNetwork() {
719
- if (this.network && this.network.stop) {
720
- await this.network.stop();
721
- }
722
- }
723
-
724
- /**
725
- * Save blockchain state
726
- */
727
- async _saveState() {
728
- if (this.blockchain && this.blockchain.save) {
729
- await this.blockchain.save();
730
- }
731
- }
732
-
733
- /**
734
- * Register this node with the JAELIS network for tracking
735
- * Mario can see all nodes that connect! Lifetime tracking!
736
- */
737
- async _registerWithNetwork() {
738
- try {
739
- const https = require('https');
740
- const http = require('http');
741
-
742
- const identity = this.walletConfig.getNodeIdentity();
743
- const rewardRecipient = this.walletConfig.getRewardRecipient();
744
-
745
- const packageJson = require('../package.json');
746
- const nodeInfo = {
747
- nodeId: identity.id,
748
- publicKey: identity.publicKey,
749
- network: this.options.network,
750
- chainId: this.options.chainId,
751
- version: packageJson.version,
752
- rewardAddress: rewardRecipient?.address || null,
753
- rewardChainType: rewardRecipient?.type || null,
754
- capabilities: ['full-node', 'rpc'],
755
- p2pPort: this.options.p2pPort,
756
- rpcPort: this.options.rpcPort
757
- };
758
-
759
- // Store for heartbeat
760
- this._nodeInfo = nodeInfo;
761
-
762
- const data = JSON.stringify({
763
- jsonrpc: '2.0',
764
- method: 'jaelis_node_register',
765
- params: [nodeInfo],
766
- id: 1
767
- });
768
-
769
- const url = new URL(this.options.remoteRpc);
770
- const protocol = url.protocol === 'https:' ? https : http;
771
-
772
- const options = {
773
- hostname: url.hostname,
774
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
775
- path: url.pathname || '/',
776
- method: 'POST',
777
- headers: {
778
- 'Content-Type': 'application/json',
779
- 'Content-Length': Buffer.byteLength(data),
780
- 'User-Agent': `JAELIS-Node/${packageJson.version}` // Identifies us as a node - gets priority treatment
781
- }
782
- };
783
-
784
- await new Promise((resolve) => {
785
- const req = protocol.request(options, (res) => {
786
- let body = '';
787
- res.on('data', chunk => body += chunk);
788
- res.on('end', () => {
789
- try {
790
- const result = JSON.parse(body);
791
- if (result.result?.registered) {
792
- console.log(`[JAELIS] Node registered with network (ID: ${identity.publicKey.slice(0, 12)}...)`);
793
- }
794
- } catch (e) {
795
- // Silent - registration is best-effort
796
- }
797
- resolve();
798
- });
799
- });
800
-
801
- req.on('error', () => resolve()); // Silent fail - will retry on heartbeat
802
- req.write(data);
803
- req.end();
804
- });
805
-
806
- } catch (error) {
807
- // Registration is best-effort, don't fail node startup
808
- console.log('[JAELIS] Network registration pending (will retry)');
809
- }
810
- }
811
-
812
- /**
813
- * Start heartbeat to keep node registration alive
814
- * Sends heartbeat every 60 seconds
815
- */
816
- _startHeartbeat() {
817
- const HEARTBEAT_INTERVAL = 60000; // 60 seconds
818
- const packageJson = require('../package.json');
819
-
820
- this._heartbeatInterval = setInterval(async () => {
821
- if (!this.isRunning || !this._nodeInfo) return;
822
-
823
- try {
824
- const https = require('https');
825
- const http = require('http');
826
-
827
- const data = JSON.stringify({
828
- jsonrpc: '2.0',
829
- method: 'jaelis_node_heartbeat',
830
- params: [this._nodeInfo.nodeId],
831
- id: 1
832
- });
833
-
834
- const url = new URL(this.options.remoteRpc);
835
- const protocol = url.protocol === 'https:' ? https : http;
836
-
837
- const options = {
838
- hostname: url.hostname,
839
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
840
- path: url.pathname || '/',
841
- method: 'POST',
842
- headers: {
843
- 'Content-Type': 'application/json',
844
- 'Content-Length': Buffer.byteLength(data),
845
- 'User-Agent': `JAELIS-Node/${packageJson.version}` // Identifies us as a node - gets priority treatment
846
- }
847
- };
848
-
849
- const req = protocol.request(options, () => {});
850
- req.on('error', () => {}); // Silent
851
- req.write(data);
852
- req.end();
853
-
854
- } catch (e) {
855
- // Silent - heartbeat is best-effort
856
- }
857
- }, HEARTBEAT_INTERVAL);
858
-
859
- console.log('[JAELIS] Heartbeat started (60s interval)');
860
- }
861
-
862
- /**
863
- * Check for updates from npm registry
864
- * Non-blocking - just notifies if a new version is available
865
- */
866
- async _checkForUpdates() {
867
- try {
868
- const https = require('https');
869
- const packageJson = require('../package.json');
870
- const currentVersion = packageJson.version;
871
-
872
- const options = {
873
- hostname: 'registry.npmjs.org',
874
- path: '/jaelis-node/latest',
875
- method: 'GET',
876
- headers: {
877
- 'Accept': 'application/json',
878
- 'User-Agent': `JAELIS-Node/${currentVersion}`
879
- },
880
- timeout: 5000
881
- };
882
-
883
- const req = https.request(options, (res) => {
884
- let body = '';
885
- res.on('data', chunk => body += chunk);
886
- res.on('end', () => {
887
- try {
888
- const data = JSON.parse(body);
889
- const latestVersion = data.version;
890
-
891
- if (latestVersion && this._isNewerVersion(currentVersion, latestVersion)) {
892
- console.log('');
893
- console.log('╔════════════════════════════════════════════════════════════════╗');
894
- console.log('║ 📦 UPDATE AVAILABLE ║');
895
- console.log('╠════════════════════════════════════════════════════════════════╣');
896
- console.log(`║ Current: v${currentVersion.padEnd(10)} → Latest: v${latestVersion.padEnd(20)}║`);
897
- console.log('║ ║');
898
- console.log('║ Run: npm update -g jaelis-node ║');
899
- console.log('║ Or: npx jaelis-node@latest start ║');
900
- console.log('╚════════════════════════════════════════════════════════════════╝');
901
- console.log('');
902
- }
903
- } catch (e) {
904
- // Silent - update check is best-effort
905
- }
906
- });
907
- });
908
-
909
- req.on('error', () => {}); // Silent fail
910
- req.on('timeout', () => req.destroy());
911
- req.end();
912
-
913
- } catch (error) {
914
- // Update check is non-critical, fail silently
915
- }
916
- }
917
-
918
- /**
919
- * Compare semver versions
920
- * @returns true if latest > current
921
- */
922
- _isNewerVersion(current, latest) {
923
- const currentParts = current.split('.').map(Number);
924
- const latestParts = latest.split('.').map(Number);
925
-
926
- for (let i = 0; i < 3; i++) {
927
- const c = currentParts[i] || 0;
928
- const l = latestParts[i] || 0;
929
- if (l > c) return true;
930
- if (l < c) return false;
931
- }
932
- return false;
933
- }
934
-
935
- /**
936
- * Stop heartbeat
937
- */
938
- _stopHeartbeat() {
939
- if (this._heartbeatInterval) {
940
- clearInterval(this._heartbeatInterval);
941
- this._heartbeatInterval = null;
942
- }
943
- }
944
-
945
- /**
946
- * Get node status
947
- */
948
- getStatus() {
949
- const packageJson = require('../package.json');
950
- const uptimeMs = this.startTime ? Date.now() - this.startTime : 0;
951
-
952
- return {
953
- healthy: this.isRunning,
954
- running: this.isRunning,
955
- version: packageJson.version,
956
- network: this.options.network,
957
- chainId: this.options.chainId,
958
- uptime: uptimeMs,
959
- uptimeHuman: this._formatUptime(uptimeMs),
960
- peers: this.peerCount,
961
- rpcPort: this.options.rpcPort,
962
- p2pPort: this.options.p2pPort,
963
- syncMode: this.options.syncMode,
964
- blockHeight: this.blockchain?.getHeight?.() || 0,
965
- nodeId: this._nodeInfo?.nodeId?.slice(0, 16) + '...' || null
966
- };
967
- }
968
-
969
- /**
970
- * Format uptime in human readable format
971
- */
972
- _formatUptime(ms) {
973
- const seconds = Math.floor(ms / 1000);
974
- const minutes = Math.floor(seconds / 60);
975
- const hours = Math.floor(minutes / 60);
976
- const days = Math.floor(hours / 24);
977
-
978
- if (days > 0) return `${days}d ${hours % 24}h ${minutes % 60}m`;
979
- if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
980
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
981
- return `${seconds}s`;
982
- }
983
- }
984
-
985
- /**
986
- * BootstrapNode - Lightweight node for peer discovery
987
- */
988
- class BootstrapNode extends EventEmitter {
989
- constructor(options = {}) {
990
- super();
991
- this.port = options.port || 30305;
992
- this.network = options.network || 'testnet';
993
- this.peers = new Map();
994
- this.server = null;
995
- }
996
-
997
- async start() {
998
- console.log(`[BOOTSTRAP] Starting on port ${this.port}...`);
999
-
1000
- // Simple TCP server for peer discovery
1001
- const net = require('net');
1002
-
1003
- this.server = net.createServer((socket) => {
1004
- const peerId = `${socket.remoteAddress}:${socket.remotePort}`;
1005
- this.peers.set(peerId, { socket, connectedAt: Date.now() });
1006
-
1007
- console.log(`[BOOTSTRAP] Peer connected: ${peerId} (${this.peers.size} total)`);
1008
-
1009
- socket.on('data', (data) => {
1010
- // Handle peer discovery requests
1011
- try {
1012
- const msg = JSON.parse(data.toString());
1013
- if (msg.type === 'getPeers') {
1014
- const peerList = Array.from(this.peers.keys());
1015
- socket.write(JSON.stringify({ type: 'peers', peers: peerList }));
1016
- }
1017
- } catch (e) {
1018
- // Ignore invalid messages
1019
- }
1020
- });
1021
-
1022
- socket.on('close', () => {
1023
- this.peers.delete(peerId);
1024
- console.log(`[BOOTSTRAP] Peer disconnected: ${peerId} (${this.peers.size} remaining)`);
1025
- });
1026
-
1027
- socket.on('error', () => {
1028
- this.peers.delete(peerId);
1029
- });
1030
- });
1031
-
1032
- return new Promise((resolve, reject) => {
1033
- this.server.listen(this.port, '0.0.0.0', () => {
1034
- console.log(`[BOOTSTRAP] Listening on port ${this.port}`);
1035
- resolve();
1036
- });
1037
-
1038
- this.server.on('error', reject);
1039
- });
1040
- }
1041
-
1042
- async stop() {
1043
- if (this.server) {
1044
- this.server.close();
1045
- }
1046
- }
1047
-
1048
- getPeerCount() {
1049
- return this.peers.size;
1050
- }
1051
- }
1052
-
1053
- // ============================================================
1054
- // EMBEDDED IMPLEMENTATIONS WITH UNIFIED VM
1055
- // ============================================================
1056
-
1057
- // Import VM Executor (UnifiedJaelisVM integration)
1058
- let VMExecutor;
1059
- try {
1060
- VMExecutor = require('./vm').VMExecutor;
1061
- } catch (e) {
1062
- console.log('[JAELIS] VM module not loaded - running in light mode');
1063
- }
1064
-
1065
- class EmbeddedBlockchain {
1066
- constructor(options = {}) {
1067
- this.chainId = options.chainId || 4545;
1068
- this.chain = [];
1069
- this.state = new Map();
1070
- this.pendingTransactions = [];
1071
-
1072
- // Initialize VM Executor (UnifiedJaelisVM - NO BRIDGES!)
1073
- this.vmExecutor = null;
1074
- }
1075
-
1076
- async initialize() {
1077
- // Create genesis block if needed
1078
- if (this.chain.length === 0) {
1079
- this.chain.push({
1080
- number: 0,
1081
- hash: '0x' + '0'.repeat(64),
1082
- parentHash: '0x' + '0'.repeat(64),
1083
- timestamp: Date.now(),
1084
- transactions: [],
1085
- stateRoot: '0x' + '0'.repeat(64)
1086
- });
1087
- }
1088
-
1089
- // Initialize the Unified VM
1090
- if (VMExecutor) {
1091
- this.vmExecutor = new VMExecutor(this, {
1092
- debug: false
1093
- });
1094
- console.log('[BLOCKCHAIN] Unified VM initialized - TRUE cross-chain interop!');
1095
- }
1096
- }
1097
-
1098
- getHeight() {
1099
- return this.chain.length - 1;
1100
- }
1101
-
1102
- getLatestBlock() {
1103
- return this.chain[this.chain.length - 1];
1104
- }
1105
-
1106
- getBlock(numberOrHash) {
1107
- if (typeof numberOrHash === 'number') {
1108
- return this.chain[numberOrHash];
1109
- }
1110
- return this.chain.find(b => b.hash === numberOrHash);
1111
- }
1112
-
1113
- /**
1114
- * Deploy a smart contract (ANY language!)
1115
- */
1116
- async deployContract(source, language, options = {}) {
1117
- if (!this.vmExecutor) {
1118
- throw new Error('VM not available - initialize blockchain first');
1119
- }
1120
- return this.vmExecutor.deployContract(source, language, options);
38
+ rpcEndpoint: 'https://mainnet.jaelis.io',
39
+ wsEndpoint: 'wss://mainnet.jaelis.io/ws',
40
+ explorerUrl: 'https://explorer.jaelis.io'
1121
41
  }
42
+ });
1122
43
 
1123
- /**
1124
- * Execute a contract call
1125
- */
1126
- async executeContract(to, functionName, args = [], options = {}) {
1127
- if (!this.vmExecutor) {
1128
- throw new Error('VM not available');
1129
- }
1130
- return this.vmExecutor.executeCall(to, functionName, args, options);
1131
- }
1132
-
1133
- /**
1134
- * Cross-contract call (NO BRIDGES!)
1135
- */
1136
- async crossContractCall(from, to, functionName, args = []) {
1137
- if (!this.vmExecutor) {
1138
- throw new Error('VM not available');
1139
- }
1140
- return this.vmExecutor.crossContractCall(from, to, functionName, args);
1141
- }
1142
-
1143
- /**
1144
- * Process a transaction
1145
- */
1146
- async processTransaction(tx) {
1147
- if (this.vmExecutor) {
1148
- return this.vmExecutor.processTransaction(tx);
1149
- }
1150
- // Fallback: just store in pending
1151
- this.pendingTransactions.push(tx);
1152
- return { status: 'pending' };
1153
- }
1154
-
1155
- /**
1156
- * Create a new block with pending transactions
1157
- */
1158
- async createBlock(validator) {
1159
- const crypto = require('crypto');
1160
- const parentBlock = this.getLatestBlock();
1161
-
1162
- const block = {
1163
- number: parentBlock.number + 1,
1164
- hash: null,
1165
- parentHash: parentBlock.hash,
1166
- timestamp: Date.now(),
1167
- validator: validator || '0x0',
1168
- transactions: [...this.pendingTransactions],
1169
- stateRoot: this._computeStateRoot(),
1170
- gasUsed: 0 // ZERO FEES!
1171
- };
1172
-
1173
- // Compute block hash
1174
- block.hash = '0x' + crypto.createHash('sha256')
1175
- .update(JSON.stringify({
1176
- number: block.number,
1177
- parentHash: block.parentHash,
1178
- timestamp: block.timestamp,
1179
- stateRoot: block.stateRoot
1180
- }))
1181
- .digest('hex');
1182
-
1183
- // Add to chain
1184
- this.chain.push(block);
1185
- this.pendingTransactions = [];
1186
-
1187
- console.log(`[BLOCKCHAIN] Block #${block.number} created with ${block.transactions.length} txs`);
1188
-
1189
- return block;
1190
- }
1191
-
1192
- _computeStateRoot() {
1193
- const crypto = require('crypto');
1194
- const stateData = JSON.stringify(Array.from(this.state.entries()));
1195
- return '0x' + crypto.createHash('sha256').update(stateData).digest('hex');
1196
- }
1197
-
1198
- /**
1199
- * Get VM stats
1200
- */
1201
- getVMStats() {
1202
- return this.vmExecutor?.getStats?.() || { vmAvailable: false };
1203
- }
1204
-
1205
- /**
1206
- * Get all deployed contracts
1207
- */
1208
- getContracts() {
1209
- return this.vmExecutor?.getAllContracts?.() || [];
1210
- }
1211
-
1212
- /**
1213
- * Add a block received from sync (RPC fallback or P2P)
1214
- * Validates the block before adding to chain
1215
- */
1216
- async addBlock(block) {
1217
- if (!block || block.number === undefined) {
1218
- throw new Error('Invalid block');
1219
- }
1220
-
1221
- // Normalize block number - could be hex string (0x1) or decimal
1222
- let blockNumber = block.number;
1223
- if (typeof blockNumber === 'string') {
1224
- if (blockNumber.startsWith('0x')) {
1225
- blockNumber = parseInt(blockNumber, 16);
1226
- } else {
1227
- blockNumber = parseInt(blockNumber, 10);
1228
- }
1229
- }
1230
-
1231
- const expectedNumber = this.chain.length;
1232
- if (blockNumber !== expectedNumber) {
1233
- // Skip if we already have this block or it's out of order
1234
- if (blockNumber < expectedNumber) {
1235
- return { status: 'skipped', reason: 'already_have' };
1236
- }
1237
- throw new Error(`Block out of order: expected ${expectedNumber}, got ${blockNumber}`);
1238
- }
1239
-
1240
- // Store normalized block number for consistency
1241
- block.number = blockNumber;
1242
-
1243
- // Validate parent hash (skip for first sync - genesis hash may differ)
1244
- // Like Ethereum "fast sync" - we trust the RPC source initially
1245
- const parentBlock = this.getLatestBlock();
1246
- if (blockNumber > 1 && block.parentHash && parentBlock.hash) {
1247
- // Only validate parent hash for blocks after genesis
1248
- // During initial sync, parent hashes are validated by the remote node
1249
- if (block.parentHash !== parentBlock.hash) {
1250
- // Check if this is initial sync (chain is mostly empty)
1251
- if (this.chain.length > 10) {
1252
- throw new Error('Invalid parent hash - possible chain reorg');
1253
- }
1254
- // During initial sync, log but continue
1255
- console.log(`[SYNC] Accepting block ${blockNumber} (trusting remote during initial sync)`);
1256
- }
1257
- }
1258
-
1259
- // Add block to chain
1260
- this.chain.push(block);
1261
-
1262
- // Process transactions in the block
1263
- if (block.transactions && Array.isArray(block.transactions)) {
1264
- for (const tx of block.transactions) {
1265
- // Update account states from transactions
1266
- if (tx.from && tx.to && tx.value) {
1267
- const value = BigInt(tx.value || '0');
1268
- const fromBalance = BigInt(this.state.get(tx.from)?.balance || '0');
1269
- const toBalance = BigInt(this.state.get(tx.to)?.balance || '0');
1270
-
1271
- // Debit sender, credit receiver
1272
- this.state.set(tx.from, {
1273
- ...this.state.get(tx.from),
1274
- balance: (fromBalance - value).toString(),
1275
- nonce: (parseInt(this.state.get(tx.from)?.nonce || '0') + 1).toString()
1276
- });
1277
- this.state.set(tx.to, {
1278
- ...this.state.get(tx.to),
1279
- balance: (toBalance + value).toString()
1280
- });
1281
- }
1282
- }
1283
- }
1284
-
1285
- console.log(`[BLOCKCHAIN] Synced block #${block.number} (${block.transactions?.length || 0} txs)`);
1286
-
1287
- return { status: 'added', blockNumber: block.number };
1288
- }
1289
-
1290
- /**
1291
- * Get sync status (jaelis_syncing compatible)
1292
- */
1293
- getSyncStatus(remoteHeight) {
1294
- const localHeight = this.getHeight();
1295
- const isSyncing = localHeight < remoteHeight;
1296
-
1297
- if (!isSyncing) {
1298
- return false; // Fully synced
1299
- }
1300
-
1301
- return {
1302
- startingBlock: '0x0',
1303
- currentBlock: '0x' + localHeight.toString(16),
1304
- highestBlock: '0x' + remoteHeight.toString(16),
1305
- syncedBlocks: localHeight,
1306
- remainingBlocks: remoteHeight - localHeight,
1307
- percentComplete: ((localHeight / remoteHeight) * 100).toFixed(2) + '%'
1308
- };
1309
- }
1310
-
1311
- async save() {
1312
- // Save blockchain state to disk
1313
- // TODO: Implement persistent storage
1314
- }
1315
- }
1316
-
1317
- class EmbeddedNetwork extends EventEmitter {
1318
- constructor(options = {}) {
1319
- super();
1320
- this.port = options.port || 30303;
1321
- this.host = options.host || '0.0.0.0';
1322
- this.maxPeers = options.maxPeers || 50;
1323
- this.bootstrapNodes = options.bootstrapNodes || [];
1324
- this.remoteRpc = options.remoteRpc || 'https://rpc.jaelis.io';
1325
- this.blockchain = options.blockchain;
1326
-
1327
- this.libp2p = null;
1328
- this.peers = new Map();
1329
- this.isP2PConnected = false;
1330
- this.isRPCFallback = false;
1331
- this.syncInterval = null;
1332
- }
1333
-
1334
- async start() {
1335
- // ALWAYS start RPC sync first - this is reliable through Cloudflare
1336
- // P2P is optional/additional for peer discovery
1337
- console.log(`[NETWORK] Starting RPC sync from ${this.remoteRpc}`);
1338
- await this._startRPCFallback();
1339
- this.isRPCFallback = true;
1340
-
1341
- // Try P2P alongside (optional - for peer discovery when bootstrap nodes available)
1342
- if (this.bootstrapNodes && this.bootstrapNodes.length > 0) {
1343
- try {
1344
- await this._startLibp2p();
1345
- this.isP2PConnected = true;
1346
- console.log(`[NETWORK] P2P network also started on port ${this.port}`);
1347
- } catch (err) {
1348
- console.log(`[NETWORK] P2P unavailable (${err.message}), continuing with RPC only`);
1349
- }
1350
- } else {
1351
- console.log(`[NETWORK] No bootstrap nodes configured - using RPC sync only`);
1352
- console.log(`[NETWORK] This is fine! RPC sync is reliable and works through firewalls.`);
1353
- }
1354
- }
1355
-
1356
- async _startLibp2p() {
1357
- // Dynamic imports for libp2p (ES modules)
1358
- const { createLibp2p } = await import('libp2p');
1359
- const { tcp } = await import('@libp2p/tcp');
1360
- const { noise } = await import('@chainsafe/libp2p-noise');
1361
- const { yamux } = await import('@chainsafe/libp2p-yamux');
1362
- const { identify } = await import('@libp2p/identify');
1363
-
1364
- // Create libp2p node - JAELIS user node
1365
- // Like Ethereum: start listening, bootstrap nodes come later via DHT
1366
- const config = {
1367
- addresses: {
1368
- listen: [`/ip4/${this.host}/tcp/${this.port}`]
1369
- },
1370
- transports: [tcp()],
1371
- connectionEncryption: [noise()],
1372
- streamMuxers: [yamux()],
1373
- services: {
1374
- identify: identify()
1375
- }
1376
- };
1377
-
1378
- this.libp2p = await createLibp2p(config);
1379
- await this.libp2p.start();
1380
-
1381
- console.log(`[P2P] Node started with ID: ${this.libp2p.peerId.toString().slice(0, 16)}...`);
1382
-
1383
- // Track peers
1384
- this.libp2p.addEventListener('peer:connect', (evt) => {
1385
- const peerId = evt.detail.toString();
1386
- this.peers.set(peerId, { connectedAt: Date.now() });
1387
- console.log(`[P2P] Peer connected: ${peerId.slice(0, 16)}... (${this.peers.size} total)`);
1388
- });
1389
-
1390
- this.libp2p.addEventListener('peer:disconnect', (evt) => {
1391
- const peerId = evt.detail.toString();
1392
- this.peers.delete(peerId);
1393
- console.log(`[P2P] Peer disconnected (${this.peers.size} remaining)`);
1394
- });
1395
-
1396
- // Try to dial bootstrap nodes directly (without peer ID)
1397
- // Like Ethereum: we connect via TCP, then learn peer ID during handshake
1398
- for (const addr of this.bootstrapNodes) {
1399
- try {
1400
- const { multiaddr } = await import('@multiformats/multiaddr');
1401
- const ma = multiaddr(addr);
1402
- await this.libp2p.dial(ma);
1403
- console.log(`[P2P] Connected to bootstrap: ${addr}`);
1404
- } catch (err) {
1405
- // Bootstrap unavailable - this is OK, we'll use RPC fallback
1406
- console.log(`[P2P] Bootstrap ${addr.slice(0, 30)}... unavailable`);
1407
- }
1408
- }
1409
- }
1410
-
1411
- async _startRPCFallback() {
1412
- // Track sync state
1413
- this.syncState = {
1414
- localHeight: 0,
1415
- remoteHeight: 0,
1416
- syncing: false,
1417
- lastSync: null,
1418
- blocksDownloaded: 0,
1419
- mode: 'polling' // 'websocket' or 'polling'
1420
- };
1421
-
1422
- // Helper to make RPC calls to remote node
1423
- const rpcCall = async (method, params = []) => {
1424
- return new Promise((resolve, reject) => {
1425
- const https = require('https');
1426
- const http = require('http');
1427
- const url = new URL(this.remoteRpc);
1428
- const protocol = url.protocol === 'https:' ? https : http;
1429
-
1430
- const data = JSON.stringify({
1431
- jsonrpc: '2.0',
1432
- method,
1433
- params,
1434
- id: Date.now()
1435
- });
1436
-
1437
- const req = protocol.request({
1438
- hostname: url.hostname,
1439
- port: url.port || (url.protocol === 'https:' ? 443 : 80),
1440
- path: url.pathname || '/',
1441
- method: 'POST',
1442
- headers: {
1443
- 'Content-Type': 'application/json',
1444
- 'Content-Length': Buffer.byteLength(data),
1445
- 'User-Agent': 'JAELIS-Node/1.8.0' // Node sync traffic
1446
- }
1447
- }, (res) => {
1448
- let body = '';
1449
- res.on('data', chunk => body += chunk);
1450
- res.on('end', () => {
1451
- try {
1452
- const json = JSON.parse(body);
1453
- if (json.error) reject(new Error(json.error.message));
1454
- else resolve(json.result);
1455
- } catch (e) { reject(e); }
1456
- });
1457
- });
1458
-
1459
- req.on('error', reject);
1460
- req.write(data);
1461
- req.end();
1462
- });
1463
- };
1464
-
1465
- // Fetch and add a specific block
1466
- const fetchBlock = async (blockNum) => {
1467
- const blockHex = '0x' + blockNum.toString(16);
1468
- const block = await rpcCall('jaelis_getBlockByNumber', [blockHex, true]);
1469
- if (block && this.blockchain?.addBlock) {
1470
- await this.blockchain.addBlock(block);
1471
- this.syncState.blocksDownloaded++;
1472
- return true;
1473
- }
1474
- return false;
1475
- };
1476
-
1477
- // Sync missing blocks (used for both initial sync and catching up)
1478
- const syncMissingBlocks = async () => {
1479
- if (this.syncState.syncing) return;
1480
- this.syncState.syncing = true;
1481
-
1482
- try {
1483
- const remoteHeightHex = await rpcCall('jaelis_blockNumber');
1484
- this.syncState.remoteHeight = parseInt(remoteHeightHex, 16);
1485
- this.syncState.localHeight = this.blockchain?.getHeight?.() || 0;
1486
-
1487
- // Loop until fully synced
1488
- while (this.syncState.localHeight < this.syncState.remoteHeight) {
1489
- const blocksToSync = this.syncState.remoteHeight - this.syncState.localHeight;
1490
-
1491
- // Batch size: larger during initial sync, smaller when near tip
1492
- const batchSize = blocksToSync > 50 ? 25 : (blocksToSync > 10 ? 10 : blocksToSync);
1493
-
1494
- if (blocksToSync > 1) {
1495
- console.log(`[SYNC] Syncing ${batchSize} blocks (${this.syncState.localHeight + 1} → ${this.syncState.localHeight + batchSize})`);
1496
- }
1497
-
1498
- for (let i = 1; i <= batchSize; i++) {
1499
- await fetchBlock(this.syncState.localHeight + i);
1500
- }
1501
-
1502
- this.syncState.localHeight += batchSize;
1503
-
1504
- if (batchSize > 1) {
1505
- console.log(`[SYNC] Local height: ${this.syncState.localHeight}/${this.syncState.remoteHeight}`);
1506
- }
1507
-
1508
- // Re-check remote height in case new blocks arrived during sync
1509
- const newRemoteHex = await rpcCall('jaelis_blockNumber');
1510
- this.syncState.remoteHeight = parseInt(newRemoteHex, 16);
1511
- }
1512
-
1513
- this.syncState.lastSync = Date.now();
1514
- this.emit('sync', this.syncState);
1515
-
1516
- } catch (err) {
1517
- console.error('[SYNC] Error:', err.message);
1518
- } finally {
1519
- this.syncState.syncing = false;
1520
- }
1521
- };
1522
-
1523
- // ============================================================
1524
- // WEBSOCKET SUBSCRIPTION (PREFERRED - like Ethereum newHeads)
1525
- // ============================================================
1526
- const tryWebSocketSync = async () => {
1527
- try {
1528
- const WebSocket = require('ws');
1529
- const wsUrl = this.remoteRpc.replace('https://', 'wss://').replace('http://', 'ws://') + '/ws';
1530
-
1531
- console.log(`[SYNC] Trying WebSocket subscription at ${wsUrl}`);
1532
-
1533
- return new Promise((resolve, reject) => {
1534
- const ws = new WebSocket(wsUrl, {
1535
- headers: { 'User-Agent': 'JAELIS-Node/1.8.0' }
1536
- });
1537
-
1538
- const timeout = setTimeout(() => {
1539
- ws.close();
1540
- reject(new Error('WebSocket connection timeout'));
1541
- }, 10000);
1542
-
1543
- ws.on('open', () => {
1544
- clearTimeout(timeout);
1545
- console.log('[SYNC] WebSocket connected! Subscribing to newHeads...');
1546
-
1547
- // Subscribe to new block headers
1548
- ws.send(JSON.stringify({
1549
- jsonrpc: '2.0',
1550
- method: 'jaelis_subscribe',
1551
- params: ['newHeads'],
1552
- id: 1
1553
- }));
1554
-
1555
- this.syncState.mode = 'websocket';
1556
- this.wsConnection = ws;
1557
- resolve(true);
1558
- });
1559
-
1560
- ws.on('message', async (data) => {
1561
- try {
1562
- const msg = JSON.parse(data.toString());
1563
-
1564
- // Subscription confirmation
1565
- if (msg.id === 1 && msg.result) {
1566
- console.log(`[SYNC] ✓ Subscribed to newHeads (id: ${msg.result})`);
1567
- console.log('[SYNC] Listening for new blocks via WebSocket...');
1568
- return;
1569
- }
1570
-
1571
- // New block notification
1572
- if (msg.method === 'jaelis_subscription' && msg.params?.result) {
1573
- const header = msg.params.result;
1574
- const blockNum = typeof header.number === 'string'
1575
- ? parseInt(header.number, 16)
1576
- : header.number;
1577
-
1578
- // Fetch and sync the new block
1579
- if (blockNum > this.syncState.localHeight) {
1580
- // If we're behind, catch up first
1581
- await syncMissingBlocks();
1582
- }
1583
-
1584
- this.syncState.remoteHeight = blockNum;
1585
- this.emit('newBlock', header);
1586
- }
1587
- } catch (e) {
1588
- // Ignore parse errors
1589
- }
1590
- });
1591
-
1592
- ws.on('error', (err) => {
1593
- clearTimeout(timeout);
1594
- reject(err);
1595
- });
1596
-
1597
- ws.on('close', () => {
1598
- console.log('[SYNC] WebSocket disconnected, falling back to polling...');
1599
- this.syncState.mode = 'polling';
1600
- this.wsConnection = null;
1601
- // Fall back to polling
1602
- startPolling();
1603
- });
1604
- });
1605
- } catch (err) {
1606
- // WebSocket module not available or connection failed
1607
- return false;
1608
- }
1609
- };
1610
-
1611
- // ============================================================
1612
- // HTTP POLLING FALLBACK (like traditional Geth sync)
1613
- // ============================================================
1614
- const SYNCED_INTERVAL = 6000; // Poll every 6s when synced (2x block time)
1615
- const CATCHUP_INTERVAL = 500; // Fast poll during catchup
1616
-
1617
- const startPolling = () => {
1618
- if (this.syncInterval) return; // Already polling
1619
-
1620
- console.log('[SYNC] Using HTTP polling mode');
1621
- this.syncState.mode = 'polling';
1622
-
1623
- let pollInterval = CATCHUP_INTERVAL;
1624
- let lastHeight = 0;
1625
-
1626
- const poll = async () => {
1627
- await syncMissingBlocks();
1628
-
1629
- // Adjust polling speed based on sync status
1630
- const isSynced = this.syncState.localHeight >= this.syncState.remoteHeight;
1631
- const heightChanged = this.syncState.remoteHeight !== lastHeight;
1632
-
1633
- if (isSynced && !heightChanged && pollInterval !== SYNCED_INTERVAL) {
1634
- // Synced and no new blocks - slow down
1635
- pollInterval = SYNCED_INTERVAL;
1636
- clearInterval(this.syncInterval);
1637
- this.syncInterval = setInterval(poll, pollInterval);
1638
- console.log(`[SYNC] ✓ Synced at block ${this.syncState.localHeight}. Polling every ${pollInterval/1000}s`);
1639
- } else if (!isSynced && pollInterval !== CATCHUP_INTERVAL) {
1640
- // Falling behind - speed up
1641
- pollInterval = CATCHUP_INTERVAL;
1642
- clearInterval(this.syncInterval);
1643
- this.syncInterval = setInterval(poll, pollInterval);
1644
- }
1645
-
1646
- lastHeight = this.syncState.remoteHeight;
1647
- };
1648
-
1649
- // Start polling
1650
- this.syncInterval = setInterval(poll, pollInterval);
1651
- };
1652
-
1653
- // ============================================================
1654
- // INITIAL SYNC
1655
- // ============================================================
1656
- console.log('[SYNC] Starting sync from', this.remoteRpc);
1657
-
1658
- // Do initial block sync first
1659
- await syncMissingBlocks();
1660
-
1661
- // Try WebSocket first (like Ethereum pub/sub), fall back to polling
1662
- const wsSuccess = await tryWebSocketSync().catch(() => false);
1663
-
1664
- if (!wsSuccess) {
1665
- console.log('[SYNC] WebSocket unavailable, using HTTP polling');
1666
- startPolling();
1667
- }
1668
- }
1669
-
1670
- async stop() {
1671
- // Close WebSocket connection if active
1672
- if (this.wsConnection) {
1673
- this.wsConnection.close();
1674
- this.wsConnection = null;
1675
- }
1676
- // Stop libp2p if active
1677
- if (this.libp2p) {
1678
- await this.libp2p.stop();
1679
- }
1680
- // Stop polling interval if active
1681
- if (this.syncInterval) {
1682
- clearInterval(this.syncInterval);
1683
- this.syncInterval = null;
1684
- }
1685
- }
1686
-
1687
- getPeerCount() {
1688
- return this.peers.size;
1689
- }
1690
-
1691
- getConnectionMode() {
1692
- if (this.isP2PConnected) return 'p2p';
1693
- if (this.isRPCFallback) return 'rpc-fallback';
1694
- return 'disconnected';
1695
- }
1696
- }
1697
-
1698
- class EmbeddedRpcServer {
1699
- constructor(blockchain, port, network = null) {
1700
- this.blockchain = blockchain;
1701
- this.port = port;
1702
- this.network = network; // Reference to EmbeddedNetwork for sync status
1703
- this.app = null;
1704
- }
1705
-
1706
- async start() {
1707
- const express = require('express');
1708
- const cors = require('cors');
1709
-
1710
- this.app = express();
1711
- this.app.use(cors());
1712
- this.app.use(express.json({ limit: '50mb' })); // Allow large contract deployments
1713
-
1714
- // Main RPC endpoint
1715
- this.app.post('/', async (req, res) => {
1716
- const { method, params, id } = req.body;
1717
-
1718
- try {
1719
- const result = await this._handleMethod(method, params || []);
1720
- res.json({ jsonrpc: '2.0', id, result });
1721
- } catch (error) {
1722
- res.json({
1723
- jsonrpc: '2.0',
1724
- id,
1725
- error: { code: -32000, message: error.message }
1726
- });
1727
- }
1728
- });
1729
-
1730
- // Health check
1731
- this.app.get('/', (req, res) => {
1732
- const vmStats = this.blockchain?.getVMStats?.() || {};
1733
- res.json({
1734
- status: 'online',
1735
- service: 'JAELIS Node',
1736
- chainId: this.blockchain?.chainId,
1737
- blockHeight: this.blockchain?.getHeight?.() || 0,
1738
- vm: {
1739
- available: vmStats.vmAvailable || false,
1740
- contractsDeployed: vmStats.contractsDeployed || 0,
1741
- crossContractCalls: vmStats.crossContractCalls || 0
1742
- },
1743
- features: [
1744
- 'ZERO_FEES',
1745
- 'UNIFIED_VM',
1746
- 'NO_BRIDGES',
1747
- 'CROSS_CHAIN_INTEROP'
1748
- ]
1749
- });
1750
- });
1751
-
1752
- return new Promise((resolve) => {
1753
- this.server = this.app.listen(this.port, '0.0.0.0', () => {
1754
- resolve();
1755
- });
1756
- });
1757
- }
1758
-
1759
- async _handleMethod(method, params) {
1760
- switch (method) {
1761
- // ═══════════════════════════════════════════════════════════════
1762
- // LAYER 1: NATIVE JAELIS METHODS (PRIMARY)
1763
- // ═══════════════════════════════════════════════════════════════
1764
- case 'jaelis_chainId':
1765
- return '0x' + (this.blockchain?.chainId || 4545).toString(16);
1766
-
1767
- case 'jaelis_blockNumber':
1768
- return '0x' + (this.blockchain?.getHeight?.() || 0).toString(16);
1769
-
1770
- case 'jaelis_syncing':
1771
- // Return sync status - false if fully synced, object if syncing
1772
- if (this.network?.syncState) {
1773
- return this.blockchain?.getSyncStatus?.(this.network.syncState.remoteHeight) || false;
1774
- }
1775
- return false;
1776
-
1777
- case 'jaelis_getBlockByNumber':
1778
- // Native JAELIS method - returns JAELIS-native block fields
1779
- const jaelisBlockNum = typeof params[0] === 'string' && params[0].startsWith('0x')
1780
- ? parseInt(params[0], 16)
1781
- : params[0];
1782
- const jaelisBlock = this.blockchain?.getBlock?.(jaelisBlockNum);
1783
- if (jaelisBlock) {
1784
- // Add JAELIS-specific fields
1785
- return {
1786
- ...jaelisBlock,
1787
- lodeUsed: '0x0', // ZERO FEES
1788
- consensus: 'PoEC', // Proof of Entropy Contribution
1789
- jaelisVersion: '1.0.0'
1790
- };
1791
- }
1792
- return null;
1793
-
1794
- case 'jaelis_getBlockByHash':
1795
- // Find block by hash
1796
- const targetHash = params[0];
1797
- const foundBlock = this.blockchain?.chain?.find(b => b.hash === targetHash);
1798
- return foundBlock || null;
1799
-
1800
- // ═══════════════════════════════════════════════════════════════
1801
- // LAYER 2: ETHEREUM COMPATIBILITY (PROXIES → jaelis_*)
1802
- // ═══════════════════════════════════════════════════════════════
1803
- case 'eth_chainId':
1804
- return this._handleMethod('jaelis_chainId', params);
1805
-
1806
- case 'eth_blockNumber':
1807
- return this._handleMethod('jaelis_blockNumber', params);
1808
-
1809
- case 'eth_getBlockByNumber':
1810
- const blockNum = parseInt(params[0], 16);
1811
- return this.blockchain?.getBlock?.(blockNum) || null;
1812
-
1813
- case 'eth_sendTransaction':
1814
- case 'eth_sendRawTransaction':
1815
- return this._sendTransaction(params[0]);
1816
-
1817
- case 'eth_call':
1818
- return this._call(params[0], params[1]);
1819
-
1820
- case 'eth_getTransactionReceipt':
1821
- return this.blockchain?.vmExecutor?.getReceipt?.(params[0]) || null;
1822
-
1823
- case 'eth_getCode':
1824
- // CRITICAL: This is how block explorers detect contracts!
1825
- // Returns bytecode at address, or '0x' for EOAs
1826
- return this._getCode(params[0], params[1] || 'latest');
1827
-
1828
- case 'eth_getBalance':
1829
- return this._getBalance(params[0], params[1] || 'latest');
1830
-
1831
- case 'eth_getTransactionCount':
1832
- return this._getTransactionCount(params[0], params[1] || 'latest');
1833
-
1834
- case 'eth_getStorageAt':
1835
- return this._getStorageAt(params[0], params[1], params[2] || 'latest');
1836
-
1837
- case 'eth_estimateGas':
1838
- // JAELIS has ZERO GAS - always return minimal
1839
- return '0x5208'; // 21000 in hex
1840
-
1841
- case 'eth_gasPrice':
1842
- // JAELIS has ZERO GAS
1843
- return '0x0';
1844
-
1845
- // ═══════════════════════════════════════════════════════════════
1846
- // JAELIS-SPECIFIC METHODS (Unified VM)
1847
- // ═══════════════════════════════════════════════════════════════
1848
- case 'jaelis_getCode':
1849
- // Native JAELIS method - returns contract bytecode
1850
- return this._getCode(params[0], params[1] || 'latest');
1851
-
1852
- case 'jaelis_getHealth':
1853
- return {
1854
- status: 'healthy',
1855
- chainId: this.blockchain?.chainId,
1856
- blockHeight: this.blockchain?.getHeight?.() || 0,
1857
- vmActive: !!this.blockchain?.vmExecutor
1858
- };
1859
-
1860
- case 'jaelis_deploy':
1861
- case 'jaelis_deployContract':
1862
- // Deploy contract in ANY language!
1863
- // params: [source, language, options]
1864
- return this._deployContract(params[0], params[1], params[2] || {});
1865
-
1866
- case 'jaelis_deployUnified':
1867
- // THE KEY INNOVATION: Deploy unified contract bundle!
1868
- // Compile MULTIPLE contracts from MULTIPLE languages into ONE bytecode!
1869
- // params: [{ from, sources: [{ code, language, name }...] }]
1870
- return this._deployUnified(params[0]);
1871
-
1872
- case 'jaelis_executeContract':
1873
- // Execute contract call
1874
- // params: [to, functionName, args, options]
1875
- return this._executeContract(params[0], params[1], params[2] || [], params[3] || {});
1876
-
1877
- case 'jaelis_crossContractCall':
1878
- // Cross-contract call (NO BRIDGES!)
1879
- // params: [fromContract, toContract, functionName, args]
1880
- return this._crossContractCall(params[0], params[1], params[2], params[3] || []);
1881
-
1882
- case 'jaelis_getContracts':
1883
- return this.blockchain?.getContracts?.() || [];
1884
-
1885
- case 'jaelis_getVMStats':
1886
- return this.blockchain?.getVMStats?.() || { vmAvailable: false };
1887
-
1888
- case 'jaelis_getSupportedLanguages':
1889
- return ['solidity', 'rust', 'move', 'func', 'cairo', 'vyper'];
1890
-
1891
- // ═══════════════════════════════════════════════════════════════
1892
- // CROSS-CHAIN DEPLOYMENT METHODS (NEW!)
1893
- // Query contract deployments across chains
1894
- // ═══════════════════════════════════════════════════════════════
1895
-
1896
- case 'jaelis_crossChain_getDeployment':
1897
- // Get deployment info for an address
1898
- // params: [address]
1899
- return this._getCrossChainDeployment(params[0]);
1900
-
1901
- case 'jaelis_crossChain_getChainDeployments':
1902
- // Get all deployments to a specific chain
1903
- // params: [chainId]
1904
- return this._getChainDeployments(params[0]);
1905
-
1906
- case 'jaelis_crossChain_getExplorerUrl':
1907
- // Get explorer URL for a deployment
1908
- // params: [chainId, address]
1909
- return this._getExplorerUrl(params[0], params[1]);
1910
-
1911
- case 'jaelis_crossChain_getDeploymentStats':
1912
- // Get deployment statistics
1913
- return this._getDeploymentStats();
1914
-
1915
- // ═══════════════════════════════════════════════════════════════
1916
- // MULTI-CHAIN SIGNATURE METHODS (Per-chain wallet authorization)
1917
- // ═══════════════════════════════════════════════════════════════
1918
-
1919
- case 'jaelis_crossChain_getSigningRequests':
1920
- // Get signing messages for each target chain
1921
- // params: [bytecode, targetChains[]]
1922
- // Returns array of { chainId, message, switchParams } for frontend
1923
- return this._getMultiChainSigningRequests(params[0], params[1]);
1924
-
1925
- case 'jaelis_crossChain_deployWithSignatures':
1926
- // Deploy with per-chain wallet signatures
1927
- // params: [{ from, bytecode, abi, targetChains, chainSignatures: {chainId: sig} }]
1928
- return this._deployWithMultiChainSignatures(params[0]);
1929
-
1930
- // ═══════════════════════════════════════════════════════════════
1931
- // CROSS-CHAIN SETTLEMENT METHODS (Universal State Layer!)
1932
- // ═══════════════════════════════════════════════════════════════
1933
-
1934
- case 'jaelis_crossChain_getChains':
1935
- // Get all registered chains
1936
- return this._getCrossChainRegistry();
1937
-
1938
- case 'jaelis_crossChain_getChainInfo':
1939
- // Get info for specific chain ID
1940
- return this._getCrossChainInfo(params[0]);
1941
-
1942
- case 'jaelis_crossChain_readState':
1943
- // Read settled state from external chain
1944
- // params: [chainId, contractAddress, variableName]
1945
- return this._readCrossChainState(params[0], params[1], params[2]);
1946
-
1947
- case 'jaelis_crossChain_settleState':
1948
- // Settle external chain state with proof
1949
- // params: [chainId, contractAddress, variableName, value, proof]
1950
- return this._settleCrossChainState(params[0], params[1], params[2], params[3], params[4]);
1951
-
1952
- case 'jaelis_crossChain_getStateDiff':
1953
- // Get state diff between two chains
1954
- // params: [chainId1, chainId2, contractAddress, variableNames]
1955
- return this._getCrossChainStateDiff(params[0], params[1], params[2], params[3]);
1956
-
1957
- case 'jaelis_crossChain_computeSlot':
1958
- // Compute namespaced storage slot
1959
- // params: [chainId, contractAddress, variableName]
1960
- return this._computeCrossChainSlot(params[0], params[1], params[2]);
1961
-
1962
- case 'jaelis_crossChain_getLightClientStatus':
1963
- // Get light client status for a chain
1964
- return this._getLightClientStatus(params[0]);
1965
-
1966
- case 'jaelis_crossChain_getSettlements':
1967
- // Get all settlements for a contract
1968
- // params: [chainId, contractAddress]
1969
- return this._getSettlements(params[0], params[1]);
1970
-
1971
- case 'jaelis_crossChain_getStats':
1972
- // Get cross-chain stats
1973
- return this._getCrossChainStats();
1974
-
1975
- // ═══════════════════════════════════════════════════════════════
1976
- // MCP SHORTHAND ALIASES (crosschain_* → jaelis_crossChain_*)
1977
- // Unified RPC - same methods, shorter names for AI agents
1978
- // ═══════════════════════════════════════════════════════════════
1979
- case 'crosschain_getChains':
1980
- return this._handleMethod('jaelis_crossChain_getChains', params);
1981
- case 'crosschain_getChainInfo':
1982
- return this._handleMethod('jaelis_crossChain_getChainInfo', params);
1983
- case 'crosschain_readState':
1984
- return this._handleMethod('jaelis_crossChain_readState', params);
1985
- case 'crosschain_settleState':
1986
- return this._handleMethod('jaelis_crossChain_settleState', params);
1987
- case 'crosschain_getStateDiff':
1988
- return this._handleMethod('jaelis_crossChain_getStateDiff', params);
1989
- case 'crosschain_computeSlot':
1990
- return this._handleMethod('jaelis_crossChain_computeSlot', params);
1991
- case 'crosschain_getLightClientStatus':
1992
- return this._handleMethod('jaelis_crossChain_getLightClientStatus', params);
1993
- case 'crosschain_getSettlements':
1994
- return this._handleMethod('jaelis_crossChain_getSettlements', params);
1995
- case 'crosschain_getStats':
1996
- return this._handleMethod('jaelis_crossChain_getStats', params);
1997
- case 'crosschain_getDeployment':
1998
- return this._handleMethod('jaelis_crossChain_getDeployment', params);
1999
- case 'crosschain_getExplorerUrl':
2000
- return this._handleMethod('jaelis_crossChain_getExplorerUrl', params);
2001
- case 'crosschain_getDeploymentStats':
2002
- return this._handleMethod('jaelis_crossChain_getDeploymentStats', params);
2003
- case 'crosschain_getSigningRequests':
2004
- return this._handleMethod('jaelis_crossChain_getSigningRequests', params);
2005
- case 'crosschain_deployWithSignatures':
2006
- return this._handleMethod('jaelis_crossChain_deployWithSignatures', params);
2007
- case 'crosschain_deployUnified':
2008
- return this._handleMethod('jaelis_deployUnified', params);
2009
- case 'crosschain_syncState':
2010
- return this._handleMethod('jaelis_crossChain_settleState', params);
2011
-
2012
- // ═══════════════════════════════════════════════════════════════
2013
- // LAYER 2: SOLANA COMPATIBILITY (PROXIES → jaelis_*)
2014
- // For Rust/SPL developers familiar with Solana RPC
2015
- // ═══════════════════════════════════════════════════════════════
2016
- case 'solana_getBalance':
2017
- // Convert to JAELIS format then back to Solana format
2018
- const jaelisBalance = await this._handleMethod('jaelis_getBalance', params);
2019
- return { value: BigInt(jaelisBalance || '0').toString() };
2020
-
2021
- case 'solana_getHealth':
2022
- return this._handleMethod('jaelis_getHealth', params);
2023
-
2024
- case 'solana_getSlot':
2025
- return this.blockchain?.getHeight?.() || 0;
2026
-
2027
- case 'solana_getBlockHeight':
2028
- return this.blockchain?.getHeight?.() || 0;
2029
-
2030
- case 'solana_getVersion':
2031
- return { 'solana-core': '1.17.0', 'feature-set': 4545 };
2032
-
2033
- case 'solana_getLatestBlockhash':
2034
- const block = this.blockchain?.getLatestBlock?.();
2035
- return { blockhash: block?.hash || '0x0', lastValidBlockHeight: block?.number || 0 };
2036
-
2037
- // ═══════════════════════════════════════════════════════════════
2038
- // LAYER 2: MOVE COMPATIBILITY (PROXIES → jaelis_*)
2039
- // For Aptos/Sui developers familiar with Move
2040
- // ═══════════════════════════════════════════════════════════════
2041
- case 'move_getLedgerInfo':
2042
- const height = this.blockchain?.getHeight?.() || 0;
2043
- return {
2044
- chain_id: this.blockchain?.chainId || 4545,
2045
- ledger_version: height.toString(),
2046
- block_height: height.toString()
2047
- };
2048
-
2049
- case 'move_getAccountResource':
2050
- return this._handleMethod('jaelis_getMoveResource', params);
2051
-
2052
- case 'move_getAccountModule':
2053
- return this._handleMethod('jaelis_getMoveModule', params);
2054
-
2055
- // ═══════════════════════════════════════════════════════════════
2056
- // LAYER 2: TON COMPATIBILITY (PROXIES → jaelis_*)
2057
- // For FunC/Telegram developers (900M+ users!)
2058
- // ═══════════════════════════════════════════════════════════════
2059
- case 'ton_getAddressBalance':
2060
- const tonBalance = await this._handleMethod('jaelis_getBalance', params);
2061
- return { balance: tonBalance || '0' };
2062
-
2063
- case 'ton_getMasterchainInfo':
2064
- const tonBlock = this.blockchain?.getLatestBlock?.();
2065
- return {
2066
- last: {
2067
- workchain: 0,
2068
- seqno: tonBlock?.number || 0,
2069
- root_hash: tonBlock?.hash || '0x0'
2070
- }
2071
- };
2072
-
2073
- case 'ton_getAccountState':
2074
- return this._handleMethod('jaelis_getTonAccount', params);
2075
-
2076
- case 'ton_runGetMethod':
2077
- return this._handleMethod('jaelis_callTonMethod', params);
2078
-
2079
- // ═══════════════════════════════════════════════════════════════
2080
- // LAYER 2: BITCOIN COMPATIBILITY (PROXIES → jaelis_*)
2081
- // For Bitcoin developers - familiar Bitcoin Core RPC methods
2082
- // ═══════════════════════════════════════════════════════════════
2083
- case 'btc_getbalance':
2084
- const btcBal = await this._handleMethod('jaelis_getBalance', params);
2085
- const satoshis = BigInt(btcBal?.jaelis || '0') / BigInt(10 ** 10);
2086
- return Number(satoshis) / 100000000;
2087
-
2088
- case 'btc_getblockcount':
2089
- return this.blockchain?.getHeight?.() || 0;
2090
-
2091
- case 'btc_getblockhash':
2092
- const btcBlock = this.blockchain?.getBlock?.(params[0]);
2093
- return btcBlock?.hash || null;
2094
-
2095
- case 'btc_getblock':
2096
- const blk = this.blockchain?.getBlockByHash?.(params[0]);
2097
- if (!blk) return null;
2098
- return {
2099
- hash: blk.hash,
2100
- confirmations: 1,
2101
- height: blk.number,
2102
- version: 1,
2103
- time: blk.timestamp,
2104
- tx: blk.transactions?.map(t => t.hash || t) || [],
2105
- previousblockhash: blk.parentHash
2106
- };
2107
-
2108
- case 'btc_gettransaction':
2109
- return this._handleMethod('jaelis_getTransactionByHash', params);
2110
-
2111
- case 'btc_sendrawtransaction':
2112
- return this._handleMethod('jaelis_sendRawTransaction', params);
2113
-
2114
- case 'btc_getblockchaininfo':
2115
- const btcHeight = this.blockchain?.getHeight?.() || 0;
2116
- return {
2117
- chain: (this.blockchain?.chainId || 4545) === 4545 ? 'test' : 'main',
2118
- blocks: btcHeight,
2119
- headers: btcHeight,
2120
- bestblockhash: this.blockchain?.getLatestBlock?.()?.hash,
2121
- difficulty: 1,
2122
- warnings: 'JAELIS Bitcoin compatibility - zero gas fees!'
2123
- };
2124
-
2125
- case 'btc_getnetworkinfo':
2126
- return {
2127
- version: 250000,
2128
- subversion: '/JAELIS:1.0.0/',
2129
- protocolversion: 70016,
2130
- warnings: 'JAELIS Bitcoin compatibility layer'
2131
- };
2132
-
2133
- case 'btc_getmempoolinfo':
2134
- return { loaded: true, size: 0, mempoolminfee: 0 };
2135
-
2136
- case 'btc_estimatesmartfee':
2137
- return { feerate: 0, blocks: params[0] || 1 }; // Zero fees!
2138
-
2139
- // ═══════════════════════════════════════════════════════════════
2140
- // LAYER 2: WASM COMPATIBILITY (PROXIES → jaelis_*)
2141
- // For CosmWasm/NEAR/Polkadot WASM developers
2142
- // ═══════════════════════════════════════════════════════════════
2143
- case 'wasm_call':
2144
- return this._handleMethod('jaelis_call', [{ to: params[0], data: JSON.stringify({ method: params[1], args: params[2] }) }]);
2145
-
2146
- case 'wasm_query':
2147
- return this._handleMethod('jaelis_call', [{ to: params[0], data: JSON.stringify(params[1]) }]);
2148
-
2149
- case 'wasm_deploy':
2150
- return this._handleMethod('jaelis_deployWasmContract', params);
2151
-
2152
- case 'wasm_instantiate':
2153
- return this._handleMethod('jaelis_deployWasmContract', [{ codeId: params[0], initMsg: params[1], label: params[2] }]);
2154
-
2155
- case 'wasm_execute':
2156
- return this._handleMethod('jaelis_sendTransaction', [{ to: params[0], data: JSON.stringify(params[1]), value: params[2] || '0x0' }]);
2157
-
2158
- case 'wasm_getCode':
2159
- return this._handleMethod('jaelis_getCode', params);
2160
-
2161
- case 'wasm_getContractInfo':
2162
- const wasmCode = await this._handleMethod('jaelis_getCode', params);
2163
- return { address: params[0], has_code: wasmCode && wasmCode !== '0x' };
2164
-
2165
- case 'wasm_getContractState':
2166
- return this._handleMethod('jaelis_getStorageAt', [params[0], params[1] || '0x0', 'latest']);
2167
-
2168
- // ═══════════════════════════════════════════════════════════════
2169
- // LAYER 2: STARKNET/CAIRO COMPATIBILITY (PROXIES → jaelis_*)
2170
- // For StarkNet/Cairo developers
2171
- // ═══════════════════════════════════════════════════════════════
2172
- case 'starknet_chainId':
2173
- return this._handleMethod('jaelis_chainId', params);
2174
-
2175
- case 'starknet_blockNumber':
2176
- return this.blockchain?.getHeight?.() || 0;
2177
-
2178
- case 'starknet_getBlockWithTxHashes':
2179
- const snBlock = this.blockchain?.getBlock?.(params[0]?.block_number || 'latest');
2180
- if (!snBlock) return null;
2181
- return {
2182
- block_hash: snBlock.hash,
2183
- parent_hash: snBlock.parentHash,
2184
- block_number: snBlock.number,
2185
- transactions: snBlock.transactions || [],
2186
- status: 'ACCEPTED_ON_L1'
2187
- };
2188
-
2189
- case 'starknet_getBlockWithTxs':
2190
- return this._handleMethod('starknet_getBlockWithTxHashes', params);
2191
-
2192
- case 'starknet_getTransactionByHash':
2193
- const snTx = await this._handleMethod('jaelis_getTransactionByHash', params);
2194
- if (!snTx) return null;
2195
- return { transaction_hash: snTx.hash, type: 'INVOKE', sender_address: snTx.from };
2196
-
2197
- case 'starknet_getTransactionReceipt':
2198
- const snReceipt = await this._handleMethod('jaelis_getTransactionReceipt', params);
2199
- if (!snReceipt) return null;
2200
- return {
2201
- transaction_hash: snReceipt.transactionHash,
2202
- actual_fee: '0x0',
2203
- status: snReceipt.status === '0x1' ? 'ACCEPTED_ON_L1' : 'REJECTED'
2204
- };
2205
-
2206
- case 'starknet_call':
2207
- return this._handleMethod('jaelis_call', [{ to: params[0]?.contract_address, data: '0x' }]);
2208
-
2209
- case 'starknet_estimateFee':
2210
- return { gas_consumed: '0x0', gas_price: '0x0', overall_fee: '0x0' }; // Zero fees!
2211
-
2212
- case 'starknet_addInvokeTransaction':
2213
- return this._handleMethod('jaelis_sendTransaction', [{ from: params[0]?.sender_address, to: params[0]?.calldata?.[0], data: '0x' }]);
2214
-
2215
- case 'starknet_addDeclareTransaction':
2216
- return this._handleMethod('jaelis_deploy', [{ data: params[0]?.contract_class, from: params[0]?.sender_address }]);
2217
-
2218
- case 'starknet_getClass':
2219
- case 'starknet_getClassAt':
2220
- const cairoCode = await this._handleMethod('jaelis_getCode', [params[1] || params[0]]);
2221
- return { program: cairoCode, entry_points_by_type: { CONSTRUCTOR: [], EXTERNAL: [], L1_HANDLER: [] } };
2222
-
2223
- default:
2224
- throw new Error(`Method not found: ${method}`);
2225
- }
2226
- }
2227
-
2228
- async _sendTransaction(tx) {
2229
- if (!this.blockchain) throw new Error('Blockchain not available');
2230
- const result = await this.blockchain.processTransaction(tx);
2231
- return result.transactionHash || '0x0';
2232
- }
2233
-
2234
- /**
2235
- * Get contract bytecode at address
2236
- *
2237
- * CRITICAL: This is how block explorers detect contracts!
2238
- * - Returns bytecode hex string if contract exists
2239
- * - Returns '0x' if address is an EOA or doesn't exist
2240
- *
2241
- * Block explorers use: eth_getCode(address) !== '0x' → it's a contract!
2242
- */
2243
- async _getCode(address, blockTag = 'latest') {
2244
- if (!this.blockchain) return '0x';
2245
-
2246
- try {
2247
- // Normalize address
2248
- const normalizedAddr = address.toLowerCase();
2249
-
2250
- // Try StateManager first (persistent LevelDB storage)
2251
- if (this.blockchain.stateManager) {
2252
- const code = await this.blockchain.stateManager.getCode(normalizedAddr);
2253
- if (code && code.length > 0) {
2254
- // Return as hex string
2255
- if (Buffer.isBuffer(code)) {
2256
- return '0x' + code.toString('hex');
2257
- }
2258
- if (typeof code === 'string') {
2259
- return code.startsWith('0x') ? code : '0x' + code;
2260
- }
2261
- return '0x' + Buffer.from(code).toString('hex');
2262
- }
2263
- }
2264
-
2265
- // Try VM Executor
2266
- if (this.blockchain.vmExecutor?.stateManager) {
2267
- const code = await this.blockchain.vmExecutor.stateManager.getCode(normalizedAddr);
2268
- if (code && code.length > 0) {
2269
- if (Buffer.isBuffer(code)) {
2270
- return '0x' + code.toString('hex');
2271
- }
2272
- return code.startsWith('0x') ? code : '0x' + code;
2273
- }
2274
- }
2275
-
2276
- // Try UnifiedVM contracts
2277
- if (this.blockchain.unifiedVM?.contractManager) {
2278
- const contract = this.blockchain.unifiedVM.contractManager.contracts.get(normalizedAddr);
2279
- if (contract?.bytecode) {
2280
- if (Buffer.isBuffer(contract.bytecode)) {
2281
- return '0x' + contract.bytecode.toString('hex');
2282
- }
2283
- return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
2284
- }
2285
- }
2286
-
2287
- // Try contracts Map directly
2288
- if (this.blockchain.contracts) {
2289
- const contract = this.blockchain.contracts.get(normalizedAddr);
2290
- if (contract?.bytecode) {
2291
- if (Buffer.isBuffer(contract.bytecode)) {
2292
- return '0x' + contract.bytecode.toString('hex');
2293
- }
2294
- return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
2295
- }
2296
- }
2297
-
2298
- // No bytecode found - it's an EOA or doesn't exist
2299
- return '0x';
2300
- } catch (error) {
2301
- console.error(`[RPC] _getCode error for ${address}:`, error.message);
2302
- return '0x';
2303
- }
2304
- }
2305
-
2306
- /**
2307
- * Get account balance
2308
- */
2309
- async _getBalance(address, blockTag = 'latest') {
2310
- if (!this.blockchain) return '0x0';
2311
-
2312
- try {
2313
- const normalizedAddr = address.toLowerCase();
2314
-
2315
- // Try StateManager
2316
- if (this.blockchain.stateManager) {
2317
- const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
2318
- if (account?.balance !== undefined) {
2319
- const balance = BigInt(account.balance || 0);
2320
- return '0x' + balance.toString(16);
2321
- }
2322
- }
2323
-
2324
- // Default balance (JAELIS has no native token, but return 0)
2325
- return '0x0';
2326
- } catch (error) {
2327
- return '0x0';
2328
- }
2329
- }
2330
-
2331
- /**
2332
- * Get transaction count (nonce)
2333
- */
2334
- async _getTransactionCount(address, blockTag = 'latest') {
2335
- if (!this.blockchain) return '0x0';
2336
-
2337
- try {
2338
- const normalizedAddr = address.toLowerCase();
2339
-
2340
- // Try StateManager
2341
- if (this.blockchain.stateManager) {
2342
- const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
2343
- if (account?.nonce !== undefined) {
2344
- const nonce = BigInt(account.nonce || 0);
2345
- return '0x' + nonce.toString(16);
2346
- }
2347
- }
2348
-
2349
- return '0x0';
2350
- } catch (error) {
2351
- return '0x0';
2352
- }
2353
- }
2354
-
2355
- /**
2356
- * Get storage value at slot
2357
- */
2358
- async _getStorageAt(address, slot, blockTag = 'latest') {
2359
- if (!this.blockchain) return '0x' + '0'.repeat(64);
2360
-
2361
- try {
2362
- const normalizedAddr = address.toLowerCase();
2363
-
2364
- // Try StateManager
2365
- if (this.blockchain.stateManager) {
2366
- const value = await this.blockchain.stateManager.getStorage(normalizedAddr, slot);
2367
- if (value !== undefined) {
2368
- const bigValue = BigInt(value || 0);
2369
- return '0x' + bigValue.toString(16).padStart(64, '0');
2370
- }
2371
- }
2372
-
2373
- // Try VM Executor StateManager
2374
- if (this.blockchain.vmExecutor?.stateManager) {
2375
- const value = await this.blockchain.vmExecutor.stateManager.getStorage(normalizedAddr, slot);
2376
- if (value !== undefined) {
2377
- const bigValue = BigInt(value || 0);
2378
- return '0x' + bigValue.toString(16).padStart(64, '0');
2379
- }
2380
- }
2381
-
2382
- return '0x' + '0'.repeat(64);
2383
- } catch (error) {
2384
- return '0x' + '0'.repeat(64);
2385
- }
2386
- }
2387
-
2388
- async _call(callObject) {
2389
- if (!this.blockchain?.vmExecutor) throw new Error('VM not available');
2390
- const result = await this.blockchain.vmExecutor.executeCall(
2391
- callObject.to,
2392
- callObject.data?.functionName || 'call',
2393
- callObject.data?.args || [],
2394
- { from: callObject.from }
2395
- );
2396
- return result.returnValue;
2397
- }
2398
-
2399
- async _deployContract(source, language, options) {
2400
- if (!this.blockchain) throw new Error('Blockchain not available');
2401
- console.log(`[RPC] Deploying ${language} contract...`);
2402
- const result = await this.blockchain.deployContract(source, language, options);
2403
- return {
2404
- address: result.address,
2405
- language,
2406
- gasUsed: 0 // ZERO FEES!
2407
- };
2408
- }
2409
-
2410
- /**
2411
- * Deploy unified contract bundle (THE KEY INNOVATION!)
2412
- *
2413
- * Compiles MULTIPLE contracts from MULTIPLE languages into ONE bytecode bundle.
2414
- * All contracts share memory and can call each other DIRECTLY - no bridges!
2415
- *
2416
- * NOW WITH CROSS-CHAIN PROPAGATION:
2417
- * - Contracts appear on ETH, SOL, and other block explorers!
2418
- * - Shadow contracts on external chains proxy to JAELIS
2419
- *
2420
- * @param {Object} params - { from, sources: [{ code, language, name }...], targetChains?: number[] }
2421
- */
2422
- async _deployUnified(params) {
2423
- if (!this.blockchain) throw new Error('Blockchain not available');
2424
- if (!this.blockchain.unifiedVM) throw new Error('Unified VM not available');
2425
-
2426
- const { from, sources, targetChains } = params;
2427
-
2428
- if (!sources || !Array.isArray(sources) || sources.length === 0) {
2429
- throw new Error('sources array is required for unified deployment');
2430
- }
2431
-
2432
- console.log(`[RPC] Deploying unified bundle with ${sources.length} contracts...`);
2433
- console.log(`[RPC] Languages: ${[...new Set(sources.map(s => s.language))].join(', ')}`);
2434
-
2435
- // Default target chains if not specified - ALL 30+ chains we support!
2436
- const defaultTargetChains = [
2437
- // EVM Mainnets
2438
- 1, // Ethereum Mainnet
2439
- 137, // Polygon PoS
2440
- 42161, // Arbitrum One
2441
- 42170, // Arbitrum Nova
2442
- 10, // OP Mainnet
2443
- 8453, // Base
2444
- 56, // BNB Smart Chain
2445
- 43114, // Avalanche C-Chain
2446
- 250, // Fantom Opera
2447
- 100, // Gnosis
2448
- 59144, // Linea
2449
- 324, // zkSync Era
2450
- 534352, // Scroll
2451
- 5000, // Mantle
2452
- 81457, // Blast
2453
-
2454
- // Solana
2455
- 101, // Solana Mainnet
2456
-
2457
- // Move chains (using type prefix to differentiate)
2458
- // Note: Aptos/Sui use same IDs but different networks
2459
-
2460
- // TON
2461
- -1, // TON Mainnet
2462
-
2463
- // StarkNet (using string identifier due to large chain ID)
2464
- 'starknet-main', // StarkNet Mainnet
2465
-
2466
- // Cosmos
2467
- 118, // Cosmos Hub
2468
- 119, // Osmosis
2469
-
2470
- // Bitcoin
2471
- 0 // Bitcoin Mainnet
2472
- ];
2473
- const chains = targetChains || defaultTargetChains;
2474
-
2475
- console.log(`[RPC] Target chains: ${chains.join(', ')}`);
2476
-
2477
- // Use the new deployUnifiedWithPropagation for cross-chain deployment
2478
- if (this.blockchain.unifiedVM.deployUnifiedWithPropagation) {
2479
- const result = await this.blockchain.unifiedVM.deployUnifiedWithPropagation({
2480
- from,
2481
- sources,
2482
- targetChains: chains
2483
- });
2484
-
2485
- // Build contracts map for response
2486
- const contractsMap = {};
2487
- for (const source of sources) {
2488
- contractsMap[source.name] = {
2489
- address: result.address,
2490
- language: source.language,
2491
- name: source.name
2492
- };
2493
- }
2494
-
2495
- console.log(`[RPC] Unified bundle deployed at ${result.address}`);
2496
- console.log(`[RPC] Chain deployments: ${result.deployments?.length || 0}`);
2497
-
2498
- // Log explorer URLs
2499
- if (result.explorerUrls) {
2500
- console.log(`[RPC] Explorer URLs:`);
2501
- for (const [chainId, url] of Object.entries(result.explorerUrls)) {
2502
- console.log(`[RPC] Chain ${chainId}: ${url}`);
2503
- }
2504
- }
2505
-
2506
- return {
2507
- address: result.address,
2508
- bytecode: result.bytecode,
2509
- abi: result.abi || [],
2510
- contracts: contractsMap,
2511
- deployments: result.deployments,
2512
- explorerUrls: result.explorerUrls,
2513
- metadata: result.metadata,
2514
- gasUsed: 0 // ZERO FEES!
2515
- };
2516
- }
2517
-
2518
- // Fallback: original local-only deployment
2519
- console.log(`[RPC] Fallback: local-only deployment (no cross-chain propagation)`);
2520
-
2521
- const compiled = await this.blockchain.unifiedVM.compileBundle(sources);
2522
- const address = '0x' + require('crypto').randomBytes(20).toString('hex');
2523
-
2524
- if (this.blockchain.contracts) {
2525
- this.blockchain.contracts.set(address, {
2526
- bytecode: compiled.bytecode,
2527
- abi: compiled.abi || [],
2528
- sources: sources.map(s => s.name),
2529
- languages: [...new Set(sources.map(s => s.language))],
2530
- deployedAt: Date.now(),
2531
- deployer: from
2532
- });
2533
- }
2534
-
2535
- const contractsMap = {};
2536
- for (const source of sources) {
2537
- contractsMap[source.name] = {
2538
- address: address,
2539
- language: source.language,
2540
- name: source.name
2541
- };
2542
- }
2543
-
2544
- return {
2545
- address,
2546
- bytecode: compiled.bytecode?.toString('hex'),
2547
- abi: compiled.abi || [],
2548
- contracts: contractsMap,
2549
- metadata: compiled.metadata,
2550
- gasUsed: 0 // ZERO FEES!
2551
- };
2552
- }
2553
-
2554
- async _executeContract(to, functionName, args, options) {
2555
- if (!this.blockchain) throw new Error('Blockchain not available');
2556
- return this.blockchain.executeContract(to, functionName, args, options);
2557
- }
2558
-
2559
- async _crossContractCall(from, to, functionName, args) {
2560
- if (!this.blockchain) throw new Error('Blockchain not available');
2561
- console.log(`[RPC] Cross-call: ${from.substring(0, 10)}... → ${to.substring(0, 10)}...::${functionName}`);
2562
- return this.blockchain.crossContractCall(from, to, functionName, args);
2563
- }
2564
-
2565
- // ═══════════════════════════════════════════════════════════════
2566
- // CROSS-CHAIN SETTLEMENT IMPLEMENTATIONS
2567
- // ═══════════════════════════════════════════════════════════════
2568
-
2569
- _getCrossChainRegistry() {
2570
- // Return the VM's cross-chain registry if available
2571
- if (this.blockchain?.vmExecutor?.vm?.getAllChains) {
2572
- return this.blockchain.vmExecutor.vm.getAllChains();
2573
- }
2574
- // Fallback: return hardcoded registry
2575
- return {
2576
- JAELIS_MAINNET: { id: 4547, name: 'JAELIS Mainnet', type: 'native', status: 'upcoming' },
2577
- JAELIS_TESTNET: { id: 4545, name: 'JAELIS Testnet', type: 'native', status: 'live' },
2578
- ETH_MAINNET: { id: 1, name: 'Ethereum Mainnet', type: 'evm' },
2579
- ETH_SEPOLIA: { id: 11155111, name: 'Ethereum Sepolia', type: 'evm' },
2580
- POLYGON: { id: 137, name: 'Polygon PoS', type: 'evm' },
2581
- ARBITRUM_ONE: { id: 42161, name: 'Arbitrum One', type: 'evm' },
2582
- OPTIMISM: { id: 10, name: 'OP Mainnet', type: 'evm' },
2583
- BASE: { id: 8453, name: 'Base', type: 'evm' },
2584
- AVALANCHE_C: { id: 43114, name: 'Avalanche C-Chain', type: 'evm' },
2585
- BSC: { id: 56, name: 'BNB Smart Chain', type: 'evm' },
2586
- SOLANA_MAINNET: { id: 101, name: 'Solana Mainnet', type: 'svm', cluster: 'mainnet-beta' },
2587
- SOLANA_DEVNET: { id: 102, name: 'Solana Devnet', type: 'svm', cluster: 'devnet' },
2588
- APTOS_MAINNET: { id: 1, name: 'Aptos Mainnet', type: 'move', network: 'aptos' },
2589
- SUI_MAINNET: { id: 1, name: 'Sui Mainnet', type: 'move', network: 'sui' },
2590
- TON_MAINNET: { id: -1, name: 'TON Mainnet', type: 'tvm', workchain: -1 }
2591
- };
2592
- }
2593
-
2594
- _getCrossChainInfo(chainId) {
2595
- const chains = this._getCrossChainRegistry();
2596
- const chain = Object.values(chains).find(c => c.id === chainId);
2597
- return chain || { error: 'Chain not found', chainId };
2598
- }
2599
-
2600
- async _readCrossChainState(chainId, contractAddress, variableName) {
2601
- if (this.blockchain?.vmExecutor?.vm?.readCrossChainState) {
2602
- const value = this.blockchain.vmExecutor.vm.readCrossChainState(chainId, contractAddress, variableName);
2603
- return {
2604
- chainId,
2605
- contractAddress,
2606
- variableName,
2607
- value: value ? '0x' + value.toString('hex') : '0x0',
2608
- settled: value ? true : false
2609
- };
2610
- }
2611
- return { error: 'Cross-chain state not available', chainId, contractAddress, variableName };
2612
- }
2613
-
2614
- async _settleCrossChainState(chainId, contractAddress, variableName, value, proof) {
2615
- if (this.blockchain?.vmExecutor?.vm?.settleExternalState) {
2616
- const valueBuffer = Buffer.from(value.replace('0x', ''), 'hex');
2617
- const result = await this.blockchain.vmExecutor.vm.settleExternalState(
2618
- chainId, contractAddress, variableName, valueBuffer, proof
2619
- );
2620
- console.log(`[RPC] Cross-chain settlement: Chain ${chainId} → ${variableName}`);
2621
- return {
2622
- success: true,
2623
- chainId,
2624
- contractAddress,
2625
- variableName,
2626
- slot: result.slot ? '0x' + result.slot.toString('hex') : null,
2627
- settlement: result.settlement
2628
- };
2629
- }
2630
- return { error: 'Cross-chain settlement not available' };
2631
- }
2632
-
2633
- _getCrossChainStateDiff(chainId1, chainId2, contractAddress, variableNames) {
2634
- if (this.blockchain?.vmExecutor?.vm?.getCrossChainStateDiff) {
2635
- return this.blockchain.vmExecutor.vm.getCrossChainStateDiff(
2636
- chainId1, chainId2, contractAddress, variableNames
2637
- );
2638
- }
2639
- return { error: 'State diff not available', chainId1, chainId2 };
2640
- }
2641
-
2642
- _computeCrossChainSlot(chainId, contractAddress, variableName) {
2643
- if (this.blockchain?.vmExecutor?.vm?.computeCrossChainSlot) {
2644
- const slot = this.blockchain.vmExecutor.vm.computeCrossChainSlot(chainId, contractAddress, variableName);
2645
- return '0x' + slot.toString('hex');
2646
- }
2647
- // Fallback: compute locally
2648
- const crypto = require('crypto');
2649
- const data = Buffer.concat([
2650
- Buffer.alloc(4), // chainId placeholder
2651
- Buffer.from(contractAddress.replace('0x', ''), 'hex'),
2652
- Buffer.from(variableName, 'utf8')
2653
- ]);
2654
- data.writeUInt32LE(chainId, 0);
2655
- return '0x' + crypto.createHash('sha256').update(data).digest('hex');
2656
- }
2657
-
2658
- _getLightClientStatus(chainId) {
2659
- if (this.blockchain?.vmExecutor?.vm?.crossChainManager?.stateStore?.lightClients) {
2660
- const client = this.blockchain.vmExecutor.vm.crossChainManager.stateStore.lightClients.get(chainId);
2661
- if (client) {
2662
- return {
2663
- chainId,
2664
- synced: client.synced || false,
2665
- latestBlock: client.latestBlock || 0,
2666
- stateRoot: client.latestStateRoot ? '0x' + client.latestStateRoot.toString('hex') : null
2667
- };
2668
- }
2669
- }
2670
- return { chainId, synced: false, error: 'Light client not available' };
2671
- }
2672
-
2673
- _getSettlements(chainId, contractAddress) {
2674
- if (this.blockchain?.vmExecutor?.vm?.crossChainManager?.stateStore?.settlementLog) {
2675
- const log = this.blockchain.vmExecutor.vm.crossChainManager.stateStore.settlementLog;
2676
- return log.filter(s => s.chainId === chainId && s.contractAddress === contractAddress);
2677
- }
2678
- return [];
2679
- }
2680
-
2681
- _getCrossChainStats() {
2682
- if (this.blockchain?.vmExecutor?.vm?.crossChainManager?.getStats) {
2683
- return this.blockchain.vmExecutor.vm.crossChainManager.getStats();
2684
- }
2685
- return {
2686
- totalSettlements: 0,
2687
- chainsActive: 0,
2688
- lightClientsRegistered: 0,
2689
- stateSize: 0
2690
- };
2691
- }
2692
-
2693
- // ═══════════════════════════════════════════════════════════════
2694
- // CROSS-CHAIN DEPLOYMENT QUERY IMPLEMENTATIONS
2695
- // ═══════════════════════════════════════════════════════════════
2696
-
2697
- _getCrossChainDeployment(address) {
2698
- if (this.blockchain?.unifiedVM?.deployManager?.getDeployment) {
2699
- const deployment = this.blockchain.unifiedVM.deployManager.getDeployment(address);
2700
- if (deployment) {
2701
- return deployment;
2702
- }
2703
- }
2704
- return { error: 'Deployment not found', address };
2705
- }
2706
-
2707
- _getChainDeployments(chainId) {
2708
- if (this.blockchain?.unifiedVM?.deployManager?.getChainDeployments) {
2709
- return this.blockchain.unifiedVM.deployManager.getChainDeployments(chainId);
2710
- }
2711
- return [];
2712
- }
2713
-
2714
- _getExplorerUrl(chainId, address) {
2715
- if (this.blockchain?.unifiedVM?.deployManager?.getExplorerUrl) {
2716
- const url = this.blockchain.unifiedVM.deployManager.getExplorerUrl(chainId, address);
2717
- return { chainId, address, url };
2718
- }
2719
- return { chainId, address, url: null, error: 'Deploy manager not available' };
2720
- }
2721
-
2722
- _getDeploymentStats() {
2723
- if (this.blockchain?.unifiedVM?.deployManager?.getStats) {
2724
- return this.blockchain.unifiedVM.deployManager.getStats();
2725
- }
2726
- return {
2727
- totalDeployments: 0,
2728
- pendingCount: 0,
2729
- failedCount: 0,
2730
- chainStats: {}
2731
- };
2732
- }
2733
-
2734
- // ═══════════════════════════════════════════════════════════════
2735
- // MULTI-CHAIN SIGNATURE IMPLEMENTATIONS
2736
- // For external chains to accept deployments, user signs per-chain
2737
- // ═══════════════════════════════════════════════════════════════
2738
-
2739
- /**
2740
- * Get signing requests for each target chain
2741
- *
2742
- * Frontend flow:
2743
- * 1. Get these messages
2744
- * 2. For each chain, switch wallet network and sign
2745
- * 3. Collect all signatures
2746
- * 4. Call deployWithSignatures
2747
- */
2748
- _getMultiChainSigningRequests(bytecode, targetChains) {
2749
- if (this.blockchain?.unifiedVM?.deployManager?.getMultiChainSigningRequests) {
2750
- return this.blockchain.unifiedVM.deployManager.getMultiChainSigningRequests(bytecode, targetChains);
2751
- }
2752
-
2753
- // Fallback implementation
2754
- const crypto = require('crypto');
2755
- const bytecodeHash = crypto.createHash('sha256')
2756
- .update(bytecode || '')
2757
- .digest('hex')
2758
- .substring(0, 16);
2759
-
2760
- const timestamp = Date.now();
2761
- const requests = [];
2762
-
2763
- for (const chainId of (targetChains || [])) {
2764
- const message = `JAELIS Cross-Chain Deploy Authorization
2765
-
2766
- Chain ID: ${chainId}
2767
- Bytecode Hash: ${bytecodeHash}
2768
- Timestamp: ${timestamp}
2769
-
2770
- I authorize JAELIS to deploy this contract.
2771
- ZERO FEES - No gas cost to me.`;
2772
-
2773
- requests.push({
2774
- chainId,
2775
- message,
2776
- messageHash: '0x' + crypto.createHash('sha256')
2777
- .update(`\x19Ethereum Signed Message:\n${message.length}${message}`)
2778
- .digest('hex'),
2779
- switchParams: { chainId: '0x' + chainId.toString(16) }
2780
- });
2781
- }
2782
-
2783
- return { requests, bytecodeHash, timestamp, totalChains: targetChains?.length || 0 };
2784
- }
2785
-
2786
- /**
2787
- * Deploy with per-chain wallet signatures
2788
- *
2789
- * Each chain that needs to recognize the deployment
2790
- * requires its own signature from the deployer's wallet.
2791
- */
2792
- async _deployWithMultiChainSignatures(params) {
2793
- const { from, bytecode, abi, targetChains, chainSignatures, metadata } = params;
2794
-
2795
- console.log(`[RPC] Multi-chain signature deployment from ${from}`);
2796
- console.log(`[RPC] Target chains: ${targetChains?.length || 0}`);
2797
- console.log(`[RPC] Signatures provided: ${Object.keys(chainSignatures || {}).length}`);
2798
-
2799
- if (this.blockchain?.unifiedVM?.deployManager?.deployWithMultiChainSignatures) {
2800
- return await this.blockchain.unifiedVM.deployManager.deployWithMultiChainSignatures({
2801
- from,
2802
- bytecode,
2803
- abi,
2804
- targetChains,
2805
- chainSignatures,
2806
- metadata
2807
- });
2808
- }
2809
-
2810
- // Fallback: regular deployment without chain signatures
2811
- console.log(`[RPC] Fallback: Deploy manager not available, using standard flow`);
2812
- return {
2813
- error: 'Multi-chain signatures not supported',
2814
- message: 'Deploy manager not initialized'
2815
- };
2816
- }
2817
-
2818
- async stop() {
2819
- if (this.server) {
2820
- this.server.close();
2821
- }
2822
- }
2823
- }
2824
-
2825
- class EmbeddedStorage {
2826
- constructor() {
2827
- this.data = new Map();
2828
- }
2829
-
2830
- async get(key) {
2831
- return this.data.get(key);
2832
- }
2833
-
2834
- async put(key, value) {
2835
- this.data.set(key, value);
2836
- }
2837
- }
2838
-
2839
- // ============================================================
2840
- // EXPORTS
2841
- // ============================================================
44
+ // Version
45
+ const VERSION = '2.0.0';
2842
46
 
2843
47
  module.exports = {
2844
48
  JaelisNode,
2845
- BootstrapNode,
2846
- EmbeddedBlockchain,
2847
- EmbeddedNetwork,
2848
- EmbeddedRpcServer,
2849
- EmbeddedStorage,
2850
-
2851
- // Wallet configuration (like Geth's keystore)
2852
- NodeWalletConfig,
2853
-
2854
- // Multi-chain address utilities
2855
- ADDRESS_FORMATS,
2856
- detectAddressType,
2857
- validateAddress,
2858
- toCanonicalAddress,
2859
-
2860
- // Convenience factory
2861
- createNode: (options) => new JaelisNode(options),
2862
- createBootstrap: (options) => new BootstrapNode(options)
49
+ SyncManager,
50
+ RPCServer,
51
+ Storage,
52
+ NETWORKS,
53
+ VERSION
2863
54
  };