Skip to content

Commit

Permalink
Merge branch 'gda-docker-release' into snapshot-workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
mmd-afegbua authored Feb 21, 2024
2 parents 215c6d5 + 2799ab3 commit c98c63a
Show file tree
Hide file tree
Showing 17 changed files with 196 additions and 195 deletions.
13 changes: 8 additions & 5 deletions .env-example → .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,19 @@ HTTP_RPC_NODE=
## When running with Docker, this will affect the host port binding, not the binding inside the container.
#METRICS_PORT=9100

# Let the sentinel instance periodically report a basic metrics to a remote server.
# Set this to false in order to disable it.
## Let the sentinel instance periodically report a basic metrics to a remote server.
## Set this to false in order to disable it.
#TELEMETRY=true

# Default telemetry server instance provided by Superfluid
## Default telemetry server instance provided by Superfluid
#TELEMETRY_URL=https://sentinel-telemetry.x.superfluid.dev

# Reporting interval, defaults to 12 hours
## Reporting interval, defaults to 12 hours
#TELEMETRY_INTERVAL=43200

## Allows to set a custom instance name, included in the data sent to the telemetry server.
#INSTANCE_NAME=Sentinel

## If set, you get notified about key events like process (re)starts, configuration changes and error conditions
## to the Slack channel the hook belongs to.
#SLACK_WEBHOOK_URL=
Expand All @@ -147,7 +150,7 @@ HTTP_RPC_NODE=

## Location of the sqlite database. The file (and non-existing directories in the path) will be created if not existing.
## Note: this is ignored (overridden) when running with Docker.
#DB_PATH=db.sqlite
#DB_PATH=data/db.sqlite


## --- DOCKER PARAMETERS ---
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ artifacts
database.sqlite
.env
.env*
!.env-example
!.env.example
.DS_Store
*.sqlite
.npmrc
Expand Down
7 changes: 2 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@ WORKDIR /app

# Install dependencies
RUN apk add --update --no-cache \
g++ \
make \
python3 \
yarn \
tini && ln -sf python3 /usr/bin/python
yarn \
tini

# Copy package.json and yarn.lock
COPY package.json yarn.lock ./
Expand Down
223 changes: 122 additions & 101 deletions README.md

Large diffs are not rendered by default.

20 changes: 4 additions & 16 deletions docker-compose-with-monitoring.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
# Starts the Sentinel service and connected monitoring services: Prometheus and Grafana.

version: '3'
networks:
monitoring:
driver: bridge

services:
# the sentinel image is built from source
sentinel:
image: superfluidfinance/superfluid-sentinel
build: .
restart: unless-stopped
env_file: .env
environment:
- NODE_ENV=production
- DB_PATH=data/db.sqlite
# hardcode the port inside the container
- METRICS_PORT=9100
ports:
Expand All @@ -22,8 +19,6 @@ services:
- 9100
volumes:
- data:/app/data
networks:
- monitoring
deploy:
resources:
limits:
Expand All @@ -33,19 +28,15 @@ services:
memory: 50M
prometheus:
image: prom/prometheus:v2.36.1
container_name: prometheus
volumes:
- ./prometheus:/etc/prometheus
- prometheus_data:/prometheus
ports:
- ${PROMETHEUS_PORT:-9090}:9090
expose:
- ${PROMETHEUS_PORT:-9090}
networks:
- monitoring
grafana:
image: grafana/grafana:8.2.6
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
Expand All @@ -55,11 +46,8 @@ services:
- ${GRAFANA_PORT:-3000}:3000
expose:
- ${GRAFANA_PORT:-3000}
networks:
- monitoring

volumes:
prometheus_data: { }
grafana_data: { }
data: { }

prometheus_data:
grafana_data:
data:
11 changes: 2 additions & 9 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
# Minimal version which starts the sentinel service only, without additional monitoring services.
# Basic docker-compose file for running a sentinel.
# This is ideal for resource constrained environments or for use with custom monitoring setups.

version: '3'
services:
sentinel:
image: superfluidfinance/superfluid-sentinel:${SENTINEL_VERSION:-latest}
build: .
restart: unless-stopped
env_file: .env
environment:
- NODE_ENV=production
- DB_PATH=data/db.sqlite
# hardcode the port inside the container
- METRICS_PORT=9100
ports:
- ${METRICS_PORT:-9100}:9100
volumes:
- data:/app/data
deploy:
resources:
limits:
cpus: '0.50'
memory: 300M
reservations:
memory: 50M

