jaelis-node 1.5.0 → 1.8.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 +64 -0
- package/bin/jaelis-node.js +3 -3
- package/config/default.json +1 -3
- package/config/testnet.json +1 -3
- package/lib/JAELIS-VM/lib/unified/cross-chain-deploy.js +1678 -0
- package/lib/JAELIS-VM/lib/unified/index.js +79 -1
- package/lib/index.js +779 -37
- package/lib/settlement-server.js +999 -0
- package/package.json +12 -11
package/lib/index.js
CHANGED
|
@@ -485,6 +485,9 @@ class JaelisNode extends EventEmitter {
|
|
|
485
485
|
await this._initRpcServer();
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
+
// Start Settlement Server for cross-chain deployments
|
|
489
|
+
await this._initSettlementServer();
|
|
490
|
+
|
|
488
491
|
this.isRunning = true;
|
|
489
492
|
this.startTime = Date.now();
|
|
490
493
|
|
|
@@ -522,6 +525,11 @@ class JaelisNode extends EventEmitter {
|
|
|
522
525
|
// Stop heartbeat first
|
|
523
526
|
this._stopHeartbeat();
|
|
524
527
|
|
|
528
|
+
// Stop Settlement server
|
|
529
|
+
if (this.settlementServer) {
|
|
530
|
+
await this._stopSettlementServer();
|
|
531
|
+
}
|
|
532
|
+
|
|
525
533
|
// Stop RPC server
|
|
526
534
|
if (this.rpcServer) {
|
|
527
535
|
await this._stopRpcServer();
|
|
@@ -623,7 +631,8 @@ class JaelisNode extends EventEmitter {
|
|
|
623
631
|
console.log('[JAELIS] Initializing RPC server...');
|
|
624
632
|
|
|
625
633
|
if (this.JaelisRpcServer) {
|
|
626
|
-
|
|
634
|
+
// Pass network reference for sync status reporting
|
|
635
|
+
this.rpcServer = new this.JaelisRpcServer(this.blockchain, this.options.rpcPort, this.network);
|
|
627
636
|
|
|
628
637
|
if (this.rpcServer.start) {
|
|
629
638
|
await this.rpcServer.start();
|
|
@@ -635,6 +644,52 @@ class JaelisNode extends EventEmitter {
|
|
|
635
644
|
console.log(`[JAELIS] RPC server started on http://${this.options.rpcHost}:${this.options.rpcPort}`);
|
|
636
645
|
}
|
|
637
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Initialize Settlement Server for cross-chain deployments
|
|
649
|
+
*
|
|
650
|
+
* The Settlement Server handles propagating JAELIS contract deployments
|
|
651
|
+
* to external chains. NO GAS - NO FEES - NO SANDBOX - NO WALLS!
|
|
652
|
+
*/
|
|
653
|
+
async _initSettlementServer() {
|
|
654
|
+
console.log('[JAELIS] Initializing Settlement Server...');
|
|
655
|
+
|
|
656
|
+
try {
|
|
657
|
+
// Load the settlement server module
|
|
658
|
+
const settlementPath = path.join(__dirname, 'settlement-server.js');
|
|
659
|
+
|
|
660
|
+
if (fs.existsSync(settlementPath)) {
|
|
661
|
+
const { SettlementServer } = require(settlementPath);
|
|
662
|
+
|
|
663
|
+
this.settlementServer = new SettlementServer({
|
|
664
|
+
port: this.options.settlementPort || 3847,
|
|
665
|
+
host: this.options.rpcHost || '0.0.0.0'
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
await this.settlementServer.start();
|
|
669
|
+
|
|
670
|
+
console.log(`[JAELIS] Settlement Server started on port ${this.options.settlementPort || 3847}`);
|
|
671
|
+
console.log('[JAELIS] Cross-chain deployments: ENABLED');
|
|
672
|
+
console.log('[JAELIS] NO GAS - NO FEES - NO SANDBOX - NO WALLS!');
|
|
673
|
+
} else {
|
|
674
|
+
console.log('[JAELIS] Settlement Server not found, cross-chain disabled');
|
|
675
|
+
}
|
|
676
|
+
} catch (error) {
|
|
677
|
+
// Don't fail node startup if settlement server fails
|
|
678
|
+
console.error('[JAELIS] Settlement Server error:', error.message);
|
|
679
|
+
console.log('[JAELIS] Continuing without cross-chain support...');
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Stop Settlement Server
|
|
685
|
+
*/
|
|
686
|
+
async _stopSettlementServer() {
|
|
687
|
+
if (this.settlementServer && this.settlementServer.stop) {
|
|
688
|
+
await this.settlementServer.stop();
|
|
689
|
+
console.log('[JAELIS] Settlement Server stopped');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
638
693
|
/**
|
|
639
694
|
* Stop RPC server
|
|
640
695
|
*/
|
|
@@ -707,7 +762,8 @@ class JaelisNode extends EventEmitter {
|
|
|
707
762
|
method: 'POST',
|
|
708
763
|
headers: {
|
|
709
764
|
'Content-Type': 'application/json',
|
|
710
|
-
'Content-Length': Buffer.byteLength(data)
|
|
765
|
+
'Content-Length': Buffer.byteLength(data),
|
|
766
|
+
'User-Agent': 'JAELIS-Node/1.7.0' // Identifies us as a node - gets priority treatment
|
|
711
767
|
}
|
|
712
768
|
};
|
|
713
769
|
|
|
@@ -770,7 +826,8 @@ class JaelisNode extends EventEmitter {
|
|
|
770
826
|
method: 'POST',
|
|
771
827
|
headers: {
|
|
772
828
|
'Content-Type': 'application/json',
|
|
773
|
-
'Content-Length': Buffer.byteLength(data)
|
|
829
|
+
'Content-Length': Buffer.byteLength(data),
|
|
830
|
+
'User-Agent': 'JAELIS-Node/1.7.0' // Identifies us as a node - gets priority treatment
|
|
774
831
|
}
|
|
775
832
|
};
|
|
776
833
|
|
|
@@ -1042,6 +1099,105 @@ class EmbeddedBlockchain {
|
|
|
1042
1099
|
return this.vmExecutor?.getAllContracts?.() || [];
|
|
1043
1100
|
}
|
|
1044
1101
|
|
|
1102
|
+
/**
|
|
1103
|
+
* Add a block received from sync (RPC fallback or P2P)
|
|
1104
|
+
* Validates the block before adding to chain
|
|
1105
|
+
*/
|
|
1106
|
+
async addBlock(block) {
|
|
1107
|
+
if (!block || block.number === undefined) {
|
|
1108
|
+
throw new Error('Invalid block');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Normalize block number - could be hex string (0x1) or decimal
|
|
1112
|
+
let blockNumber = block.number;
|
|
1113
|
+
if (typeof blockNumber === 'string') {
|
|
1114
|
+
if (blockNumber.startsWith('0x')) {
|
|
1115
|
+
blockNumber = parseInt(blockNumber, 16);
|
|
1116
|
+
} else {
|
|
1117
|
+
blockNumber = parseInt(blockNumber, 10);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
const expectedNumber = this.chain.length;
|
|
1122
|
+
if (blockNumber !== expectedNumber) {
|
|
1123
|
+
// Skip if we already have this block or it's out of order
|
|
1124
|
+
if (blockNumber < expectedNumber) {
|
|
1125
|
+
return { status: 'skipped', reason: 'already_have' };
|
|
1126
|
+
}
|
|
1127
|
+
throw new Error(`Block out of order: expected ${expectedNumber}, got ${blockNumber}`);
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Store normalized block number for consistency
|
|
1131
|
+
block.number = blockNumber;
|
|
1132
|
+
|
|
1133
|
+
// Validate parent hash (skip for first sync - genesis hash may differ)
|
|
1134
|
+
// Like Ethereum "fast sync" - we trust the RPC source initially
|
|
1135
|
+
const parentBlock = this.getLatestBlock();
|
|
1136
|
+
if (blockNumber > 1 && block.parentHash && parentBlock.hash) {
|
|
1137
|
+
// Only validate parent hash for blocks after genesis
|
|
1138
|
+
// During initial sync, parent hashes are validated by the remote node
|
|
1139
|
+
if (block.parentHash !== parentBlock.hash) {
|
|
1140
|
+
// Check if this is initial sync (chain is mostly empty)
|
|
1141
|
+
if (this.chain.length > 10) {
|
|
1142
|
+
throw new Error('Invalid parent hash - possible chain reorg');
|
|
1143
|
+
}
|
|
1144
|
+
// During initial sync, log but continue
|
|
1145
|
+
console.log(`[SYNC] Accepting block ${blockNumber} (trusting remote during initial sync)`);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Add block to chain
|
|
1150
|
+
this.chain.push(block);
|
|
1151
|
+
|
|
1152
|
+
// Process transactions in the block
|
|
1153
|
+
if (block.transactions && Array.isArray(block.transactions)) {
|
|
1154
|
+
for (const tx of block.transactions) {
|
|
1155
|
+
// Update account states from transactions
|
|
1156
|
+
if (tx.from && tx.to && tx.value) {
|
|
1157
|
+
const value = BigInt(tx.value || '0');
|
|
1158
|
+
const fromBalance = BigInt(this.state.get(tx.from)?.balance || '0');
|
|
1159
|
+
const toBalance = BigInt(this.state.get(tx.to)?.balance || '0');
|
|
1160
|
+
|
|
1161
|
+
// Debit sender, credit receiver
|
|
1162
|
+
this.state.set(tx.from, {
|
|
1163
|
+
...this.state.get(tx.from),
|
|
1164
|
+
balance: (fromBalance - value).toString(),
|
|
1165
|
+
nonce: (parseInt(this.state.get(tx.from)?.nonce || '0') + 1).toString()
|
|
1166
|
+
});
|
|
1167
|
+
this.state.set(tx.to, {
|
|
1168
|
+
...this.state.get(tx.to),
|
|
1169
|
+
balance: (toBalance + value).toString()
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
console.log(`[BLOCKCHAIN] Synced block #${block.number} (${block.transactions?.length || 0} txs)`);
|
|
1176
|
+
|
|
1177
|
+
return { status: 'added', blockNumber: block.number };
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/**
|
|
1181
|
+
* Get sync status (jaelis_syncing compatible)
|
|
1182
|
+
*/
|
|
1183
|
+
getSyncStatus(remoteHeight) {
|
|
1184
|
+
const localHeight = this.getHeight();
|
|
1185
|
+
const isSyncing = localHeight < remoteHeight;
|
|
1186
|
+
|
|
1187
|
+
if (!isSyncing) {
|
|
1188
|
+
return false; // Fully synced
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return {
|
|
1192
|
+
startingBlock: '0x0',
|
|
1193
|
+
currentBlock: '0x' + localHeight.toString(16),
|
|
1194
|
+
highestBlock: '0x' + remoteHeight.toString(16),
|
|
1195
|
+
syncedBlocks: localHeight,
|
|
1196
|
+
remainingBlocks: remoteHeight - localHeight,
|
|
1197
|
+
percentComplete: ((localHeight / remoteHeight) * 100).toFixed(2) + '%'
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1045
1201
|
async save() {
|
|
1046
1202
|
// Save blockchain state to disk
|
|
1047
1203
|
// TODO: Implement persistent storage
|
|
@@ -1084,9 +1240,10 @@ class EmbeddedNetwork {
|
|
|
1084
1240
|
const { tcp } = await import('@libp2p/tcp');
|
|
1085
1241
|
const { noise } = await import('@chainsafe/libp2p-noise');
|
|
1086
1242
|
const { yamux } = await import('@chainsafe/libp2p-yamux');
|
|
1087
|
-
const { bootstrap } = await import('@libp2p/bootstrap');
|
|
1088
1243
|
const { identify } = await import('@libp2p/identify');
|
|
1089
1244
|
|
|
1245
|
+
// Create libp2p node - JAELIS user node
|
|
1246
|
+
// Like Ethereum: start listening, bootstrap nodes come later via DHT
|
|
1090
1247
|
const config = {
|
|
1091
1248
|
addresses: {
|
|
1092
1249
|
listen: [`/ip4/${this.host}/tcp/${this.port}`]
|
|
@@ -1099,17 +1256,11 @@ class EmbeddedNetwork {
|
|
|
1099
1256
|
}
|
|
1100
1257
|
};
|
|
1101
1258
|
|
|
1102
|
-
// Add bootstrap if we have nodes
|
|
1103
|
-
if (this.bootstrapNodes.length > 0) {
|
|
1104
|
-
const validBootstrap = this.bootstrapNodes.filter(n => n.includes('/p2p/'));
|
|
1105
|
-
if (validBootstrap.length > 0) {
|
|
1106
|
-
config.peerDiscovery = [bootstrap({ list: validBootstrap })];
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
1259
|
this.libp2p = await createLibp2p(config);
|
|
1111
1260
|
await this.libp2p.start();
|
|
1112
1261
|
|
|
1262
|
+
console.log(`[P2P] Node started with ID: ${this.libp2p.peerId.toString().slice(0, 16)}...`);
|
|
1263
|
+
|
|
1113
1264
|
// Track peers
|
|
1114
1265
|
this.libp2p.addEventListener('peer:connect', (evt) => {
|
|
1115
1266
|
const peerId = evt.detail.toString();
|
|
@@ -1122,33 +1273,131 @@ class EmbeddedNetwork {
|
|
|
1122
1273
|
this.peers.delete(peerId);
|
|
1123
1274
|
console.log(`[P2P] Peer disconnected (${this.peers.size} remaining)`);
|
|
1124
1275
|
});
|
|
1276
|
+
|
|
1277
|
+
// Try to dial bootstrap nodes directly (without peer ID)
|
|
1278
|
+
// Like Ethereum: we connect via TCP, then learn peer ID during handshake
|
|
1279
|
+
for (const addr of this.bootstrapNodes) {
|
|
1280
|
+
try {
|
|
1281
|
+
const { multiaddr } = await import('@multiformats/multiaddr');
|
|
1282
|
+
const ma = multiaddr(addr);
|
|
1283
|
+
await this.libp2p.dial(ma);
|
|
1284
|
+
console.log(`[P2P] Connected to bootstrap: ${addr}`);
|
|
1285
|
+
} catch (err) {
|
|
1286
|
+
// Bootstrap unavailable - this is OK, we'll use RPC fallback
|
|
1287
|
+
console.log(`[P2P] Bootstrap ${addr.slice(0, 30)}... unavailable`);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1125
1290
|
}
|
|
1126
1291
|
|
|
1127
1292
|
async _startRPCFallback() {
|
|
1128
|
-
//
|
|
1129
|
-
|
|
1130
|
-
|
|
1293
|
+
// Track sync state
|
|
1294
|
+
this.syncState = {
|
|
1295
|
+
localHeight: 0,
|
|
1296
|
+
remoteHeight: 0,
|
|
1297
|
+
syncing: false,
|
|
1298
|
+
lastSync: null,
|
|
1299
|
+
blocksDownloaded: 0
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
// Helper to make RPC calls to remote node
|
|
1303
|
+
const rpcCall = async (method, params = []) => {
|
|
1304
|
+
return new Promise((resolve, reject) => {
|
|
1131
1305
|
const https = require('https');
|
|
1132
1306
|
const http = require('http');
|
|
1133
|
-
const
|
|
1307
|
+
const url = new URL(this.remoteRpc);
|
|
1308
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
1134
1309
|
|
|
1135
1310
|
const data = JSON.stringify({
|
|
1136
1311
|
jsonrpc: '2.0',
|
|
1137
|
-
method
|
|
1138
|
-
params
|
|
1139
|
-
id:
|
|
1312
|
+
method,
|
|
1313
|
+
params,
|
|
1314
|
+
id: Date.now()
|
|
1140
1315
|
});
|
|
1141
1316
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1317
|
+
const req = protocol.request({
|
|
1318
|
+
hostname: url.hostname,
|
|
1319
|
+
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
|
1320
|
+
path: url.pathname || '/',
|
|
1321
|
+
method: 'POST',
|
|
1322
|
+
headers: {
|
|
1323
|
+
'Content-Type': 'application/json',
|
|
1324
|
+
'Content-Length': Buffer.byteLength(data),
|
|
1325
|
+
'User-Agent': 'JAELIS-Node/1.7.0' // Node sync traffic - exempt from rate limits
|
|
1326
|
+
}
|
|
1327
|
+
}, (res) => {
|
|
1328
|
+
let body = '';
|
|
1329
|
+
res.on('data', chunk => body += chunk);
|
|
1330
|
+
res.on('end', () => {
|
|
1331
|
+
try {
|
|
1332
|
+
const json = JSON.parse(body);
|
|
1333
|
+
if (json.error) reject(new Error(json.error.message));
|
|
1334
|
+
else resolve(json.result);
|
|
1335
|
+
} catch (e) { reject(e); }
|
|
1336
|
+
});
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
req.on('error', reject);
|
|
1340
|
+
req.write(data);
|
|
1341
|
+
req.end();
|
|
1342
|
+
});
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
// Sync blocks from remote RPC using JAELIS NATIVE methods
|
|
1346
|
+
const syncFromRPC = async () => {
|
|
1347
|
+
if (this.syncState.syncing) return; // Already syncing
|
|
1348
|
+
this.syncState.syncing = true;
|
|
1349
|
+
|
|
1350
|
+
try {
|
|
1351
|
+
// 1. Get remote block height using NATIVE jaelis_blockNumber
|
|
1352
|
+
const remoteHeightHex = await rpcCall('jaelis_blockNumber');
|
|
1353
|
+
this.syncState.remoteHeight = parseInt(remoteHeightHex, 16);
|
|
1354
|
+
|
|
1355
|
+
// 2. Get local block height
|
|
1356
|
+
this.syncState.localHeight = this.blockchain?.getHeight?.() || 0;
|
|
1357
|
+
|
|
1358
|
+
// 3. Sync missing blocks (batch of 10 at a time)
|
|
1359
|
+
const blocksToSync = Math.min(10, this.syncState.remoteHeight - this.syncState.localHeight);
|
|
1360
|
+
|
|
1361
|
+
if (blocksToSync > 0) {
|
|
1362
|
+
console.log(`[SYNC] Syncing blocks ${this.syncState.localHeight + 1} to ${this.syncState.localHeight + blocksToSync} (remote: ${this.syncState.remoteHeight})`);
|
|
1363
|
+
|
|
1364
|
+
for (let i = 1; i <= blocksToSync; i++) {
|
|
1365
|
+
const blockNum = this.syncState.localHeight + i;
|
|
1366
|
+
const blockHex = '0x' + blockNum.toString(16);
|
|
1367
|
+
|
|
1368
|
+
// Fetch full block with transactions using NATIVE jaelis_getBlockByNumber
|
|
1369
|
+
// This returns JAELIS-native fields (lodeUsed, consensus: PoEC, etc.)
|
|
1370
|
+
const block = await rpcCall('jaelis_getBlockByNumber', [blockHex, true]);
|
|
1371
|
+
|
|
1372
|
+
if (block) {
|
|
1373
|
+
// Store block in local blockchain
|
|
1374
|
+
if (this.blockchain?.addBlock) {
|
|
1375
|
+
await this.blockchain.addBlock(block);
|
|
1376
|
+
}
|
|
1377
|
+
this.syncState.blocksDownloaded++;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
this.syncState.localHeight += blocksToSync;
|
|
1382
|
+
console.log(`[SYNC] Downloaded ${blocksToSync} blocks. Local height: ${this.syncState.localHeight}`);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
this.syncState.lastSync = Date.now();
|
|
1386
|
+
this.emit('sync', this.syncState);
|
|
1387
|
+
|
|
1144
1388
|
} catch (err) {
|
|
1145
|
-
|
|
1389
|
+
console.error('[SYNC] Error:', err.message);
|
|
1390
|
+
} finally {
|
|
1391
|
+
this.syncState.syncing = false;
|
|
1146
1392
|
}
|
|
1147
1393
|
};
|
|
1148
1394
|
|
|
1149
|
-
//
|
|
1150
|
-
|
|
1395
|
+
// Initial sync
|
|
1396
|
+
console.log('[SYNC] Starting RPC fallback sync from', this.remoteRpc);
|
|
1151
1397
|
await syncFromRPC();
|
|
1398
|
+
|
|
1399
|
+
// Continue syncing every 3 seconds (JAELIS has 3-second blocks)
|
|
1400
|
+
this.syncInterval = setInterval(syncFromRPC, 3000);
|
|
1152
1401
|
}
|
|
1153
1402
|
|
|
1154
1403
|
async stop() {
|
|
@@ -1172,9 +1421,10 @@ class EmbeddedNetwork {
|
|
|
1172
1421
|
}
|
|
1173
1422
|
|
|
1174
1423
|
class EmbeddedRpcServer {
|
|
1175
|
-
constructor(blockchain, port) {
|
|
1424
|
+
constructor(blockchain, port, network = null) {
|
|
1176
1425
|
this.blockchain = blockchain;
|
|
1177
1426
|
this.port = port;
|
|
1427
|
+
this.network = network; // Reference to EmbeddedNetwork for sync status
|
|
1178
1428
|
this.app = null;
|
|
1179
1429
|
}
|
|
1180
1430
|
|
|
@@ -1242,6 +1492,36 @@ class EmbeddedRpcServer {
|
|
|
1242
1492
|
case 'jaelis_blockNumber':
|
|
1243
1493
|
return '0x' + (this.blockchain?.getHeight?.() || 0).toString(16);
|
|
1244
1494
|
|
|
1495
|
+
case 'jaelis_syncing':
|
|
1496
|
+
// Return sync status - false if fully synced, object if syncing
|
|
1497
|
+
if (this.network?.syncState) {
|
|
1498
|
+
return this.blockchain?.getSyncStatus?.(this.network.syncState.remoteHeight) || false;
|
|
1499
|
+
}
|
|
1500
|
+
return false;
|
|
1501
|
+
|
|
1502
|
+
case 'jaelis_getBlockByNumber':
|
|
1503
|
+
// Native JAELIS method - returns JAELIS-native block fields
|
|
1504
|
+
const jaelisBlockNum = typeof params[0] === 'string' && params[0].startsWith('0x')
|
|
1505
|
+
? parseInt(params[0], 16)
|
|
1506
|
+
: params[0];
|
|
1507
|
+
const jaelisBlock = this.blockchain?.getBlock?.(jaelisBlockNum);
|
|
1508
|
+
if (jaelisBlock) {
|
|
1509
|
+
// Add JAELIS-specific fields
|
|
1510
|
+
return {
|
|
1511
|
+
...jaelisBlock,
|
|
1512
|
+
lodeUsed: '0x0', // ZERO FEES
|
|
1513
|
+
consensus: 'PoEC', // Proof of Entropy Contribution
|
|
1514
|
+
jaelisVersion: '1.0.0'
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
return null;
|
|
1518
|
+
|
|
1519
|
+
case 'jaelis_getBlockByHash':
|
|
1520
|
+
// Find block by hash
|
|
1521
|
+
const targetHash = params[0];
|
|
1522
|
+
const foundBlock = this.blockchain?.chain?.find(b => b.hash === targetHash);
|
|
1523
|
+
return foundBlock || null;
|
|
1524
|
+
|
|
1245
1525
|
// ═══════════════════════════════════════════════════════════════
|
|
1246
1526
|
// LAYER 2: ETHEREUM COMPATIBILITY (PROXIES → jaelis_*)
|
|
1247
1527
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -1265,9 +1545,35 @@ class EmbeddedRpcServer {
|
|
|
1265
1545
|
case 'eth_getTransactionReceipt':
|
|
1266
1546
|
return this.blockchain?.vmExecutor?.getReceipt?.(params[0]) || null;
|
|
1267
1547
|
|
|
1548
|
+
case 'eth_getCode':
|
|
1549
|
+
// CRITICAL: This is how block explorers detect contracts!
|
|
1550
|
+
// Returns bytecode at address, or '0x' for EOAs
|
|
1551
|
+
return this._getCode(params[0], params[1] || 'latest');
|
|
1552
|
+
|
|
1553
|
+
case 'eth_getBalance':
|
|
1554
|
+
return this._getBalance(params[0], params[1] || 'latest');
|
|
1555
|
+
|
|
1556
|
+
case 'eth_getTransactionCount':
|
|
1557
|
+
return this._getTransactionCount(params[0], params[1] || 'latest');
|
|
1558
|
+
|
|
1559
|
+
case 'eth_getStorageAt':
|
|
1560
|
+
return this._getStorageAt(params[0], params[1], params[2] || 'latest');
|
|
1561
|
+
|
|
1562
|
+
case 'eth_estimateGas':
|
|
1563
|
+
// JAELIS has ZERO GAS - always return minimal
|
|
1564
|
+
return '0x5208'; // 21000 in hex
|
|
1565
|
+
|
|
1566
|
+
case 'eth_gasPrice':
|
|
1567
|
+
// JAELIS has ZERO GAS
|
|
1568
|
+
return '0x0';
|
|
1569
|
+
|
|
1268
1570
|
// ═══════════════════════════════════════════════════════════════
|
|
1269
1571
|
// JAELIS-SPECIFIC METHODS (Unified VM)
|
|
1270
1572
|
// ═══════════════════════════════════════════════════════════════
|
|
1573
|
+
case 'jaelis_getCode':
|
|
1574
|
+
// Native JAELIS method - returns contract bytecode
|
|
1575
|
+
return this._getCode(params[0], params[1] || 'latest');
|
|
1576
|
+
|
|
1271
1577
|
case 'jaelis_getHealth':
|
|
1272
1578
|
return {
|
|
1273
1579
|
status: 'healthy',
|
|
@@ -1307,6 +1613,45 @@ class EmbeddedRpcServer {
|
|
|
1307
1613
|
case 'jaelis_getSupportedLanguages':
|
|
1308
1614
|
return ['solidity', 'rust', 'move', 'func', 'cairo', 'vyper'];
|
|
1309
1615
|
|
|
1616
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1617
|
+
// CROSS-CHAIN DEPLOYMENT METHODS (NEW!)
|
|
1618
|
+
// Query contract deployments across chains
|
|
1619
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1620
|
+
|
|
1621
|
+
case 'jaelis_crossChain_getDeployment':
|
|
1622
|
+
// Get deployment info for an address
|
|
1623
|
+
// params: [address]
|
|
1624
|
+
return this._getCrossChainDeployment(params[0]);
|
|
1625
|
+
|
|
1626
|
+
case 'jaelis_crossChain_getChainDeployments':
|
|
1627
|
+
// Get all deployments to a specific chain
|
|
1628
|
+
// params: [chainId]
|
|
1629
|
+
return this._getChainDeployments(params[0]);
|
|
1630
|
+
|
|
1631
|
+
case 'jaelis_crossChain_getExplorerUrl':
|
|
1632
|
+
// Get explorer URL for a deployment
|
|
1633
|
+
// params: [chainId, address]
|
|
1634
|
+
return this._getExplorerUrl(params[0], params[1]);
|
|
1635
|
+
|
|
1636
|
+
case 'jaelis_crossChain_getDeploymentStats':
|
|
1637
|
+
// Get deployment statistics
|
|
1638
|
+
return this._getDeploymentStats();
|
|
1639
|
+
|
|
1640
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1641
|
+
// MULTI-CHAIN SIGNATURE METHODS (Per-chain wallet authorization)
|
|
1642
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1643
|
+
|
|
1644
|
+
case 'jaelis_crossChain_getSigningRequests':
|
|
1645
|
+
// Get signing messages for each target chain
|
|
1646
|
+
// params: [bytecode, targetChains[]]
|
|
1647
|
+
// Returns array of { chainId, message, switchParams } for frontend
|
|
1648
|
+
return this._getMultiChainSigningRequests(params[0], params[1]);
|
|
1649
|
+
|
|
1650
|
+
case 'jaelis_crossChain_deployWithSignatures':
|
|
1651
|
+
// Deploy with per-chain wallet signatures
|
|
1652
|
+
// params: [{ from, bytecode, abi, targetChains, chainSignatures: {chainId: sig} }]
|
|
1653
|
+
return this._deployWithMultiChainSignatures(params[0]);
|
|
1654
|
+
|
|
1310
1655
|
// ═══════════════════════════════════════════════════════════════
|
|
1311
1656
|
// CROSS-CHAIN SETTLEMENT METHODS (Universal State Layer!)
|
|
1312
1657
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -1352,6 +1697,43 @@ class EmbeddedRpcServer {
|
|
|
1352
1697
|
// Get cross-chain stats
|
|
1353
1698
|
return this._getCrossChainStats();
|
|
1354
1699
|
|
|
1700
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1701
|
+
// MCP SHORTHAND ALIASES (crosschain_* → jaelis_crossChain_*)
|
|
1702
|
+
// Unified RPC - same methods, shorter names for AI agents
|
|
1703
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1704
|
+
case 'crosschain_getChains':
|
|
1705
|
+
return this._handleMethod('jaelis_crossChain_getChains', params);
|
|
1706
|
+
case 'crosschain_getChainInfo':
|
|
1707
|
+
return this._handleMethod('jaelis_crossChain_getChainInfo', params);
|
|
1708
|
+
case 'crosschain_readState':
|
|
1709
|
+
return this._handleMethod('jaelis_crossChain_readState', params);
|
|
1710
|
+
case 'crosschain_settleState':
|
|
1711
|
+
return this._handleMethod('jaelis_crossChain_settleState', params);
|
|
1712
|
+
case 'crosschain_getStateDiff':
|
|
1713
|
+
return this._handleMethod('jaelis_crossChain_getStateDiff', params);
|
|
1714
|
+
case 'crosschain_computeSlot':
|
|
1715
|
+
return this._handleMethod('jaelis_crossChain_computeSlot', params);
|
|
1716
|
+
case 'crosschain_getLightClientStatus':
|
|
1717
|
+
return this._handleMethod('jaelis_crossChain_getLightClientStatus', params);
|
|
1718
|
+
case 'crosschain_getSettlements':
|
|
1719
|
+
return this._handleMethod('jaelis_crossChain_getSettlements', params);
|
|
1720
|
+
case 'crosschain_getStats':
|
|
1721
|
+
return this._handleMethod('jaelis_crossChain_getStats', params);
|
|
1722
|
+
case 'crosschain_getDeployment':
|
|
1723
|
+
return this._handleMethod('jaelis_crossChain_getDeployment', params);
|
|
1724
|
+
case 'crosschain_getExplorerUrl':
|
|
1725
|
+
return this._handleMethod('jaelis_crossChain_getExplorerUrl', params);
|
|
1726
|
+
case 'crosschain_getDeploymentStats':
|
|
1727
|
+
return this._handleMethod('jaelis_crossChain_getDeploymentStats', params);
|
|
1728
|
+
case 'crosschain_getSigningRequests':
|
|
1729
|
+
return this._handleMethod('jaelis_crossChain_getSigningRequests', params);
|
|
1730
|
+
case 'crosschain_deployWithSignatures':
|
|
1731
|
+
return this._handleMethod('jaelis_crossChain_deployWithSignatures', params);
|
|
1732
|
+
case 'crosschain_deployUnified':
|
|
1733
|
+
return this._handleMethod('jaelis_deployUnified', params);
|
|
1734
|
+
case 'crosschain_syncState':
|
|
1735
|
+
return this._handleMethod('jaelis_crossChain_settleState', params);
|
|
1736
|
+
|
|
1355
1737
|
// ═══════════════════════════════════════════════════════════════
|
|
1356
1738
|
// LAYER 2: SOLANA COMPATIBILITY (PROXIES → jaelis_*)
|
|
1357
1739
|
// For Rust/SPL developers familiar with Solana RPC
|
|
@@ -1574,6 +1956,160 @@ class EmbeddedRpcServer {
|
|
|
1574
1956
|
return result.transactionHash || '0x0';
|
|
1575
1957
|
}
|
|
1576
1958
|
|
|
1959
|
+
/**
|
|
1960
|
+
* Get contract bytecode at address
|
|
1961
|
+
*
|
|
1962
|
+
* CRITICAL: This is how block explorers detect contracts!
|
|
1963
|
+
* - Returns bytecode hex string if contract exists
|
|
1964
|
+
* - Returns '0x' if address is an EOA or doesn't exist
|
|
1965
|
+
*
|
|
1966
|
+
* Block explorers use: eth_getCode(address) !== '0x' → it's a contract!
|
|
1967
|
+
*/
|
|
1968
|
+
async _getCode(address, blockTag = 'latest') {
|
|
1969
|
+
if (!this.blockchain) return '0x';
|
|
1970
|
+
|
|
1971
|
+
try {
|
|
1972
|
+
// Normalize address
|
|
1973
|
+
const normalizedAddr = address.toLowerCase();
|
|
1974
|
+
|
|
1975
|
+
// Try StateManager first (persistent LevelDB storage)
|
|
1976
|
+
if (this.blockchain.stateManager) {
|
|
1977
|
+
const code = await this.blockchain.stateManager.getCode(normalizedAddr);
|
|
1978
|
+
if (code && code.length > 0) {
|
|
1979
|
+
// Return as hex string
|
|
1980
|
+
if (Buffer.isBuffer(code)) {
|
|
1981
|
+
return '0x' + code.toString('hex');
|
|
1982
|
+
}
|
|
1983
|
+
if (typeof code === 'string') {
|
|
1984
|
+
return code.startsWith('0x') ? code : '0x' + code;
|
|
1985
|
+
}
|
|
1986
|
+
return '0x' + Buffer.from(code).toString('hex');
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Try VM Executor
|
|
1991
|
+
if (this.blockchain.vmExecutor?.stateManager) {
|
|
1992
|
+
const code = await this.blockchain.vmExecutor.stateManager.getCode(normalizedAddr);
|
|
1993
|
+
if (code && code.length > 0) {
|
|
1994
|
+
if (Buffer.isBuffer(code)) {
|
|
1995
|
+
return '0x' + code.toString('hex');
|
|
1996
|
+
}
|
|
1997
|
+
return code.startsWith('0x') ? code : '0x' + code;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
// Try UnifiedVM contracts
|
|
2002
|
+
if (this.blockchain.unifiedVM?.contractManager) {
|
|
2003
|
+
const contract = this.blockchain.unifiedVM.contractManager.contracts.get(normalizedAddr);
|
|
2004
|
+
if (contract?.bytecode) {
|
|
2005
|
+
if (Buffer.isBuffer(contract.bytecode)) {
|
|
2006
|
+
return '0x' + contract.bytecode.toString('hex');
|
|
2007
|
+
}
|
|
2008
|
+
return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
// Try contracts Map directly
|
|
2013
|
+
if (this.blockchain.contracts) {
|
|
2014
|
+
const contract = this.blockchain.contracts.get(normalizedAddr);
|
|
2015
|
+
if (contract?.bytecode) {
|
|
2016
|
+
if (Buffer.isBuffer(contract.bytecode)) {
|
|
2017
|
+
return '0x' + contract.bytecode.toString('hex');
|
|
2018
|
+
}
|
|
2019
|
+
return contract.bytecode.startsWith('0x') ? contract.bytecode : '0x' + contract.bytecode;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
// No bytecode found - it's an EOA or doesn't exist
|
|
2024
|
+
return '0x';
|
|
2025
|
+
} catch (error) {
|
|
2026
|
+
console.error(`[RPC] _getCode error for ${address}:`, error.message);
|
|
2027
|
+
return '0x';
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
/**
|
|
2032
|
+
* Get account balance
|
|
2033
|
+
*/
|
|
2034
|
+
async _getBalance(address, blockTag = 'latest') {
|
|
2035
|
+
if (!this.blockchain) return '0x0';
|
|
2036
|
+
|
|
2037
|
+
try {
|
|
2038
|
+
const normalizedAddr = address.toLowerCase();
|
|
2039
|
+
|
|
2040
|
+
// Try StateManager
|
|
2041
|
+
if (this.blockchain.stateManager) {
|
|
2042
|
+
const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
|
|
2043
|
+
if (account?.balance !== undefined) {
|
|
2044
|
+
const balance = BigInt(account.balance || 0);
|
|
2045
|
+
return '0x' + balance.toString(16);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
// Default balance (JAELIS has no native token, but return 0)
|
|
2050
|
+
return '0x0';
|
|
2051
|
+
} catch (error) {
|
|
2052
|
+
return '0x0';
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
/**
|
|
2057
|
+
* Get transaction count (nonce)
|
|
2058
|
+
*/
|
|
2059
|
+
async _getTransactionCount(address, blockTag = 'latest') {
|
|
2060
|
+
if (!this.blockchain) return '0x0';
|
|
2061
|
+
|
|
2062
|
+
try {
|
|
2063
|
+
const normalizedAddr = address.toLowerCase();
|
|
2064
|
+
|
|
2065
|
+
// Try StateManager
|
|
2066
|
+
if (this.blockchain.stateManager) {
|
|
2067
|
+
const account = await this.blockchain.stateManager.getAccount(normalizedAddr);
|
|
2068
|
+
if (account?.nonce !== undefined) {
|
|
2069
|
+
const nonce = BigInt(account.nonce || 0);
|
|
2070
|
+
return '0x' + nonce.toString(16);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
return '0x0';
|
|
2075
|
+
} catch (error) {
|
|
2076
|
+
return '0x0';
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/**
|
|
2081
|
+
* Get storage value at slot
|
|
2082
|
+
*/
|
|
2083
|
+
async _getStorageAt(address, slot, blockTag = 'latest') {
|
|
2084
|
+
if (!this.blockchain) return '0x' + '0'.repeat(64);
|
|
2085
|
+
|
|
2086
|
+
try {
|
|
2087
|
+
const normalizedAddr = address.toLowerCase();
|
|
2088
|
+
|
|
2089
|
+
// Try StateManager
|
|
2090
|
+
if (this.blockchain.stateManager) {
|
|
2091
|
+
const value = await this.blockchain.stateManager.getStorage(normalizedAddr, slot);
|
|
2092
|
+
if (value !== undefined) {
|
|
2093
|
+
const bigValue = BigInt(value || 0);
|
|
2094
|
+
return '0x' + bigValue.toString(16).padStart(64, '0');
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
// Try VM Executor StateManager
|
|
2099
|
+
if (this.blockchain.vmExecutor?.stateManager) {
|
|
2100
|
+
const value = await this.blockchain.vmExecutor.stateManager.getStorage(normalizedAddr, slot);
|
|
2101
|
+
if (value !== undefined) {
|
|
2102
|
+
const bigValue = BigInt(value || 0);
|
|
2103
|
+
return '0x' + bigValue.toString(16).padStart(64, '0');
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
return '0x' + '0'.repeat(64);
|
|
2108
|
+
} catch (error) {
|
|
2109
|
+
return '0x' + '0'.repeat(64);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
|
|
1577
2113
|
async _call(callObject) {
|
|
1578
2114
|
if (!this.blockchain?.vmExecutor) throw new Error('VM not available');
|
|
1579
2115
|
const result = await this.blockchain.vmExecutor.executeCall(
|
|
@@ -1602,13 +2138,17 @@ class EmbeddedRpcServer {
|
|
|
1602
2138
|
* Compiles MULTIPLE contracts from MULTIPLE languages into ONE bytecode bundle.
|
|
1603
2139
|
* All contracts share memory and can call each other DIRECTLY - no bridges!
|
|
1604
2140
|
*
|
|
1605
|
-
*
|
|
2141
|
+
* NOW WITH CROSS-CHAIN PROPAGATION:
|
|
2142
|
+
* - Contracts appear on ETH, SOL, and other block explorers!
|
|
2143
|
+
* - Shadow contracts on external chains proxy to JAELIS
|
|
2144
|
+
*
|
|
2145
|
+
* @param {Object} params - { from, sources: [{ code, language, name }...], targetChains?: number[] }
|
|
1606
2146
|
*/
|
|
1607
2147
|
async _deployUnified(params) {
|
|
1608
2148
|
if (!this.blockchain) throw new Error('Blockchain not available');
|
|
1609
2149
|
if (!this.blockchain.unifiedVM) throw new Error('Unified VM not available');
|
|
1610
2150
|
|
|
1611
|
-
const { from, sources } = params;
|
|
2151
|
+
const { from, sources, targetChains } = params;
|
|
1612
2152
|
|
|
1613
2153
|
if (!sources || !Array.isArray(sources) || sources.length === 0) {
|
|
1614
2154
|
throw new Error('sources array is required for unified deployment');
|
|
@@ -1617,13 +2157,95 @@ class EmbeddedRpcServer {
|
|
|
1617
2157
|
console.log(`[RPC] Deploying unified bundle with ${sources.length} contracts...`);
|
|
1618
2158
|
console.log(`[RPC] Languages: ${[...new Set(sources.map(s => s.language))].join(', ')}`);
|
|
1619
2159
|
|
|
1620
|
-
//
|
|
1621
|
-
const
|
|
2160
|
+
// Default target chains if not specified - ALL 30+ chains we support!
|
|
2161
|
+
const defaultTargetChains = [
|
|
2162
|
+
// EVM Mainnets
|
|
2163
|
+
1, // Ethereum Mainnet
|
|
2164
|
+
137, // Polygon PoS
|
|
2165
|
+
42161, // Arbitrum One
|
|
2166
|
+
42170, // Arbitrum Nova
|
|
2167
|
+
10, // OP Mainnet
|
|
2168
|
+
8453, // Base
|
|
2169
|
+
56, // BNB Smart Chain
|
|
2170
|
+
43114, // Avalanche C-Chain
|
|
2171
|
+
250, // Fantom Opera
|
|
2172
|
+
100, // Gnosis
|
|
2173
|
+
59144, // Linea
|
|
2174
|
+
324, // zkSync Era
|
|
2175
|
+
534352, // Scroll
|
|
2176
|
+
5000, // Mantle
|
|
2177
|
+
81457, // Blast
|
|
2178
|
+
|
|
2179
|
+
// Solana
|
|
2180
|
+
101, // Solana Mainnet
|
|
2181
|
+
|
|
2182
|
+
// Move chains (using type prefix to differentiate)
|
|
2183
|
+
// Note: Aptos/Sui use same IDs but different networks
|
|
2184
|
+
|
|
2185
|
+
// TON
|
|
2186
|
+
-1, // TON Mainnet
|
|
2187
|
+
|
|
2188
|
+
// StarkNet (using string identifier due to large chain ID)
|
|
2189
|
+
'starknet-main', // StarkNet Mainnet
|
|
2190
|
+
|
|
2191
|
+
// Cosmos
|
|
2192
|
+
118, // Cosmos Hub
|
|
2193
|
+
119, // Osmosis
|
|
2194
|
+
|
|
2195
|
+
// Bitcoin
|
|
2196
|
+
0 // Bitcoin Mainnet
|
|
2197
|
+
];
|
|
2198
|
+
const chains = targetChains || defaultTargetChains;
|
|
2199
|
+
|
|
2200
|
+
console.log(`[RPC] Target chains: ${chains.join(', ')}`);
|
|
2201
|
+
|
|
2202
|
+
// Use the new deployUnifiedWithPropagation for cross-chain deployment
|
|
2203
|
+
if (this.blockchain.unifiedVM.deployUnifiedWithPropagation) {
|
|
2204
|
+
const result = await this.blockchain.unifiedVM.deployUnifiedWithPropagation({
|
|
2205
|
+
from,
|
|
2206
|
+
sources,
|
|
2207
|
+
targetChains: chains
|
|
2208
|
+
});
|
|
1622
2209
|
|
|
1623
|
-
|
|
2210
|
+
// Build contracts map for response
|
|
2211
|
+
const contractsMap = {};
|
|
2212
|
+
for (const source of sources) {
|
|
2213
|
+
contractsMap[source.name] = {
|
|
2214
|
+
address: result.address,
|
|
2215
|
+
language: source.language,
|
|
2216
|
+
name: source.name
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
console.log(`[RPC] Unified bundle deployed at ${result.address}`);
|
|
2221
|
+
console.log(`[RPC] Chain deployments: ${result.deployments?.length || 0}`);
|
|
2222
|
+
|
|
2223
|
+
// Log explorer URLs
|
|
2224
|
+
if (result.explorerUrls) {
|
|
2225
|
+
console.log(`[RPC] Explorer URLs:`);
|
|
2226
|
+
for (const [chainId, url] of Object.entries(result.explorerUrls)) {
|
|
2227
|
+
console.log(`[RPC] Chain ${chainId}: ${url}`);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
return {
|
|
2232
|
+
address: result.address,
|
|
2233
|
+
bytecode: result.bytecode,
|
|
2234
|
+
abi: result.abi || [],
|
|
2235
|
+
contracts: contractsMap,
|
|
2236
|
+
deployments: result.deployments,
|
|
2237
|
+
explorerUrls: result.explorerUrls,
|
|
2238
|
+
metadata: result.metadata,
|
|
2239
|
+
gasUsed: 0 // ZERO FEES!
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
// Fallback: original local-only deployment
|
|
2244
|
+
console.log(`[RPC] Fallback: local-only deployment (no cross-chain propagation)`);
|
|
2245
|
+
|
|
2246
|
+
const compiled = await this.blockchain.unifiedVM.compileBundle(sources);
|
|
1624
2247
|
const address = '0x' + require('crypto').randomBytes(20).toString('hex');
|
|
1625
2248
|
|
|
1626
|
-
// Store the unified contract
|
|
1627
2249
|
if (this.blockchain.contracts) {
|
|
1628
2250
|
this.blockchain.contracts.set(address, {
|
|
1629
2251
|
bytecode: compiled.bytecode,
|
|
@@ -1635,15 +2257,10 @@ class EmbeddedRpcServer {
|
|
|
1635
2257
|
});
|
|
1636
2258
|
}
|
|
1637
2259
|
|
|
1638
|
-
console.log(`[RPC] Unified bundle deployed at ${address}`);
|
|
1639
|
-
console.log(`[RPC] Bytecode: ${compiled.bytecode?.length || 0} bytes`);
|
|
1640
|
-
console.log(`[RPC] Cross-references: ${compiled.crossReferences?.size || 0}`);
|
|
1641
|
-
|
|
1642
|
-
// Build contracts map for response
|
|
1643
2260
|
const contractsMap = {};
|
|
1644
2261
|
for (const source of sources) {
|
|
1645
2262
|
contractsMap[source.name] = {
|
|
1646
|
-
address: address,
|
|
2263
|
+
address: address,
|
|
1647
2264
|
language: source.language,
|
|
1648
2265
|
name: source.name
|
|
1649
2266
|
};
|
|
@@ -1798,6 +2415,131 @@ class EmbeddedRpcServer {
|
|
|
1798
2415
|
};
|
|
1799
2416
|
}
|
|
1800
2417
|
|
|
2418
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2419
|
+
// CROSS-CHAIN DEPLOYMENT QUERY IMPLEMENTATIONS
|
|
2420
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2421
|
+
|
|
2422
|
+
_getCrossChainDeployment(address) {
|
|
2423
|
+
if (this.blockchain?.unifiedVM?.deployManager?.getDeployment) {
|
|
2424
|
+
const deployment = this.blockchain.unifiedVM.deployManager.getDeployment(address);
|
|
2425
|
+
if (deployment) {
|
|
2426
|
+
return deployment;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
return { error: 'Deployment not found', address };
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
_getChainDeployments(chainId) {
|
|
2433
|
+
if (this.blockchain?.unifiedVM?.deployManager?.getChainDeployments) {
|
|
2434
|
+
return this.blockchain.unifiedVM.deployManager.getChainDeployments(chainId);
|
|
2435
|
+
}
|
|
2436
|
+
return [];
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
_getExplorerUrl(chainId, address) {
|
|
2440
|
+
if (this.blockchain?.unifiedVM?.deployManager?.getExplorerUrl) {
|
|
2441
|
+
const url = this.blockchain.unifiedVM.deployManager.getExplorerUrl(chainId, address);
|
|
2442
|
+
return { chainId, address, url };
|
|
2443
|
+
}
|
|
2444
|
+
return { chainId, address, url: null, error: 'Deploy manager not available' };
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
_getDeploymentStats() {
|
|
2448
|
+
if (this.blockchain?.unifiedVM?.deployManager?.getStats) {
|
|
2449
|
+
return this.blockchain.unifiedVM.deployManager.getStats();
|
|
2450
|
+
}
|
|
2451
|
+
return {
|
|
2452
|
+
totalDeployments: 0,
|
|
2453
|
+
pendingCount: 0,
|
|
2454
|
+
failedCount: 0,
|
|
2455
|
+
chainStats: {}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2460
|
+
// MULTI-CHAIN SIGNATURE IMPLEMENTATIONS
|
|
2461
|
+
// For external chains to accept deployments, user signs per-chain
|
|
2462
|
+
// ═══════════════════════════════════════════════════════════════
|
|
2463
|
+
|
|
2464
|
+
/**
|
|
2465
|
+
* Get signing requests for each target chain
|
|
2466
|
+
*
|
|
2467
|
+
* Frontend flow:
|
|
2468
|
+
* 1. Get these messages
|
|
2469
|
+
* 2. For each chain, switch wallet network and sign
|
|
2470
|
+
* 3. Collect all signatures
|
|
2471
|
+
* 4. Call deployWithSignatures
|
|
2472
|
+
*/
|
|
2473
|
+
_getMultiChainSigningRequests(bytecode, targetChains) {
|
|
2474
|
+
if (this.blockchain?.unifiedVM?.deployManager?.getMultiChainSigningRequests) {
|
|
2475
|
+
return this.blockchain.unifiedVM.deployManager.getMultiChainSigningRequests(bytecode, targetChains);
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// Fallback implementation
|
|
2479
|
+
const crypto = require('crypto');
|
|
2480
|
+
const bytecodeHash = crypto.createHash('sha256')
|
|
2481
|
+
.update(bytecode || '')
|
|
2482
|
+
.digest('hex')
|
|
2483
|
+
.substring(0, 16);
|
|
2484
|
+
|
|
2485
|
+
const timestamp = Date.now();
|
|
2486
|
+
const requests = [];
|
|
2487
|
+
|
|
2488
|
+
for (const chainId of (targetChains || [])) {
|
|
2489
|
+
const message = `JAELIS Cross-Chain Deploy Authorization
|
|
2490
|
+
|
|
2491
|
+
Chain ID: ${chainId}
|
|
2492
|
+
Bytecode Hash: ${bytecodeHash}
|
|
2493
|
+
Timestamp: ${timestamp}
|
|
2494
|
+
|
|
2495
|
+
I authorize JAELIS to deploy this contract.
|
|
2496
|
+
ZERO FEES - No gas cost to me.`;
|
|
2497
|
+
|
|
2498
|
+
requests.push({
|
|
2499
|
+
chainId,
|
|
2500
|
+
message,
|
|
2501
|
+
messageHash: '0x' + crypto.createHash('sha256')
|
|
2502
|
+
.update(`\x19Ethereum Signed Message:\n${message.length}${message}`)
|
|
2503
|
+
.digest('hex'),
|
|
2504
|
+
switchParams: { chainId: '0x' + chainId.toString(16) }
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
return { requests, bytecodeHash, timestamp, totalChains: targetChains?.length || 0 };
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
/**
|
|
2512
|
+
* Deploy with per-chain wallet signatures
|
|
2513
|
+
*
|
|
2514
|
+
* Each chain that needs to recognize the deployment
|
|
2515
|
+
* requires its own signature from the deployer's wallet.
|
|
2516
|
+
*/
|
|
2517
|
+
async _deployWithMultiChainSignatures(params) {
|
|
2518
|
+
const { from, bytecode, abi, targetChains, chainSignatures, metadata } = params;
|
|
2519
|
+
|
|
2520
|
+
console.log(`[RPC] Multi-chain signature deployment from ${from}`);
|
|
2521
|
+
console.log(`[RPC] Target chains: ${targetChains?.length || 0}`);
|
|
2522
|
+
console.log(`[RPC] Signatures provided: ${Object.keys(chainSignatures || {}).length}`);
|
|
2523
|
+
|
|
2524
|
+
if (this.blockchain?.unifiedVM?.deployManager?.deployWithMultiChainSignatures) {
|
|
2525
|
+
return await this.blockchain.unifiedVM.deployManager.deployWithMultiChainSignatures({
|
|
2526
|
+
from,
|
|
2527
|
+
bytecode,
|
|
2528
|
+
abi,
|
|
2529
|
+
targetChains,
|
|
2530
|
+
chainSignatures,
|
|
2531
|
+
metadata
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
// Fallback: regular deployment without chain signatures
|
|
2536
|
+
console.log(`[RPC] Fallback: Deploy manager not available, using standard flow`);
|
|
2537
|
+
return {
|
|
2538
|
+
error: 'Multi-chain signatures not supported',
|
|
2539
|
+
message: 'Deploy manager not initialized'
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
|
|
1801
2543
|
async stop() {
|
|
1802
2544
|
if (this.server) {
|
|
1803
2545
|
this.server.close();
|