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.
- package/README.md +156 -439
- package/bin/jaelis-node.js +83 -504
- package/lib/index.js +31 -2840
- package/lib/node.js +309 -0
- package/lib/rpc.js +315 -0
- package/lib/storage.js +198 -0
- package/lib/sync.js +366 -0
- package/package.json +19 -53
- package/config/default.json +0 -74
- package/config/mainnet.json +0 -30
- package/config/testnet.json +0 -26
- package/lib/JAELIS-VM/lib/adapters/evm-adapter.js +0 -454
- package/lib/JAELIS-VM/lib/adapters/index.js +0 -411
- package/lib/JAELIS-VM/lib/adapters/svm-adapter.js +0 -457
- package/lib/JAELIS-VM/lib/compiler/jir-compiler.js +0 -1097
- package/lib/JAELIS-VM/lib/execution/engine.js +0 -1183
- package/lib/JAELIS-VM/lib/index.js +0 -440
- package/lib/JAELIS-VM/lib/integration/jaelis-integration.js +0 -543
- package/lib/JAELIS-VM/lib/serialization/serializer.js +0 -819
- package/lib/JAELIS-VM/lib/state/state-manager.js +0 -1116
- package/lib/JAELIS-VM/lib/translator/bytecode-translator.js +0 -1222
- package/lib/JAELIS-VM/lib/unified/cross-chain-deploy.js +0 -1678
- package/lib/JAELIS-VM/lib/unified/cross-chain-state.js +0 -836
- package/lib/JAELIS-VM/lib/unified/dynamic-contracts.js +0 -1127
- package/lib/JAELIS-VM/lib/unified/index.js +0 -456
- package/lib/JAELIS-VM/lib/unified/jaelis-abi.js +0 -1150
- package/lib/JAELIS-VM/lib/unified/unified-compiler.js +0 -1350
- package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds +0 -12
- package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/.bin/download-cbor-prebuilds.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds +0 -12
- package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/.bin/download-msgpackr-prebuilds.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages +0 -12
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional +0 -12
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-optional.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test +0 -12
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages-test.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/.bin/node-gyp-build-optional-packages.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/.package-lock.json +0 -127
- package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/README.md +0 -1
- package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/index.js +0 -0
- package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/node.abi115.node +0 -0
- package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/node.napi.node +0 -0
- package/lib/JAELIS-VM/node_modules/@cbor-extract/cbor-extract-win32-x64/package.json +0 -17
- package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/README.md +0 -1
- package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/index.js +0 -0
- package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/node.abi115.node +0 -0
- package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/node.napi.node +0 -0
- package/lib/JAELIS-VM/node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/package.json +0 -17
- package/lib/JAELIS-VM/node_modules/cbor-extract/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/cbor-extract/README.md +0 -5
- package/lib/JAELIS-VM/node_modules/cbor-extract/bin/download-prebuilds.js +0 -11
- package/lib/JAELIS-VM/node_modules/cbor-extract/binding.gyp +0 -60
- package/lib/JAELIS-VM/node_modules/cbor-extract/index.js +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-extract/package.json +0 -50
- package/lib/JAELIS-VM/node_modules/cbor-extract/src/extract.cpp +0 -198
- package/lib/JAELIS-VM/node_modules/cbor-x/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/cbor-x/README.md +0 -380
- package/lib/JAELIS-VM/node_modules/cbor-x/SECURITY.md +0 -11
- package/lib/JAELIS-VM/node_modules/cbor-x/benchmark.md +0 -73
- package/lib/JAELIS-VM/node_modules/cbor-x/browser.js +0 -11
- package/lib/JAELIS-VM/node_modules/cbor-x/decode.d.ts +0 -2
- package/lib/JAELIS-VM/node_modules/cbor-x/decode.js +0 -1300
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/decode-no-eval.cjs +0 -1244
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/decode-no-eval.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.cjs +0 -2509
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.min.js +0 -2
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index-no-eval.min.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.js +0 -2508
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.min.js +0 -2
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/index.min.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/node.cjs +0 -2629
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/node.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/test.js +0 -3343
- package/lib/JAELIS-VM/node_modules/cbor-x/dist/test.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/encode.d.ts +0 -1
- package/lib/JAELIS-VM/node_modules/cbor-x/encode.js +0 -1231
- package/lib/JAELIS-VM/node_modules/cbor-x/index.d.ts +0 -79
- package/lib/JAELIS-VM/node_modules/cbor-x/index.js +0 -3
- package/lib/JAELIS-VM/node_modules/cbor-x/iterators.js +0 -85
- package/lib/JAELIS-VM/node_modules/cbor-x/node-index.js +0 -24
- package/lib/JAELIS-VM/node_modules/cbor-x/package.json +0 -94
- package/lib/JAELIS-VM/node_modules/cbor-x/rollup.config.js +0 -88
- package/lib/JAELIS-VM/node_modules/cbor-x/stream.js +0 -61
- package/lib/JAELIS-VM/node_modules/cbor-x/webpack.config.js +0 -19
- package/lib/JAELIS-VM/node_modules/detect-libc/LICENSE +0 -201
- package/lib/JAELIS-VM/node_modules/detect-libc/README.md +0 -163
- package/lib/JAELIS-VM/node_modules/detect-libc/index.d.ts +0 -14
- package/lib/JAELIS-VM/node_modules/detect-libc/lib/detect-libc.js +0 -313
- package/lib/JAELIS-VM/node_modules/detect-libc/lib/elf.js +0 -39
- package/lib/JAELIS-VM/node_modules/detect-libc/lib/filesystem.js +0 -51
- package/lib/JAELIS-VM/node_modules/detect-libc/lib/process.js +0 -24
- package/lib/JAELIS-VM/node_modules/detect-libc/package.json +0 -44
- package/lib/JAELIS-VM/node_modules/msgpackr/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/msgpackr/README.md +0 -372
- package/lib/JAELIS-VM/node_modules/msgpackr/SECURITY.md +0 -11
- package/lib/JAELIS-VM/node_modules/msgpackr/benchmark.md +0 -67
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.cjs +0 -2407
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.min.js +0 -2
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index-no-eval.min.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.js +0 -2406
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.min.js +0 -2
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/index.min.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/node.cjs +0 -3320
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/node.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/test.js +0 -4540
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/test.js.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/unpack-no-eval.cjs +0 -1250
- package/lib/JAELIS-VM/node_modules/msgpackr/dist/unpack-no-eval.cjs.map +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/index.d.cts +0 -91
- package/lib/JAELIS-VM/node_modules/msgpackr/index.d.ts +0 -91
- package/lib/JAELIS-VM/node_modules/msgpackr/index.js +0 -5
- package/lib/JAELIS-VM/node_modules/msgpackr/iterators.js +0 -87
- package/lib/JAELIS-VM/node_modules/msgpackr/node-index.js +0 -25
- package/lib/JAELIS-VM/node_modules/msgpackr/pack.d.cts +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/pack.d.ts +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr/pack.js +0 -1141
- package/lib/JAELIS-VM/node_modules/msgpackr/package.json +0 -104
- package/lib/JAELIS-VM/node_modules/msgpackr/rollup.config.js +0 -88
- package/lib/JAELIS-VM/node_modules/msgpackr/stream.js +0 -57
- package/lib/JAELIS-VM/node_modules/msgpackr/struct.js +0 -815
- package/lib/JAELIS-VM/node_modules/msgpackr/test-worker.js +0 -3
- package/lib/JAELIS-VM/node_modules/msgpackr/unpack.d.cts +0 -2
- package/lib/JAELIS-VM/node_modules/msgpackr/unpack.d.ts +0 -2
- package/lib/JAELIS-VM/node_modules/msgpackr/unpack.js +0 -1221
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/README.md +0 -5
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/bin/download-prebuilds.js +0 -13
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/binding.gyp +0 -63
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/index.js +0 -1
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages +0 -12
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional +0 -12
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-optional.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test +0 -12
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages-test.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages.cmd +0 -17
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/.bin/node-gyp-build-optional-packages.ps1 +0 -28
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/README.md +0 -58
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/bin.js +0 -82
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/build-test.js +0 -19
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/index.js +0 -6
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/node-gyp-build.js +0 -236
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/optional.js +0 -7
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages/package.json +0 -32
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/package.json +0 -50
- package/lib/JAELIS-VM/node_modules/msgpackr-extract/src/extract.cpp +0 -274
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/LICENSE +0 -21
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/README.md +0 -58
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/bin.js +0 -77
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/build-test.js +0 -19
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/index.js +0 -224
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/optional.js +0 -7
- package/lib/JAELIS-VM/node_modules/node-gyp-build-optional-packages/package.json +0 -32
- package/lib/JAELIS-VM/package-lock.json +0 -284
- package/lib/JAELIS-VM/package.json +0 -38
- package/lib/JAELIS-VM/test/comprehensive.test.js +0 -267
- package/lib/JAELIS-VM/test/cross-chain-test.js +0 -470
- package/lib/JAELIS-VM/test/unified-vm-test.js +0 -459
- package/lib/JAELIS-VM/test/unified.test.js +0 -166
- package/lib/JAELIS-VM/test/vm.test.js +0 -599
- package/lib/settlement-server.js +0 -999
- package/lib/vm/index.js +0 -397
package/lib/index.js
CHANGED
|
@@ -1,2863 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JAELIS Node
|
|
3
|
-
* Core node implementation for the JAELIS blockchain
|
|
2
|
+
* JAELIS External Node
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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
|
-
//
|
|
336
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
363
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
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
|
};
|