volumes:
data:
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "superfluid-sentinel",
"version": "0.11.0",
"version": "1.0.0",
"description": "Superfluid Sentinel",
"main": "main.js",
"scripts": {
Expand Down Expand Up @@ -28,7 +28,7 @@
"@decentral.ee/web3-helpers": "^0.5.3",
"@slack/webhook": "^6.1.0",
"@superfluid-finance/ethereum-contracts": "1.9.0",
"@superfluid-finance/metadata": "1.1.27",
"@superfluid-finance/metadata": "^1.1.27",
"async": "^3.2.4",
"axios": "^1.4.0",
"bip39": "^3.1.0",
Expand Down
42 changes: 17 additions & 25 deletions scripts/printTOGAstatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,11 @@
*/

require("dotenv").config();
const togaABI = require("../src/inc/TOGA.json");
const Web3 = require("web3");
const togaArtifact = require("@superfluid-finance/ethereum-contracts/build/truffle/TOGA.json");
const { Web3 } = require("web3");
const axios = require("axios");
const { wad4human, toBN } = require("@decentral.ee/web3-helpers");

/* CONFIGS */
const NETWORKS = {
100: {
name: "xdai",
theGraphQueryUrl: "https://api.thegraph.com/subgraphs/name/superfluid-finance/protocol-v1-xdai",
toga: "0xb7DE52F4281a7a276E18C40F94cd93159C4A2d22"
},
137: {
name: "matic",
theGraphQueryUrl: "https://api.thegraph.com/subgraphs/name/superfluid-finance/protocol-v1-matic",
toga: "0x6AEAeE5Fd4D05A741723D752D30EE4D72690A8f7"
}
};
const { wad4human } = require("@decentral.ee/web3-helpers");
const metadata = require("@superfluid-finance/metadata");

async function getSuperTokens (graphAPI) {
const query = `query MyQuery {
Expand All @@ -45,16 +32,21 @@ async function getSuperTokens (graphAPI) {

(async () => {
const web3 = new Web3(process.env.HTTP_RPC_NODE);
const chainId = await web3.eth.getChainId();
const networkConfig = NETWORKS[chainId];
if (networkConfig === undefined) {
console.error(`no config found for chainId ${chainId}`);
const chainId = parseInt(await web3.eth.getChainId());
const network = metadata.getNetworkByChainId(chainId);
if (network === undefined) {
console.error(`no network config found for chainId ${chainId}`);
process.exit(1);
}
if (network.contractsV1.toga === undefined) {
console.error(`no TOGA contract in metadata for chainId ${chainId}`);
process.exit(1);
}

const toga = new web3.eth.Contract(togaABI, networkConfig.toga);
const toga = new web3.eth.Contract(togaArtifact.abi, network.contractsV1.toga);
const graphApiUrl = `https://${network.name}.subgraph.x.superfluid.dev`;
const table = [];
const superTokens = await getSuperTokens(networkConfig.theGraphQueryUrl);
const superTokens = await getSuperTokens(graphApiUrl);

for (let i = 0; i < superTokens.length; i++) {
try {
Expand All @@ -65,7 +57,7 @@ async function getSuperTokens (graphAPI) {
symbol: superTokens[i].symbol,
PIC: picInfo.pic,
Bond: wad4human(picInfo.bond),
ExitRatePerDay: wad4human(toBN(picInfo.exitRate).mul(toBN(3600 * 24)))
ExitRatePerDay: wad4human(picInfo.exitRate * 86400n)
});
} else {
console.log(`skipping ${superTokens[i].symbol} (no PIC set, zero bond)`);
Expand All @@ -74,6 +66,6 @@ async function getSuperTokens (graphAPI) {
console.error(err);
}
}
console.log(`Super Tokens on ${networkConfig.name} with a PIC set and/or a non-zero bond:`);
console.log(`Super Tokens on ${network.name} with a PIC set and/or a non-zero bond:`);
console.table(table, ["name", "symbol", "PIC", "Bond", "ExitRatePerDay"]);
})();
13 changes: 7 additions & 6 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const Telemetry = require("./services/telemetry");
const Errors = require("./utils/errors/errors");
const CircularBuffer = require("./utils/circularBuffer");
const { wad4human } = require("@decentral.ee/web3-helpers");
const packageVersion = require("../package.json").version;

