jaelis-node 1.0.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 +203 -0
- package/bin/jaelis-node.js +315 -0
- package/config/default.json +76 -0
- package/config/mainnet.json +30 -0
- package/config/testnet.json +28 -0
- package/lib/index.js +496 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# @jaelis/node
|
|
2
|
+
|
|
3
|
+
Official node software for the **JAELIS Blockchain** - run a full node, validator, or bootstrap node on the JAELIS network.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@jaelis/node)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Full Node** - Sync and validate the entire JAELIS blockchain
|
|
11
|
+
- **Validator Node** - Participate in PoEC consensus and earn rewards
|
|
12
|
+
- **Bootstrap Node** - Help with peer discovery on the network
|
|
13
|
+
- **Zero Gas Fees** - LODE-based computational metering
|
|
14
|
+
- **Multi-VM Support** - EVM, Solana, WASM, Bitcoin, Move, TON
|
|
15
|
+
- **AI-Native** - Built-in MCP server and x402 payment protocol
|
|
16
|
+
- **LevelDB Storage** - Fast, persistent blockchain storage
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
### Global Installation (Recommended)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @jaelis/node
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Local Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @jaelis/node
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Start a Full Node
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Start on testnet (default)
|
|
38
|
+
jaelis-node start
|
|
39
|
+
|
|
40
|
+
# Start on testnet with custom ports
|
|
41
|
+
jaelis-node start --rpc-port 8545 --p2p-port 30303
|
|
42
|
+
|
|
43
|
+
# Start on mainnet (when available)
|
|
44
|
+
jaelis-node start --network mainnet
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Check Node Status
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
jaelis-node status
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### View Connected Peers
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
jaelis-node peers
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Display Node Info
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
jaelis-node info
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Commands
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|---------|-------------|
|
|
69
|
+
| `start` | Start a full JAELIS node |
|
|
70
|
+
| `validator` | Run as a validator node (PoEC) |
|
|
71
|
+
| `bootstrap` | Run as a bootstrap/seed node |
|
|
72
|
+
| `status` | Check local node status |
|
|
73
|
+
| `peers` | List connected peers |
|
|
74
|
+
| `info` | Display node information |
|
|
75
|
+
|
|
76
|
+
## Start Options
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
jaelis-node start [options]
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
-n, --network <network> Network: testnet or mainnet (default: testnet)
|
|
83
|
+
-r, --rpc-port <port> RPC server port (default: 8545)
|
|
84
|
+
-p, --p2p-port <port> P2P network port (default: 30303)
|
|
85
|
+
-d, --data-dir <path> Data directory (default: ./jaelis-data)
|
|
86
|
+
--rpc-host <host> RPC host (default: 0.0.0.0)
|
|
87
|
+
--no-rpc Disable RPC server
|
|
88
|
+
--sync-mode <mode> Sync mode: full, light, archive (default: full)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Programmatic Usage
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const { JaelisNode, BootstrapNode } = require('@jaelis/node');
|
|
95
|
+
|
|
96
|
+
// Create and start a full node
|
|
97
|
+
const node = new JaelisNode({
|
|
98
|
+
network: 'testnet',
|
|
99
|
+
rpcPort: 8545,
|
|
100
|
+
p2pPort: 30303,
|
|
101
|
+
dataDir: './my-node-data'
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
await node.start();
|
|
105
|
+
|
|
106
|
+
// Get node status
|
|
107
|
+
const status = node.getStatus();
|
|
108
|
+
console.log(status);
|
|
109
|
+
|
|
110
|
+
// Listen for events
|
|
111
|
+
node.on('peer:connect', (peerCount) => {
|
|
112
|
+
console.log(`Peers connected: ${peerCount}`);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Stop the node
|
|
116
|
+
await node.stop();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Bootstrap Node
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
const { BootstrapNode } = require('@jaelis/node');
|
|
123
|
+
|
|
124
|
+
const bootstrap = new BootstrapNode({
|
|
125
|
+
port: 30305,
|
|
126
|
+
network: 'testnet'
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await bootstrap.start();
|
|
130
|
+
console.log(`Bootstrap node running, ${bootstrap.getPeerCount()} peers`);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Network Configuration
|
|
134
|
+
|
|
135
|
+
| Network | Chain ID | RPC URL | Status |
|
|
136
|
+
|---------|----------|---------|--------|
|
|
137
|
+
| Testnet | 4545 | https://rpc.jaelis.io | Active |
|
|
138
|
+
| Mainnet | 4547 | https://mainnet.jaelis.io | Coming Soon |
|
|
139
|
+
|
|
140
|
+
## System Requirements
|
|
141
|
+
|
|
142
|
+
| Requirement | Minimum | Recommended |
|
|
143
|
+
|-------------|---------|-------------|
|
|
144
|
+
| Node.js | v18.0.0 | v20.x LTS |
|
|
145
|
+
| RAM | 4 GB | 8+ GB |
|
|
146
|
+
| Storage | 50 GB SSD | 200+ GB SSD |
|
|
147
|
+
| CPU | 2 cores | 4+ cores |
|
|
148
|
+
| Network | 10 Mbps | 100+ Mbps |
|
|
149
|
+
|
|
150
|
+
## Data Directory Structure
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
jaelis-data/
|
|
154
|
+
├── blockchain/ # Block and state data
|
|
155
|
+
│ ├── blocks/ # Block storage (LevelDB)
|
|
156
|
+
│ └── state/ # State trie (LevelDB)
|
|
157
|
+
├── peers/ # Peer database
|
|
158
|
+
├── keys/ # Node keys (if validator)
|
|
159
|
+
└── logs/ # Node logs
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Environment Variables
|
|
163
|
+
|
|
164
|
+
| Variable | Description | Default |
|
|
165
|
+
|----------|-------------|---------|
|
|
166
|
+
| `JAELIS_CORE_PATH` | Path to JAELIS core | Auto-detected |
|
|
167
|
+
| `JAELIS_DATA_DIR` | Data directory | `./jaelis-data` |
|
|
168
|
+
| `JAELIS_NETWORK` | Network (testnet/mainnet) | `testnet` |
|
|
169
|
+
| `JAELIS_RPC_PORT` | RPC port | `8545` |
|
|
170
|
+
| `JAELIS_P2P_PORT` | P2P port | `30303` |
|
|
171
|
+
|
|
172
|
+
## Docker
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Pull the image
|
|
176
|
+
docker pull jaelis/node
|
|
177
|
+
|
|
178
|
+
# Run a full node
|
|
179
|
+
docker run -d \
|
|
180
|
+
--name jaelis-node \
|
|
181
|
+
-p 8545:8545 \
|
|
182
|
+
-p 30303:30303 \
|
|
183
|
+
-v jaelis-data:/data \
|
|
184
|
+
jaelis/node start --network testnet
|
|
185
|
+
|
|
186
|
+
# Check logs
|
|
187
|
+
docker logs -f jaelis-node
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Contributing
|
|
191
|
+
|
|
192
|
+
Contributions are welcome! Please see our contributing guidelines.
|
|
193
|
+
|
|
194
|
+
## Links
|
|
195
|
+
|
|
196
|
+
- Website: [jaelis.io](https://jaelis.io)
|
|
197
|
+
- SDK: [npm/jaelis.js](https://www.npmjs.com/package/jaelis.js)
|
|
198
|
+
- Documentation: [docs.jaelis.io](https://docs.jaelis.io)
|
|
199
|
+
- GitHub: [github.com/jaelis-foundation](https://github.com/jaelis-foundation)
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT - JAELIS Foundation
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* JAELIS Node CLI
|
|
5
|
+
* Official command-line interface for running JAELIS blockchain nodes
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* jaelis-node start [--network testnet|mainnet] [--rpc-port 8545] [--p2p-port 30303]
|
|
9
|
+
* jaelis-node validator [--stake <amount>]
|
|
10
|
+
* jaelis-node bootstrap [--ports 30305,30306,30307]
|
|
11
|
+
* jaelis-node status
|
|
12
|
+
* jaelis-node peers
|
|
13
|
+
*
|
|
14
|
+
* @version 1.0.0
|
|
15
|
+
* @author JAELIS Foundation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { Command } = require('commander');
|
|
19
|
+
const chalk = require('chalk');
|
|
20
|
+
const ora = require('ora');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
|
|
24
|
+
// Version from package.json
|
|
25
|
+
const { version } = require('../package.json');
|
|
26
|
+
|
|
27
|
+
// ASCII Art Banner
|
|
28
|
+
const BANNER = `
|
|
29
|
+
██╗ █████╗ ███████╗██╗ ██╗███████╗
|
|
30
|
+
██║██╔══██╗██╔════╝██║ ██║██╔════╝
|
|
31
|
+
██║███████║█████╗ ██║ ██║███████╗
|
|
32
|
+
██ ██║██╔══██║██╔══╝ ██║ ██║╚════██║
|
|
33
|
+
╚█████╔╝██║ ██║███████╗███████╗██║███████║
|
|
34
|
+
╚════╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚══════╝
|
|
35
|
+
NODE v${version}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
// Network configurations
|
|
39
|
+
const NETWORKS = {
|
|
40
|
+
testnet: {
|
|
41
|
+
chainId: 4545,
|
|
42
|
+
name: 'JAELIS Testnet',
|
|
43
|
+
symbol: 'tJAELIS',
|
|
44
|
+
rpcUrl: 'https://rpc.jaelis.io',
|
|
45
|
+
bootstrapNodes: [
|
|
46
|
+
'/dns4/rpc.jaelis.io/tcp/30305/p2p/QmBootstrap1',
|
|
47
|
+
'/dns4/rpc.jaelis.io/tcp/30306/p2p/QmBootstrap2',
|
|
48
|
+
'/dns4/rpc.jaelis.io/tcp/30307/p2p/QmBootstrap3'
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
mainnet: {
|
|
52
|
+
chainId: 4547,
|
|
53
|
+
name: 'JAELIS Mainnet',
|
|
54
|
+
symbol: 'JAELIS',
|
|
55
|
+
rpcUrl: 'https://mainnet.jaelis.io',
|
|
56
|
+
bootstrapNodes: [] // Coming soon
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Initialize CLI
|
|
61
|
+
const program = new Command();
|
|
62
|
+
|
|
63
|
+
program
|
|
64
|
+
.name('jaelis-node')
|
|
65
|
+
.description('Official JAELIS Blockchain Node CLI')
|
|
66
|
+
.version(version);
|
|
67
|
+
|
|
68
|
+
// START command - Start a full node
|
|
69
|
+
program
|
|
70
|
+
.command('start')
|
|
71
|
+
.description('Start a JAELIS full node')
|
|
72
|
+
.option('-n, --network <network>', 'Network to connect to (testnet/mainnet)', 'testnet')
|
|
73
|
+
.option('-r, --rpc-port <port>', 'RPC server port', '8545')
|
|
74
|
+
.option('-p, --p2p-port <port>', 'P2P network port', '30303')
|
|
75
|
+
.option('-d, --data-dir <path>', 'Data directory for blockchain storage', './jaelis-data')
|
|
76
|
+
.option('--rpc-host <host>', 'RPC server host', '0.0.0.0')
|
|
77
|
+
.option('--no-rpc', 'Disable RPC server')
|
|
78
|
+
.option('--sync-mode <mode>', 'Sync mode: full, light, archive', 'full')
|
|
79
|
+
.action(async (options) => {
|
|
80
|
+
console.log(chalk.cyan(BANNER));
|
|
81
|
+
console.log(chalk.green('Starting JAELIS Node...'));
|
|
82
|
+
console.log();
|
|
83
|
+
|
|
84
|
+
const network = NETWORKS[options.network];
|
|
85
|
+
if (!network) {
|
|
86
|
+
console.error(chalk.red(`Unknown network: ${options.network}`));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log(chalk.white('Configuration:'));
|
|
91
|
+
console.log(chalk.gray(` Network: ${network.name} (Chain ID: ${network.chainId})`));
|
|
92
|
+
console.log(chalk.gray(` Symbol: ${network.symbol}`));
|
|
93
|
+
console.log(chalk.gray(` RPC Port: ${options.rpc ? options.rpcPort : 'disabled'}`));
|
|
94
|
+
console.log(chalk.gray(` P2P Port: ${options.p2pPort}`));
|
|
95
|
+
console.log(chalk.gray(` Data Dir: ${options.dataDir}`));
|
|
96
|
+
console.log(chalk.gray(` Sync Mode: ${options.syncMode}`));
|
|
97
|
+
console.log();
|
|
98
|
+
|
|
99
|
+
const spinner = ora('Initializing node...').start();
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
// Import and start the node
|
|
103
|
+
const { JaelisNode } = require('../lib/index.js');
|
|
104
|
+
|
|
105
|
+
const node = new JaelisNode({
|
|
106
|
+
network: options.network,
|
|
107
|
+
chainId: network.chainId,
|
|
108
|
+
rpcPort: parseInt(options.rpcPort),
|
|
109
|
+
rpcHost: options.rpcHost,
|
|
110
|
+
p2pPort: parseInt(options.p2pPort),
|
|
111
|
+
dataDir: options.dataDir,
|
|
112
|
+
syncMode: options.syncMode,
|
|
113
|
+
enableRpc: options.rpc,
|
|
114
|
+
bootstrapNodes: network.bootstrapNodes
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
spinner.text = 'Connecting to network...';
|
|
118
|
+
await node.start();
|
|
119
|
+
|
|
120
|
+
spinner.succeed(chalk.green('Node started successfully!'));
|
|
121
|
+
console.log();
|
|
122
|
+
console.log(chalk.cyan('═══════════════════════════════════════════════════'));
|
|
123
|
+
console.log(chalk.white(` RPC Endpoint: http://${options.rpcHost}:${options.rpcPort}`));
|
|
124
|
+
console.log(chalk.white(` P2P Endpoint: 0.0.0.0:${options.p2pPort}`));
|
|
125
|
+
console.log(chalk.white(` Network: ${network.name}`));
|
|
126
|
+
console.log(chalk.cyan('═══════════════════════════════════════════════════'));
|
|
127
|
+
console.log();
|
|
128
|
+
console.log(chalk.gray('Press Ctrl+C to stop the node'));
|
|
129
|
+
|
|
130
|
+
// Handle shutdown
|
|
131
|
+
process.on('SIGINT', async () => {
|
|
132
|
+
console.log();
|
|
133
|
+
spinner.start('Shutting down node...');
|
|
134
|
+
await node.stop();
|
|
135
|
+
spinner.succeed('Node stopped gracefully');
|
|
136
|
+
process.exit(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
} catch (error) {
|
|
140
|
+
spinner.fail(chalk.red('Failed to start node'));
|
|
141
|
+
console.error(chalk.red(error.message));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// VALIDATOR command - Run as a validator
|
|
147
|
+
program
|
|
148
|
+
.command('validator')
|
|
149
|
+
.description('Run node as a validator (PoEC consensus)')
|
|
150
|
+
.option('-n, --network <network>', 'Network to validate on', 'testnet')
|
|
151
|
+
.option('-s, --stake <amount>', 'Amount of JAELIS to stake', '1000')
|
|
152
|
+
.option('-k, --key <path>', 'Path to validator key file')
|
|
153
|
+
.action(async (options) => {
|
|
154
|
+
console.log(chalk.cyan(BANNER));
|
|
155
|
+
console.log(chalk.yellow('Starting JAELIS Validator Node...'));
|
|
156
|
+
console.log();
|
|
157
|
+
console.log(chalk.white('Validator Configuration:'));
|
|
158
|
+
console.log(chalk.gray(` Network: ${options.network}`));
|
|
159
|
+
console.log(chalk.gray(` Stake: ${options.stake} JAELIS`));
|
|
160
|
+
console.log(chalk.gray(` Key: ${options.key || 'auto-generated'}`));
|
|
161
|
+
console.log();
|
|
162
|
+
|
|
163
|
+
console.log(chalk.yellow('Validator mode coming soon!'));
|
|
164
|
+
console.log(chalk.gray('For now, run as a full node with: jaelis-node start'));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// BOOTSTRAP command - Run as a bootstrap node
|
|
168
|
+
program
|
|
169
|
+
.command('bootstrap')
|
|
170
|
+
.description('Run as a bootstrap/seed node for peer discovery')
|
|
171
|
+
.option('-p, --ports <ports>', 'Comma-separated list of P2P ports', '30305,30306,30307')
|
|
172
|
+
.option('-n, --network <network>', 'Network', 'testnet')
|
|
173
|
+
.action(async (options) => {
|
|
174
|
+
console.log(chalk.cyan(BANNER));
|
|
175
|
+
console.log(chalk.magenta('Starting JAELIS Bootstrap Node...'));
|
|
176
|
+
console.log();
|
|
177
|
+
|
|
178
|
+
const ports = options.ports.split(',').map(p => parseInt(p.trim()));
|
|
179
|
+
console.log(chalk.white('Bootstrap Configuration:'));
|
|
180
|
+
console.log(chalk.gray(` Ports: ${ports.join(', ')}`));
|
|
181
|
+
console.log(chalk.gray(` Network: ${options.network}`));
|
|
182
|
+
console.log();
|
|
183
|
+
|
|
184
|
+
const spinner = ora('Starting bootstrap nodes...').start();
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const { BootstrapNode } = require('../lib/index.js');
|
|
188
|
+
|
|
189
|
+
for (const port of ports) {
|
|
190
|
+
const bootstrap = new BootstrapNode({ port, network: options.network });
|
|
191
|
+
await bootstrap.start();
|
|
192
|
+
console.log(chalk.green(` ✓ Bootstrap node started on port ${port}`));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
spinner.succeed('All bootstrap nodes running');
|
|
196
|
+
console.log();
|
|
197
|
+
console.log(chalk.gray('Press Ctrl+C to stop'));
|
|
198
|
+
|
|
199
|
+
} catch (error) {
|
|
200
|
+
spinner.fail(chalk.red('Failed to start bootstrap nodes'));
|
|
201
|
+
console.error(chalk.red(error.message));
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// STATUS command - Check node status
|
|
207
|
+
program
|
|
208
|
+
.command('status')
|
|
209
|
+
.description('Check the status of the local node')
|
|
210
|
+
.option('-r, --rpc <url>', 'RPC endpoint to query', 'http://localhost:8545')
|
|
211
|
+
.action(async (options) => {
|
|
212
|
+
console.log(chalk.cyan('Checking node status...'));
|
|
213
|
+
console.log();
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
const response = await fetch(options.rpc, {
|
|
217
|
+
method: 'POST',
|
|
218
|
+
headers: { 'Content-Type': 'application/json' },
|
|
219
|
+
body: JSON.stringify({
|
|
220
|
+
jsonrpc: '2.0',
|
|
221
|
+
method: 'jaelis_getHealth',
|
|
222
|
+
params: [],
|
|
223
|
+
id: 1
|
|
224
|
+
})
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const data = await response.json();
|
|
228
|
+
|
|
229
|
+
if (data.result) {
|
|
230
|
+
console.log(chalk.green('✓ Node is running'));
|
|
231
|
+
console.log();
|
|
232
|
+
console.log(chalk.white('Status:'));
|
|
233
|
+
console.log(chalk.gray(JSON.stringify(data.result, null, 2)));
|
|
234
|
+
} else {
|
|
235
|
+
console.log(chalk.red('✗ Node returned error'));
|
|
236
|
+
console.log(chalk.gray(JSON.stringify(data.error, null, 2)));
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.log(chalk.red('✗ Node is not reachable'));
|
|
240
|
+
console.log(chalk.gray(` Endpoint: ${options.rpc}`));
|
|
241
|
+
console.log(chalk.gray(` Error: ${error.message}`));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// PEERS command - List connected peers
|
|
246
|
+
program
|
|
247
|
+
.command('peers')
|
|
248
|
+
.description('List connected P2P peers')
|
|
249
|
+
.option('-r, --rpc <url>', 'RPC endpoint', 'http://localhost:8545')
|
|
250
|
+
.action(async (options) => {
|
|
251
|
+
console.log(chalk.cyan('Fetching peer information...'));
|
|
252
|
+
console.log();
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const response = await fetch(options.rpc, {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
headers: { 'Content-Type': 'application/json' },
|
|
258
|
+
body: JSON.stringify({
|
|
259
|
+
jsonrpc: '2.0',
|
|
260
|
+
method: 'jaelis_getPeerInfo',
|
|
261
|
+
params: [],
|
|
262
|
+
id: 1
|
|
263
|
+
})
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const data = await response.json();
|
|
267
|
+
|
|
268
|
+
if (data.result) {
|
|
269
|
+
const peers = data.result.peers || [];
|
|
270
|
+
console.log(chalk.green(`Connected Peers: ${peers.length}`));
|
|
271
|
+
console.log();
|
|
272
|
+
peers.forEach((peer, i) => {
|
|
273
|
+
console.log(chalk.white(` ${i + 1}. ${peer.id || peer}`));
|
|
274
|
+
if (peer.address) console.log(chalk.gray(` Address: ${peer.address}`));
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
console.log(chalk.yellow('No peer information available'));
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.log(chalk.red('Failed to fetch peers'));
|
|
281
|
+
console.log(chalk.gray(error.message));
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// INFO command - Display node information
|
|
286
|
+
program
|
|
287
|
+
.command('info')
|
|
288
|
+
.description('Display information about the JAELIS node')
|
|
289
|
+
.action(() => {
|
|
290
|
+
console.log(chalk.cyan(BANNER));
|
|
291
|
+
console.log(chalk.white('JAELIS Node Information:'));
|
|
292
|
+
console.log();
|
|
293
|
+
console.log(chalk.gray(' Version: ') + chalk.white(version));
|
|
294
|
+
console.log(chalk.gray(' Node.js: ') + chalk.white(process.version));
|
|
295
|
+
console.log(chalk.gray(' Platform: ') + chalk.white(process.platform));
|
|
296
|
+
console.log(chalk.gray(' Architecture: ') + chalk.white(process.arch));
|
|
297
|
+
console.log();
|
|
298
|
+
console.log(chalk.white('Networks:'));
|
|
299
|
+
console.log(chalk.gray(' Testnet: Chain ID 4545 (active)'));
|
|
300
|
+
console.log(chalk.gray(' Mainnet: Chain ID 4547 (coming soon)'));
|
|
301
|
+
console.log();
|
|
302
|
+
console.log(chalk.white('Features:'));
|
|
303
|
+
console.log(chalk.gray(' • Zero gas fees (LODE-based metering)'));
|
|
304
|
+
console.log(chalk.gray(' • PoEC consensus (Proof of Ephemeral Contribution)'));
|
|
305
|
+
console.log(chalk.gray(' • Multi-VM support (EVM, Solana, WASM, Bitcoin, Move, TON)'));
|
|
306
|
+
console.log(chalk.gray(' • AI-native blockchain with MCP & x402 support'));
|
|
307
|
+
console.log(chalk.gray(' • JZK privacy protocol (no trusted setup)'));
|
|
308
|
+
console.log(chalk.gray(' • Built-in MEV protection'));
|
|
309
|
+
console.log();
|
|
310
|
+
console.log(chalk.cyan('Documentation: https://docs.jaelis.io'));
|
|
311
|
+
console.log(chalk.cyan('Website: https://jaelis.io'));
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Parse and execute
|
|
315
|
+
program.parse();
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": {
|
|
3
|
+
"name": "my-jaelis-node",
|
|
4
|
+
"type": "full",
|
|
5
|
+
"version": "1.0.0"
|
|
6
|
+
},
|
|
7
|
+
"network": {
|
|
8
|
+
"name": "testnet",
|
|
9
|
+
"chainId": 4545,
|
|
10
|
+
"bootstrapNodes": [
|
|
11
|
+
"/dns4/rpc.jaelis.io/tcp/30305/p2p/QmBootstrap1",
|
|
12
|
+
"/dns4/rpc.jaelis.io/tcp/30306/p2p/QmBootstrap2",
|
|
13
|
+
"/dns4/rpc.jaelis.io/tcp/30307/p2p/QmBootstrap3"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"rpc": {
|
|
17
|
+
"enabled": true,
|
|
18
|
+
"host": "0.0.0.0",
|
|
19
|
+
"port": 8545,
|
|
20
|
+
"cors": "*",
|
|
21
|
+
"methods": {
|
|
22
|
+
"eth": true,
|
|
23
|
+
"jaelis": true,
|
|
24
|
+
"net": true,
|
|
25
|
+
"web3": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"p2p": {
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"port": 30303,
|
|
31
|
+
"maxPeers": 50,
|
|
32
|
+
"discovery": true
|
|
33
|
+
},
|
|
34
|
+
"storage": {
|
|
35
|
+
"dataDir": "./jaelis-data",
|
|
36
|
+
"engine": "leveldb",
|
|
37
|
+
"cache": {
|
|
38
|
+
"blocks": 512,
|
|
39
|
+
"states": 256
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"sync": {
|
|
43
|
+
"mode": "full",
|
|
44
|
+
"fastSync": true,
|
|
45
|
+
"pruning": false
|
|
46
|
+
},
|
|
47
|
+
"validator": {
|
|
48
|
+
"enabled": false,
|
|
49
|
+
"stake": "1000",
|
|
50
|
+
"keyFile": null,
|
|
51
|
+
"autoCompound": true
|
|
52
|
+
},
|
|
53
|
+
"metrics": {
|
|
54
|
+
"enabled": true,
|
|
55
|
+
"port": 9090,
|
|
56
|
+
"path": "/metrics"
|
|
57
|
+
},
|
|
58
|
+
"logging": {
|
|
59
|
+
"level": "info",
|
|
60
|
+
"console": true,
|
|
61
|
+
"file": {
|
|
62
|
+
"enabled": true,
|
|
63
|
+
"path": "./jaelis-data/logs/node.log",
|
|
64
|
+
"maxSize": "100MB",
|
|
65
|
+
"maxFiles": 10
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"security": {
|
|
69
|
+
"maxRequestSize": "1MB",
|
|
70
|
+
"rateLimit": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"windowMs": 60000,
|
|
73
|
+
"maxRequests": 1000
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "default",
|
|
3
|
+
"network": {
|
|
4
|
+
"name": "mainnet",
|
|
5
|
+
"chainId": 4547,
|
|
6
|
+
"symbol": "JAELIS",
|
|
7
|
+
"rpcEndpoints": [
|
|
8
|
+
"https://mainnet.jaelis.io"
|
|
9
|
+
],
|
|
10
|
+
"bootstrapNodes": [],
|
|
11
|
+
"genesis": {
|
|
12
|
+
"timestamp": null,
|
|
13
|
+
"difficulty": 1,
|
|
14
|
+
"gasLimit": "0x0",
|
|
15
|
+
"extraData": "0x4a41454c495320426c6f636b636861696e202d204d61696e6e6574"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"storage": {
|
|
19
|
+
"dataDir": "./jaelis-data/mainnet"
|
|
20
|
+
},
|
|
21
|
+
"logging": {
|
|
22
|
+
"level": "info"
|
|
23
|
+
},
|
|
24
|
+
"validator": {
|
|
25
|
+
"enabled": false,
|
|
26
|
+
"stake": "10000",
|
|
27
|
+
"autoCompound": true
|
|
28
|
+
},
|
|
29
|
+
"_status": "coming-soon"
|
|
30
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "default",
|
|
3
|
+
"network": {
|
|
4
|
+
"name": "testnet",
|
|
5
|
+
"chainId": 4545,
|
|
6
|
+
"symbol": "tJAELIS",
|
|
7
|
+
"rpcEndpoints": [
|
|
8
|
+
"https://rpc.jaelis.io"
|
|
9
|
+
],
|
|
10
|
+
"bootstrapNodes": [
|
|
11
|
+
"/dns4/rpc.jaelis.io/tcp/30305/p2p/QmBootstrap1",
|
|
12
|
+
"/dns4/rpc.jaelis.io/tcp/30306/p2p/QmBootstrap2",
|
|
13
|
+
"/dns4/rpc.jaelis.io/tcp/30307/p2p/QmBootstrap3"
|
|
14
|
+
],
|
|
15
|
+
"genesis": {
|
|
16
|
+
"timestamp": 1731398400000,
|
|
17
|
+
"difficulty": 1,
|
|
18
|
+
"gasLimit": "0x0",
|
|
19
|
+
"extraData": "0x4a41454c495320426c6f636b636861696e202d2054657374676e6573697320426c6f636b"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"storage": {
|
|
23
|
+
"dataDir": "./jaelis-data/testnet"
|
|
24
|
+
},
|
|
25
|
+
"logging": {
|
|
26
|
+
"level": "debug"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JAELIS Node Library
|
|
3
|
+
* Core node implementation for the JAELIS blockchain
|
|
4
|
+
*
|
|
5
|
+
* This module provides the JaelisNode class which wraps the core blockchain
|
|
6
|
+
* implementation for easy deployment as a standalone node.
|
|
7
|
+
*
|
|
8
|
+
* @version 1.0.0
|
|
9
|
+
* @author JAELIS Foundation
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const EventEmitter = require('events');
|
|
15
|
+
|
|
16
|
+
// JAELIS Network Endpoints - users connect here automatically
|
|
17
|
+
const JAELIS_NETWORKS = {
|
|
18
|
+
testnet: {
|
|
19
|
+
name: 'JAELIS Testnet',
|
|
20
|
+
chainId: 4545,
|
|
21
|
+
symbol: 'tJAELIS',
|
|
22
|
+
rpcUrl: 'https://rpc.jaelis.io',
|
|
23
|
+
wsUrl: 'wss://rpc.jaelis.io/ws',
|
|
24
|
+
explorerUrl: 'https://explorer.jaelis.io',
|
|
25
|
+
bootstrapNodes: [
|
|
26
|
+
'/dns4/rpc.jaelis.io/tcp/30303'
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
mainnet: {
|
|
30
|
+
name: 'JAELIS Mainnet',
|
|
31
|
+
chainId: 4547,
|
|
32
|
+
symbol: 'JAELIS',
|
|
33
|
+
rpcUrl: 'https://mainnet.jaelis.io',
|
|
34
|
+
wsUrl: 'wss://mainnet.jaelis.io/ws',
|
|
35
|
+
explorerUrl: 'https://explorer.jaelis.io',
|
|
36
|
+
bootstrapNodes: [],
|
|
37
|
+
status: 'coming-soon'
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* JaelisNode - Main node class for running a JAELIS blockchain node
|
|
43
|
+
*/
|
|
44
|
+
class JaelisNode extends EventEmitter {
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
super();
|
|
47
|
+
|
|
48
|
+
// Get network config - defaults to testnet
|
|
49
|
+
const networkName = options.network || 'testnet';
|
|
50
|
+
const networkConfig = JAELIS_NETWORKS[networkName] || JAELIS_NETWORKS.testnet;
|
|
51
|
+
|
|
52
|
+
this.options = {
|
|
53
|
+
network: networkName,
|
|
54
|
+
networkConfig: networkConfig,
|
|
55
|
+
chainId: networkConfig.chainId,
|
|
56
|
+
rpcPort: options.rpcPort || 8545,
|
|
57
|
+
rpcHost: options.rpcHost || '0.0.0.0',
|
|
58
|
+
p2pPort: options.p2pPort || 30303,
|
|
59
|
+
dataDir: options.dataDir || './jaelis-data',
|
|
60
|
+
syncMode: options.syncMode || 'full',
|
|
61
|
+
enableRpc: options.enableRpc !== false,
|
|
62
|
+
enableP2p: options.enableP2p !== false,
|
|
63
|
+
bootstrapNodes: options.bootstrapNodes || networkConfig.bootstrapNodes,
|
|
64
|
+
maxPeers: options.maxPeers || 50,
|
|
65
|
+
// Remote RPC to sync from
|
|
66
|
+
remoteRpc: options.remoteRpc || networkConfig.rpcUrl,
|
|
67
|
+
...options
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
this.blockchain = null;
|
|
71
|
+
this.network = null;
|
|
72
|
+
this.rpcServer = null;
|
|
73
|
+
this.isRunning = false;
|
|
74
|
+
this.startTime = null;
|
|
75
|
+
this.peerCount = 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Start the node
|
|
80
|
+
*/
|
|
81
|
+
async start() {
|
|
82
|
+
if (this.isRunning) {
|
|
83
|
+
throw new Error('Node is already running');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log(`[JAELIS] Starting node on ${this.options.network}...`);
|
|
87
|
+
|
|
88
|
+
// Ensure data directory exists
|
|
89
|
+
if (!fs.existsSync(this.options.dataDir)) {
|
|
90
|
+
fs.mkdirSync(this.options.dataDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Try to load from JAELIS core
|
|
95
|
+
await this._loadCore();
|
|
96
|
+
|
|
97
|
+
// Initialize blockchain
|
|
98
|
+
await this._initBlockchain();
|
|
99
|
+
|
|
100
|
+
// Start P2P networking
|
|
101
|
+
if (this.options.enableP2p) {
|
|
102
|
+
await this._initNetwork();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Start RPC server
|
|
106
|
+
if (this.options.enableRpc) {
|
|
107
|
+
await this._initRpcServer();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.isRunning = true;
|
|
111
|
+
this.startTime = Date.now();
|
|
112
|
+
|
|
113
|
+
this.emit('started', {
|
|
114
|
+
network: this.options.network,
|
|
115
|
+
chainId: this.options.chainId,
|
|
116
|
+
rpcPort: this.options.rpcPort,
|
|
117
|
+
p2pPort: this.options.p2pPort
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log(`[JAELIS] Node started successfully`);
|
|
121
|
+
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(`[JAELIS] Failed to start node: ${error.message}`);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Stop the node
|
|
130
|
+
*/
|
|
131
|
+
async stop() {
|
|
132
|
+
if (!this.isRunning) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
console.log('[JAELIS] Stopping node...');
|
|
137
|
+
|
|
138
|
+
// Stop RPC server
|
|
139
|
+
if (this.rpcServer) {
|
|
140
|
+
await this._stopRpcServer();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Stop P2P network
|
|
144
|
+
if (this.network) {
|
|
145
|
+
await this._stopNetwork();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Save blockchain state
|
|
149
|
+
if (this.blockchain) {
|
|
150
|
+
await this._saveState();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
this.isRunning = false;
|
|
154
|
+
this.emit('stopped');
|
|
155
|
+
|
|
156
|
+
console.log('[JAELIS] Node stopped');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Initialize node modules - connects to JAELIS network
|
|
161
|
+
*/
|
|
162
|
+
async _loadCore() {
|
|
163
|
+
console.log(`[JAELIS] Connecting to ${this.options.networkConfig.name}...`);
|
|
164
|
+
console.log(`[JAELIS] RPC: ${this.options.remoteRpc}`);
|
|
165
|
+
|
|
166
|
+
// Use embedded light client that syncs from remote RPC
|
|
167
|
+
this.JaelisBlockchain = EmbeddedBlockchain;
|
|
168
|
+
this.JaelisNetwork = EmbeddedNetwork;
|
|
169
|
+
this.JaelisRpcServer = EmbeddedRpcServer;
|
|
170
|
+
this.JaelisStorage = EmbeddedStorage;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Initialize blockchain
|
|
175
|
+
*/
|
|
176
|
+
async _initBlockchain() {
|
|
177
|
+
console.log('[JAELIS] Initializing blockchain...');
|
|
178
|
+
|
|
179
|
+
const storagePath = path.join(this.options.dataDir, 'blockchain');
|
|
180
|
+
|
|
181
|
+
if (this.JaelisBlockchain) {
|
|
182
|
+
this.blockchain = new this.JaelisBlockchain({
|
|
183
|
+
chainId: this.options.chainId,
|
|
184
|
+
dataDir: storagePath,
|
|
185
|
+
network: this.options.network
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (this.blockchain.initialize) {
|
|
189
|
+
await this.blockchain.initialize();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log(`[JAELIS] Blockchain initialized (Chain ID: ${this.options.chainId})`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Initialize P2P network
|
|
198
|
+
*/
|
|
199
|
+
async _initNetwork() {
|
|
200
|
+
console.log('[JAELIS] Initializing P2P network...');
|
|
201
|
+
|
|
202
|
+
if (this.JaelisNetwork) {
|
|
203
|
+
this.network = new this.JaelisNetwork({
|
|
204
|
+
port: this.options.p2pPort,
|
|
205
|
+
maxPeers: this.options.maxPeers,
|
|
206
|
+
bootstrapNodes: this.options.bootstrapNodes,
|
|
207
|
+
blockchain: this.blockchain
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (this.network.start) {
|
|
211
|
+
await this.network.start();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Track peer count
|
|
215
|
+
if (this.network.on) {
|
|
216
|
+
this.network.on('peer:connect', () => {
|
|
217
|
+
this.peerCount++;
|
|
218
|
+
this.emit('peer:connect', this.peerCount);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
this.network.on('peer:disconnect', () => {
|
|
222
|
+
this.peerCount = Math.max(0, this.peerCount - 1);
|
|
223
|
+
this.emit('peer:disconnect', this.peerCount);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log(`[JAELIS] P2P network started on port ${this.options.p2pPort}`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Initialize RPC server
|
|
233
|
+
*/
|
|
234
|
+
async _initRpcServer() {
|
|
235
|
+
console.log('[JAELIS] Initializing RPC server...');
|
|
236
|
+
|
|
237
|
+
if (this.JaelisRpcServer) {
|
|
238
|
+
this.rpcServer = new this.JaelisRpcServer(this.blockchain, this.options.rpcPort);
|
|
239
|
+
|
|
240
|
+
if (this.rpcServer.start) {
|
|
241
|
+
await this.rpcServer.start();
|
|
242
|
+
} else if (this.rpcServer.listen) {
|
|
243
|
+
await this.rpcServer.listen(this.options.rpcPort, this.options.rpcHost);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
console.log(`[JAELIS] RPC server started on http://${this.options.rpcHost}:${this.options.rpcPort}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Stop RPC server
|
|
252
|
+
*/
|
|
253
|
+
async _stopRpcServer() {
|
|
254
|
+
if (this.rpcServer && this.rpcServer.stop) {
|
|
255
|
+
await this.rpcServer.stop();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Stop P2P network
|
|
261
|
+
*/
|
|
262
|
+
async _stopNetwork() {
|
|
263
|
+
if (this.network && this.network.stop) {
|
|
264
|
+
await this.network.stop();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Save blockchain state
|
|
270
|
+
*/
|
|
271
|
+
async _saveState() {
|
|
272
|
+
if (this.blockchain && this.blockchain.save) {
|
|
273
|
+
await this.blockchain.save();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Get node status
|
|
279
|
+
*/
|
|
280
|
+
getStatus() {
|
|
281
|
+
return {
|
|
282
|
+
running: this.isRunning,
|
|
283
|
+
network: this.options.network,
|
|
284
|
+
chainId: this.options.chainId,
|
|
285
|
+
uptime: this.startTime ? Date.now() - this.startTime : 0,
|
|
286
|
+
peers: this.peerCount,
|
|
287
|
+
rpcPort: this.options.rpcPort,
|
|
288
|
+
p2pPort: this.options.p2pPort,
|
|
289
|
+
syncMode: this.options.syncMode,
|
|
290
|
+
blockHeight: this.blockchain?.getHeight?.() || 0
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* BootstrapNode - Lightweight node for peer discovery
|
|
297
|
+
*/
|
|
298
|
+
class BootstrapNode extends EventEmitter {
|
|
299
|
+
constructor(options = {}) {
|
|
300
|
+
super();
|
|
301
|
+
this.port = options.port || 30305;
|
|
302
|
+
this.network = options.network || 'testnet';
|
|
303
|
+
this.peers = new Map();
|
|
304
|
+
this.server = null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async start() {
|
|
308
|
+
console.log(`[BOOTSTRAP] Starting on port ${this.port}...`);
|
|
309
|
+
|
|
310
|
+
// Simple TCP server for peer discovery
|
|
311
|
+
const net = require('net');
|
|
312
|
+
|
|
313
|
+
this.server = net.createServer((socket) => {
|
|
314
|
+
const peerId = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
315
|
+
this.peers.set(peerId, { socket, connectedAt: Date.now() });
|
|
316
|
+
|
|
317
|
+
console.log(`[BOOTSTRAP] Peer connected: ${peerId} (${this.peers.size} total)`);
|
|
318
|
+
|
|
319
|
+
socket.on('data', (data) => {
|
|
320
|
+
// Handle peer discovery requests
|
|
321
|
+
try {
|
|
322
|
+
const msg = JSON.parse(data.toString());
|
|
323
|
+
if (msg.type === 'getPeers') {
|
|
324
|
+
const peerList = Array.from(this.peers.keys());
|
|
325
|
+
socket.write(JSON.stringify({ type: 'peers', peers: peerList }));
|
|
326
|
+
}
|
|
327
|
+
} catch (e) {
|
|
328
|
+
// Ignore invalid messages
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
socket.on('close', () => {
|
|
333
|
+
this.peers.delete(peerId);
|
|
334
|
+
console.log(`[BOOTSTRAP] Peer disconnected: ${peerId} (${this.peers.size} remaining)`);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
socket.on('error', () => {
|
|
338
|
+
this.peers.delete(peerId);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
return new Promise((resolve, reject) => {
|
|
343
|
+
this.server.listen(this.port, '0.0.0.0', () => {
|
|
344
|
+
console.log(`[BOOTSTRAP] Listening on port ${this.port}`);
|
|
345
|
+
resolve();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
this.server.on('error', reject);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async stop() {
|
|
353
|
+
if (this.server) {
|
|
354
|
+
this.server.close();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
getPeerCount() {
|
|
359
|
+
return this.peers.size;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ============================================================
|
|
364
|
+
// EMBEDDED MINIMAL IMPLEMENTATIONS (for standalone operation)
|
|
365
|
+
// ============================================================
|
|
366
|
+
|
|
367
|
+
class EmbeddedBlockchain {
|
|
368
|
+
constructor(options = {}) {
|
|
369
|
+
this.chainId = options.chainId || 4545;
|
|
370
|
+
this.chain = [];
|
|
371
|
+
this.state = new Map();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async initialize() {
|
|
375
|
+
// Create genesis block if needed
|
|
376
|
+
if (this.chain.length === 0) {
|
|
377
|
+
this.chain.push({
|
|
378
|
+
number: 0,
|
|
379
|
+
hash: '0x' + '0'.repeat(64),
|
|
380
|
+
timestamp: Date.now(),
|
|
381
|
+
transactions: []
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
getHeight() {
|
|
387
|
+
return this.chain.length - 1;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async save() {
|
|
391
|
+
// Minimal save
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
class EmbeddedNetwork {
|
|
396
|
+
constructor(options = {}) {
|
|
397
|
+
this.port = options.port;
|
|
398
|
+
this.peers = [];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async start() {
|
|
402
|
+
console.log(`[NETWORK] Embedded network started on port ${this.port}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
async stop() {
|
|
406
|
+
// Cleanup
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
class EmbeddedRpcServer {
|
|
411
|
+
constructor(blockchain, port) {
|
|
412
|
+
this.blockchain = blockchain;
|
|
413
|
+
this.port = port;
|
|
414
|
+
this.app = null;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
async start() {
|
|
418
|
+
const express = require('express');
|
|
419
|
+
const cors = require('cors');
|
|
420
|
+
|
|
421
|
+
this.app = express();
|
|
422
|
+
this.app.use(cors());
|
|
423
|
+
this.app.use(express.json());
|
|
424
|
+
|
|
425
|
+
// Basic RPC endpoint
|
|
426
|
+
this.app.post('/', (req, res) => {
|
|
427
|
+
const { method, params, id } = req.body;
|
|
428
|
+
|
|
429
|
+
let result = null;
|
|
430
|
+
let error = null;
|
|
431
|
+
|
|
432
|
+
switch (method) {
|
|
433
|
+
case 'jaelis_getHealth':
|
|
434
|
+
case 'eth_chainId':
|
|
435
|
+
result = { status: 'healthy', chainId: this.blockchain?.chainId };
|
|
436
|
+
break;
|
|
437
|
+
case 'jaelis_blockNumber':
|
|
438
|
+
case 'eth_blockNumber':
|
|
439
|
+
result = '0x' + (this.blockchain?.getHeight?.() || 0).toString(16);
|
|
440
|
+
break;
|
|
441
|
+
default:
|
|
442
|
+
error = { code: -32601, message: 'Method not found' };
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
res.json({ jsonrpc: '2.0', id, result, error });
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Health check
|
|
449
|
+
this.app.get('/', (req, res) => {
|
|
450
|
+
res.json({ status: 'online', service: 'JAELIS Node', chainId: this.blockchain?.chainId });
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
return new Promise((resolve) => {
|
|
454
|
+
this.server = this.app.listen(this.port, '0.0.0.0', () => {
|
|
455
|
+
resolve();
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
async stop() {
|
|
461
|
+
if (this.server) {
|
|
462
|
+
this.server.close();
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
class EmbeddedStorage {
|
|
468
|
+
constructor() {
|
|
469
|
+
this.data = new Map();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async get(key) {
|
|
473
|
+
return this.data.get(key);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async put(key, value) {
|
|
477
|
+
this.data.set(key, value);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// ============================================================
|
|
482
|
+
// EXPORTS
|
|
483
|
+
// ============================================================
|
|
484
|
+
|
|
485
|
+
module.exports = {
|
|
486
|
+
JaelisNode,
|
|
487
|
+
BootstrapNode,
|
|
488
|
+
EmbeddedBlockchain,
|
|
489
|
+
EmbeddedNetwork,
|
|
490
|
+
EmbeddedRpcServer,
|
|
491
|
+
EmbeddedStorage,
|
|
492
|
+
|
|
493
|
+
// Convenience factory
|
|
494
|
+
createNode: (options) => new JaelisNode(options),
|
|
495
|
+
createBootstrap: (options) => new BootstrapNode(options)
|
|
496
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jaelis-node",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official JAELIS Blockchain Node - Run a full node, validator, or bootstrap node on the JAELIS network",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jaelis-node": "./bin/jaelis-node.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/jaelis-node.js start",
|
|
11
|
+
"start:testnet": "node bin/jaelis-node.js start --network testnet",
|
|
12
|
+
"start:mainnet": "node bin/jaelis-node.js start --network mainnet",
|
|
13
|
+
"validator": "node bin/jaelis-node.js validator",
|
|
14
|
+
"bootstrap": "node bin/jaelis-node.js bootstrap",
|
|
15
|
+
"status": "node bin/jaelis-node.js status",
|
|
16
|
+
"test": "node test/node.test.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"jaelis",
|
|
20
|
+
"blockchain",
|
|
21
|
+
"node",
|
|
22
|
+
"validator",
|
|
23
|
+
"p2p",
|
|
24
|
+
"web3",
|
|
25
|
+
"ethereum",
|
|
26
|
+
"zero-gas",
|
|
27
|
+
"zero-fee",
|
|
28
|
+
"proof-of-stake",
|
|
29
|
+
"poec",
|
|
30
|
+
"consensus",
|
|
31
|
+
"decentralized",
|
|
32
|
+
"crypto",
|
|
33
|
+
"cryptocurrency",
|
|
34
|
+
"ai-native",
|
|
35
|
+
"mcp",
|
|
36
|
+
"full-node",
|
|
37
|
+
"bootstrap-node",
|
|
38
|
+
"rpc-server"
|
|
39
|
+
],
|
|
40
|
+
"author": "JAELIS Foundation",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/jaelis-foundation/jaelis-node"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/jaelis-foundation/jaelis-node/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://jaelis.io",
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"level": "^8.0.0",
|
|
55
|
+
"libp2p": "^0.46.0",
|
|
56
|
+
"express": "^4.18.2",
|
|
57
|
+
"cors": "^2.8.5",
|
|
58
|
+
"commander": "^11.1.0",
|
|
59
|
+
"chalk": "^4.1.2",
|
|
60
|
+
"ora": "^5.4.1"
|
|
61
|
+
},
|
|
62
|
+
"files": [
|
|
63
|
+
"bin/",
|
|
64
|
+
"lib/",
|
|
65
|
+
"config/",
|
|
66
|
+
"README.md"
|
|
67
|
+
]
|
|
68
|
+
}
|