Skip to content

Commit

Permalink
Merge pull request #74 from eosnetworkfoundation/elmato/ws-proxy
Browse files Browse the repository at this point in the history
Add initial version of eos-evm-ws-proxy
  • Loading branch information
elmato authored Oct 13, 2023
2 parents 28b9adc + 9937d84 commit a6bf409
Show file tree
Hide file tree
Showing 12 changed files with 4,564 additions and 0 deletions.
8 changes: 8 additions & 0 deletions peripherals/eos-evm-ws-proxy/.env-sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
WEB3_RPC_ENDPOINT=http://localhost:5000
NODEOS_RPC_ENDPOINT=http://localhost:8889
POLL_INTERVAL=500
WS_LISTENING_PORT=3333
WS_LISTENING_HOST=localhost
MAX_LOGS_SUBS_PER_CONNECTION=1
MAX_MINEDTX_SUBS_PER_CONNECTION=1
LOG_LEVEL=debug
42 changes: 42 additions & 0 deletions peripherals/eos-evm-ws-proxy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Node modules folder
node_modules/

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*.swn
*.bak

# OS generated files
.DS_Store
Thumbs.db
44 changes: 44 additions & 0 deletions peripherals/eos-evm-ws-proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# eos-evm-ws-proxy

### Installation

1. **Clone the Repository**
```
git clone https://github.com/eosnetworkfoundation/eos-evm-node
cd eos-evm-node/peripherals/eos-evm-ws-proxy
```

2. **Install Dependencies**
```
npm install
```

3. **Setup Configuration**

Copy the `.env-sample` file and rename it to `.env`.
```
cp .env-sample .env
```

Modify the `.env` file to adjust the configuration to your setup.

### Running the Proxy

Execute the `main.js` script to start the proxy server:

```
node main.js
```

## Configuration

The following environment variables are available for configuration in the `.env` file:

- `WEB3_RPC_ENDPOINT`: The endpoint for the eos-evm-rpc
- `NODEOS_RPC_ENDPOINT`: The endpoint for the nodeos RPC
- `POLL_INTERVAL`: The interval (in milliseconds) at which the blockchain is polled
- `WS_LISTENING_PORT`: The port on which the WebSocket server listens
- `WS_LISTENING_HOST`: The host address on which the WebSocket server listens
- `MAX_LOGS_SUBS_PER_CONNECTION`: The maximum number of `logs`` subscriptions per connection.
- `MAX_MINEDTX_SUBS_PER_CONNECTION`: The maximum number of `minedTransactions` subscriptions per connection.
- `LOG_LEVEL`: Logging level (e.g., `debug`).
101 changes: 101 additions & 0 deletions peripherals/eos-evm-ws-proxy/block-monitor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const EventEmitter = require('events');
const axios = require('axios');
const {Web3} = require('web3');
const Deque = require('collections/deque');
const {num_from_id} = require('./utils');
class BlockMonitor extends EventEmitter {

constructor({ web3_rpc_endpoint, nodeos_rpc_endpoint, poll_interval, logger}) {
super();
this.web3_rpc_endpoint = web3_rpc_endpoint;
this.nodeos_rpc_endpoint = nodeos_rpc_endpoint;
this.poll_interval = poll_interval;
this.web3 = new Web3(web3_rpc_endpoint);
this.logger = logger;

this.reversible_blocks = new Deque();
this.run = false;
this.timer_id = null;
}

async get_eos_lib() {
const response = await axios.post(this.nodeos_rpc_endpoint+'/v1/chain/get_info', {});
return response.data.last_irreversible_block_num;
}

remove_front_block() {
const block = this.reversible_blocks.shift();
this.emit('block_removed', {block});
}

fork_last_block() {
const block = this.reversible_blocks.pop();
this.logger.debug(`FORK_LAST_BLOCK ${block}`);
this.emit('block_forked', {block});
return this.reversible_blocks.peekBack();
}

append_new_block(block) {
this.reversible_blocks.add(block);
this.emit('block_appended', {block});
}

async poll() {
try {
let last = this.reversible_blocks.peekBack();
if( last == undefined ) {
last = await this.web3.eth.getBlock("latest", true);
this.append_new_block(last);
}

let next_block = await this.web3.eth.getBlock(last.number+BigInt(1), true);
let found_next_block = false;
while(last != null && next_block != null) {
found_next_block = true;
if(next_block.parentHash == last.hash) {
this.append_new_block(next_block);
last = next_block;
next_block = await this.web3.eth.getBlock(last.number+BigInt(1), true);
} else {
last = this.fork_last_block();
}
}

if( found_next_block == true ) {
const eos_lib = await this.get_eos_lib();
while(this.reversible_blocks.length > 0 && num_from_id(this.reversible_blocks.peek().mixHash) <= eos_lib) {
this.logger.debug(`eoslib: ${eos_lib} ${num_from_id(this.reversible_blocks.peek().mixHash)} ${this.reversible_blocks.peek().number} ${this.reversible_blocks.peek().mixHash}`);
this.remove_front_block();
}
}

} catch (error) {
this.logger.error(error.message);
}

if(this.run == true) {
this.timer_id = setTimeout(() => this.poll(), this.poll_interval || 5000);
} else {
this.reversible_blocks.clear();
this.logger.info("BlockMonitor stopped");
}
}

start() {
this.logger.info("BlockMonitor start");
this.run = true;
setTimeout(() => this.poll(), 0);
}

stop() {
clearTimeout(this.timer_id);
this.logger.info("BlockMonitor stopping");
this.run = false;
}

is_running() {
return this.run;
}
}

module.exports = BlockMonitor;
20 changes: 20 additions & 0 deletions peripherals/eos-evm-ws-proxy/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require('dotenv').config();
const ws_listening_port = parseInt(process.env.WS_LISTENING_PORT, 10) || 3333;
const ws_listening_host = process.env.WS_LISTENING_HOST || "localhost";
const web3_rpc_endpoint = process.env.WEB3_RPC_ENDPOINT || "http://localhost:5000/";
const nodeos_rpc_endpoint = process.env.NODEOS_RPC_ENDPOINT || "http://127.0.0.1:8888/";
const poll_interval = parseInt(process.env.POLL_INTERVAL, 10) || 1000;
const max_logs_subs_per_connection = parseInt(process.env.MAX_LOGS_SUBS_PER_CONNECTION, 10) || 1;
const max_minedtx_subs_per_connection = parseInt(process.env.MAX_MINEDTX_SUBS_PER_CONNECTION, 10) || 1;
const log_level = process.env.LOG_LEVEL || 'info';

module.exports = {
ws_listening_port,
ws_listening_host,
web3_rpc_endpoint,
nodeos_rpc_endpoint,
poll_interval,
max_logs_subs_per_connection,
max_minedtx_subs_per_connection,
log_level
};
25 changes: 25 additions & 0 deletions peripherals/eos-evm-ws-proxy/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const config = require('./config');
const winston = require('winston');

const logger = winston.createLogger({
level: config.log_level,
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});

//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
}

module.exports = {
logger
};
5 changes: 5 additions & 0 deletions peripherals/eos-evm-ws-proxy/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const config = require('./config');
const {logger} = require('./logger');
const SubscriptionServer = require('./subscription-server');
const server = new SubscriptionServer({...config, logger});
server.start();
Loading

0 comments on commit a6bf409

Please sign in to comment.