class App {
/*
Expand Down Expand Up @@ -178,7 +179,7 @@ class App {

async start() {
try {
this.logger.debug(`booting - ${this.config.INSTANCE_NAME}`);
this.logger.debug(`booting version ${packageVersion} - ${this.config.INSTANCE_NAME}`);
this._isShutdown = false;
// send notification about time sentinel started including timestamp
this.notifier.sendNotification(`Sentinel started at ${new Date()}`);
Expand All @@ -194,9 +195,11 @@ class App {
}
// create all web3 infrastructure needed
await this.client.init();
const balanceMsg = `RPC connected with chainId ${await this.client.getChainId()}, account ${this.client.accountManager.getAccountAddress(0)} has balance ${wad4human(await this.client.accountManager.getAccountBalance(0))}`;
const balanceMsg = `RPC connected with chainId ${await this.client.getChainId()}`
+ this.config.OBSERVER ? "" :
`account ${this.client.accountManager.getAccountAddress(0)} has balance ${wad4human(await this.client.accountManager.getAccountBalance(0))}`;
this.notifier.sendNotification(balanceMsg);

//check conditions to decide if getting snapshot data
if ((!dbFileExist || this.config.COLD_BOOT) &&
this.config.FASTSYNC && this.config.CID) {
Expand Down Expand Up @@ -241,13 +244,11 @@ class App {
try {
const thresholds = require("../thresholds.json");
const tokensThresholds = thresholds.networks[await this.client.getChainId()];
this.config.SENTINEL_BALANCE_THRESHOLD = tokensThresholds.minSentinelBalanceThreshold;
// update thresholds on database
await this.db.sysQueries.updateThresholds(tokensThresholds.thresholds);
} catch (err) {
this.logger.warn(`error loading thresholds.json`);
this.logger.warn(`thresholds.json not loaded`);
await this.db.sysQueries.updateThresholds({});
this.config.SENTINEL_BALANCE_THRESHOLD = 0;
}


Expand Down
11 changes: 7 additions & 4 deletions src/config/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Config {
this.MAX_QUERY_BLOCK_RANGE = config.max_query_block_range || 2000;
this.TOKENS = config.TOKENS?.split(",");
this.EXCLUDED_TOKENS = config.EXCLUDED_TOKENS?.split(",");
this.DB = (config.db_path !== undefined && config.db_path !== "") ? config.db_path : "./db.sqlite";
this.DB = (config.db_path !== undefined && config.db_path !== "") ? config.db_path : "data/db.sqlite";
this.ADDITIONAL_LIQUIDATION_DELAY = config.additional_liquidation_delay || 0;
this.TX_TIMEOUT = config.tx_timeout * 1000 || 60000;
this.PROTOCOL_RELEASE_VERSION = config.protocol_release_version || "v1";
Expand All @@ -67,6 +67,7 @@ class Config {
this.PIRATE = this._parseToBool(config.pirate);
this.INSTANCE_NAME = config.INSTANCE_NAME || "Sentinel";
this.RPC_STUCK_THRESHOLD = config.rpc_stuck_threshold;
this.SENTINEL_BALANCE_THRESHOLD = config.sentinel_balance_threshold;
}

_initializeFromEnvVariables() {
Expand All @@ -79,7 +80,7 @@ class Config {
this.TOKENS = undefined;
this.TOKENS = process.env.TOKENS?.split(",");
this.EXCLUDED_TOKENS = process.env.EXCLUDED_TOKENS?.split(",");
this.DB = (process.env.DB_PATH !== undefined && process.env.DB_PATH !== "") ? process.env.DB_PATH : "./db.sqlite";
this.DB = (process.env.DB_PATH !== undefined && process.env.DB_PATH !== "") ? process.env.DB_PATH : "data/db.sqlite";
this.ADDITIONAL_LIQUIDATION_DELAY = process.env.ADDITIONAL_LIQUIDATION_DELAY || 0;
this.TX_TIMEOUT = process.env.TX_TIMEOUT * 1000 || 60000;
this.PROTOCOL_RELEASE_VERSION = process.env.PROTOCOL_RELEASE_VERSION || "v1";
Expand Down Expand Up @@ -112,8 +113,9 @@ class Config {
this.BLOCK_OFFSET = process.env.BLOCK_OFFSET || 12;
this.MAX_TX_NUMBER = process.env.MAX_TX_NUMBER || 100;
this.NO_REMOTE_MANIFEST = this._parseToBool(process.env.NO_REMOTE_MANIFEST, false);
this.INSTANCE_NAME = process.env.INSTANCE_NAME || "Sentinel";
this.RPC_STUCK_THRESHOLD = process.env.RPC_STUCK_THRESHOLD || (this.POLLING_INTERVAL * 4) / 1000;
this.INSTANCE_NAME = process.env.INSTANCE_NAME || "Sentinel";
this.SENTINEL_BALANCE_THRESHOLD = process.env.SENTINEL_BALANCE_THRESHOLD || 0;
}

_parseToBool(value, defaultValue = false) {
Expand Down Expand Up @@ -183,7 +185,6 @@ class Config {

getConfigurationInfo () {
return {
INSTANCE_NAME: this.INSTANCE_NAME,
HTTP_RPC_NODE: this.HTTP_RPC_NODE,
FASTSYNC: this.FASTSYNC,
OBSERVER: this.OBSERVER,
Expand Down Expand Up @@ -214,6 +215,8 @@ class Config {
SLACK_WEBHOOK_URL: this.SLACK_WEBHOOK_URL,
TELEGRAM_BOT_TOKEN: this.TELEGRAM_BOT_TOKEN,
TELEGRAM_CHAT_ID: this.TELEGRAM_CHAT_ID,
INSTANCE_NAME: this.INSTANCE_NAME,
SENTINEL_BALANCE_THRESHOLD: this.SENTINEL_BALANCE_THRESHOLD,
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/config/loadCmdArgs.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ program
.option("--max-query-block-range [value]", "Max query block range (default: 2000)")
.option("-t, --tokens [value]", "Addresses of SuperTokens the sentinel should watch (default: all SuperTokens)")
.option("-e, --exclude-tokens [value]", "Addresses of SuperTokens the sentinel should excluded (default: none)")
.option("-p, --db-path [value]", "Path of the DB file (default: db.sqlite)")
.option("-p, --db-path [value]", "Path of the DB file (default: data/db.sqlite)")
.option("-d, --additional-liquidation-delay [value]", "Time to wait (seconds) after an agreement becoming critical before doing a liquidation (default: 0)")
.option("--tx-timeout [value]", "Time to wait (seconds) before re-broadcasting a pending transaction with higher gas price (default: 60)")
.option("--protocol-release-version [value]", "Superfluid Protocol Release Version (default: v1)")
Expand Down
10 changes: 6 additions & 4 deletions src/httpserver/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ class Report {
}
},

account: {
address: this.app.client.getAccountAddress(),
balance: (await this.app.client.getAccountBalance()).toString(),
},
...(this.app.config.OBSERVER ? {} : {
account: {
address: this.app.client.getAccountAddress(),
balance: (await this.app.client.getAccountBalance()).toString(),
}
}),

queues: {
agreementQueue: this.app.queues.getAgreementQueueLength(),
Expand Down
18 changes: 10 additions & 8 deletions src/services/notificationJobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ class NotificationJobs {

async sendReport () {
const healthcheck = await this.app.healthReport.fullReport();
if(!healthcheck.healthy) {
if (!healthcheck.healthy) {
const healthData = `Instance Name: ${this.app.config.INSTANCE_NAME}\nHealthy: ${healthcheck.healthy}\nChainId: ${healthcheck.network.chainId}\nReasons: ${healthcheck.reasons.join('\n')}`;
this.app.notifier.sendNotification(healthData);
}
const currentTime = Date.now();
if(currentTime - this._lastBalanceReportTime >= BALANCE_REPORT_INTERVAL) {
const balanceQuery = await this.app.client.isAccountBalanceBelowMinimum();
if(balanceQuery.isBelow) {
this.app.notifier.sendNotification(`Attention: Sentinel balance: ${wad4human(balanceQuery.balance)}`);
// update the time of last balance report
this._lastBalanceReportTime = currentTime;
if (!this.app.config.OBSERVER) {
const currentTime = Date.now();
if(currentTime - this._lastBalanceReportTime >= BALANCE_REPORT_INTERVAL) {
const balanceQuery = await this.app.client.isAccountBalanceBelowMinimum();
if(balanceQuery.isBelow) {
this.app.notifier.sendNotification(`Attention: Sentinel balance: ${wad4human(balanceQuery.balance)}`);
// update the time of last balance report
this._lastBalanceReportTime = currentTime;
}
}
}
}
Expand Down
Loading

0 comments on commit c98c63a

Please sign in to comment.