merge 3.3.5 to master
This commit is contained in:
+12
-33
@@ -11,33 +11,8 @@ pids
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
@@ -51,18 +26,11 @@ typings/
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
.idea
|
||||
*.pem
|
||||
ecosystem.config.js
|
||||
package-lock.json
|
||||
connections.json
|
||||
connections.json2
|
||||
@@ -106,9 +74,20 @@ modules/**/*.js.map
|
||||
addons/**/*.js
|
||||
addons/**/*.js.map
|
||||
|
||||
scripts/hpm.js
|
||||
scripts/hpm.js.map
|
||||
|
||||
scripts/hyp-config.js
|
||||
scripts/hyp-config.js.map
|
||||
|
||||
plugins/.state.json
|
||||
plugins/repos
|
||||
plugins/*.js
|
||||
.gitmodules
|
||||
|
||||
docker/redis/data
|
||||
docker/elasticsearch/data
|
||||
docker/eosio/data
|
||||
docker/rabbitmq/data
|
||||
docker/eosio/data
|
||||
|
||||
configuration_backups
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
curl -sL https://deb.nodesource.com/setup_13.x | sudo -E bash -
|
||||
sudo apt-get install -y nodejs
|
||||
sudo npm install pm2 -g
|
||||
npm install
|
||||
@@ -1,303 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
#terminal colors
|
||||
COLOR_NC=$(tput sgr0)
|
||||
export COLOR_NC
|
||||
COLOR_RED=$(tput setaf 1)
|
||||
export COLOR_RED
|
||||
COLOR_GREEN=$(tput setaf 2)
|
||||
export COLOR_GREEN
|
||||
COLOR_YELLOW=$(tput setaf 3)
|
||||
export COLOR_YELLOW
|
||||
COLOR_BLUE=$(tput setaf 4)
|
||||
export COLOR_BLUE
|
||||
|
||||
#variables
|
||||
INITIAL_PARAMS="$*"
|
||||
GLOBAL="y"
|
||||
RABBIT_USER="hyperion"
|
||||
RABBIT_PASSWORD="123456"
|
||||
NODE=false
|
||||
ELASTIC=false
|
||||
REDIS=false
|
||||
RABBIT=false
|
||||
RAM=0
|
||||
|
||||
help_function() {
|
||||
echo -e "\nAutomated shell script that installs all dependencies and then configure Hyperion."
|
||||
echo -e "\nUsage: ./install.env <option>\n"
|
||||
echo -e "Options:"
|
||||
echo -e " --help ==> Show this menu"
|
||||
echo -e " --version ==> Show Hyperion current version\n"
|
||||
exit 0
|
||||
}
|
||||
|
||||
version (){
|
||||
echo -e "\nv3.0.0\n"
|
||||
exit 0
|
||||
|
||||
}
|
||||
|
||||
arg_checker() {
|
||||
if [ "${INITIAL_PARAMS}" == "--help" ]; then
|
||||
help_function
|
||||
|
||||
elif [ "${INITIAL_PARAMS}" == "--version" ]; then
|
||||
version
|
||||
fi
|
||||
}
|
||||
|
||||
check_ram() {
|
||||
RAM=$(free --giga | awk '/Mem/ {print $2}')
|
||||
}
|
||||
|
||||
# first check if is installed, after check version
|
||||
check_dependencies() {
|
||||
echo -e "\n\n${COLOR_BLUE}Checking dependencies...${COLOR_NC}\n\n"
|
||||
CMD=$(if (dpkg --compare-versions $(dpkg -s nodejs 2>/dev/null | awk '/Version/ {print $2}') ge "5" 2>/dev/null); then echo true; else echo false; fi)
|
||||
if ("$CMD" = true); then
|
||||
NODE=true
|
||||
echo -e "\n\n${COLOR_BLUE}Nodejs compatible version already installed ${COLOR_NC}"
|
||||
elif ("$CMD" = false); then
|
||||
echo -e "\n\n${COLOR_RED}Nodejs installed version is < 13. Please, update and try again. ${COLOR_NC}\n\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n\n${COLOR_BLUE}Nodejs not installed ${COLOR_NC}"
|
||||
fi
|
||||
CMD=$(if (dpkg --compare-versions $(dpkg -s elasticsearch 2>/dev/null | awk '/Version/ {print $2}') ge "7.6" 2>/dev/null); then echo true; else echo false; fi)
|
||||
if ("$CMD" = true); then
|
||||
ELASTIC=true
|
||||
echo -e "\n${COLOR_BLUE}Elasticsearch compatible version already installed ${COLOR_NC}"
|
||||
elif ("$CMD" = false); then
|
||||
echo -e "\n\n${COLOR_RED}Elasticsearch installed version is < 7.6. Please, update and try again. ${COLOR_NC}\n\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${COLOR_BLUE}Elasticsearch not installed ${COLOR_NC}"
|
||||
fi
|
||||
CMD=$(if (dpkg --compare-versions $(dpkg -s redis-server 2>/dev/null | awk '/Version/ {print $2}') ge "5" 2>/dev/null); then echo true; else echo false; fi)
|
||||
if ("$CMD" = true); then
|
||||
REDIS=true
|
||||
echo -e "\n${COLOR_BLUE}Redis compatible version already installed ${COLOR_NC}"
|
||||
elif ("$CMD" = false); then
|
||||
echo -e "\n\n${COLOR_RED}Redis installed version is < 5. Please, update and try again. ${COLOR_NC}\n\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${COLOR_BLUE}Redis not installed ${COLOR_NC}"
|
||||
fi
|
||||
CMD=$(if (dpkg --compare-versions $(dpkg -s rabbitmq-server 2>/dev/null | awk '/Version/ {print $2}') ge "3.8" 2>/dev/null); then echo true; else echo false; fi)
|
||||
if ("$CMD" = true); then
|
||||
RABBIT=true
|
||||
echo -e "\n${COLOR_BLUE}RabbitMQ compatible version already installed ${COLOR_NC}"
|
||||
elif ("$CMD" = false); then
|
||||
echo -e "\n\n${COLOR_RED}RabbitMQ installed version is < 3.8. Please, update and try again. ${COLOR_NC}\n\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${COLOR_BLUE}RabbitMQ not installed ${COLOR_NC}"
|
||||
fi
|
||||
CMD=$(if (dpkg --compare-versions $(dpkg -s kibana 2>/dev/null | awk '/Version/ {print $2}') ge "7.6" 2>/dev/null); then echo true; else echo false; fi)
|
||||
if ("$CMD" = true); then
|
||||
RABBIT=true
|
||||
echo -e "\n${COLOR_BLUE}Kibana compatible version already installed ${COLOR_NC}"
|
||||
elif ("$CMD" = false); then
|
||||
echo -e "\n\n${COLOR_RED}Kibana installed version is < 7.6. Please, update and try again. ${COLOR_NC}\n\n"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${COLOR_BLUE}Kibana not installed ${COLOR_NC}\n\n"
|
||||
fi
|
||||
}
|
||||
|
||||
# Make a directory for global installations
|
||||
configure_npm() {
|
||||
mkdir ~/.npm-global
|
||||
npm config set prefix "$HOME/.npm-global"
|
||||
# export path for the current session
|
||||
export PATH=$HOME/.npm-global/bin:$PATH
|
||||
# make PATH persistent
|
||||
echo "export PATH=~/.npm-global/bin:$PATH" >>"$HOME/.profile"
|
||||
# shellcheck source=/dev/null
|
||||
source ~/.profile
|
||||
}
|
||||
|
||||
install_keys_sources() {
|
||||
echo -e "\n\n${COLOR_BLUE}Configuring keys and sources...${COLOR_NC}\n\n"
|
||||
|
||||
PPA="https://artifacts.elastic.co/packages/7.x/apt stable main"
|
||||
if ! grep -q "^deb .*$PPA" /etc/apt/sources.list /etc/apt/sources.list.d/*; then
|
||||
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
|
||||
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
|
||||
fi
|
||||
|
||||
PPA="http://dl.bintray.com/rabbitmq-erlang/debian bionic erlang"
|
||||
if ! grep -q "^deb .*$PPA" /etc/apt/sources.list /etc/apt/sources.list.d/*; then
|
||||
curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo apt-key add -
|
||||
echo "deb http://dl.bintray.com/rabbitmq-erlang/debian bionic erlang" | sudo tee /etc/apt/sources.list.d/bintray.rabbitmq.list
|
||||
curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.deb.sh | sudo bash
|
||||
fi
|
||||
|
||||
PPA="https://deb.nodesource.com/node_13.x bionic main"
|
||||
if ! grep -q "^deb .*$PPA" /etc/apt/sources.list /etc/apt/sources.list.d/*; then
|
||||
curl -sL "https://deb.nodesource.com/setup_13.x" | sudo -E bash -
|
||||
fi
|
||||
|
||||
sudo apt update -y
|
||||
}
|
||||
|
||||
install_dep() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing dependencies...${COLOR_NC}\n\n"
|
||||
# sudo apt install -y build-essential curl
|
||||
sudo apt install -y curl
|
||||
}
|
||||
|
||||
install_node() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing nodejs...${COLOR_NC}\n\n"
|
||||
sudo apt install -y nodejs
|
||||
read -p "Do you want to create a directory for npm global installations [Y/n] : " GLOBAL
|
||||
GLOBAL=${GLOBAL:-y}
|
||||
if [ "$GLOBAL" = "y" ]; then
|
||||
configure_npm
|
||||
fi
|
||||
}
|
||||
|
||||
install_build_hyperion() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing packages and building hyperion...${COLOR_NC}\n\n"
|
||||
npm install
|
||||
}
|
||||
|
||||
install_pm2() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing pm2...${COLOR_NC}\n\n"
|
||||
if [ "$GLOBAL" = "y" ]; then
|
||||
npm install pm2@latest -g
|
||||
else
|
||||
sudo npm install pm2@latest -g
|
||||
fi
|
||||
}
|
||||
|
||||
install_redis() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing redis...${COLOR_NC}\n\n"
|
||||
sudo apt install -y redis-server
|
||||
sudo sed -ie 's/supervised no/supervised systemd/' /etc/redis/redis.conf
|
||||
sudo systemctl restart redis.service
|
||||
}
|
||||
|
||||
install_erlang() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing erlang...${COLOR_NC}\n\n"
|
||||
sudo apt -y install erlang
|
||||
}
|
||||
|
||||
#ask user for rabbit credentials
|
||||
rabbit_credentials() {
|
||||
read -p "Enter rabbitmq user [hyperion]: " RABBIT_USER
|
||||
RABBIT_USER=${RABBIT_USER:-hyperion}
|
||||
|
||||
read -p "Enter rabbitmq password [123456]: " RABBIT_PASSWORD
|
||||
RABBIT_PASSWORD=${RABBIT_PASSWORD:-123456}
|
||||
|
||||
}
|
||||
|
||||
install_rabittmq() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing rabbit-mq...${COLOR_NC}\n\n"
|
||||
sudo apt install -y rabbitmq-server
|
||||
#enable web gui
|
||||
sudo rabbitmq-plugins enable rabbitmq_management
|
||||
sudo rabbitmqctl add_vhost /hyperion
|
||||
sudo rabbitmqctl add_user ${RABBIT_USER} ${RABBIT_PASSWORD}
|
||||
sudo rabbitmqctl set_user_tags ${RABBIT_USER} administrator
|
||||
sudo rabbitmqctl set_permissions -p /hyperion ${RABBIT_USER} ".*" ".*" ".*"
|
||||
}
|
||||
|
||||
install_elastic() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing elastic...${COLOR_NC}\n\n"
|
||||
sudo apt install -y elasticsearch
|
||||
|
||||
echo -e "\n\n${COLOR_BLUE}Configuring elastic...${COLOR_NC}\n\n"
|
||||
|
||||
# edit configs
|
||||
sudo sed -ie 's/#cluster.name: my-application/cluster.name: myCluster/; s/#bootstrap.memory_lock: true/bootstrap.memory_lock: true/' /etc/elasticsearch/elasticsearch.yml
|
||||
# set jvm options based on system RAM
|
||||
check_ram
|
||||
if [ "$RAM" -lt 32 ]; then
|
||||
(( RAM=RAM/2 ))
|
||||
sudo sed -ie 's/-Xms1g/-Xms'"$RAM"'g/; s/-Xmx1g/-Xmx'"$RAM"'g/' /etc/elasticsearch/jvm.options
|
||||
else
|
||||
sudo sed -ie 's/-Xms1g/-Xms16g/; s/-Xmx1g/-Xmx16g/' /etc/elasticsearch/jvm.options
|
||||
fi
|
||||
|
||||
sudo bash -c 'echo "xpack.security.enabled: true" >> /etc/elasticsearch/elasticsearch.yml'
|
||||
|
||||
sudo mkdir -p /etc/systemd/system/elasticsearch.service.d/
|
||||
echo -e "[Service]\nLimitMEMLOCK=infinity" | sudo tee /etc/systemd/system/elasticsearch.service.d/override.conf
|
||||
sudo systemctl daemon-reload
|
||||
sudo service elasticsearch start
|
||||
sudo systemctl enable elasticsearch
|
||||
|
||||
echo -e "\n\n${COLOR_BLUE}Generating Elasticsearch cluster passwords...${COLOR_NC}\n\n"
|
||||
|
||||
echo "y" | sudo /usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto >elastic_pass.txt
|
||||
|
||||
}
|
||||
|
||||
install_kibana() {
|
||||
echo -e "\n\n${COLOR_BLUE}Installing and configuring Kibana...${COLOR_NC}\n\n"
|
||||
sudo apt install -y kibana
|
||||
|
||||
KIBANA_PASSWORD=$(awk <elastic_pass.txt '/PASSWORD kibana =/ {print $4}')
|
||||
|
||||
sudo sed -ie 's/#elasticsearch.password: "pass"/elasticsearch.password: '"$KIBANA_PASSWORD"'/; s/#elasticsearch.username: "kibana"/elasticsearch.username: "kibana"/' /etc/kibana/kibana.yml
|
||||
|
||||
sudo systemctl enable kibana
|
||||
sudo systemctl start kibana
|
||||
}
|
||||
|
||||
#******************
|
||||
# End of functions
|
||||
#******************
|
||||
arg_checker
|
||||
|
||||
echo -e "\n\n${COLOR_BLUE}*** STARTING HYPERION HISTORY API CONFIGURATION ***${COLOR_NC}\n\n"
|
||||
|
||||
check_dependencies
|
||||
|
||||
if [ "$RABBIT" = false ]; then
|
||||
rabbit_credentials
|
||||
fi
|
||||
|
||||
install_dep
|
||||
|
||||
install_keys_sources
|
||||
if [ "$NODE" = false ]; then
|
||||
install_node
|
||||
fi
|
||||
|
||||
install_pm2
|
||||
|
||||
install_build_hyperion
|
||||
|
||||
if [ "$RABBIT" = false ]; then
|
||||
install_rabittmq
|
||||
fi
|
||||
|
||||
if [ "$REDIS" = false ]; then
|
||||
install_redis
|
||||
fi
|
||||
|
||||
if [ "$ELASTIC" = false ]; then
|
||||
install_elastic
|
||||
install_kibana
|
||||
fi
|
||||
|
||||
printf "
|
||||
_ ___ ______ _____ ____ ___ ___ _ _
|
||||
| | | \ \ / / _ \| ____| _ \|_ _/ _ \| \ | |
|
||||
| |_| |\ V /| |_) | _| | |_) || | | | | \| |
|
||||
| _ | | | | __/| |___| _ < | | |_| | |\ |
|
||||
|_| |_| |_| |_| |_____|_| \_\___\___/|_| \_|
|
||||
Made with ♥ by EOS Rio
|
||||
"
|
||||
echo -e "\n\n${COLOR_GREEN}Hyperion History API successfully installed!!${COLOR_NC}"
|
||||
echo -e "\n${COLOR_YELLOW}Please, check your installation: ${COLOR_NC}"
|
||||
echo -e "${COLOR_YELLOW}- Elastic: http://localhost:9200 ${COLOR_NC}"
|
||||
echo -e "${COLOR_YELLOW}- Kibana: http://localhost:5601 ${COLOR_NC}"
|
||||
echo -e "${COLOR_YELLOW}- RabbitMQ: http://localhost:15672 ${COLOR_NC}"
|
||||
echo -e "${COLOR_YELLOW}- Refer to the elastic_pas.txt file for the elasticsearch passwords. ${COLOR_NC}"
|
||||
echo -e "${COLOR_YELLOW}- Make pm2 auto-boot at server restart: \$ pm2 startup ${COLOR_NC}\n"
|
||||
@@ -1,6 +1,6 @@
|
||||
# Hyperion History API
|
||||
|
||||
<img height="64" src="https://eosrio.io/hyperion.png">
|
||||
<img alt="hyperion logo" height="64" src="https://eosrio.io/hyperion.png">
|
||||
<br/>
|
||||
Scalable Full History API Solution for EOSIO based blockchains
|
||||
|
||||
@@ -21,30 +21,30 @@ no blocks or transaction data is stored, all information can be reconstructed fr
|
||||
|
||||
### 2. Architecture
|
||||
The following components are required in order to have a fully functional Hyperion API deployment,
|
||||
for small use cases its fine to run all components on a single machine. But for larger chains and
|
||||
for small use cases its absolutely fine to run all components on a single machine. For larger chains and
|
||||
production environments we recommend setting them up into different servers under a high-speed local network.
|
||||
|
||||
#### 2.1 - Elasticsearch Cluster
|
||||
#### 2.1 Elasticsearch Cluster
|
||||
The ES cluster is responsible for storing all indexed data.
|
||||
Direct access to the Hyperion API and Indexer must be provided. We recommend nodes in the
|
||||
cluster to have at least 32GB of RAM and 8 cpu cores. SSD/NVME drives are recommended for
|
||||
cluster to have at least 32 GB of RAM and 8 cpu cores. SSD/NVME drives are recommended for
|
||||
maximum indexing throughput. For production environments a multi-node cluster is highly recommended.
|
||||
|
||||
#### 2.2 - Hyperion Indexer
|
||||
#### 2.2 Hyperion Indexer
|
||||
The Indexer is a Node.js based app that process data from the state history plugin and allows it to be indexed.
|
||||
The PM2 process manager is used to launch and operate the indexer. The configuration flexibility is very extensive,
|
||||
so system recommendations will depend on the use case and data load. It will require access to at least one ES node,
|
||||
RabbitMQ and the state history node.
|
||||
|
||||
#### 2.3 - Hyperion API
|
||||
#### 2.3 Hyperion API
|
||||
Parallelizable API server that provides the V2 and V1 (legacy history plugin) endpoints.
|
||||
It is launched by PM2 and can also operate in cluster mode. It requires direct access to
|
||||
at least one ES node for the queries and all other services for full healthcheck
|
||||
|
||||
#### 2.4 - RabbitMQ
|
||||
#### 2.4 RabbitMQ
|
||||
Use as messaging queue and data transport between the indexer stages
|
||||
|
||||
#### 2.5 - EOSIO State History
|
||||
#### 2.5 EOSIO State History
|
||||
Nodeos plugin used to collect action traces and state deltas. Provides data via websocket to the indexer
|
||||
|
||||
### 3. How to use
|
||||
|
||||
@@ -4,6 +4,7 @@ export function generateOpenApiConfig(config: HyperionConfig) {
|
||||
const packageData = require('../../package');
|
||||
const health_link = `https://${config.api.server_name}/v2/health`;
|
||||
const explorer_link = `https://${config.api.server_name}/v2/explore`;
|
||||
|
||||
let description = `
|
||||
<img height="64" src="https://eosrio.io/hyperion.png">
|
||||
### Scalable Full History API Solution for EOSIO based blockchains
|
||||
@@ -13,9 +14,15 @@ export function generateOpenApiConfig(config: HyperionConfig) {
|
||||
#### Provided by [${config.api.provider_name}](${config.api.provider_url})
|
||||
#### Health API: <a target="_blank" href="${health_link}">${health_link}</a>
|
||||
`;
|
||||
if(config.api.enable_explorer) {
|
||||
description += `#### Integrated Explorer: <a target="_blank" href="${explorer_link}">${explorer_link}</a>`
|
||||
|
||||
if (config.plugins) {
|
||||
if (config.plugins.explorer) {
|
||||
if (config.plugins.explorer.enabled) {
|
||||
description += `#### Integrated Explorer: <a target="_blank" href="${explorer_link}">${explorer_link}</a>`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
routePrefix: '/v2/docs',
|
||||
exposeRoute: true,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import {HyperionConfig} from "../../interfaces/hyperionConfig";
|
||||
import {createHash} from "crypto";
|
||||
import {FastifyRequest} from "fastify";
|
||||
|
||||
export interface CacheConfig {
|
||||
ttl: number;
|
||||
}
|
||||
|
||||
export interface CachedEntry {
|
||||
data: string;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export class CacheManager {
|
||||
|
||||
v1CacheConfigs: Map<string, CacheConfig> = new Map<string, CacheConfig>();
|
||||
v1Caches: Map<string, Map<string, CachedEntry>> = new Map<string, Map<string, CachedEntry>>();
|
||||
|
||||
constructor(conf: HyperionConfig) {
|
||||
if (conf.api.v1_chain_cache) {
|
||||
conf.api.v1_chain_cache.forEach(value => {
|
||||
this.v1CacheConfigs.set(value.path, {
|
||||
ttl: value.ttl
|
||||
});
|
||||
this.v1Caches.set(value.path, new Map<string, CachedEntry>());
|
||||
});
|
||||
}
|
||||
setInterval(() => {
|
||||
try {
|
||||
// remove expired entries
|
||||
let removeCount = 0;
|
||||
const now = Date.now();
|
||||
this.v1Caches.forEach((pathCacheMap: Map<string, CachedEntry>, pathKey: string) => {
|
||||
pathCacheMap.forEach((cache: CachedEntry, entryKey: string, map: Map<string, CachedEntry>) => {
|
||||
if (cache.exp < now) {
|
||||
map.delete(entryKey);
|
||||
removeCount++;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (removeCount > 0) {
|
||||
console.log(`${removeCount} expired cache entries removed`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
hashPayload(input: string): string {
|
||||
return createHash('sha256').update(input).digest().toString('hex');
|
||||
}
|
||||
|
||||
setCachedData(hash: string, path: string, payload: any): void {
|
||||
if (this.v1CacheConfigs.has(path) && this.v1Caches.has(path)) {
|
||||
this.v1Caches.get(path).set(hash, {
|
||||
data: payload,
|
||||
exp: this.v1CacheConfigs.get(path).ttl + Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getCachedData(request: FastifyRequest): [string | null, string, string] {
|
||||
const urlParts = request.url.split("?");
|
||||
const pathComponents = urlParts[0].split('/');
|
||||
const path = pathComponents.at(-1);
|
||||
let payload = '';
|
||||
if (request.method === 'POST') {
|
||||
payload = JSON.stringify(request.body);
|
||||
} else if (request.method === 'GET') {
|
||||
payload = request.url;
|
||||
}
|
||||
const hashedString = this.hashPayload(payload);
|
||||
if (this.v1Caches.has(path)) {
|
||||
const entry = this.v1Caches.get(path).get(hashedString);
|
||||
if (entry && entry.exp > Date.now()) {
|
||||
return [entry.data, hashedString, path];
|
||||
} else {
|
||||
return [null, hashedString, path];
|
||||
}
|
||||
} else {
|
||||
return [null, hashedString, path];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+389
-363
@@ -1,433 +1,459 @@
|
||||
import {createHash} from "crypto";
|
||||
import * as _ from "lodash";
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest, HTTPMethod, RouteSchema} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest, FastifySchema, HTTPMethods} from "fastify";
|
||||
import got from "got";
|
||||
|
||||
export function extendResponseSchema(responseProps: any) {
|
||||
const props = {
|
||||
query_time_ms: {type: "number"},
|
||||
cached: {type: "boolean"},
|
||||
hot_only: {type: "boolean"},
|
||||
lib: {type: "number"},
|
||||
total: {
|
||||
type: "object",
|
||||
properties: {
|
||||
value: {type: "number"},
|
||||
relation: {type: "string"}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const p in responseProps) {
|
||||
if (responseProps.hasOwnProperty(p)) {
|
||||
props[p] = responseProps[p];
|
||||
}
|
||||
}
|
||||
return {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: props
|
||||
}
|
||||
};
|
||||
const props = {
|
||||
query_time_ms: {type: "number"},
|
||||
cached: {type: "boolean"},
|
||||
hot_only: {type: "boolean"},
|
||||
lib: {type: "number"},
|
||||
total: {
|
||||
type: "object",
|
||||
properties: {
|
||||
value: {type: "number"},
|
||||
relation: {type: "string"}
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const p in responseProps) {
|
||||
if (responseProps.hasOwnProperty(p)) {
|
||||
props[p] = responseProps[p];
|
||||
}
|
||||
}
|
||||
return {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: props
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function extendQueryStringSchema(queryParams: any, required?: string[]) {
|
||||
const params = {
|
||||
limit: {
|
||||
description: 'limit of [n] results per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
skip: {
|
||||
description: 'skip [n] results',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
}
|
||||
};
|
||||
for (const p in queryParams) {
|
||||
if (queryParams.hasOwnProperty(p)) {
|
||||
params[p] = queryParams[p];
|
||||
}
|
||||
}
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: params
|
||||
}
|
||||
if (required && required.length > 0) {
|
||||
schema["required"] = required;
|
||||
}
|
||||
return schema;
|
||||
const params = {
|
||||
limit: {
|
||||
description: 'limit of [n] results per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
skip: {
|
||||
description: 'skip [n] results',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
}
|
||||
};
|
||||
for (const p in queryParams) {
|
||||
if (queryParams.hasOwnProperty(p)) {
|
||||
params[p] = queryParams[p];
|
||||
}
|
||||
}
|
||||
const schema = {
|
||||
type: 'object',
|
||||
properties: params
|
||||
}
|
||||
if (required && required.length > 0) {
|
||||
schema["required"] = required;
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
export async function getCacheByHash(redis, key, chain) {
|
||||
const hash = createHash('sha256');
|
||||
const query_hash = hash.update(chain + "-" + key).digest('hex');
|
||||
return [await redis.get(query_hash), query_hash];
|
||||
const hash = createHash('sha256');
|
||||
const query_hash = hash.update(chain + "-" + key).digest('hex');
|
||||
return [await redis.get(query_hash), query_hash];
|
||||
}
|
||||
|
||||
export function mergeActionMeta(action) {
|
||||
const name = action.act.name;
|
||||
if (action['@' + name]) {
|
||||
action['act']['data'] = _.merge(action['@' + name], action['act']['data']);
|
||||
delete action['@' + name];
|
||||
}
|
||||
action['timestamp'] = action['@timestamp'];
|
||||
// delete action['@timestamp'];
|
||||
const name = action.act.name;
|
||||
if (action['@' + name]) {
|
||||
action['act']['data'] = _.merge(action['@' + name], action['act']['data']);
|
||||
delete action['@' + name];
|
||||
}
|
||||
action['timestamp'] = action['@timestamp'];
|
||||
// delete action['@timestamp'];
|
||||
}
|
||||
|
||||
export function mergeDeltaMeta(delta: any) {
|
||||
const name = delta.table;
|
||||
if (delta["@" + name]) {
|
||||
delta['data'] = _.merge(delta['@' + name], delta['data']);
|
||||
delete delta['@' + name];
|
||||
}
|
||||
delta['timestamp'] = delta['@timestamp'];
|
||||
delete delta['@timestamp'];
|
||||
return delta;
|
||||
const name = delta.table;
|
||||
if (delta["@" + name]) {
|
||||
delta['data'] = _.merge(delta['@' + name], delta['data']);
|
||||
delete delta['@' + name];
|
||||
}
|
||||
delta['timestamp'] = delta['@timestamp'];
|
||||
delete delta['@timestamp'];
|
||||
return delta;
|
||||
}
|
||||
|
||||
export function setCacheByHash(fastify, hash, response, expiration?: number) {
|
||||
if (fastify.manager.config.api.enable_caching) {
|
||||
let exp;
|
||||
if (expiration) {
|
||||
exp = expiration;
|
||||
} else {
|
||||
exp = fastify.manager.config.api.cache_life;
|
||||
}
|
||||
fastify.redis.set(hash, JSON.stringify(response), 'EX', exp).catch(console.log);
|
||||
}
|
||||
if (fastify.manager.config.api.enable_caching) {
|
||||
let exp;
|
||||
if (expiration) {
|
||||
exp = expiration;
|
||||
} else {
|
||||
exp = fastify.manager.config.api.cache_life;
|
||||
}
|
||||
fastify.redis.set(hash, JSON.stringify(response), 'EX', exp).catch(console.log);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRouteName(filename: string) {
|
||||
const arr = filename.split("/");
|
||||
return arr[arr.length - 2];
|
||||
const arr = filename.split("/");
|
||||
return arr[arr.length - 2];
|
||||
}
|
||||
|
||||
export function addApiRoute(
|
||||
fastifyInstance: FastifyInstance,
|
||||
method: HTTPMethod | HTTPMethod[],
|
||||
routeName: string,
|
||||
routeBuilder: (fastify: FastifyInstance, route: string) => (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => Promise<void>, schema: RouteSchema) {
|
||||
fastifyInstance.route({
|
||||
url: '/' + routeName,
|
||||
method,
|
||||
handler: routeBuilder(fastifyInstance, routeName),
|
||||
schema
|
||||
});
|
||||
fastifyInstance: FastifyInstance,
|
||||
method: HTTPMethods | HTTPMethods[],
|
||||
routeName: string,
|
||||
routeBuilder: (fastify: FastifyInstance, route: string) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>,
|
||||
schema: FastifySchema
|
||||
) {
|
||||
fastifyInstance.route({
|
||||
url: '/' + routeName,
|
||||
method,
|
||||
handler: routeBuilder(fastifyInstance, routeName),
|
||||
schema
|
||||
});
|
||||
}
|
||||
|
||||
export async function getCachedResponse(server: FastifyInstance, route: string, key: any) {
|
||||
const chain = server.manager.chain;
|
||||
let resp, hash;
|
||||
if (server.manager.config.api.enable_caching) {
|
||||
[resp, hash] = await getCacheByHash(server.redis, route + JSON.stringify(key), chain);
|
||||
if (resp) {
|
||||
resp = JSON.parse(resp);
|
||||
resp['cached'] = true;
|
||||
return [resp, hash];
|
||||
} else {
|
||||
return [null, hash];
|
||||
}
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
const chain = server.manager.chain;
|
||||
let resp, hash;
|
||||
if (server.manager.config.api.enable_caching) {
|
||||
[resp, hash] = await getCacheByHash(server.redis, route + JSON.stringify(key), chain);
|
||||
if (resp) {
|
||||
resp = JSON.parse(resp);
|
||||
resp['cached'] = true;
|
||||
return [resp, hash];
|
||||
} else {
|
||||
return [null, hash];
|
||||
}
|
||||
} else {
|
||||
return [null, null];
|
||||
}
|
||||
}
|
||||
|
||||
export function getTrackTotalHits(query) {
|
||||
let trackTotalHits: number | boolean = 10000;
|
||||
if (query?.track) {
|
||||
if (query.track === 'true') {
|
||||
trackTotalHits = true;
|
||||
} else if (query.track === 'false') {
|
||||
trackTotalHits = false;
|
||||
} else {
|
||||
const parsed = parseInt(query.track, 10);
|
||||
if (parsed > 0) {
|
||||
trackTotalHits = parsed;
|
||||
} else {
|
||||
throw new Error('failed to parse track param');
|
||||
}
|
||||
}
|
||||
}
|
||||
return trackTotalHits;
|
||||
let trackTotalHits: number | boolean = 10000;
|
||||
if (query?.track) {
|
||||
if (query.track === 'true') {
|
||||
trackTotalHits = true;
|
||||
} else if (query.track === 'false') {
|
||||
trackTotalHits = false;
|
||||
} else {
|
||||
const parsed = parseInt(query.track, 10);
|
||||
if (parsed > 0) {
|
||||
trackTotalHits = parsed;
|
||||
} else {
|
||||
throw new Error('failed to parse track param');
|
||||
}
|
||||
}
|
||||
}
|
||||
return trackTotalHits;
|
||||
}
|
||||
|
||||
function bigint2Milliseconds(input: bigint) {
|
||||
return parseFloat((parseInt(input.toString()) / 1000000).toFixed(3));
|
||||
return parseFloat((parseInt(input.toString()) / 1000000).toFixed(3));
|
||||
}
|
||||
|
||||
const defaultRouteCacheMap = {
|
||||
get_resource_usage: 3600,
|
||||
get_creator: 3600 * 24
|
||||
get_resource_usage: 3600,
|
||||
get_creator: 3600 * 24
|
||||
}
|
||||
|
||||
export async function timedQuery(
|
||||
queryFunction: (fastify: FastifyInstance, request: FastifyRequest) => Promise<any>,
|
||||
fastify: FastifyInstance, request: FastifyRequest, route: string): Promise<any> {
|
||||
queryFunction: (fastify: FastifyInstance, request: FastifyRequest) => Promise<any>,
|
||||
fastify: FastifyInstance, request: FastifyRequest, route: string): Promise<any> {
|
||||
|
||||
// get reference time in nanoseconds
|
||||
const t0 = process.hrtime.bigint();
|
||||
// get reference time in nanoseconds
|
||||
const t0 = process.hrtime.bigint();
|
||||
|
||||
// check for cached data, return the response hash if caching is enabled
|
||||
const [cachedResponse, hash] = await getCachedResponse(
|
||||
fastify,
|
||||
route,
|
||||
request.req.method === 'POST' ? request.body : request.query
|
||||
);
|
||||
// check for cached data, return the response hash if caching is enabled
|
||||
const [cachedResponse, hash] = await getCachedResponse(
|
||||
fastify,
|
||||
route,
|
||||
request.method === 'POST' ? request.body : request.query
|
||||
);
|
||||
|
||||
if (cachedResponse && !request.query.ignoreCache) {
|
||||
// add cached query time
|
||||
cachedResponse['query_time_ms'] = bigint2Milliseconds(process.hrtime.bigint() - t0);
|
||||
return cachedResponse;
|
||||
}
|
||||
if (cachedResponse && !request.query["ignoreCache"]) {
|
||||
// add cached query time
|
||||
cachedResponse['query_time_ms'] = bigint2Milliseconds(process.hrtime.bigint() - t0);
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// call query function
|
||||
const response = await queryFunction(fastify, request);
|
||||
// call query function
|
||||
const response = await queryFunction(fastify, request);
|
||||
|
||||
// save response to cash
|
||||
if (hash) {
|
||||
let EX = null;
|
||||
if (defaultRouteCacheMap[route]) {
|
||||
EX = defaultRouteCacheMap[route];
|
||||
}
|
||||
setCacheByHash(fastify, hash, response, EX);
|
||||
}
|
||||
// save response to cash
|
||||
if (hash) {
|
||||
let EX = null;
|
||||
if (defaultRouteCacheMap[route]) {
|
||||
EX = defaultRouteCacheMap[route];
|
||||
}
|
||||
setCacheByHash(fastify, hash, response, EX);
|
||||
}
|
||||
|
||||
// add normal query time
|
||||
if (response) {
|
||||
response['query_time_ms'] = bigint2Milliseconds(process.hrtime.bigint() - t0);
|
||||
return response;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
// add normal query time
|
||||
if (response) {
|
||||
response['query_time_ms'] = bigint2Milliseconds(process.hrtime.bigint() - t0);
|
||||
return response;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function chainApiHandler(fastify: FastifyInstance) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
await handleChainApiRedirect(request, reply, fastify);
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
// check cache
|
||||
const [cachedData, hash, path] = fastify.cacheManager.getCachedData(request);
|
||||
if (cachedData) {
|
||||
// console.log('cache hit:', path, hash);
|
||||
reply.headers({'hyperion-cached': true}).send(cachedData);
|
||||
} else {
|
||||
// call actual request
|
||||
const apiResponse = await handleChainApiRedirect(request, reply, fastify);
|
||||
// console.log('cache miss:', path, hash);
|
||||
fastify.cacheManager.setCachedData(hash, path, apiResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleChainApiRedirect(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply<ServerResponse>,
|
||||
fastify: FastifyInstance
|
||||
) {
|
||||
const urlParts = request.req.url.split("?");
|
||||
let reqUrl = fastify.chain_api + urlParts[0];
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply,
|
||||
fastify: FastifyInstance
|
||||
): Promise<string> {
|
||||
const urlParts = request.url.split("?");
|
||||
let reqUrl = fastify.chain_api + urlParts[0];
|
||||
|
||||
if (urlParts[0] === '/v1/chain/push_transaction' && fastify.push_api && fastify.push_api !== "") {
|
||||
reqUrl = fastify.push_api + urlParts[0];
|
||||
}
|
||||
// const pathComponents = urlParts[0].split('/');
|
||||
// const path = pathComponents.at(-1);
|
||||
|
||||
const opts = {};
|
||||
|
||||
if (request.req.method === 'POST') {
|
||||
if (request.body) {
|
||||
if (typeof request.body === 'string') {
|
||||
opts['body'] = request.body;
|
||||
} else if (typeof request.body === 'object') {
|
||||
opts['body'] = JSON.stringify(request.body);
|
||||
}
|
||||
} else {
|
||||
opts['body'] = "";
|
||||
}
|
||||
} else if (request.req.method === 'GET') {
|
||||
opts['json'] = request.query;
|
||||
}
|
||||
if (urlParts[0] === '/v1/chain/push_transaction' && fastify.push_api && fastify.push_api !== "") {
|
||||
reqUrl = fastify.push_api + urlParts[0];
|
||||
}
|
||||
|
||||
try {
|
||||
const apiResponse = await got.post(reqUrl, opts);
|
||||
reply.headers({"Content-Type": "application/json"});
|
||||
if (request.req.method === 'HEAD') {
|
||||
reply.headers({"Content-Length": apiResponse.body.length});
|
||||
reply.send("");
|
||||
} else {
|
||||
reply.send(apiResponse.body);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.response) {
|
||||
reply.status(error.response.statusCode).send(error.response.body);
|
||||
} else {
|
||||
console.log(error);
|
||||
reply.status(500).send();
|
||||
}
|
||||
if (fastify.manager.config.api.chain_api_error_log) {
|
||||
try {
|
||||
if (error.response) {
|
||||
const error_msg = JSON.parse(error.response.body).error.details[0].message;
|
||||
console.log(`endpoint: ${request.req.url} | status: ${error.response.statusCode} | error: ${error_msg}`);
|
||||
} else {
|
||||
console.log(error);
|
||||
}
|
||||
// if (request.req.url === '/v1/chain/push_transaction') {
|
||||
// const packedTrx = JSON.parse(opts['body']).packed_trx;
|
||||
// const trxBuffer = Buffer.from(packedTrx, 'hex');
|
||||
// const trxData = await fastify.eosjs.api.deserializeTransactionWithActions(trxBuffer);
|
||||
// console.log(trxData);
|
||||
// }
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
const opts = {};
|
||||
|
||||
if (request.method === 'POST') {
|
||||
if (request.body) {
|
||||
if (typeof request.body === 'string') {
|
||||
opts['body'] = request.body;
|
||||
} else if (typeof request.body === 'object') {
|
||||
opts['body'] = JSON.stringify(request.body);
|
||||
}
|
||||
} else {
|
||||
opts['body'] = "";
|
||||
}
|
||||
} else if (request.method === 'GET') {
|
||||
opts['json'] = request.query;
|
||||
}
|
||||
|
||||
try {
|
||||
const apiResponse = await got.post(reqUrl, opts);
|
||||
reply.headers({"Content-Type": "application/json"});
|
||||
if (request.method === 'HEAD') {
|
||||
reply.headers({"Content-Length": apiResponse.body.length});
|
||||
reply.send("");
|
||||
return '';
|
||||
} else {
|
||||
reply.send(apiResponse.body);
|
||||
return apiResponse.body;
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
if (error.response) {
|
||||
reply.status(error.response.statusCode).send(error.response.body);
|
||||
} else {
|
||||
console.log(error);
|
||||
reply.status(500).send();
|
||||
}
|
||||
|
||||
if (fastify.manager.config.api.chain_api_error_log) {
|
||||
try {
|
||||
if (error.response) {
|
||||
const error_msg = JSON.parse(error.response.body).error.details[0].message;
|
||||
console.log(`endpoint: ${request.raw.url} | status: ${error.response.statusCode} | error: ${error_msg}`);
|
||||
} else {
|
||||
console.log(error);
|
||||
}
|
||||
// if (request.req.url === '/v1/chain/push_transaction') {
|
||||
// const packedTrx = JSON.parse(opts['body']).packed_trx;
|
||||
// const trxBuffer = Buffer.from(packedTrx, 'hex');
|
||||
// const trxData = await fastify.eosjs.api.deserializeTransactionWithActions(trxBuffer);
|
||||
// console.log(trxData);
|
||||
// }
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function addChainApiRoute(fastify: FastifyInstance, routeName, description, props?, required?) {
|
||||
const baseSchema = {
|
||||
description: description,
|
||||
summary: description,
|
||||
tags: ['chain']
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
['GET', 'HEAD'],
|
||||
routeName,
|
||||
chainApiHandler,
|
||||
{
|
||||
...baseSchema,
|
||||
querystring: props ? {
|
||||
type: 'object',
|
||||
properties: props,
|
||||
required: required
|
||||
} : undefined
|
||||
}
|
||||
);
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'POST',
|
||||
routeName,
|
||||
chainApiHandler,
|
||||
{
|
||||
...baseSchema,
|
||||
body: props ? {
|
||||
type: ['object', 'string'],
|
||||
properties: props,
|
||||
required: required
|
||||
} : undefined
|
||||
}
|
||||
);
|
||||
const baseSchema = {
|
||||
description: description,
|
||||
summary: description,
|
||||
tags: ['chain']
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
['GET', 'HEAD'],
|
||||
routeName,
|
||||
chainApiHandler,
|
||||
{
|
||||
...baseSchema,
|
||||
querystring: props ? {
|
||||
type: 'object',
|
||||
properties: props,
|
||||
required: required
|
||||
} : undefined
|
||||
}
|
||||
);
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'POST',
|
||||
routeName,
|
||||
chainApiHandler,
|
||||
{
|
||||
...baseSchema,
|
||||
body: props ? {
|
||||
type: ['object', 'string'],
|
||||
properties: props,
|
||||
required: required
|
||||
} : undefined
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function addSharedSchemas(fastify: FastifyInstance) {
|
||||
fastify.addSchema({
|
||||
$id: "WholeNumber",
|
||||
description: "A whole number",
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
pattern: "^\\d+$"
|
||||
},
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
],
|
||||
});
|
||||
fastify.addSchema({
|
||||
$id: "WholeNumber",
|
||||
title: "Integer",
|
||||
description: "Integer or String",
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
pattern: "^\\d+$"
|
||||
},
|
||||
{
|
||||
type: "integer"
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "Symbol",
|
||||
type: "string",
|
||||
description: "A symbol composed of capital letters between 1-7.",
|
||||
pattern: "^([A-Z]{1,7})$",
|
||||
title: "Symbol"
|
||||
});
|
||||
fastify.addSchema({
|
||||
$id: "Symbol",
|
||||
type: "string",
|
||||
description: "A symbol composed of capital letters between 1-7.",
|
||||
pattern: "^([A-Z]{1,7})$",
|
||||
title: "Symbol"
|
||||
});
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "Signature",
|
||||
type: "string",
|
||||
description: "String representation of an EOSIO compatible cryptographic signature",
|
||||
pattern: "^SIG_([RK]1|WA)_[1-9A-HJ-NP-Za-km-z]+$",
|
||||
title: "Signature"
|
||||
})
|
||||
fastify.addSchema({
|
||||
$id: "Signature",
|
||||
type: "string",
|
||||
description: "String representation of an EOSIO compatible cryptographic signature",
|
||||
pattern: "^SIG_([RK]1|WA)_[1-9A-HJ-NP-Za-km-z]+$",
|
||||
title: "Signature"
|
||||
})
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "AccountName",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of privileged EOSIO name type",
|
||||
"pattern": "^(eosio[\\.][a-z1-5]{1,6})([a-j]{1})?$",
|
||||
"title": "NamePrivileged"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of basic EOSIO name type, must be 12 characters and contain only a-z and 0-5",
|
||||
"pattern": "^([a-z]{1}[a-z1-5]{11})([a-j]{1})?$",
|
||||
"title": "NameBasic"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of EOSIO bid name type, 1-12 characters and only a-z and 0-5 are allowed",
|
||||
"pattern": "^([a-z1-5]{1,12})([a-j]{1})?$",
|
||||
"title": "NameBid"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of EOSIO name type",
|
||||
"pattern": "^([a-z1-5]{1}[a-z1-5\\.]{0,10}[a-z1-5]{1})([a-j]{1})?$",
|
||||
"title": "NameCatchAll"
|
||||
}
|
||||
],
|
||||
"title": "Name"
|
||||
});
|
||||
fastify.addSchema({
|
||||
$id: "AccountName",
|
||||
description: "String representation of an EOSIO compatible account name",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of privileged EOSIO name type",
|
||||
"pattern": "^(eosio[\\.][a-z1-5]{1,6})([a-j]{1})?$",
|
||||
"title": "NamePrivileged"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of basic EOSIO name type, must be 12 characters and contain only a-z and 0-5",
|
||||
"pattern": "^([a-z]{1}[a-z1-5]{11})([a-j]{1})?$",
|
||||
"title": "NameBasic"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of EOSIO bid name type, 1-12 characters and only a-z and 0-5 are allowed",
|
||||
"pattern": "^([a-z1-5]{1,12})([a-j]{1})?$",
|
||||
"title": "NameBid"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "String representation of EOSIO name type",
|
||||
"pattern": "^([a-z1-5]{1}[a-z1-5\\.]{0,10}[a-z1-5]{1})([a-j]{1})?$",
|
||||
"title": "NameCatchAll"
|
||||
}
|
||||
],
|
||||
"title": "Name"
|
||||
});
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "Expiration",
|
||||
"description": "Time that transaction must be confirmed by.",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$",
|
||||
"title": "DateTime"
|
||||
});
|
||||
fastify.addSchema({
|
||||
$id: "Expiration",
|
||||
"description": "Time that transaction must be confirmed by.",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$",
|
||||
"title": "DateTime"
|
||||
});
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "BlockExtensions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [{"type": "integer"}, {"type": "string"}]
|
||||
},
|
||||
"title": "Extension"
|
||||
})
|
||||
fastify.addSchema({
|
||||
$id: "BlockExtensions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{"type": "integer"},
|
||||
{"type": "string"}
|
||||
]
|
||||
},
|
||||
"title": "Extension"
|
||||
})
|
||||
|
||||
fastify.addSchema({
|
||||
$id: "ActionItems",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"minProperties": 5,
|
||||
"required": [
|
||||
"account",
|
||||
"name",
|
||||
"authorization",
|
||||
"data",
|
||||
"hex_data"
|
||||
],
|
||||
"properties": {
|
||||
"account": 'AccountName#',
|
||||
"name": 'AccountName#',
|
||||
"authorization": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"minProperties": 2,
|
||||
"required": [
|
||||
"actor",
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"actor": 'AccountName#',
|
||||
"permission": 'AccountName#'
|
||||
},
|
||||
"title": "Authority"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"hex_data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Action"
|
||||
});
|
||||
fastify.addSchema({
|
||||
$id: "ActionItems",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"minProperties": 5,
|
||||
"required": [
|
||||
"account",
|
||||
"name",
|
||||
"authorization",
|
||||
"data",
|
||||
"hex_data"
|
||||
],
|
||||
"properties": {
|
||||
"account": {$ref: 'AccountName#'},
|
||||
"name": {$ref: 'AccountName#'},
|
||||
"authorization": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"minProperties": 2,
|
||||
"required": [
|
||||
"actor",
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"actor": {$ref: 'AccountName#'},
|
||||
"permission": {$ref: 'AccountName#'}
|
||||
},
|
||||
"title": "Authority"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"hex_data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"title": "Action"
|
||||
});
|
||||
}
|
||||
|
||||
+22
-12
@@ -2,22 +2,32 @@ import * as Fastify from "fastify";
|
||||
import {IncomingMessage, Server, ServerResponse} from "http";
|
||||
|
||||
// fastify plugins
|
||||
import * as fastify_elasticsearch from 'fastify-elasticsearch';
|
||||
import * as fastify_oas from 'fastify-oas';
|
||||
import * as fastify_cors from 'fastify-cors';
|
||||
import * as fastify_formbody from 'fastify-formbody';
|
||||
import * as fastify_redis from 'fastify-redis';
|
||||
import * as fastify_rate_limit from 'fastify-rate-limit';
|
||||
import * as fastifyElasticsearch from 'fastify-elasticsearch';
|
||||
import fastifySwagger from 'fastify-swagger';
|
||||
import fastifyCors from 'fastify-cors';
|
||||
import fastifyFormbody from 'fastify-formbody';
|
||||
import fastifyRedis from 'fastify-redis';
|
||||
import fastifyRateLimit from 'fastify-rate-limit';
|
||||
|
||||
// custom plugins
|
||||
import fastify_eosjs from "./plugins/fastify-eosjs";
|
||||
|
||||
export function registerPlugins(server: Fastify.FastifyInstance<Server, IncomingMessage, ServerResponse>, params: any) {
|
||||
server.register(fastify_elasticsearch, params.fastify_elasticsearch);
|
||||
server.register(fastify_oas, params.fastify_oas);
|
||||
server.register(fastify_cors);
|
||||
server.register(fastify_formbody);
|
||||
server.register(fastify_redis, params.fastify_redis);
|
||||
server.register(fastifyElasticsearch, params.fastify_elasticsearch);
|
||||
|
||||
if (params.fastify_swagger) {
|
||||
server.register(fastifySwagger, params.fastify_swagger);
|
||||
}
|
||||
|
||||
server.register(fastifyCors);
|
||||
|
||||
server.register(fastifyFormbody);
|
||||
|
||||
server.register(fastifyRedis, params.fastify_redis);
|
||||
|
||||
if (params.fastify_rate_limit) {
|
||||
server.register(fastifyRateLimit, params.fastify_rate_limit);
|
||||
}
|
||||
|
||||
server.register(fastify_eosjs, params.fastify_eosjs);
|
||||
server.register(fastify_rate_limit, params.fastify_rate_limit);
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
const fp = require('fastify-plugin');
|
||||
const {Api} = require("eosjs");
|
||||
const {ConnectionManager} = require('../../connections/manager');
|
||||
const manager = new ConnectionManager();
|
||||
|
||||
module.exports = fp(async (fastify, options, next) => {
|
||||
const rpc = manager.nodeosJsonRPC;
|
||||
const chain_data = await rpc.get_info();
|
||||
const api = new Api({
|
||||
rpc,
|
||||
signatureProvider: null,
|
||||
chainId: chain_data.chain_id,
|
||||
textDecoder: new TextDecoder(),
|
||||
textEncoder: new TextEncoder(),
|
||||
});
|
||||
fastify.decorate('eosjs', {api, rpc});
|
||||
next();
|
||||
}, {
|
||||
fastify: '>=2.0.0',
|
||||
name: 'fastify-eosjs'
|
||||
});
|
||||
@@ -1,20 +1,19 @@
|
||||
import * as fp from 'fastify-plugin';
|
||||
import {FastifyInstance} from "fastify";
|
||||
import {FastifyInstance, FastifyPluginOptions} from "fastify";
|
||||
import {Api} from "eosjs/dist";
|
||||
import fp from "fastify-plugin";
|
||||
|
||||
export default fp(async (fastify: FastifyInstance, options, next) => {
|
||||
const rpc = fastify.manager.nodeosJsonRPC;
|
||||
const chain_data = await rpc.get_info();
|
||||
const api = new Api({
|
||||
rpc,
|
||||
signatureProvider: null,
|
||||
chainId: chain_data.chain_id,
|
||||
textDecoder: new TextDecoder(),
|
||||
textEncoder: new TextEncoder(),
|
||||
});
|
||||
fastify.decorate('eosjs', {api, rpc});
|
||||
next();
|
||||
export default fp(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => {
|
||||
const rpc = fastify.manager.nodeosJsonRPC;
|
||||
const chain_data = await rpc.get_info();
|
||||
const api = new Api({
|
||||
rpc,
|
||||
signatureProvider: null,
|
||||
chainId: chain_data.chain_id,
|
||||
textDecoder: new TextDecoder(),
|
||||
textEncoder: new TextEncoder(),
|
||||
});
|
||||
fastify.decorate('eosjs', {api, rpc});
|
||||
}, {
|
||||
fastify: '>=2.0.0',
|
||||
name: 'fastify-eosjs'
|
||||
fastify: '>=2.0.0',
|
||||
name: 'fastify-eosjs'
|
||||
});
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
const fp = require('fastify-plugin');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
module.exports = fp((fastify, opts, next) => {
|
||||
|
||||
const wss = new WebSocket.Server({
|
||||
server: fastify.server
|
||||
});
|
||||
|
||||
const ws = new WebSocket('ws://127.0.0.1:7001', [], {
|
||||
perMessageDeflate: false
|
||||
});
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('router connected');
|
||||
});
|
||||
|
||||
fastify.decorate('wss', wss);
|
||||
fastify.decorate('ws', ws);
|
||||
|
||||
fastify.addHook('onClose', (fastify, done) => {
|
||||
fastify.ws.close(done);
|
||||
fastify.wss.close(done);
|
||||
});
|
||||
|
||||
next();
|
||||
}, {
|
||||
fastify: '2.x',
|
||||
name: 'fastify-websocket'
|
||||
});
|
||||
+27
-89
@@ -1,27 +1,28 @@
|
||||
import * as fastify_static from "fastify-static";
|
||||
import {join} from "path";
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {createReadStream, existsSync, readFileSync, unlinkSync} from "fs";
|
||||
import * as AutoLoad from "fastify-autoload";
|
||||
import {FastifyError, FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {createReadStream} from "fs";
|
||||
import {addSharedSchemas, handleChainApiRedirect} from "./helpers/functions";
|
||||
import autoLoad from 'fastify-autoload';
|
||||
import got from "got";
|
||||
|
||||
function addRedirect(server: FastifyInstance, url: string, redirectTo: string) {
|
||||
server.route({
|
||||
url,
|
||||
method: 'GET',
|
||||
schema: {hide: true},
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
schema: {
|
||||
hide: true
|
||||
},
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.redirect(redirectTo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addRoute(server: FastifyInstance, handlersPath: string, prefix: string) {
|
||||
server.register(AutoLoad, {
|
||||
server.register(autoLoad, {
|
||||
dir: join(__dirname, 'routes', handlersPath),
|
||||
ignorePattern: /.*(handler|schema).js/,
|
||||
dirNameRoutePrefix: false,
|
||||
options: {prefix}
|
||||
});
|
||||
}
|
||||
@@ -36,11 +37,11 @@ export function registerRoutes(server: FastifyInstance) {
|
||||
'/v2/history',
|
||||
'/v2/state',
|
||||
'/v1/chain/*',
|
||||
'/v1/chain',
|
||||
'/v1/chain'
|
||||
];
|
||||
server.addHook('onRoute', opts => {
|
||||
if (!ignoreList.includes(opts.url)) {
|
||||
if (opts.url.startsWith('/v') && !opts.url.startsWith('/v2/explore') && !opts.url.startsWith('/v2/docs')) {
|
||||
if (opts.url.startsWith('/v')) {
|
||||
routeSet.add(opts.url);
|
||||
}
|
||||
}
|
||||
@@ -60,6 +61,8 @@ export function registerRoutes(server: FastifyInstance) {
|
||||
|
||||
// chain api redirects
|
||||
addRoute(server, 'v1-chain', '/v1/chain');
|
||||
|
||||
// other v1 requests
|
||||
server.route({
|
||||
url: '/v1/chain/*',
|
||||
method: ["GET", "POST"],
|
||||
@@ -67,7 +70,10 @@ export function registerRoutes(server: FastifyInstance) {
|
||||
summary: "Wildcard chain api handler",
|
||||
tags: ["chain"]
|
||||
},
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
|
||||
console.log(request.url);
|
||||
|
||||
await handleChainApiRedirect(request, reply, server);
|
||||
}
|
||||
});
|
||||
@@ -80,7 +86,7 @@ export function registerRoutes(server: FastifyInstance) {
|
||||
summary: "Get list of supported APIs",
|
||||
tags: ["node"]
|
||||
},
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
handler: async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
const data = await got.get(`${server.chain_api}/v1/node/get_supported_apis`).json() as any;
|
||||
if (data.apis && data.apis.length > 0) {
|
||||
const apiSet = new Set(server.routeSet);
|
||||
@@ -92,88 +98,20 @@ export function registerRoutes(server: FastifyInstance) {
|
||||
}
|
||||
});
|
||||
|
||||
server.addHook('onError', (request, reply, error, done) => {
|
||||
console.log(`[${request.req.headers['x-real-ip']}] ${request.req.method} ${request.req.url} failed with error: ${error.message}`);
|
||||
server.addHook('onError', (request: FastifyRequest, reply: FastifyReply, error: FastifyError, done) => {
|
||||
console.log(`[${request.headers['x-real-ip'] || request.ip}] ${request.method} ${request.url} failed >> ${error.message}`);
|
||||
done();
|
||||
});
|
||||
|
||||
// server.addHook('onResponse', (request, reply, done) => {
|
||||
// if (reply.res.statusCode !== 200) {
|
||||
// console.log(`${request.req.url} - code: ${reply.res.statusCode}`);
|
||||
// }
|
||||
// done();
|
||||
// });
|
||||
|
||||
// Serve integrated explorer
|
||||
if (server.manager.config.api.enable_explorer) {
|
||||
|
||||
server.register(require('fastify-compress'), {global: false});
|
||||
|
||||
try {
|
||||
const _data = readFileSync(join(__dirname, '..', 'hyperion-explorer', 'src', 'manifest.webmanifest'));
|
||||
const tempPath = join(__dirname, '..', 'hyperion-explorer', 'dist', 'manifest.webmanifest');
|
||||
if (existsSync(tempPath)) {
|
||||
unlinkSync(tempPath);
|
||||
}
|
||||
const baseManifest = JSON.parse(_data.toString());
|
||||
baseManifest.name = `Hyperion Explorer - ${server.manager.config.api.chain_name}`;
|
||||
baseManifest.short_name = baseManifest.name;
|
||||
server.get('/v2/explore/manifest.webmanifest', (request, reply) => {
|
||||
reply.send(baseManifest);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
server.register(fastify_static, {
|
||||
root: join(__dirname, '..', 'hyperion-explorer', 'dist'),
|
||||
redirect: true,
|
||||
wildcard: false,
|
||||
prefix: '/v2/explore',
|
||||
setHeaders: (res: ServerResponse, path) => {
|
||||
if (path.endsWith('/ngsw-worker.js')) {
|
||||
res.setHeader('Service-Worker-Allowed', '/');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.get(
|
||||
'/v2/explore/**/*',
|
||||
{
|
||||
schema: {
|
||||
tags: ['internal']
|
||||
}
|
||||
},
|
||||
(request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.sendFile('index.html', join(__dirname, '..', 'hyperion-explorer', 'dist'));
|
||||
}
|
||||
);
|
||||
|
||||
server.get(
|
||||
'/v2/explorer_metadata',
|
||||
{
|
||||
schema: {
|
||||
tags: ['internal']
|
||||
}
|
||||
},
|
||||
(request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send({
|
||||
logo: server.manager.config.api.chain_logo_url,
|
||||
provider: server.manager.config.api.provider_name,
|
||||
provider_url: server.manager.config.api.provider_url,
|
||||
chain_name: server.manager.config.api.chain_name,
|
||||
chain_id: server.manager.conn.chains[server.manager.chain].chain_id,
|
||||
custom_core_token: server.manager.config.api.custom_core_token
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (server.manager.config.features.streaming) {
|
||||
// steam client lib
|
||||
server.get('/stream-client.js', {schema: {tags: ['internal']}}, (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
const stream = createReadStream('./client_bundle.js');
|
||||
reply.type('application/javascript').send(stream);
|
||||
});
|
||||
server.get(
|
||||
'/stream-client.js',
|
||||
{schema: {tags: ['internal']}},
|
||||
(request: FastifyRequest, reply: FastifyReply) => {
|
||||
const stream = createReadStream('./hyperion-stream-client.js');
|
||||
reply.type('application/javascript').send(stream);
|
||||
});
|
||||
}
|
||||
|
||||
// Redirect routes to documentation
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
const {getCacheByHash} = require("../../helpers/functions");
|
||||
|
||||
const {getTransactedAccountsSchema} = require("../../schemas");
|
||||
|
||||
async function getTransactedAccounts(fastify, request) {
|
||||
const t0 = Date.now();
|
||||
const {redis, elastic} = fastify;
|
||||
const [cachedResponse, hash] = await getCacheByHash(redis, JSON.stringify(request.query));
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
const {account, min, max, direction, limit, contract, symbol} = request.query;
|
||||
let _limit = 100;
|
||||
if (limit) {
|
||||
_limit = parseInt(limit, 10);
|
||||
if (_limit > 500) {
|
||||
_limit = 500;
|
||||
}
|
||||
}
|
||||
let _min;
|
||||
if (min) {
|
||||
_min = parseInt(min, 10);
|
||||
if (_min < 0) {
|
||||
_min = 0;
|
||||
}
|
||||
}
|
||||
let _max;
|
||||
if (max) {
|
||||
_max = parseInt(max, 10);
|
||||
if (_max < 0) {
|
||||
_max = null;
|
||||
}
|
||||
}
|
||||
|
||||
const response = {
|
||||
query_time: null,
|
||||
account: account
|
||||
};
|
||||
|
||||
const must_array = [
|
||||
{term: {"notified": account}},
|
||||
{term: {"act.name": "transfer"}}
|
||||
];
|
||||
|
||||
if (min || max) {
|
||||
const _range = {
|
||||
"@transfer.amount": {}
|
||||
};
|
||||
if (min) {
|
||||
_range["@transfer.amount"]["gte"] = _min;
|
||||
}
|
||||
if (max) {
|
||||
_range["@transfer.amount"]["lt"] = _max;
|
||||
}
|
||||
must_array.push({range: _range});
|
||||
}
|
||||
|
||||
if (contract) {
|
||||
must_array.push({term: {"act.account": contract}});
|
||||
response['contract'] = contract;
|
||||
}
|
||||
|
||||
if (symbol) {
|
||||
must_array.push({term: {"@transfer.symbol": symbol}});
|
||||
response['symbol'] = symbol;
|
||||
}
|
||||
|
||||
const sum_agg = {
|
||||
"total_amount": {
|
||||
sum: {
|
||||
field: "@transfer.amount"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let filter_array = [];
|
||||
if (request.query['after'] || request.query['before']) {
|
||||
let _lte = "now";
|
||||
let _gte = 0;
|
||||
if (request.query['before']) {
|
||||
_lte = request.query['before'];
|
||||
}
|
||||
if (request.query['after']) {
|
||||
_gte = request.query['after'];
|
||||
}
|
||||
filter_array.push({
|
||||
range: {
|
||||
"@timestamp": {
|
||||
"gte": _gte,
|
||||
"lte": _lte
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
filter_array.push({
|
||||
range: {
|
||||
"block_num": {
|
||||
"gte": 0
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (direction === 'out' || direction === 'both') {
|
||||
const outResults = await elastic.search({
|
||||
index: process.env.CHAIN + '-action-*',
|
||||
body: {
|
||||
size: 0,
|
||||
aggs: {
|
||||
"receivers": {
|
||||
terms: {
|
||||
field: "@transfer.to",
|
||||
size: _limit,
|
||||
order: {
|
||||
"total_amount": "desc"
|
||||
}
|
||||
},
|
||||
aggs: sum_agg
|
||||
}
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
must: must_array,
|
||||
filter: filter_array,
|
||||
must_not: [
|
||||
{term: {"@transfer.to": account}}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
response['total_out'] = 0;
|
||||
response['outputs'] = outResults['body']['aggregations']['receivers']['buckets'].map((bucket) => {
|
||||
const _sum = parseFloat(bucket.total_amount.value.toFixed(4));
|
||||
response['total_out'] += _sum;
|
||||
return {
|
||||
account: bucket.key,
|
||||
sum: _sum,
|
||||
transfers: bucket['doc_count'],
|
||||
average: parseFloat((bucket.total_amount.value / bucket['doc_count']).toFixed(4))
|
||||
};
|
||||
});
|
||||
response['total_out'] = parseFloat(response['total_out'].toFixed(4));
|
||||
}
|
||||
|
||||
if (direction === 'in' || direction === 'both') {
|
||||
const inResults = await elastic.search({
|
||||
index: process.env.CHAIN + '-action-*',
|
||||
body: {
|
||||
size: 0,
|
||||
aggs: {
|
||||
"senders": {
|
||||
terms: {
|
||||
field: "@transfer.from",
|
||||
size: _limit,
|
||||
order: {
|
||||
"total_amount": "desc"
|
||||
}
|
||||
},
|
||||
aggs: sum_agg
|
||||
}
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
must: must_array,
|
||||
filter: filter_array,
|
||||
must_not: [
|
||||
{term: {"@transfer.from": account}}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
response['total_in'] = 0;
|
||||
response['inputs'] = inResults['body']['aggregations']['senders']['buckets'].map((bucket) => {
|
||||
const _sum = parseFloat(bucket.total_amount.value.toFixed(4));
|
||||
response['total_in'] += _sum;
|
||||
return {
|
||||
account: bucket.key,
|
||||
sum: _sum,
|
||||
transfers: bucket['doc_count'],
|
||||
average: parseFloat((bucket.total_amount.value / bucket['doc_count']).toFixed(4))
|
||||
};
|
||||
});
|
||||
response['total_in'] = parseFloat(response['total_in'].toFixed(4));
|
||||
}
|
||||
|
||||
response['query_time'] = Date.now() - t0;
|
||||
redis.set(hash, JSON.stringify(response), 'EX', 600);
|
||||
return response;
|
||||
}
|
||||
|
||||
module.exports = function (fastify, opts, next) {
|
||||
fastify.get('/get_transacted_accounts', {
|
||||
schema: getTransactedAccountsSchema.GET
|
||||
}, async (request, reply) => {
|
||||
reply.send(await getTransactedAccounts(fastify, request));
|
||||
});
|
||||
next()
|
||||
};
|
||||
@@ -1,98 +0,0 @@
|
||||
const {getTransfersSchema} = require("../../schemas");
|
||||
const _ = require('lodash');
|
||||
const {getCacheByHash} = require("../../helpers/functions");
|
||||
const route = '/get_transfers';
|
||||
|
||||
const maxActions = 1000;
|
||||
|
||||
function processActions(results) {
|
||||
const action_traces = results['body']['hits']['hits'];
|
||||
const actions = [];
|
||||
let sum = 0;
|
||||
for (const aTrace of action_traces) {
|
||||
const action = aTrace['_source'];
|
||||
const name = action.act.name;
|
||||
if (action['@' + name]) {
|
||||
if (action['@transfer']) {
|
||||
sum += parseFloat(action['@transfer']['amount']);
|
||||
}
|
||||
action['act']['data'] = _.merge(action['@' + name], action['act']['data']);
|
||||
delete action['@' + name];
|
||||
}
|
||||
actions.push(action);
|
||||
}
|
||||
return [sum, actions];
|
||||
}
|
||||
|
||||
async function getTransfers(fastify, request) {
|
||||
const {redis, elastic} = fastify;
|
||||
const [cachedResponse, hash] = await getCacheByHash(redis, route + JSON.stringify(request.query));
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
const must_array = [];
|
||||
must_array.push({"term": {"act.name": {"value": "transfer"}}});
|
||||
if (request.query['from']) {
|
||||
must_array.push({"term": {"@transfer.from": {"value": request.query['from'].toLowerCase()}}});
|
||||
}
|
||||
if (request.query['to']) {
|
||||
must_array.push({"term": {"@transfer.to": {"value": request.query['to'].toLowerCase()}}});
|
||||
}
|
||||
if (request.query['symbol']) {
|
||||
must_array.push({"term": {"@transfer.symbol": {"value": request.query['symbol'].toUpperCase()}}});
|
||||
}
|
||||
if (request.query['contract']) {
|
||||
must_array.push({"term": {"act.account": {"value": request.query['contract'].toLowerCase()}}});
|
||||
}
|
||||
if (request.query['after'] || request.query['before']) {
|
||||
let _lte = "now";
|
||||
let _gte = 0;
|
||||
if (request.query['before']) {
|
||||
_lte = request.query['before'];
|
||||
}
|
||||
if (request.query['after']) {
|
||||
_gte = request.query['after'];
|
||||
}
|
||||
must_array.push({
|
||||
range: {
|
||||
"@timestamp": {
|
||||
"gte": _gte,
|
||||
"lte": _lte
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
let limit, skip;
|
||||
limit = parseInt(request.query.limit, 10);
|
||||
if (limit < 1) {
|
||||
return 'invalid limit parameter';
|
||||
}
|
||||
skip = parseInt(request.query.skip, 10);
|
||||
if (skip < 0) {
|
||||
return 'invalid skip parameter';
|
||||
}
|
||||
const body = {"query": {"bool": {"must": must_array}}};
|
||||
const results = await elastic.search({
|
||||
"index": process.env.CHAIN + '-action-*',
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxActions ? maxActions : limit) || 10,
|
||||
"body": body
|
||||
});
|
||||
const [sum, _actions] = processActions(results);
|
||||
const response = {
|
||||
"action_count": results['body']['hits']['total']['value'],
|
||||
"total_amount": sum,
|
||||
"actions": _actions
|
||||
};
|
||||
redis.set(hash, JSON.stringify(response), 'EX', 30);
|
||||
return response;
|
||||
}
|
||||
|
||||
module.exports = function (fastify, opts, next) {
|
||||
fastify.get(route, {
|
||||
schema: getTransfersSchema.GET
|
||||
}, async (request, reply) => {
|
||||
reply.send(await getTransfers(fastify, request));
|
||||
});
|
||||
next()
|
||||
};
|
||||
@@ -7,8 +7,8 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Returns an object containing rows from the specified table.',
|
||||
{
|
||||
"code": 'AccountName#',
|
||||
"action": 'AccountName#',
|
||||
"code": {$ref: 'AccountName#'},
|
||||
"action": {$ref: 'AccountName#'},
|
||||
"binargs": {
|
||||
"type": "string",
|
||||
"pattern": "^(0x)(([0-9a-f][0-9a-f])+)?$",
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
fastify,
|
||||
getRouteName(__filename),
|
||||
'Retrieves the ABI for a contract based on its account name',
|
||||
{"account_name": 'AccountName#'},
|
||||
{"account_name": {$ref: 'AccountName#'}},
|
||||
["account_name"]
|
||||
);
|
||||
next();
|
||||
|
||||
@@ -6,7 +6,9 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
fastify,
|
||||
getRouteName(__filename),
|
||||
'Returns an object containing various details about a specific account on the blockchain.',
|
||||
{"account_name": 'AccountName#'},
|
||||
{
|
||||
"account_name": {$ref: 'AccountName#'}
|
||||
},
|
||||
["account_name"]
|
||||
);
|
||||
next();
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Retrieves contract code',
|
||||
{
|
||||
"account_name": 'AccountName#',
|
||||
"account_name": {$ref: 'AccountName#'},
|
||||
"code_as_wasm": {
|
||||
"type": "integer",
|
||||
"default": 1,
|
||||
|
||||
@@ -7,9 +7,9 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Retrieves the current balance',
|
||||
{
|
||||
"code": 'AccountName#',
|
||||
"account": 'AccountName#',
|
||||
"symbol": 'Symbol#',
|
||||
"code": {$ref: 'AccountName#'},
|
||||
"account": {$ref: 'AccountName#'},
|
||||
"symbol": {$ref: 'Symbol#'}
|
||||
},
|
||||
["code", "account", "symbol"]
|
||||
);
|
||||
|
||||
@@ -7,12 +7,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Retrieves currency stats',
|
||||
{
|
||||
"code": {
|
||||
description: 'contract name',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"code": {$ref: 'AccountName#'},
|
||||
"symbol": {
|
||||
description: 'token symbol',
|
||||
type: 'string',
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Retrieves raw ABI for a contract based on account name',
|
||||
{
|
||||
"account_name": 'AccountName#'
|
||||
"account_name": {$ref: 'AccountName#'}
|
||||
},
|
||||
["account_name"]
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
getRouteName(__filename),
|
||||
'Retrieves raw code and ABI for a contract based on account name',
|
||||
{
|
||||
"account_name": 'AccountName#'
|
||||
"account_name": {$ref: 'AccountName#'}
|
||||
},
|
||||
["account_name"]
|
||||
);
|
||||
|
||||
@@ -36,7 +36,8 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
"lower_bound": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
["code", "table", "scope"]
|
||||
);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
signatures: {
|
||||
"type": "array",
|
||||
"description": "array of signatures required to authorize transaction",
|
||||
"items": 'Signature#'
|
||||
"items": {$ref: 'Signature#'}
|
||||
},
|
||||
compression: {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -2,45 +2,54 @@ import {FastifyInstance} from "fastify";
|
||||
import {addApiRoute, chainApiHandler, getRouteName} from "../../../helpers/functions";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'POST',
|
||||
getRouteName(__filename),
|
||||
chainApiHandler,
|
||||
{
|
||||
description: "This method expects a transaction in JSON format and will attempt to apply it to the blockchain.",
|
||||
summary: "This method expects a transaction in JSON format and will attempt to apply it to the blockchain.",
|
||||
tags: ['chain'],
|
||||
body: {
|
||||
type: ['array', 'object', 'string'],
|
||||
items: {
|
||||
type: ['object', 'string'],
|
||||
additionalProperties: false,
|
||||
minProperties: 8,
|
||||
required: [
|
||||
"expiration",
|
||||
"ref_block_num",
|
||||
"ref_block_prefix",
|
||||
"max_net_usage_words",
|
||||
"max_cpu_usage_ms",
|
||||
"delay_sec",
|
||||
"context_free_actions",
|
||||
"actions"
|
||||
],
|
||||
properties: {
|
||||
"expiration": 'Expiration#',
|
||||
"ref_block_num": {"type": "integer"},
|
||||
"ref_block_prefix": {"type": "integer"},
|
||||
"max_net_usage_words": 'WholeNumber#',
|
||||
"max_cpu_usage_ms": 'WholeNumber#',
|
||||
"delay_sec": {"type": "integer"},
|
||||
"context_free_actions": {"type": "array", "items": 'ActionItems#'},
|
||||
"actions": {"type": "array", "items": 'ActionItems#'},
|
||||
"transaction_extensions": {"type": "array", "items": 'BlockExtensions#'}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
next();
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'POST',
|
||||
getRouteName(__filename),
|
||||
chainApiHandler,
|
||||
{
|
||||
description: "This method expects a transaction in JSON format and will attempt to apply it to the blockchain.",
|
||||
summary: "This method expects a transaction in JSON format and will attempt to apply it to the blockchain.",
|
||||
tags: ['chain'],
|
||||
body: {
|
||||
type: ['array', 'object', 'string'],
|
||||
items: {
|
||||
type: ['object', 'string'],
|
||||
additionalProperties: false,
|
||||
minProperties: 8,
|
||||
required: [
|
||||
"expiration",
|
||||
"ref_block_num",
|
||||
"ref_block_prefix",
|
||||
"max_net_usage_words",
|
||||
"max_cpu_usage_ms",
|
||||
"delay_sec",
|
||||
"context_free_actions",
|
||||
"actions"
|
||||
],
|
||||
properties: {
|
||||
"expiration": {$ref: 'Expiration#'},
|
||||
"ref_block_num": {"type": "integer"},
|
||||
"ref_block_prefix": {"type": "integer"},
|
||||
"max_net_usage_words": {$ref: 'WholeNumber#'},
|
||||
"max_cpu_usage_ms": {$ref: 'WholeNumber#'},
|
||||
"delay_sec": {"type": "integer"},
|
||||
"context_free_actions": {
|
||||
"type": "array",
|
||||
"items": {$ref: 'ActionItems#'}
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {$ref: 'ActionItems#'}
|
||||
},
|
||||
"transaction_extensions": {
|
||||
"type": "array",
|
||||
"items": {$ref: 'BlockExtensions#'}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
signatures: {
|
||||
"type": "array",
|
||||
"description": "array of signatures required to authorize transaction",
|
||||
"items": 'Signature#'
|
||||
"items": {$ref: 'Signature#'}
|
||||
},
|
||||
compression: {
|
||||
"type": "boolean",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {mergeActionMeta, timedQuery} from "../../../helpers/functions";
|
||||
import * as flatstr from 'flatstr';
|
||||
import flatstr from 'flatstr';
|
||||
|
||||
const terms = ["notified", "act.authorization.actor"];
|
||||
const extendedActions = new Set(["transfer", "newaccount", "updateauth"]);
|
||||
@@ -12,7 +11,7 @@ async function getActions(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
request.body = JSON.parse(request.body);
|
||||
}
|
||||
|
||||
const reqBody = request.body;
|
||||
const reqBody = request.body as any;
|
||||
const should_array = [];
|
||||
for (const entry of terms) {
|
||||
const tObj = {term: {}};
|
||||
@@ -191,7 +190,7 @@ async function getActions(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getActionsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getActions, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,72 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {ApiResponse} from "@elastic/elasticsearch";
|
||||
|
||||
async function getControlledAccounts(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
if (typeof request.body === 'string') {
|
||||
request.body = JSON.parse(request.body)
|
||||
}
|
||||
let controlling_account = request.body.controlling_account;
|
||||
const results: ApiResponse = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: 100,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {"@updateauth.auth.accounts.permission.actor": controlling_account}},
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{term: {"act.account": "eosio"}},
|
||||
{term: {"act.name": "newaccount"}},
|
||||
{term: {"act.authorization.actor": controlling_account}}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
},
|
||||
sort: [
|
||||
{"global_sequence": {"order": "desc"}}
|
||||
]
|
||||
}
|
||||
});
|
||||
if (typeof request.body === 'string') {
|
||||
request.body = JSON.parse(request.body)
|
||||
}
|
||||
let controlling_account = request.body["controlling_account"];
|
||||
const results: ApiResponse = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: 100,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
term: {"@updateauth.auth.accounts.permission.actor": controlling_account}
|
||||
},
|
||||
{
|
||||
bool: {
|
||||
must: [
|
||||
{term: {"act.account": "eosio"}},
|
||||
{term: {"act.name": "newaccount"}},
|
||||
{term: {"act.authorization.actor": controlling_account}}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
},
|
||||
sort: [
|
||||
{"global_sequence": {"order": "desc"}}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const response = {
|
||||
controlled_accounts: []
|
||||
};
|
||||
const response = {
|
||||
controlled_accounts: []
|
||||
};
|
||||
|
||||
const hits = results.body.hits.hits;
|
||||
const hits = results.body.hits.hits;
|
||||
|
||||
if (hits.length > 0) {
|
||||
response.controlled_accounts = hits.map((v) => {
|
||||
if (v._source.act.name === 'newaccount') {
|
||||
if (v._source['@newaccount'].newact) {
|
||||
return v._source['@newaccount'].newact;
|
||||
} else if (v._source.act.data.newact) {
|
||||
return v._source.act.data.newact;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (v._source.act.name === 'updateauth') {
|
||||
return v._source.act.data.account;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (response.controlled_accounts.length > 0) {
|
||||
response.controlled_accounts = [...(new Set(response.controlled_accounts))];
|
||||
}
|
||||
return response;
|
||||
if (hits.length > 0) {
|
||||
response.controlled_accounts = hits.map((v) => {
|
||||
if (v._source.act.name === 'newaccount') {
|
||||
if (v._source['@newaccount'].newact) {
|
||||
return v._source['@newaccount'].newact;
|
||||
} else if (v._source.act.data.newact) {
|
||||
return v._source.act.data.newact;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (v._source.act.name === 'updateauth') {
|
||||
return v._source.act.data.account;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (response.controlled_accounts.length > 0) {
|
||||
response.controlled_accounts = [...(new Set(response.controlled_accounts))];
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getControlledAccountsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getControlledAccounts, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getControlledAccounts, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {mergeActionMeta, timedQuery} from "../../../helpers/functions";
|
||||
import {createHash} from "crypto";
|
||||
import * as flatstr from 'flatstr';
|
||||
import flatstr from 'flatstr';
|
||||
import {GetInfoResult} from "eosjs/dist/eosjs-rpc-interfaces";
|
||||
|
||||
async function getTransaction(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
if (typeof request.body === 'string') {
|
||||
request.body = JSON.parse(request.body)
|
||||
}
|
||||
const pResults = await Promise.all([fastify.eosjs.rpc.get_info(), fastify.elastic['search']({
|
||||
"index": fastify.manager.chain + '-action-*',
|
||||
"body": {
|
||||
"query": {
|
||||
"bool": {
|
||||
must: [
|
||||
{term: {"trx_id": request.body.id.toLowerCase()}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"global_sequence": "asc"
|
||||
}
|
||||
}
|
||||
})]);
|
||||
const results = pResults[1];
|
||||
const body: any = request.body;
|
||||
const redis = fastify.redis;
|
||||
const trxId = body.id.toLowerCase();
|
||||
const conf = fastify.manager.config;
|
||||
const cachedData = await redis.hgetall('trx_' + trxId);
|
||||
|
||||
const response: any = {
|
||||
"id": request.body.id,
|
||||
"id": body.id,
|
||||
"trx": {
|
||||
"receipt": {
|
||||
"status": "executed",
|
||||
@@ -42,11 +32,89 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
},
|
||||
"block_num": 0,
|
||||
"block_time": "",
|
||||
"last_irreversible_block": pResults[0].last_irreversible_block_num,
|
||||
"last_irreversible_block": undefined,
|
||||
"traces": []
|
||||
};
|
||||
|
||||
const hits = results['body']['hits']['hits'];
|
||||
let hits;
|
||||
|
||||
// build get_info request with caching
|
||||
const $getInfo = new Promise<GetInfoResult>(resolve => {
|
||||
const key = `${fastify.manager.chain}_get_info`;
|
||||
fastify.redis.get(key).then(value => {
|
||||
if (value) {
|
||||
resolve(JSON.parse(value));
|
||||
} else {
|
||||
fastify.eosjs.rpc.get_info().then(value1 => {
|
||||
fastify.redis.set(key, JSON.stringify(value1), 'EX', 6);
|
||||
resolve(value1);
|
||||
}).catch((reason) => {
|
||||
console.log(reason);
|
||||
response.error = 'failed to get last_irreversible_block_num'
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// reconstruct hits from cached data
|
||||
if (cachedData && Object.keys(cachedData).length > 0) {
|
||||
const gsArr = [];
|
||||
for (let cachedDataKey in cachedData) {
|
||||
gsArr.push(cachedData[cachedDataKey]);
|
||||
}
|
||||
gsArr.sort((a, b) => {
|
||||
return a.global_sequence - b.global_sequence;
|
||||
});
|
||||
hits = gsArr.map(value => {
|
||||
return {
|
||||
_source: JSON.parse(value)
|
||||
};
|
||||
});
|
||||
const promiseResults = await Promise.all([
|
||||
redis.ttl('trx_' + trxId),
|
||||
$getInfo
|
||||
]);
|
||||
response.cache_expires_in = promiseResults[0];
|
||||
response.last_irreversible_block = promiseResults[1].last_irreversible_block_num;
|
||||
}
|
||||
|
||||
// search on ES if cache is not present
|
||||
if (!hits) {
|
||||
const _size = conf.api.limits.get_trx_actions || 100;
|
||||
const blockHint = parseInt(body.block_num_hint, 10);
|
||||
let indexPattern = '';
|
||||
if (blockHint) {
|
||||
const idxPart = Math.ceil(blockHint / conf.settings.index_partition_size).toString().padStart(6, '0');
|
||||
indexPattern = fastify.manager.chain + `-action-${conf.settings.index_version}-${idxPart}`;
|
||||
} else {
|
||||
indexPattern = fastify.manager.chain + '-action-*';
|
||||
}
|
||||
let pResults;
|
||||
try {
|
||||
|
||||
// build search request
|
||||
const $search = fastify.elastic.search({
|
||||
index: indexPattern,
|
||||
size: _size,
|
||||
body: {
|
||||
query: {bool: {must: [{term: {trx_id: trxId}}]}},
|
||||
sort: {global_sequence: "asc"}
|
||||
}
|
||||
});
|
||||
|
||||
// execute in parallel
|
||||
pResults = await Promise.all([$getInfo, $search]);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
if (e.meta.statusCode === 404) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
hits = pResults[1]['body']['hits']['hits'];
|
||||
response.last_irreversible_block = pResults[0].last_irreversible_block_num;
|
||||
}
|
||||
|
||||
|
||||
if (hits.length > 0) {
|
||||
const actions = hits;
|
||||
@@ -98,7 +166,7 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
except: null,
|
||||
inline_traces: [],
|
||||
producer_block_id: "",
|
||||
trx_id: request.body.id,
|
||||
trx_id: body.id,
|
||||
notified: action.notified
|
||||
};
|
||||
let hash = createHash('sha256');
|
||||
@@ -157,7 +225,7 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
except: null,
|
||||
inline_traces: [],
|
||||
producer_block_id: "",
|
||||
trx_id: request.body.id,
|
||||
trx_id: body.id,
|
||||
};
|
||||
traces[action.global_sequence].inline_traces.unshift(trace);
|
||||
response.traces.push(trace);
|
||||
@@ -166,7 +234,7 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const errmsg = "Transaction " + request.body.id.toLowerCase() + " not found in history and no block hint was given";
|
||||
const errmsg = "Transaction " + body.id.toLowerCase() + " not found in history and no block hint was given";
|
||||
return {
|
||||
code: 500,
|
||||
message: "Internal Service Error",
|
||||
@@ -189,7 +257,7 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
}
|
||||
|
||||
export function getTransactionHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getTransaction, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
tags: ['history'],
|
||||
body: {
|
||||
type: ['object', 'string'],
|
||||
properties: {id: {description: 'transaction id', type: 'string'}},
|
||||
properties: {
|
||||
id: {description: 'transaction id', type: 'string'},
|
||||
block_num_hint: {description: 'block number hint', type: 'integer'},
|
||||
},
|
||||
required: ["id"]
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import * as _ from "lodash";
|
||||
|
||||
@@ -19,7 +18,7 @@ async function getBlockTrace(fastify: FastifyInstance, request: FastifyRequest)
|
||||
request.body = JSON.parse(request.body);
|
||||
}
|
||||
|
||||
const reqBody = request.body;
|
||||
const reqBody: any = request.body;
|
||||
const targetBlock = parseInt(reqBody.block_num);
|
||||
let searchBody;
|
||||
|
||||
@@ -121,7 +120,7 @@ async function getBlockTrace(fastify: FastifyInstance, request: FastifyRequest)
|
||||
}
|
||||
|
||||
export function getBlockTraceHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getBlockTrace, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function checkTransaction(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query: any = request.query;
|
||||
const txId = query.id.toLowerCase();
|
||||
const data = await fastify.redis.get(txId) as any;
|
||||
if (data) {
|
||||
const jsonObj = JSON.parse(data);
|
||||
const response = {
|
||||
id: txId,
|
||||
status: jsonObj.status,
|
||||
block_num: jsonObj.b,
|
||||
root_action: jsonObj.a,
|
||||
signatures: [],
|
||||
};
|
||||
if (jsonObj.s && jsonObj.s.length > 0) {
|
||||
response.signatures = jsonObj.s;
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
return {
|
||||
id: txId,
|
||||
status: 'unknown'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function checkTransactionHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(checkTransaction, fastify, request, route));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import {FastifyInstance} from "fastify";
|
||||
import {addApiRoute, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
import {checkTransactionHandler} from "./check_transaction";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema = {
|
||||
description: 'check if a transaction was included in a block',
|
||||
summary: 'check if a transaction was included in a block',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"id": {
|
||||
description: 'transaction id',
|
||||
type: 'string',
|
||||
minLength: 64,
|
||||
maxLength: 64
|
||||
}
|
||||
},
|
||||
required: ["id"]
|
||||
},
|
||||
response: extendResponseSchema({
|
||||
"id": {type: "string"},
|
||||
"status": {type: "string"},
|
||||
"block_num": {type: "number"},
|
||||
"root_action": {
|
||||
type: "object",
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"name": {type: "string"},
|
||||
"authorization": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"actor": {type: "string"},
|
||||
"permission": {type: "string"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"data": {type: "string"}
|
||||
}
|
||||
},
|
||||
"signatures": {type: "array", items: {type: 'string'}}
|
||||
})
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
checkTransactionHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function exportActions(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
}
|
||||
|
||||
export function exportActionsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(exportActions, fastify, request, route));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {addApiRoute, getRouteName} from "../../../helpers/functions";
|
||||
import {getCreatorHandler} from "../get_creator/get_creator";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: FastifySchema = {
|
||||
description: 'request large action data export',
|
||||
summary: 'request large action data export',
|
||||
tags: ['history']
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
getCreatorHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getAbiSnapshot(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
@@ -8,9 +7,11 @@ async function getAbiSnapshot(fastify: FastifyInstance, request: FastifyRequest)
|
||||
block_num: null
|
||||
};
|
||||
|
||||
const code = request.query.contract;
|
||||
const block = request.query.block;
|
||||
const should_fetch = request.query.fetch;
|
||||
const query: any = request.query;
|
||||
|
||||
const code = query.contract;
|
||||
const block = query.block;
|
||||
const should_fetch = query.fetch;
|
||||
|
||||
const mustArray = [];
|
||||
|
||||
@@ -21,7 +22,7 @@ async function getAbiSnapshot(fastify: FastifyInstance, request: FastifyRequest)
|
||||
}
|
||||
|
||||
const results = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-abi',
|
||||
index: fastify.manager.chain + '-abi-*',
|
||||
size: 1,
|
||||
body: {
|
||||
query: {bool: {must: mustArray}},
|
||||
@@ -44,7 +45,7 @@ async function getAbiSnapshot(fastify: FastifyInstance, request: FastifyRequest)
|
||||
}
|
||||
|
||||
export function getAbiSnapshotHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getAbiSnapshot, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,215 +1,236 @@
|
||||
import {extendedActions, primaryTerms, terms} from "./definitions";
|
||||
import {primaryTerms, terms} from "./definitions";
|
||||
|
||||
export function addSortedBy(query, queryBody, sort_direction) {
|
||||
if (query['sortedBy']) {
|
||||
const opts = query['sortedBy'].split(":");
|
||||
const sortedByObj = {};
|
||||
sortedByObj[opts[0]] = opts[1];
|
||||
queryBody['sort'] = sortedByObj;
|
||||
} else {
|
||||
queryBody['sort'] = {
|
||||
"global_sequence": sort_direction
|
||||
};
|
||||
}
|
||||
if (query['sortedBy']) {
|
||||
const opts = query['sortedBy'].split(":");
|
||||
const sortedByObj = {};
|
||||
sortedByObj[opts[0]] = opts[1];
|
||||
queryBody['sort'] = sortedByObj;
|
||||
} else {
|
||||
queryBody['sort'] = {
|
||||
"global_sequence": sort_direction
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function processMultiVars(queryStruct, parts, field) {
|
||||
const must = [];
|
||||
const mustNot = [];
|
||||
const must = [];
|
||||
const mustNot = [];
|
||||
|
||||
parts.forEach(part => {
|
||||
if (part.startsWith("!")) {
|
||||
mustNot.push(part.replace("!", ""));
|
||||
} else {
|
||||
must.push(part);
|
||||
}
|
||||
});
|
||||
parts.forEach(part => {
|
||||
if (part.startsWith("!")) {
|
||||
mustNot.push(part.replace("!", ""));
|
||||
} else {
|
||||
must.push(part);
|
||||
}
|
||||
});
|
||||
|
||||
if (must.length > 1) {
|
||||
queryStruct.bool.must.push({
|
||||
bool: {
|
||||
should: must.map(elem => {
|
||||
const _q = {};
|
||||
_q[field] = elem;
|
||||
return {term: _q}
|
||||
})
|
||||
}
|
||||
});
|
||||
} else if (must.length === 1) {
|
||||
const mustQuery = {};
|
||||
mustQuery[field] = must[0];
|
||||
queryStruct.bool.must.push({term: mustQuery});
|
||||
}
|
||||
if (must.length > 1) {
|
||||
queryStruct.bool.must.push({
|
||||
bool: {
|
||||
should: must.map(elem => {
|
||||
const _q = {};
|
||||
_q[field] = elem;
|
||||
return {term: _q}
|
||||
})
|
||||
}
|
||||
});
|
||||
} else if (must.length === 1) {
|
||||
const mustQuery = {};
|
||||
mustQuery[field] = must[0];
|
||||
queryStruct.bool.must.push({term: mustQuery});
|
||||
}
|
||||
|
||||
if (mustNot.length > 1) {
|
||||
queryStruct.bool.must_not.push({
|
||||
bool: {
|
||||
should: mustNot.map(elem => {
|
||||
const _q = {};
|
||||
_q[field] = elem;
|
||||
return {term: _q}
|
||||
})
|
||||
}
|
||||
});
|
||||
} else if (mustNot.length === 1) {
|
||||
const mustNotQuery = {};
|
||||
mustNotQuery[field] = mustNot[0].replace("!", "");
|
||||
queryStruct.bool.must_not.push({term: mustNotQuery});
|
||||
}
|
||||
if (mustNot.length > 1) {
|
||||
queryStruct.bool.must_not.push({
|
||||
bool: {
|
||||
should: mustNot.map(elem => {
|
||||
const _q = {};
|
||||
_q[field] = elem;
|
||||
return {term: _q}
|
||||
})
|
||||
}
|
||||
});
|
||||
} else if (mustNot.length === 1) {
|
||||
const mustNotQuery = {};
|
||||
mustNotQuery[field] = mustNot[0].replace("!", "");
|
||||
queryStruct.bool.must_not.push({term: mustNotQuery});
|
||||
}
|
||||
}
|
||||
|
||||
function addRangeQuery(queryStruct, prop, pkey, query) {
|
||||
const _termQuery = {};
|
||||
const parts = query[prop].split("-");
|
||||
_termQuery[pkey] = {
|
||||
"gte": parts[0],
|
||||
"lte": parts[1]
|
||||
};
|
||||
queryStruct.bool.must.push({range: _termQuery});
|
||||
const _termQuery = {};
|
||||
const parts = query[prop].split("-");
|
||||
_termQuery[pkey] = {
|
||||
"gte": parts[0],
|
||||
"lte": parts[1]
|
||||
};
|
||||
queryStruct.bool.must.push({range: _termQuery});
|
||||
}
|
||||
|
||||
export function applyTimeFilter(query, queryStruct) {
|
||||
if (query['after'] || query['before']) {
|
||||
let _lte = "now";
|
||||
let _gte = "0";
|
||||
if (query['before']) {
|
||||
_lte = query['before'];
|
||||
if (!_lte.endsWith("Z")) {
|
||||
_lte += "Z";
|
||||
}
|
||||
}
|
||||
if (query['after']) {
|
||||
_gte = query['after'];
|
||||
if (!_gte.endsWith("Z")) {
|
||||
_gte += "Z";
|
||||
}
|
||||
}
|
||||
if (!queryStruct.bool['filter']) {
|
||||
queryStruct.bool['filter'] = [];
|
||||
}
|
||||
queryStruct.bool['filter'].push({
|
||||
range: {
|
||||
"@timestamp": {
|
||||
"gte": _gte,
|
||||
"lte": _lte
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
if (query['after'] || query['before']) {
|
||||
let _lte = "now";
|
||||
let _gte = "0";
|
||||
if (query['before']) {
|
||||
try {
|
||||
_lte = new Date(query['before']).toISOString();
|
||||
} catch (e) {
|
||||
throw new Error(e.message + ' [before]');
|
||||
}
|
||||
}
|
||||
if (query['after']) {
|
||||
try {
|
||||
_gte = new Date(query['after']).toISOString();
|
||||
} catch (e) {
|
||||
throw new Error(e.message + ' [after]');
|
||||
}
|
||||
}
|
||||
if (!queryStruct.bool['filter']) {
|
||||
queryStruct.bool['filter'] = [];
|
||||
}
|
||||
queryStruct.bool['filter'].push({
|
||||
range: {
|
||||
"@timestamp": {
|
||||
"gte": _gte,
|
||||
"lte": _lte
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function applyGenericFilters(query, queryStruct) {
|
||||
for (const prop in query) {
|
||||
if (Object.prototype.hasOwnProperty.call(query, prop)) {
|
||||
const pair = prop.split(".");
|
||||
if (pair.length > 1 || primaryTerms.includes(pair[0])) {
|
||||
let pkey;
|
||||
if (pair.length > 1) {
|
||||
pkey = extendedActions.has(pair[0]) ? "@" + prop : prop;
|
||||
} else {
|
||||
pkey = prop;
|
||||
}
|
||||
if (query[prop].indexOf("-") !== -1) {
|
||||
addRangeQuery(queryStruct, prop, pkey, query);
|
||||
} else {
|
||||
const _termQuery = {};
|
||||
const parts = query[prop].split(",");
|
||||
if (parts.length > 1) {
|
||||
processMultiVars(queryStruct, parts, prop);
|
||||
} else if (parts.length === 1) {
|
||||
const andParts = parts[0].split(" ");
|
||||
if (andParts.length > 1) {
|
||||
andParts.forEach(value => {
|
||||
const _q = {};
|
||||
console.log(value);
|
||||
_q[pkey] = value;
|
||||
queryStruct.bool.must.push({term: _q});
|
||||
});
|
||||
} else {
|
||||
if (parts[0].startsWith("!")) {
|
||||
_termQuery[pkey] = parts[0].replace("!", "");
|
||||
queryStruct.bool.must_not.push({term: _termQuery});
|
||||
} else {
|
||||
_termQuery[pkey] = parts[0];
|
||||
queryStruct.bool.must.push({term: _termQuery});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export function applyGenericFilters(query, queryStruct, allowedExtraParams: Set<string>) {
|
||||
for (const prop in query) {
|
||||
if (Object.prototype.hasOwnProperty.call(query, prop)) {
|
||||
const pair = prop.split(".");
|
||||
if (pair.length > 1 || primaryTerms.includes(pair[0])) {
|
||||
let pkey;
|
||||
if (pair.length > 1 && allowedExtraParams) {
|
||||
pkey = allowedExtraParams.has(pair[0]) ? "@" + prop : prop;
|
||||
} else {
|
||||
pkey = prop;
|
||||
}
|
||||
if (query[prop].indexOf("-") !== -1) {
|
||||
addRangeQuery(queryStruct, prop, pkey, query);
|
||||
} else {
|
||||
const _qObj = {};
|
||||
const parts = query[prop].split(",");
|
||||
if (parts.length > 1) {
|
||||
processMultiVars(queryStruct, parts, prop);
|
||||
} else if (parts.length === 1) {
|
||||
|
||||
// @transfer.memo special case
|
||||
if (pkey === '@transfer.memo') {
|
||||
_qObj[pkey] = {
|
||||
query: parts[0]
|
||||
};
|
||||
|
||||
if (query.match_fuzziness) {
|
||||
_qObj[pkey].fuzziness = query.match_fuzziness;
|
||||
}
|
||||
|
||||
if (query.match_operator) {
|
||||
_qObj[pkey].operator = query.match_operator;
|
||||
}
|
||||
|
||||
queryStruct.bool.must.push({
|
||||
match: _qObj
|
||||
});
|
||||
} else {
|
||||
const andParts = parts[0].split(" ");
|
||||
if (andParts.length > 1) {
|
||||
andParts.forEach(value => {
|
||||
const _q = {};
|
||||
_q[pkey] = value;
|
||||
queryStruct.bool.must.push({term: _q});
|
||||
});
|
||||
} else {
|
||||
if (parts[0].startsWith("!")) {
|
||||
_qObj[pkey] = parts[0].replace("!", "");
|
||||
queryStruct.bool.must_not.push({term: _qObj});
|
||||
} else {
|
||||
_qObj[pkey] = parts[0];
|
||||
queryStruct.bool.must.push({term: _qObj});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function makeShouldArray(query) {
|
||||
const should_array = [];
|
||||
for (const entry of terms) {
|
||||
const tObj = {term: {}};
|
||||
tObj.term[entry] = query.account;
|
||||
should_array.push(tObj);
|
||||
}
|
||||
return should_array;
|
||||
const should_array = [];
|
||||
for (const entry of terms) {
|
||||
const tObj = {term: {}};
|
||||
tObj.term[entry] = query.account;
|
||||
should_array.push(tObj);
|
||||
}
|
||||
return should_array;
|
||||
}
|
||||
|
||||
export function applyCodeActionFilters(query, queryStruct) {
|
||||
let filterObj = [];
|
||||
if (query.filter) {
|
||||
for (const filter of query.filter.split(',')) {
|
||||
if (filter !== '*:*') {
|
||||
const _arr = [];
|
||||
const parts = filter.split(':');
|
||||
if (parts.length === 2) {
|
||||
const [code, method] = parts;
|
||||
if (code && code !== "*") {
|
||||
_arr.push({'term': {'act.account': code}});
|
||||
}
|
||||
if (method && method !== "*") {
|
||||
_arr.push({'term': {'act.name': method}});
|
||||
}
|
||||
}
|
||||
if (_arr.length > 0) {
|
||||
filterObj.push({bool: {must: _arr}});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filterObj.length > 0) {
|
||||
queryStruct.bool['should'] = filterObj;
|
||||
queryStruct.bool['minimum_should_match'] = 1;
|
||||
}
|
||||
}
|
||||
let filterObj = [];
|
||||
if (query.filter) {
|
||||
for (const filter of query.filter.split(',')) {
|
||||
if (filter !== '*:*') {
|
||||
const _arr = [];
|
||||
const parts = filter.split(':');
|
||||
if (parts.length === 2) {
|
||||
const [code, method] = parts;
|
||||
if (code && code !== "*") {
|
||||
_arr.push({'term': {'act.account': code}});
|
||||
}
|
||||
if (method && method !== "*") {
|
||||
_arr.push({'term': {'act.name': method}});
|
||||
}
|
||||
}
|
||||
if (_arr.length > 0) {
|
||||
filterObj.push({bool: {must: _arr}});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filterObj.length > 0) {
|
||||
queryStruct.bool['should'] = filterObj;
|
||||
queryStruct.bool['minimum_should_match'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getSkipLimit(query, max?: number) {
|
||||
let skip, limit;
|
||||
skip = parseInt(query.skip, 10);
|
||||
if (skip < 0) {
|
||||
throw new Error('invalid skip parameter');
|
||||
}
|
||||
limit = parseInt(query.limit, 10);
|
||||
if (limit < 1) {
|
||||
throw new Error('invalid limit parameter');
|
||||
} else if (limit > max) {
|
||||
throw new Error(`limit too big, maximum: ${max}`);
|
||||
}
|
||||
return {skip, limit};
|
||||
let skip, limit;
|
||||
skip = parseInt(query.skip, 10);
|
||||
if (skip < 0) {
|
||||
throw new Error('invalid skip parameter');
|
||||
}
|
||||
limit = parseInt(query.limit, 10);
|
||||
if (limit < 1) {
|
||||
throw new Error('invalid limit parameter');
|
||||
} else if (limit > max) {
|
||||
throw new Error(`limit too big, maximum: ${max}`);
|
||||
}
|
||||
return {skip, limit};
|
||||
}
|
||||
|
||||
export function getSortDir(query) {
|
||||
let sort_direction = 'desc';
|
||||
if (query.sort) {
|
||||
if (query.sort === 'asc' || query.sort === '1') {
|
||||
sort_direction = 'asc';
|
||||
} else if (query.sort === 'desc' || query.sort === '-1') {
|
||||
sort_direction = 'desc'
|
||||
} else {
|
||||
throw new Error('invalid sort direction');
|
||||
}
|
||||
}
|
||||
return sort_direction;
|
||||
let sort_direction = 'desc';
|
||||
if (query.sort) {
|
||||
if (query.sort === 'asc' || query.sort === '1') {
|
||||
sort_direction = 'asc';
|
||||
} else if (query.sort === 'desc' || query.sort === '-1') {
|
||||
sort_direction = 'desc'
|
||||
} else {
|
||||
throw new Error('invalid sort direction');
|
||||
}
|
||||
}
|
||||
return sort_direction;
|
||||
}
|
||||
|
||||
export function applyAccountFilters(query, queryStruct) {
|
||||
if (query.account) {
|
||||
queryStruct.bool.must.push({"bool": {should: makeShouldArray(query)}});
|
||||
}
|
||||
if (query.account) {
|
||||
queryStruct.bool.must.push({"bool": {should: makeShouldArray(query)}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,118 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {getTrackTotalHits, mergeActionMeta, timedQuery} from "../../../helpers/functions";
|
||||
import {
|
||||
addSortedBy,
|
||||
applyAccountFilters,
|
||||
applyCodeActionFilters,
|
||||
applyGenericFilters,
|
||||
applyTimeFilter,
|
||||
getSkipLimit,
|
||||
getSortDir
|
||||
addSortedBy,
|
||||
applyAccountFilters,
|
||||
applyCodeActionFilters,
|
||||
applyGenericFilters,
|
||||
applyTimeFilter,
|
||||
getSkipLimit,
|
||||
getSortDir
|
||||
} from "./functions";
|
||||
|
||||
async function getActions(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query = request.query;
|
||||
const maxActions = fastify.manager.config.api.limits.get_actions;
|
||||
const queryStruct = {
|
||||
"bool": {
|
||||
must: [],
|
||||
must_not: [],
|
||||
boost: 1.0
|
||||
}
|
||||
};
|
||||
const query: any = request.query;
|
||||
const maxActions = fastify.manager.config.api.limits.get_actions;
|
||||
const queryStruct = {
|
||||
"bool": {
|
||||
must: [],
|
||||
must_not: [],
|
||||
boost: 1.0
|
||||
}
|
||||
};
|
||||
|
||||
const {skip, limit} = getSkipLimit(query, maxActions);
|
||||
const sort_direction = getSortDir(query);
|
||||
applyAccountFilters(query, queryStruct);
|
||||
applyGenericFilters(query, queryStruct);
|
||||
applyTimeFilter(query, queryStruct);
|
||||
applyCodeActionFilters(query, queryStruct);
|
||||
// allow precise counting of total hits
|
||||
const trackTotalHits = getTrackTotalHits(query);
|
||||
const {skip, limit} = getSkipLimit(query, maxActions);
|
||||
const sort_direction = getSortDir(query);
|
||||
applyAccountFilters(query, queryStruct);
|
||||
applyGenericFilters(query, queryStruct, fastify.allowedActionQueryParamSet);
|
||||
applyTimeFilter(query, queryStruct);
|
||||
applyCodeActionFilters(query, queryStruct);
|
||||
// allow precise counting of total hits
|
||||
const trackTotalHits = getTrackTotalHits(query);
|
||||
|
||||
// Prepare query body
|
||||
const query_body = {
|
||||
"track_total_hits": trackTotalHits,
|
||||
"query": queryStruct
|
||||
};
|
||||
// Prepare query body
|
||||
const query_body = {
|
||||
"track_total_hits": trackTotalHits,
|
||||
"query": queryStruct
|
||||
};
|
||||
|
||||
// Include sorting
|
||||
addSortedBy(query, query_body, sort_direction);
|
||||
// Include sorting
|
||||
addSortedBy(query, query_body, sort_direction);
|
||||
|
||||
// Perform search
|
||||
// Perform search
|
||||
|
||||
let indexPattern = fastify.manager.chain + '-action-*';
|
||||
if (query.hot_only) {
|
||||
indexPattern = fastify.manager.chain + '-action';
|
||||
}
|
||||
let indexPattern = fastify.manager.chain + '-action-*';
|
||||
if (query.hot_only) {
|
||||
indexPattern = fastify.manager.chain + '-action';
|
||||
}
|
||||
|
||||
const esResults = await fastify.elastic.search({
|
||||
"index": indexPattern,
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxActions ? maxActions : limit) || 10,
|
||||
"body": query_body
|
||||
});
|
||||
const esResults = await fastify.elastic.search({
|
||||
"index": indexPattern,
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxActions ? maxActions : limit) || 10,
|
||||
"body": query_body
|
||||
});
|
||||
|
||||
const results = esResults['body']['hits'];
|
||||
const response: any = {
|
||||
cached: false,
|
||||
lib: 0,
|
||||
total: results['total']
|
||||
};
|
||||
const results = esResults['body']['hits'];
|
||||
const response: any = {
|
||||
cached: false,
|
||||
lib: 0,
|
||||
total: results['total']
|
||||
};
|
||||
|
||||
if (query.hot_only) {
|
||||
response.hot_only = true;
|
||||
}
|
||||
if (query.hot_only) {
|
||||
response.hot_only = true;
|
||||
}
|
||||
|
||||
if (query.checkLib) {
|
||||
response.lib = (await fastify.eosjs.rpc.get_info()).last_irreversible_block_num;
|
||||
}
|
||||
if (query.checkLib) {
|
||||
response.lib = (await fastify.eosjs.rpc.get_info()).last_irreversible_block_num;
|
||||
}
|
||||
|
||||
if (query.simple) {
|
||||
response['simple_actions'] = [];
|
||||
} else {
|
||||
response['actions'] = [];
|
||||
}
|
||||
if (query.simple) {
|
||||
response['simple_actions'] = [];
|
||||
} else {
|
||||
response['actions'] = [];
|
||||
}
|
||||
|
||||
if (results['hits'].length > 0) {
|
||||
const actions = results['hits'];
|
||||
for (let action of actions) {
|
||||
action = action._source;
|
||||
mergeActionMeta(action);
|
||||
if (results['hits'].length > 0) {
|
||||
const actions = results['hits'];
|
||||
for (let action of actions) {
|
||||
action = action._source;
|
||||
mergeActionMeta(action);
|
||||
|
||||
if (query.noBinary === true) {
|
||||
for (const key in action['act']['data']) {
|
||||
if (action['act']['data'].hasOwnProperty(key)) {
|
||||
if (typeof action['act']['data'][key] === 'string' && action['act']['data'][key].length > 256) {
|
||||
action['act']['data'][key] = action['act']['data'][key].slice(0, 32) + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.noBinary === true) {
|
||||
for (const key in action['act']['data']) {
|
||||
if (action['act']['data'].hasOwnProperty(key)) {
|
||||
if (typeof action['act']['data'][key] === 'string' && action['act']['data'][key].length > 256) {
|
||||
action['act']['data'][key] = action['act']['data'][key].slice(0, 32) + "...";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (query.simple) {
|
||||
response.simple_actions.push({
|
||||
block: action['block_num'],
|
||||
irreversible: response.lib !== 0 ? action['block_num'] < response.lib : undefined,
|
||||
timestamp: action['@timestamp'],
|
||||
transaction_id: action['trx_id'],
|
||||
actors: action['act']['authorization'].map(a => `${a.actor}@${a.permission}`).join(","),
|
||||
notified: action['notified'].join(','),
|
||||
contract: action['act']['account'],
|
||||
action: action['act']['name'],
|
||||
data: action['act']['data']
|
||||
});
|
||||
if (query.simple) {
|
||||
response.simple_actions.push({
|
||||
block: action['block_num'],
|
||||
irreversible: response.lib !== 0 ? action['block_num'] < response.lib : undefined,
|
||||
timestamp: action['@timestamp'],
|
||||
transaction_id: action['trx_id'],
|
||||
actors: action['act']['authorization'].map(a => `${a.actor}@${a.permission}`).join(","),
|
||||
notified: action['notified'].join(','),
|
||||
contract: action['act']['account'],
|
||||
action: action['act']['name'],
|
||||
data: action['act']['data']
|
||||
});
|
||||
|
||||
} else {
|
||||
response.actions.push(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
response.actions.push(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getActionsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getActions, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getActions, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +1,132 @@
|
||||
import {FastifyInstance, RouteSchema} from "fastify";
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {getActionsHandler} from "./get_actions";
|
||||
import {addApiRoute, extendQueryStringSchema, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
|
||||
export const getActionResponseSchema = {
|
||||
"@timestamp": {type: "string"},
|
||||
"timestamp": {type: "string"},
|
||||
"block_num": {type: "number"},
|
||||
"trx_id": {type: "string"},
|
||||
"act": {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"name": {type: "string"}
|
||||
},
|
||||
additionalProperties: true
|
||||
},
|
||||
"notified": {
|
||||
type: "array", items: {type: "string"}
|
||||
},
|
||||
"cpu_usage_us": {type: "number"},
|
||||
"net_usage_words": {type: "number"},
|
||||
"account_ram_deltas": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"delta": {type: "number"}
|
||||
},
|
||||
additionalProperties: true
|
||||
}
|
||||
},
|
||||
"global_sequence": {type: "number"},
|
||||
"receiver": {type: 'string'},
|
||||
"producer": {type: "string"},
|
||||
"parent": {type: "number"},
|
||||
"action_ordinal": {type: 'number'},
|
||||
"creator_action_ordinal": {type: 'number'}
|
||||
"@timestamp": {type: "string"},
|
||||
"timestamp": {type: "string"},
|
||||
"block_num": {type: "number"},
|
||||
"trx_id": {type: "string"},
|
||||
"act": {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"name": {type: "string"}
|
||||
},
|
||||
additionalProperties: true
|
||||
},
|
||||
"notified": {
|
||||
type: "array", items: {type: "string"}
|
||||
},
|
||||
"cpu_usage_us": {type: "number"},
|
||||
"net_usage_words": {type: "number"},
|
||||
"account_ram_deltas": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"delta": {type: "number"}
|
||||
},
|
||||
additionalProperties: true
|
||||
}
|
||||
},
|
||||
"global_sequence": {type: "number"},
|
||||
"receiver": {type: 'string'},
|
||||
"producer": {type: "string"},
|
||||
"parent": {type: "number"},
|
||||
"action_ordinal": {type: 'number'},
|
||||
"creator_action_ordinal": {type: 'number'},
|
||||
"signatures": {type: "array", items: {type: 'string'}}
|
||||
};
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: RouteSchema = {
|
||||
description: 'get actions based on notified account. this endpoint also accepts generic filters based on indexed fields' +
|
||||
' (e.g. act.authorization.actor=eosio or act.name=delegatebw), if included they will be combined with a AND operator',
|
||||
summary: 'get root actions',
|
||||
tags: ['history'],
|
||||
querystring: extendQueryStringSchema({
|
||||
"account": {
|
||||
description: 'notified account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"track": {
|
||||
description: 'total results to track (count) [number or true]',
|
||||
type: 'string'
|
||||
},
|
||||
"filter": {
|
||||
description: 'code:name filter',
|
||||
type: 'string',
|
||||
minLength: 3
|
||||
},
|
||||
"sort": {
|
||||
description: 'sort direction',
|
||||
enum: ['desc', 'asc', '1', '-1'],
|
||||
type: 'string'
|
||||
},
|
||||
"after": {
|
||||
description: 'filter after specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"simple": {
|
||||
description: 'simplified output mode',
|
||||
type: 'boolean'
|
||||
},
|
||||
"hot_only": {
|
||||
description: 'search only the latest hot index',
|
||||
type: 'boolean'
|
||||
},
|
||||
"noBinary": {
|
||||
description: "exclude large binary data",
|
||||
type: 'boolean'
|
||||
},
|
||||
"checkLib": {
|
||||
description: "perform reversibility check",
|
||||
type: 'boolean'
|
||||
},
|
||||
}),
|
||||
response: extendResponseSchema({
|
||||
"simple_actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"block": {type: "number"},
|
||||
"timestamp": {type: "string"},
|
||||
"irreversible": {type: "boolean"},
|
||||
"contract": {type: "string"},
|
||||
"action": {type: "string"},
|
||||
"actors": {type: "string"},
|
||||
"notified": {type: "string"},
|
||||
"transaction_id": {type: "string"},
|
||||
"data": {
|
||||
additionalProperties: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: getActionResponseSchema
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
getActionsHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
const schema: FastifySchema = {
|
||||
description: 'get actions based on notified account. this endpoint also accepts generic filters based on indexed fields' +
|
||||
' (e.g. act.authorization.actor=eosio or act.name=delegatebw), if included they will be combined with a AND operator',
|
||||
summary: 'get root actions',
|
||||
tags: ['history'],
|
||||
querystring: extendQueryStringSchema({
|
||||
"account": {
|
||||
description: 'notified account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"track": {
|
||||
description: 'total results to track (count) [number or true]',
|
||||
type: 'string'
|
||||
},
|
||||
"filter": {
|
||||
description: 'code:name filter',
|
||||
type: 'string',
|
||||
minLength: 3
|
||||
},
|
||||
"sort": {
|
||||
description: 'sort direction',
|
||||
enum: ['desc', 'asc', '1', '-1'],
|
||||
type: 'string'
|
||||
},
|
||||
"after": {
|
||||
description: 'filter after specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"simple": {
|
||||
description: 'simplified output mode',
|
||||
type: 'boolean'
|
||||
},
|
||||
"hot_only": {
|
||||
description: 'search only the latest hot index',
|
||||
type: 'boolean'
|
||||
},
|
||||
"noBinary": {
|
||||
description: "exclude large binary data",
|
||||
type: 'boolean'
|
||||
},
|
||||
"checkLib": {
|
||||
description: "perform reversibility check",
|
||||
type: 'boolean'
|
||||
},
|
||||
}),
|
||||
response: extendResponseSchema({
|
||||
"simple_actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"block": {type: "number"},
|
||||
"timestamp": {type: "string"},
|
||||
"irreversible": {type: "boolean"},
|
||||
"contract": {type: "string"},
|
||||
"action": {type: "string"},
|
||||
"actors": {type: "string"},
|
||||
"notified": {type: "string"},
|
||||
"transaction_id": {type: "string"},
|
||||
"data": {
|
||||
additionalProperties: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: getActionResponseSchema
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
getActionsHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {getSkipLimit} from "../get_actions/functions";
|
||||
|
||||
async function getCreatedAccounts(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
const maxActions = fastify.manager.config.api.limits.get_created_accounts;
|
||||
const results = await fastify.elastic.search({
|
||||
"index": fastify.manager.chain + '-action-*',
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxActions ? maxActions : limit) || 100,
|
||||
"body": {
|
||||
"query": {
|
||||
"bool": {
|
||||
must: [
|
||||
{term: {"act.authorization.actor": request.query.account.toLowerCase()}},
|
||||
{term: {"act.name": "newaccount"}},
|
||||
{term: {"act.account": "eosio"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
"global_sequence": "desc"
|
||||
}
|
||||
}
|
||||
});
|
||||
const query: any = request.query;
|
||||
const {skip, limit} = getSkipLimit(query);
|
||||
const maxActions = fastify.manager.config.api.limits.get_created_accounts;
|
||||
const results = await fastify.elastic.search({
|
||||
"index": fastify.manager.chain + '-action-*',
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxActions ? maxActions : limit) || 100,
|
||||
"body": {
|
||||
"query": {
|
||||
"bool": {
|
||||
must: [
|
||||
{term: {"act.authorization.actor": query.account.toLowerCase()}},
|
||||
{term: {"act.name": "newaccount"}},
|
||||
{term: {"act.account": "eosio"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
"global_sequence": "desc"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const response = {accounts: []};
|
||||
const response = {accounts: []};
|
||||
|
||||
if (results['body']['hits']['hits'].length > 0) {
|
||||
const actions = results['body']['hits']['hits'];
|
||||
for (let action of actions) {
|
||||
action = action._source;
|
||||
const _tmp = {};
|
||||
if (action['act']['data']['newact']) {
|
||||
_tmp['name'] = action['act']['data']['newact'];
|
||||
} else if (action['@newaccount']['newact']) {
|
||||
_tmp['name'] = action['@newaccount']['newact'];
|
||||
} else {
|
||||
console.log(action);
|
||||
}
|
||||
_tmp['trx_id'] = action['trx_id'];
|
||||
_tmp['timestamp'] = action['@timestamp'];
|
||||
response.accounts.push(_tmp);
|
||||
}
|
||||
}
|
||||
if (results['body']['hits']['hits'].length > 0) {
|
||||
const actions = results['body']['hits']['hits'];
|
||||
for (let action of actions) {
|
||||
action = action._source;
|
||||
const _tmp = {};
|
||||
if (action['act']['data']['newact']) {
|
||||
_tmp['name'] = action['act']['data']['newact'];
|
||||
} else if (action['@newaccount']['newact']) {
|
||||
_tmp['name'] = action['@newaccount']['newact'];
|
||||
} else {
|
||||
console.log(action);
|
||||
}
|
||||
_tmp['trx_id'] = action['trx_id'];
|
||||
_tmp['timestamp'] = action['@timestamp'];
|
||||
response.accounts.push(_tmp);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getCreatedAccountsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getCreatedAccounts, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getCreatedAccounts, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,44 @@
|
||||
import {FastifyInstance, RouteSchema} from "fastify";
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {getCreatedAccountsHandler} from "./get_created_accounts";
|
||||
import {addApiRoute, extendQueryStringSchema, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: RouteSchema = {
|
||||
description: 'get all accounts created by one creator',
|
||||
summary: 'get created accounts',
|
||||
tags: ['accounts'],
|
||||
querystring: extendQueryStringSchema({
|
||||
"account": {
|
||||
description: 'creator account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
}, ["account"]),
|
||||
response: extendResponseSchema({
|
||||
"query_time": {
|
||||
type: "number"
|
||||
},
|
||||
"total": {
|
||||
type: "object",
|
||||
properties: {
|
||||
"value": {type: "number"},
|
||||
"relation": {type: "string"}
|
||||
}
|
||||
},
|
||||
"accounts": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'name': {type: 'string'},
|
||||
'timestamp': {type: 'string'},
|
||||
'trx_id': {type: 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(fastify, 'GET', getRouteName(__filename), getCreatedAccountsHandler, schema);
|
||||
next();
|
||||
const schema: FastifySchema = {
|
||||
description: 'get all accounts created by one creator',
|
||||
summary: 'get created accounts',
|
||||
tags: ['accounts'],
|
||||
querystring: extendQueryStringSchema({
|
||||
"account": {
|
||||
description: 'creator account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
}, ["account"]),
|
||||
response: extendResponseSchema({
|
||||
"query_time": {
|
||||
type: "number"
|
||||
},
|
||||
"total": {
|
||||
type: "object",
|
||||
properties: {
|
||||
"value": {type: "number"},
|
||||
"relation": {type: "string"}
|
||||
}
|
||||
},
|
||||
"accounts": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'name': {type: 'string'},
|
||||
'timestamp': {type: 'string'},
|
||||
'trx_id': {type: 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(fastify, 'GET', getRouteName(__filename), getCreatedAccountsHandler, schema);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getCreator(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
const query: any = request.query;
|
||||
|
||||
const response = {
|
||||
account: request.query.account,
|
||||
account: query.account,
|
||||
creator: '',
|
||||
timestamp: '',
|
||||
block_num: 0,
|
||||
trx_id: '',
|
||||
};
|
||||
|
||||
if (request.query.account === fastify.manager.config.settings.eosio_alias) {
|
||||
if (query.account === fastify.manager.config.settings.eosio_alias) {
|
||||
const genesisBlock = await fastify.eosjs.rpc.get_block(1);
|
||||
if (genesisBlock) {
|
||||
response.creator = '__self__';
|
||||
@@ -31,7 +32,7 @@ async function getCreator(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
size: 1,
|
||||
query: {
|
||||
bool: {
|
||||
must: [{term: {"@newaccount.newact": request.query.account}}]
|
||||
must: [{term: {"@newaccount.newact": query.account}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +48,7 @@ async function getCreator(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
} else {
|
||||
let accountInfo;
|
||||
try {
|
||||
accountInfo = await fastify.eosjs.rpc.get_account(request.query.account);
|
||||
accountInfo = await fastify.eosjs.rpc.get_account(query.account);
|
||||
} catch (e) {
|
||||
throw new Error("account not found");
|
||||
}
|
||||
@@ -73,7 +74,7 @@ async function getCreator(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const actions = transaction.trx.transaction.actions;
|
||||
for (const act of actions) {
|
||||
if (act.name === 'newaccount') {
|
||||
if (act.data.name === request.query.account) {
|
||||
if (act.data.name === query.account) {
|
||||
response.creator = act.data.creator;
|
||||
response.trx_id = transaction.id;
|
||||
return response;
|
||||
@@ -93,7 +94,7 @@ async function getCreator(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getCreatorHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getCreator, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
import {FastifyInstance, RouteSchema} from "fastify";
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {getCreatorHandler} from "./get_creator";
|
||||
import {addApiRoute, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: RouteSchema = {
|
||||
description: 'get account creator',
|
||||
summary: 'get account creator',
|
||||
tags: ['accounts'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'created account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
},
|
||||
required: ["account"]
|
||||
},
|
||||
response: extendResponseSchema({
|
||||
"account": {
|
||||
type: "string"
|
||||
},
|
||||
"creator": {
|
||||
type: "string"
|
||||
},
|
||||
"timestamp": {
|
||||
type: "string"
|
||||
},
|
||||
"block_num": {
|
||||
type: "integer"
|
||||
},
|
||||
"trx_id": {
|
||||
type: "string"
|
||||
},
|
||||
"indirect_creator": {
|
||||
type: "string"
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(fastify, 'GET', getRouteName(__filename), getCreatorHandler, schema);
|
||||
next();
|
||||
const schema: FastifySchema = {
|
||||
description: 'get account creator',
|
||||
summary: 'get account creator',
|
||||
tags: ['accounts'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'created account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
},
|
||||
required: ["account"]
|
||||
},
|
||||
response: extendResponseSchema({
|
||||
"account": {
|
||||
type: "string"
|
||||
},
|
||||
"creator": {
|
||||
type: "string"
|
||||
},
|
||||
"timestamp": {
|
||||
type: "string"
|
||||
},
|
||||
"block_num": {
|
||||
type: "integer"
|
||||
},
|
||||
"trx_id": {
|
||||
type: "string"
|
||||
},
|
||||
"indirect_creator": {
|
||||
type: "string"
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(fastify, 'GET', getRouteName(__filename), getCreatorHandler, schema);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {mergeDeltaMeta, timedQuery} from "../../../helpers/functions";
|
||||
import {applyTimeFilter} from "../get_actions/functions";
|
||||
|
||||
@@ -7,9 +6,10 @@ async function getDeltas(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
let skip, limit;
|
||||
let sort_direction = 'desc';
|
||||
const mustArray = [];
|
||||
for (const param in request.query) {
|
||||
if (Object.prototype.hasOwnProperty.call(request.query, param)) {
|
||||
const value = request.query[param];
|
||||
const query: any = request.query;
|
||||
for (const param in query) {
|
||||
if (Object.prototype.hasOwnProperty.call(query, param)) {
|
||||
const value = query[param];
|
||||
switch (param) {
|
||||
case 'limit': {
|
||||
limit = parseInt(value, 10);
|
||||
@@ -42,7 +42,7 @@ async function getDeltas(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const values = request.query[param].split(",");
|
||||
const values = query[param].split(",");
|
||||
if (values.length > 1) {
|
||||
const terms = {};
|
||||
terms[param] = values;
|
||||
@@ -61,7 +61,7 @@ async function getDeltas(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const maxDeltas = fastify.manager.config.api.limits.get_deltas ?? 1000;
|
||||
const queryStruct = {bool: {must: mustArray}};
|
||||
|
||||
applyTimeFilter(request.query, queryStruct);
|
||||
applyTimeFilter(query, queryStruct);
|
||||
|
||||
const results = await fastify.elastic.search({
|
||||
"index": fastify.manager.chain + '-delta-*',
|
||||
@@ -85,7 +85,7 @@ async function getDeltas(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getDeltasHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getDeltas, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {FastifyInstance, RouteSchema} from "fastify";
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {getDeltasHandler} from "./get_deltas";
|
||||
import {addApiRoute, extendQueryStringSchema, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: RouteSchema = {
|
||||
const schema: FastifySchema = {
|
||||
description: 'get state deltas',
|
||||
summary: 'get state deltas',
|
||||
tags: ['history'],
|
||||
@@ -31,7 +31,11 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601)',
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
"present": {
|
||||
description: 'delta present flag',
|
||||
type: 'number'
|
||||
},
|
||||
}),
|
||||
response: extendResponseSchema({
|
||||
"deltas": {
|
||||
@@ -40,12 +44,12 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"timestamp": {type: 'string'},
|
||||
"present": {type: 'number'},
|
||||
"code": {type: 'string'},
|
||||
"scope": {type: 'string'},
|
||||
"table": {type: 'string'},
|
||||
"primary_key": {type: 'string'},
|
||||
"payer": {type: 'string'},
|
||||
"present": {type: 'boolean'},
|
||||
"block_num": {type: 'number'},
|
||||
"block_id": {type: 'string'},
|
||||
"data": {
|
||||
|
||||
@@ -1,71 +1,73 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {createHash} from "crypto";
|
||||
import {base58ToBinary, binaryToBase58} from "eosjs/dist/eosjs-numeric";
|
||||
import {Search} from "@elastic/elasticsearch/api/requestParams";
|
||||
|
||||
function convertToLegacyKey(block_signing_key: string) {
|
||||
if (block_signing_key.startsWith("PUB_K1_")) {
|
||||
const buf = base58ToBinary(37, block_signing_key.substr(7));
|
||||
const data = buf.slice(0, buf.length - 4);
|
||||
const merged = Buffer.concat([
|
||||
data,
|
||||
createHash('ripemd160')
|
||||
.update(data)
|
||||
.digest()
|
||||
.slice(0, 4)
|
||||
]);
|
||||
return "EOS" + binaryToBase58(merged);
|
||||
} else {
|
||||
return block_signing_key;
|
||||
}
|
||||
if (block_signing_key.startsWith("PUB_K1_")) {
|
||||
const buf = base58ToBinary(37, block_signing_key.substr(7));
|
||||
const data = buf.slice(0, buf.length - 4);
|
||||
const merged = Buffer.concat([
|
||||
data,
|
||||
createHash('ripemd160')
|
||||
.update(data)
|
||||
.digest()
|
||||
.slice(0, 4)
|
||||
]);
|
||||
return "EOS" + binaryToBase58(merged);
|
||||
} else {
|
||||
return block_signing_key;
|
||||
}
|
||||
}
|
||||
|
||||
async function getSchedule(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const response: any = {
|
||||
producers: []
|
||||
};
|
||||
const searchParams: Search<any> = {
|
||||
track_total_hits: true,
|
||||
index: fastify.manager.chain + "-block-*",
|
||||
size: 1,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: []
|
||||
}
|
||||
},
|
||||
sort: {block_num: "desc"}
|
||||
}
|
||||
};
|
||||
if (request.query.version) {
|
||||
searchParams.body.query.bool.must.push({"term": {"new_producers.version": {"value": request.query.version}}});
|
||||
} else {
|
||||
searchParams.body.query.bool.must.push({
|
||||
"exists": {
|
||||
"field": "new_producers.version"
|
||||
}
|
||||
});
|
||||
}
|
||||
const apiResponse = await fastify.elastic.search(searchParams);
|
||||
const results = apiResponse.body.hits.hits;
|
||||
if (results) {
|
||||
response.timestamp = results[0]._source["@timestamp"];
|
||||
response.block_num = results[0]._source.block_num;
|
||||
response.version = results[0]._source.new_producers.version;
|
||||
response.producers = results[0]._source.new_producers.producers.map(prod => {
|
||||
return {
|
||||
...prod,
|
||||
legacy_key: convertToLegacyKey(prod.block_signing_key)
|
||||
}
|
||||
});
|
||||
}
|
||||
return response;
|
||||
|
||||
const query: any = request.query;
|
||||
|
||||
const response: any = {
|
||||
producers: []
|
||||
};
|
||||
const searchParams: Search<any> = {
|
||||
track_total_hits: true,
|
||||
index: fastify.manager.chain + "-block-*",
|
||||
size: 1,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: []
|
||||
}
|
||||
},
|
||||
sort: {block_num: "desc"}
|
||||
}
|
||||
};
|
||||
if (query.version) {
|
||||
searchParams.body.query.bool.must.push({"term": {"new_producers.version": {"value": query.version}}});
|
||||
} else {
|
||||
searchParams.body.query.bool.must.push({
|
||||
"exists": {
|
||||
"field": "new_producers.version"
|
||||
}
|
||||
});
|
||||
}
|
||||
const apiResponse = await fastify.elastic.search(searchParams);
|
||||
const results = apiResponse.body.hits.hits;
|
||||
if (results) {
|
||||
response.timestamp = results[0]._source["@timestamp"];
|
||||
response.block_num = results[0]._source.block_num;
|
||||
response.version = results[0]._source.new_producers.version;
|
||||
response.producers = results[0]._source.new_producers.producers.map(prod => {
|
||||
return {
|
||||
...prod,
|
||||
legacy_key: convertToLegacyKey(prod.block_signing_key)
|
||||
}
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getScheduleHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getSchedule, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getSchedule, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {mergeDeltaMeta, timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getTableState(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query: any = request.query;
|
||||
const response = {
|
||||
...query,
|
||||
results: [],
|
||||
next_key: null
|
||||
};
|
||||
const mustArray: any[] = [
|
||||
{"term": {"code": query.code}},
|
||||
{"term": {"table": query.table}}
|
||||
];
|
||||
if (query.block_num) {
|
||||
mustArray.push({"range": {"block_num": {"lte": query.block_num}}});
|
||||
}
|
||||
let after_key = "";
|
||||
if (query.after_key) {
|
||||
after_key = query.after_key;
|
||||
}
|
||||
const results = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-delta-*',
|
||||
body: {
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": mustArray
|
||||
}
|
||||
},
|
||||
"size": 0,
|
||||
"sort": [
|
||||
{"block_num": "desc"},
|
||||
{"primary_key": "desc"}
|
||||
],
|
||||
"aggs": {
|
||||
"scope_buckets": {
|
||||
"composite": {
|
||||
"size": 25,
|
||||
"after": {
|
||||
"scope_pk": after_key
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"scope_pk": {
|
||||
"terms": {
|
||||
"script": {
|
||||
"source": "return doc['scope'].value + '-' + doc['primary_key'].value",
|
||||
"lang": "painless"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"aggs": {
|
||||
"last_doc": {
|
||||
"top_hits": {
|
||||
"size": 1,
|
||||
"sort": [{"block_num": "desc"}],
|
||||
"_source": {
|
||||
"excludes": ["code"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const scope_buckets = results.body.aggregations.scope_buckets;
|
||||
if (scope_buckets && scope_buckets.buckets.length > 0) {
|
||||
if (scope_buckets.after_key) {
|
||||
response.next_key = scope_buckets.after_key.scope_pk;
|
||||
}
|
||||
response.results = scope_buckets.buckets.map(bucket => {
|
||||
const row = mergeDeltaMeta(bucket.last_doc.hits.hits[0]._source);
|
||||
delete row.table;
|
||||
return row;
|
||||
});
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getTableStateHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getTableState, fastify, request, route));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import {FastifyInstance} from "fastify";
|
||||
import {addApiRoute, extendResponseSchema, getRouteName} from "../../../helpers/functions";
|
||||
import {getTableStateHandler} from "./get_table_state";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema = {
|
||||
description: 'get table state at a specific block height',
|
||||
summary: 'get table state at a specific block height',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
required: ["code","table"],
|
||||
properties: {
|
||||
"code": {
|
||||
description: 'search by contract',
|
||||
type: 'string'
|
||||
},
|
||||
"table": {
|
||||
description: 'search by key',
|
||||
type: 'string'
|
||||
},
|
||||
"block_num": {
|
||||
description: 'target block',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
"after_key": {
|
||||
description: 'last key for pagination',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
response: extendResponseSchema({
|
||||
"code": {type: 'string'},
|
||||
"table": {type: 'string'},
|
||||
"block_num": {type: 'number'},
|
||||
"after_key": {type: 'string'},
|
||||
"next_key": {type: 'string'},
|
||||
"results": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"scope": {type: 'string'},
|
||||
"primary_key": {type: 'string'},
|
||||
"payer": {type: 'string'},
|
||||
"timestamp": {type: 'string'},
|
||||
"block_num": {type: 'number'},
|
||||
"block_id": {type: 'string'},
|
||||
"present": {type: 'number'},
|
||||
"data": {
|
||||
type: 'object',
|
||||
additionalProperties: true
|
||||
}
|
||||
},
|
||||
additionalProperties: true
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
getTableStateHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
}
|
||||
@@ -1,28 +1,49 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {mergeActionMeta, timedQuery} from "../../../helpers/functions";
|
||||
import {GetInfoResult} from "eosjs/dist/eosjs-rpc-interfaces";
|
||||
|
||||
async function getTransaction(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
const redis = fastify.redis;
|
||||
const trxId = request.query.id.toLowerCase();
|
||||
const cachedData = await redis.hgetall(trxId);
|
||||
|
||||
let hits;
|
||||
let hits2;
|
||||
|
||||
const query: any = request.query;
|
||||
const trxId = query.id.toLowerCase();
|
||||
const conf = fastify.manager.config;
|
||||
const cachedData = await redis.hgetall('trx_' + trxId);
|
||||
const response = {
|
||||
"query_time_ms": undefined,
|
||||
"cached": undefined,
|
||||
"cache_expires_in": undefined,
|
||||
"executed": false,
|
||||
"hot_only": false,
|
||||
"trx_id": trxId,
|
||||
"lib": undefined,
|
||||
"actions": [],
|
||||
"generated": undefined
|
||||
query_time_ms: undefined,
|
||||
executed: false,
|
||||
cached: undefined,
|
||||
cache_expires_in: undefined,
|
||||
trx_id: query.id,
|
||||
lib: undefined,
|
||||
cached_lib: false,
|
||||
actions: undefined,
|
||||
generated: undefined,
|
||||
error: undefined
|
||||
};
|
||||
let hits;
|
||||
|
||||
// build get_info request with caching
|
||||
const $getInfo = new Promise<GetInfoResult>(resolve => {
|
||||
const key = `${fastify.manager.chain}_get_info`;
|
||||
fastify.redis.get(key).then(value => {
|
||||
if (value) {
|
||||
response.cached_lib = true;
|
||||
resolve(JSON.parse(value));
|
||||
} else {
|
||||
fastify.eosjs.rpc.get_info().then(value1 => {
|
||||
fastify.redis.set(key, JSON.stringify(value1), 'EX', 6);
|
||||
response.cached_lib = false;
|
||||
resolve(value1);
|
||||
}).catch((reason) => {
|
||||
console.log(reason);
|
||||
response.error = 'failed to get last_irreversible_block_num'
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// reconstruct hits from cached data
|
||||
if (cachedData && Object.keys(cachedData).length > 0) {
|
||||
const gsArr = [];
|
||||
for (let cachedDataKey in cachedData) {
|
||||
@@ -31,83 +52,77 @@ async function getTransaction(fastify: FastifyInstance, request: FastifyRequest)
|
||||
gsArr.sort((a, b) => {
|
||||
return a.global_sequence - b.global_sequence;
|
||||
});
|
||||
response.cache_expires_in = await redis.ttl(trxId);
|
||||
hits = gsArr.map(value => {
|
||||
return {_source: JSON.parse(value)}
|
||||
return {
|
||||
_source: JSON.parse(value)
|
||||
};
|
||||
});
|
||||
const promiseResults = await Promise.all([
|
||||
redis.ttl('trx_' + trxId),
|
||||
$getInfo
|
||||
]);
|
||||
response.cache_expires_in = promiseResults[0];
|
||||
response.lib = promiseResults[1].last_irreversible_block_num;
|
||||
}
|
||||
|
||||
// search on ES if cache is not present
|
||||
if (!hits) {
|
||||
const _size = fastify.manager.config.api.limits.get_trx_actions || 100;
|
||||
let indexPattern = fastify.manager.chain + '-action-*';
|
||||
if (request.query.hot_only) {
|
||||
indexPattern = fastify.manager.chain + '-action';
|
||||
const _size = conf.api.limits.get_trx_actions || 100;
|
||||
const blockHint = parseInt(query.block_hint, 10);
|
||||
let indexPattern = '';
|
||||
if (blockHint) {
|
||||
const idxPart = Math.ceil(blockHint / conf.settings.index_partition_size).toString().padStart(6, '0');
|
||||
indexPattern = fastify.manager.chain + `-action-${conf.settings.index_version}-${idxPart}`;
|
||||
} else {
|
||||
indexPattern = fastify.manager.chain + '-action-*';
|
||||
}
|
||||
let pResults;
|
||||
try {
|
||||
|
||||
const pResults = await Promise.all([
|
||||
fastify.eosjs.rpc.get_info(),
|
||||
fastify.elastic.search({
|
||||
// build search request
|
||||
const $search = fastify.elastic.search({
|
||||
index: indexPattern,
|
||||
size: _size,
|
||||
body: {
|
||||
query: {bool: {must: [{term: {trx_id: request.query.id.toLowerCase()}}]}},
|
||||
query: {bool: {must: [{term: {trx_id: trxId}}]}},
|
||||
sort: {global_sequence: "asc"}
|
||||
}
|
||||
}),
|
||||
fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-gentrx-*',
|
||||
size: _size,
|
||||
body: {
|
||||
query: {bool: {must: [{term: {trx_id: request.query.id.toLowerCase()}}]}}
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
const results = pResults[1];
|
||||
const genTrxRes = pResults[2];
|
||||
});
|
||||
|
||||
// execute in parallel
|
||||
pResults = await Promise.all([$getInfo, $search]);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
if (e.meta.statusCode === 404) {
|
||||
response.error = 'no data near block_hint'
|
||||
return response;
|
||||
}
|
||||
}
|
||||
hits = pResults[1]['body']['hits']['hits'];
|
||||
response.lib = pResults[0].last_irreversible_block_num;
|
||||
|
||||
if (request.query.hot_only) {
|
||||
response.hot_only = true;
|
||||
}
|
||||
|
||||
hits = results['body']['hits']['hits'];
|
||||
hits2 = genTrxRes['body']['hits']['hits'];
|
||||
}
|
||||
|
||||
if (hits) {
|
||||
if (hits.length > 0) {
|
||||
let highestBlockNum = 0;
|
||||
for (let action of hits) {
|
||||
if (action._source.block_num > highestBlockNum) {
|
||||
highestBlockNum = action._source.block_num;
|
||||
}
|
||||
if (hits.length > 0) {
|
||||
let highestBlockNum = 0;
|
||||
for (let action of hits) {
|
||||
if (action._source.block_num > highestBlockNum) {
|
||||
highestBlockNum = action._source.block_num;
|
||||
}
|
||||
for (let action of hits) {
|
||||
if (action._source.block_num === highestBlockNum) {
|
||||
action = action._source;
|
||||
mergeActionMeta(action);
|
||||
response.actions.push(action);
|
||||
}
|
||||
}
|
||||
response.actions = [];
|
||||
for (let action of hits) {
|
||||
if (action._source.block_num === highestBlockNum) {
|
||||
mergeActionMeta(action._source);
|
||||
response.actions.push(action._source);
|
||||
}
|
||||
response.executed = true;
|
||||
}
|
||||
response.executed = true;
|
||||
}
|
||||
|
||||
if (hits2 && hits2.length > 0) {
|
||||
if (hits2[0]._source['@timestamp']) {
|
||||
hits2[0]._source['timestamp'] = hits2[0]._source['@timestamp'];
|
||||
delete hits2[0]._source['@timestamp'];
|
||||
}
|
||||
response.generated = hits2[0]._source;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getTransactionHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getTransaction, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,13 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"id": {
|
||||
id: {
|
||||
description: 'transaction id',
|
||||
type: 'string'
|
||||
},
|
||||
block_hint: {
|
||||
description: 'block hint to speed up tx recovery',
|
||||
type: 'integer'
|
||||
}
|
||||
},
|
||||
required: ["id"]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import got from "got";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getAccount(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
|
||||
const query: any = request.query;
|
||||
|
||||
const response = {
|
||||
account: null,
|
||||
actions: null,
|
||||
@@ -13,7 +15,7 @@ async function getAccount(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
links: null
|
||||
};
|
||||
|
||||
const account = request.query.account;
|
||||
const account = query.account;
|
||||
const reqQueue = [];
|
||||
|
||||
try {
|
||||
@@ -45,7 +47,7 @@ async function getAccount(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getAccountHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getAccount, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,137 +1,139 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {Numeric} from "eosjs/dist";
|
||||
import {getSkipLimit} from "../../v2-history/get_actions/functions";
|
||||
|
||||
function invalidKey() {
|
||||
const err: any = new Error();
|
||||
err.statusCode = 400;
|
||||
err.message = 'invalid public key';
|
||||
throw err;
|
||||
const err: any = new Error();
|
||||
err.statusCode = 400;
|
||||
err.message = 'invalid public key';
|
||||
throw err;
|
||||
}
|
||||
|
||||
async function getKeyAccounts(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
let publicKey;
|
||||
if (typeof request.body === 'string' && request.req.method === 'POST') {
|
||||
request.body = JSON.parse(request.body);
|
||||
}
|
||||
let publicKey;
|
||||
if (typeof request.body === 'string' && request.method === 'POST') {
|
||||
request.body = JSON.parse(request.body);
|
||||
}
|
||||
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
const maxDocs = fastify.manager.config.api.limits.get_key_accounts ?? 1000;
|
||||
const body: any = request.body;
|
||||
const query: any = request.query;
|
||||
|
||||
const public_Key = request.req.method === 'POST' ? request.body.public_key : request.query.public_key;
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
const maxDocs = fastify.manager.config.api.limits.get_key_accounts ?? 1000;
|
||||
|
||||
if (public_Key.startsWith("PUB_")) {
|
||||
publicKey = public_Key;
|
||||
} else if (public_Key.startsWith("EOS")) {
|
||||
try {
|
||||
publicKey = Numeric.convertLegacyPublicKey(public_Key);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
invalidKey();
|
||||
}
|
||||
} else {
|
||||
invalidKey();
|
||||
}
|
||||
const public_Key = request.method === 'POST' ? body.public_key : query.public_key;
|
||||
|
||||
const response = {
|
||||
account_names: []
|
||||
} as any;
|
||||
if (public_Key.startsWith("PUB_")) {
|
||||
publicKey = public_Key;
|
||||
} else if (public_Key.startsWith("EOS")) {
|
||||
try {
|
||||
publicKey = Numeric.convertLegacyPublicKey(public_Key);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
invalidKey();
|
||||
}
|
||||
} else {
|
||||
invalidKey();
|
||||
}
|
||||
|
||||
if (request.req.method === 'GET' && request.query.details) {
|
||||
response.permissions = [];
|
||||
}
|
||||
const response = {
|
||||
account_names: []
|
||||
} as any;
|
||||
|
||||
try {
|
||||
if (request.method === 'GET' && query.details) {
|
||||
response.permissions = [];
|
||||
}
|
||||
|
||||
const permTableResults = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-perm-*',
|
||||
size: (limit > maxDocs ? maxDocs : limit) || 100,
|
||||
from: skip || 0,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
"auth.keys.key.keyword": publicKey
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
try {
|
||||
|
||||
if (permTableResults.body.hits.hits.length > 0) {
|
||||
for (const perm of permTableResults.body.hits.hits) {
|
||||
response.account_names.push(perm._source.owner);
|
||||
if (request.req.method === 'GET' && request.query.details) {
|
||||
response.permissions.push(perm._source);
|
||||
}
|
||||
}
|
||||
}
|
||||
const permTableResults = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-perm-*',
|
||||
size: (limit > maxDocs ? maxDocs : limit) || 100,
|
||||
from: skip || 0,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
"auth.keys.key.keyword": publicKey
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (permTableResults.body.hits.hits.length > 0) {
|
||||
for (const perm of permTableResults.body.hits.hits) {
|
||||
response.account_names.push(perm._source.owner);
|
||||
if (request.method === 'GET' && query.details) {
|
||||
response.permissions.push(perm._source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (response.account_names.length > 0) {
|
||||
response.account_names = [...(new Set(response.account_names))];
|
||||
return response;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
// Fallback to action search
|
||||
const _body = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{term: {"@updateauth.auth.keys.key.keyword": publicKey}},
|
||||
{term: {"@newaccount.active.keys.key.keyword": publicKey}},
|
||||
{term: {"@newaccount.owner.keys.key.keyword": publicKey}}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
},
|
||||
sort: [{"global_sequence": {"order": "desc"}}]
|
||||
};
|
||||
if (response.account_names.length > 0) {
|
||||
response.account_names = [...(new Set(response.account_names))];
|
||||
return response;
|
||||
}
|
||||
|
||||
const results = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: (limit > maxDocs ? maxDocs : limit) || 100,
|
||||
from: skip || 0,
|
||||
body: _body
|
||||
});
|
||||
// Fallback to action search
|
||||
const _body = {
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{term: {"@updateauth.auth.keys.key.keyword": publicKey}},
|
||||
{term: {"@newaccount.active.keys.key.keyword": publicKey}},
|
||||
{term: {"@newaccount.owner.keys.key.keyword": publicKey}}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
},
|
||||
sort: [{"global_sequence": {"order": "desc"}}]
|
||||
};
|
||||
|
||||
if (results['body']['hits']['hits'].length > 0) {
|
||||
response.account_names = results['body']['hits']['hits'].map((v) => {
|
||||
if (v._source.act.name === 'newaccount') {
|
||||
if (v._source['@newaccount'].newact) {
|
||||
return v._source['@newaccount'].newact;
|
||||
} else if (v._source.act.data.newact) {
|
||||
return v._source.act.data.newact;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (v._source.act.name === 'updateauth') {
|
||||
return v._source.act.data.account;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
const results = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: (limit > maxDocs ? maxDocs : limit) || 100,
|
||||
from: skip || 0,
|
||||
body: _body
|
||||
});
|
||||
|
||||
if (response.account_names.length > 0) {
|
||||
response.account_names = [...(new Set(response.account_names))];
|
||||
}
|
||||
if (results['body']['hits']['hits'].length > 0) {
|
||||
response.account_names = results['body']['hits']['hits'].map((v) => {
|
||||
if (v._source.act.name === 'newaccount') {
|
||||
if (v._source['@newaccount'].newact) {
|
||||
return v._source['@newaccount'].newact;
|
||||
} else if (v._source.act.data.newact) {
|
||||
return v._source.act.data.newact;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (v._source.act.name === 'updateauth') {
|
||||
return v._source.act.data.account;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
if (response.account_names.length > 0) {
|
||||
response.account_names = [...(new Set(response.account_names))];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getKeyAccountsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getKeyAccounts, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getKeyAccounts, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
last_updated: {type: 'string'},
|
||||
auth: {},
|
||||
name: {type: 'string'},
|
||||
present: {type: 'boolean'}
|
||||
present: {type: 'number'}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {getTrackTotalHits, timedQuery} from "../../../helpers/functions";
|
||||
import {ApiResponse} from "@elastic/elasticsearch";
|
||||
import {getSkipLimit} from "../../v2-history/get_actions/functions";
|
||||
|
||||
async function getLinks(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query = request.query;
|
||||
const {account, code, action, permissions} = query;
|
||||
const {skip, limit} = getSkipLimit(query);
|
||||
const query: any = request.query;
|
||||
const {account, code, action, permissions} = query;
|
||||
const {skip, limit} = getSkipLimit(query);
|
||||
|
||||
const queryStruct = {
|
||||
"bool": {
|
||||
must: []
|
||||
}
|
||||
};
|
||||
const queryStruct = {
|
||||
"bool": {
|
||||
must: [],
|
||||
must_not: []
|
||||
}
|
||||
};
|
||||
|
||||
if (account) {
|
||||
queryStruct.bool.must.push({'term': {'account': account}});
|
||||
}
|
||||
if (account) {
|
||||
queryStruct.bool.must.push({'term': {'account': account}});
|
||||
}
|
||||
|
||||
if (code) {
|
||||
queryStruct.bool.must.push({'term': {'code': code}});
|
||||
}
|
||||
if (code) {
|
||||
queryStruct.bool.must.push({'term': {'code': code}});
|
||||
}
|
||||
|
||||
if (action) {
|
||||
queryStruct.bool.must.push({'term': {'action': action}});
|
||||
}
|
||||
if (action) {
|
||||
queryStruct.bool.must.push({'term': {'action': action}});
|
||||
}
|
||||
|
||||
if (permissions) {
|
||||
queryStruct.bool.must.push({'term': {'permissions': permissions}});
|
||||
}
|
||||
if (permissions) {
|
||||
queryStruct.bool.must.push({'term': {'permissions': permissions}});
|
||||
}
|
||||
|
||||
// only present deltas
|
||||
queryStruct.bool.must.push({'term': {'present': true}});
|
||||
// only present deltas
|
||||
queryStruct.bool.must_not.push({'term': {'present': 0}});
|
||||
|
||||
// Prepare query body
|
||||
const query_body = {
|
||||
track_total_hits: getTrackTotalHits(query),
|
||||
query: queryStruct,
|
||||
sort: {block_num: 'desc'}
|
||||
};
|
||||
// Prepare query body
|
||||
const query_body = {
|
||||
track_total_hits: getTrackTotalHits(query),
|
||||
query: queryStruct,
|
||||
sort: {block_num: 'desc'}
|
||||
};
|
||||
|
||||
const maxLinks = fastify.manager.config.api.limits.get_links;
|
||||
const results: ApiResponse = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-link-*',
|
||||
from: skip || 0,
|
||||
size: (limit > maxLinks ? maxLinks : limit) || 50,
|
||||
body: query_body
|
||||
});
|
||||
const hits = results.body.hits.hits;
|
||||
const response: any = {
|
||||
cached: false,
|
||||
total: results.body.hits.total,
|
||||
links: []
|
||||
};
|
||||
const maxLinks = fastify.manager.config.api.limits.get_links;
|
||||
const results: ApiResponse = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-link-*',
|
||||
from: skip || 0,
|
||||
size: (limit > maxLinks ? maxLinks : limit) || 50,
|
||||
body: query_body
|
||||
});
|
||||
const hits = results.body.hits.hits;
|
||||
const response: any = {
|
||||
cached: false,
|
||||
total: results.body.hits.total,
|
||||
links: []
|
||||
};
|
||||
|
||||
for (const hit of hits) {
|
||||
const link = hit._source;
|
||||
link.timestamp = link['@timestamp'];
|
||||
delete link['@timestamp'];
|
||||
response.links.push(link);
|
||||
}
|
||||
return response;
|
||||
for (const hit of hits) {
|
||||
const link = hit._source;
|
||||
link.timestamp = link['@timestamp'];
|
||||
delete link['@timestamp'];
|
||||
response.links.push(link);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getLinksHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getLinks, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getLinks, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {getTrackTotalHits, timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getProposals(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
const query: any = request.query;
|
||||
|
||||
// Pagination
|
||||
let skip, limit;
|
||||
skip = parseInt(request.query.skip, 10);
|
||||
skip = parseInt(query.skip, 10);
|
||||
if (skip < 0) {
|
||||
return 'invalid skip parameter';
|
||||
}
|
||||
limit = parseInt(request.query.limit, 10);
|
||||
limit = parseInt(query.limit, 10);
|
||||
if (limit < 1) {
|
||||
return 'invalid limit parameter';
|
||||
}
|
||||
@@ -22,8 +23,8 @@ async function getProposals(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
};
|
||||
|
||||
// Filter by accounts
|
||||
if (request.query.account) {
|
||||
const accounts = request.query.account.split(',');
|
||||
if (query.account) {
|
||||
const accounts = query.account.split(',');
|
||||
for(const acc of accounts) {
|
||||
queryStruct.bool.must.push({
|
||||
"bool": {
|
||||
@@ -37,28 +38,28 @@ async function getProposals(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
// Filter by proposer account
|
||||
if (request.query.proposer) {
|
||||
queryStruct.bool.must.push({"term": {"proposer": request.query.proposer}});
|
||||
if (query.proposer) {
|
||||
queryStruct.bool.must.push({"term": {"proposer": query.proposer}});
|
||||
}
|
||||
|
||||
// Filter by proposal name
|
||||
if (request.query.proposal) {
|
||||
queryStruct.bool.must.push({"term": {"proposal_name": request.query.proposal}});
|
||||
if (query.proposal) {
|
||||
queryStruct.bool.must.push({"term": {"proposal_name": query.proposal}});
|
||||
}
|
||||
|
||||
// Filter by execution status
|
||||
if (typeof request.query.executed !== 'undefined') {
|
||||
queryStruct.bool.must.push({"term": {"executed": request.query.executed}});
|
||||
if (typeof query.executed !== 'undefined') {
|
||||
queryStruct.bool.must.push({"term": {"executed": query.executed}});
|
||||
}
|
||||
|
||||
// Filter by requested actors
|
||||
if (request.query.requested) {
|
||||
queryStruct.bool.must.push({"term": {"requested_approvals.actor": request.query.requested}});
|
||||
if (query.requested) {
|
||||
queryStruct.bool.must.push({"term": {"requested_approvals.actor": query.requested}});
|
||||
}
|
||||
|
||||
// Filter by provided actors
|
||||
if (request.query.provided) {
|
||||
queryStruct.bool.must.push({"term": {"provided_approvals.actor": request.query.provided}});
|
||||
if (query.provided) {
|
||||
queryStruct.bool.must.push({"term": {"provided_approvals.actor": query.provided}});
|
||||
}
|
||||
|
||||
// If no filter switch to full match
|
||||
@@ -97,7 +98,7 @@ async function getProposals(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getProposalsHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getProposals, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {getSkipLimit} from "../../v2-history/get_actions/functions";
|
||||
@@ -6,7 +5,9 @@ import {getSkipLimit} from "../../v2-history/get_actions/functions";
|
||||
|
||||
async function getTokens(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
const response = {'account': request.query.account, 'tokens': []};
|
||||
const query: any = request.query;
|
||||
|
||||
const response = {'account': query.account, 'tokens': []};
|
||||
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
const maxDocs = fastify.manager.config.api.limits.get_tokens ?? 100;
|
||||
@@ -18,19 +19,26 @@ async function getTokens(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
"body": {
|
||||
query: {
|
||||
bool: {
|
||||
filter: [{term: {"scope": request.query.account}}]
|
||||
filter: [{term: {"scope": query.account}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const testSet = new Set();
|
||||
for (const hit of stateResult.body.hits.hits) {
|
||||
const data = hit._source;
|
||||
if (typeof data.present !== "undefined" && data.present === false) {
|
||||
if (typeof data.present !== "undefined" && data.present === 0) {
|
||||
continue;
|
||||
}
|
||||
let precision;
|
||||
const key = `${data.code}_${data.symbol}`;
|
||||
|
||||
if (testSet.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
testSet.add(key);
|
||||
if (!fastify.tokenCache) {
|
||||
fastify.tokenCache = new Map<string, any>();
|
||||
}
|
||||
@@ -39,7 +47,7 @@ async function getTokens(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
} else {
|
||||
let token_data;
|
||||
try {
|
||||
token_data = await fastify.eosjs.rpc.get_currency_balance(data.code, request.query.account, data.symbol);
|
||||
token_data = await fastify.eosjs.rpc.get_currency_balance(data.code, query.account, data.symbol);
|
||||
if (token_data.length > 0) {
|
||||
const [amount, symbol] = token_data[0].split(" ");
|
||||
const amount_arr = amount.split(".");
|
||||
@@ -50,7 +58,7 @@ async function getTokens(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`get_currency_balance error - contract:${data.code} - account:${request.query.account}`);
|
||||
console.log(`get_currency_balance error - contract:${data.code} - account:${query.account}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +74,7 @@ async function getTokens(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
}
|
||||
|
||||
export function getTokensHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getTokens, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {getSkipLimit} from "../../v2-history/get_actions/functions";
|
||||
|
||||
async function getVoters(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
const query: any = request.query;
|
||||
const {skip, limit} = getSkipLimit(request.query);
|
||||
|
||||
const response = {
|
||||
voter_count: 0,
|
||||
'voters': []
|
||||
};
|
||||
let queryStruct: any = {
|
||||
"bool": {
|
||||
"must": []
|
||||
}
|
||||
};
|
||||
const response = {
|
||||
voter_count: 0,
|
||||
'voters': []
|
||||
};
|
||||
let queryStruct: any = {
|
||||
"bool": {
|
||||
"must": []
|
||||
}
|
||||
};
|
||||
|
||||
if (request.query.producer) {
|
||||
for (const bp of request.query.producer.split(",")) {
|
||||
queryStruct.bool.must.push({"term": {"producers": bp}});
|
||||
}
|
||||
}
|
||||
if (query.producer) {
|
||||
for (const bp of query.producer.split(",")) {
|
||||
queryStruct.bool.must.push({"term": {"producers": bp}});
|
||||
}
|
||||
}
|
||||
|
||||
if (request.query.proxy === 'true') {
|
||||
queryStruct.bool.must.push({"term": {"is_proxy": true}});
|
||||
}
|
||||
if (query.proxy === 'true') {
|
||||
queryStruct.bool.must.push({"term": {"is_proxy": true}});
|
||||
}
|
||||
|
||||
if (queryStruct.bool.must.length === 0) {
|
||||
queryStruct = {
|
||||
"match_all": {}
|
||||
};
|
||||
}
|
||||
if (queryStruct.bool.must.length === 0) {
|
||||
queryStruct = {
|
||||
"match_all": {}
|
||||
};
|
||||
}
|
||||
|
||||
const maxDocs = fastify.manager.config.api.limits.get_voters ?? 100;
|
||||
const results = await fastify.elastic.search({
|
||||
"index": fastify.manager.chain + '-table-voters-*',
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxDocs ? maxDocs : limit) || 10,
|
||||
"body": {
|
||||
"query": queryStruct,
|
||||
"sort": [{"last_vote_weight": "desc"}]
|
||||
}
|
||||
});
|
||||
const hits = results['body']['hits']['hits'];
|
||||
for (const hit of hits) {
|
||||
const voter = hit._source;
|
||||
response.voters.push({
|
||||
account: voter.voter,
|
||||
weight: voter.last_vote_weight,
|
||||
last_vote: voter.block_num
|
||||
});
|
||||
}
|
||||
response.voter_count = results['body']['hits']['total']['value'];
|
||||
return response;
|
||||
const maxDocs = fastify.manager.config.api.limits.get_voters ?? 100;
|
||||
const results = await fastify.elastic.search({
|
||||
"index": fastify.manager.chain + '-table-voters-*',
|
||||
"from": skip || 0,
|
||||
"size": (limit > maxDocs ? maxDocs : limit) || 10,
|
||||
"body": {
|
||||
"query": queryStruct,
|
||||
"sort": [{"last_vote_weight": "desc"}]
|
||||
}
|
||||
});
|
||||
const hits = results['body']['hits']['hits'];
|
||||
for (const hit of hits) {
|
||||
const voter = hit._source;
|
||||
response.voters.push({
|
||||
account: voter.voter,
|
||||
weight: voter.last_vote_weight,
|
||||
last_vote: voter.block_num
|
||||
});
|
||||
}
|
||||
response.voter_count = results['body']['hits']['total']['value'];
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getVotersHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getVoters, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getVoters, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,120 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
async function getLastSeq(fastify: FastifyInstance, date: string) {
|
||||
const req = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
body: {
|
||||
"size": 1,
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"range": {"@timestamp": {"lt": date}}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_source": {
|
||||
"includes": ["global_sequence"]
|
||||
},
|
||||
"sort": [{"global_sequence": "desc"}]
|
||||
}
|
||||
});
|
||||
return req.body.hits.hits[0]._source.global_sequence;
|
||||
const req = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
body: {
|
||||
"size": 1,
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"range": {"@timestamp": {"lt": date}}}
|
||||
]
|
||||
}
|
||||
},
|
||||
"_source": {
|
||||
"includes": ["global_sequence"]
|
||||
},
|
||||
"sort": [{"global_sequence": "desc"}]
|
||||
}
|
||||
});
|
||||
return req.body.hits.hits[0]._source.global_sequence;
|
||||
}
|
||||
|
||||
async function getTxCount(fastify: FastifyInstance, dateFrom: string, dateTo: string) {
|
||||
const req = await fastify.elastic.count({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
body: {
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"term": {"creator_action_ordinal": 0}},
|
||||
{"range": {"@timestamp": {"gt": dateFrom, "lt": dateTo}}}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return req.body.count;
|
||||
const req = await fastify.elastic.count({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
body: {
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"term": {"creator_action_ordinal": 0}},
|
||||
{"range": {"@timestamp": {"gt": dateFrom, "lt": dateTo}}}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return req.body.count;
|
||||
}
|
||||
|
||||
async function getUniqueActors(fastify: FastifyInstance, dateFrom: string, dateTo: string) {
|
||||
const req = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: 0,
|
||||
body: {
|
||||
"aggs": {
|
||||
"unique_actors": {
|
||||
"cardinality": {
|
||||
"field": "act.authorization.actor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"bool": {
|
||||
"filter": [{"range": {"@timestamp": {"gt": dateFrom, "lt": dateTo}}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return req.body.aggregations.unique_actors.value;
|
||||
const req = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
size: 0,
|
||||
body: {
|
||||
"aggs": {
|
||||
"unique_actors": {
|
||||
"cardinality": {
|
||||
"field": "act.authorization.actor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"query": {
|
||||
"bool": {
|
||||
"filter": [{"range": {"@timestamp": {"gt": dateFrom, "lt": dateTo}}}]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return req.body.aggregations.unique_actors.value;
|
||||
}
|
||||
|
||||
async function getActionUsage(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query = request.query;
|
||||
let now = new Date();
|
||||
let expiration = 86400;
|
||||
if (query.end_date) {
|
||||
now = new Date(query.end_date);
|
||||
}
|
||||
now.setMinutes(0, 0, 0);
|
||||
let period;
|
||||
if (query.period === '1h') {
|
||||
period = 3600000;
|
||||
} else if (query.period === '24h' || query.period === '1d') {
|
||||
period = 86400000;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
const query: any = request.query;
|
||||
let now = new Date();
|
||||
let expiration = 86400;
|
||||
if (query.end_date) {
|
||||
now = new Date(query.end_date);
|
||||
}
|
||||
now.setMinutes(0, 0, 0);
|
||||
let period;
|
||||
if (query.period === '1h') {
|
||||
period = 3600000;
|
||||
} else if (query.period === '24h' || query.period === '1d') {
|
||||
period = 86400000;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!query.end_date) {
|
||||
expiration = Math.floor((period - (Date.now() - now.getTime())) / 1000);
|
||||
}
|
||||
if (!query.end_date) {
|
||||
expiration = Math.floor((period - (Date.now() - now.getTime())) / 1000);
|
||||
}
|
||||
|
||||
const response = {} as any;
|
||||
const periodStart = new Date(now.getTime() - period);
|
||||
const pStart = periodStart.toISOString();
|
||||
const pEnd = now.toISOString();
|
||||
const response = {} as any;
|
||||
const periodStart = new Date(now.getTime() - period);
|
||||
const pStart = periodStart.toISOString();
|
||||
const pEnd = now.toISOString();
|
||||
|
||||
const cacheKey = `${fastify.manager.chain}_act_stats_${pStart}_${pEnd}_${query.unique_actors}`;
|
||||
const cachedValue = await fastify.redis.get(cacheKey);
|
||||
if (cachedValue) {
|
||||
const cachedResponse = JSON.parse(cachedValue);
|
||||
cachedResponse.cached = true;
|
||||
cachedResponse.cache_exp = expiration;
|
||||
return cachedResponse;
|
||||
}
|
||||
const cacheKey = `${fastify.manager.chain}_act_stats_${pStart}_${pEnd}_${query.unique_actors}`;
|
||||
const cachedValue = await fastify.redis.get(cacheKey);
|
||||
if (cachedValue) {
|
||||
const cachedResponse = JSON.parse(cachedValue);
|
||||
cachedResponse.cached = true;
|
||||
cachedResponse.cache_exp = expiration;
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
const firstReq = await getLastSeq(fastify, pStart);
|
||||
const lastReq = await getLastSeq(fastify, pEnd);
|
||||
response.action_count = lastReq - firstReq;
|
||||
response.tx_count = await getTxCount(fastify, pStart, pEnd);
|
||||
const firstReq = await getLastSeq(fastify, pStart);
|
||||
const lastReq = await getLastSeq(fastify, pEnd);
|
||||
response.action_count = lastReq - firstReq;
|
||||
response.tx_count = await getTxCount(fastify, pStart, pEnd);
|
||||
|
||||
if (query.unique_actors) {
|
||||
response.unique_actors = await getUniqueActors(fastify, pStart, pEnd);
|
||||
}
|
||||
if (query.unique_actors) {
|
||||
response.unique_actors = await getUniqueActors(fastify, pStart, pEnd);
|
||||
}
|
||||
|
||||
response.period = query.period;
|
||||
response.from = pStart;
|
||||
response.to = pEnd;
|
||||
response.period = query.period;
|
||||
response.from = pStart;
|
||||
response.to = pEnd;
|
||||
|
||||
await fastify.redis.set(cacheKey, JSON.stringify(response), 'EX', expiration);
|
||||
return response;
|
||||
await fastify.redis.set(cacheKey, JSON.stringify(response), 'EX', expiration);
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getActionUsageHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getActionUsage, fastify, request, route));
|
||||
}
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getActionUsage, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {ServerResponse} from "http";
|
||||
import {Search} from "@elastic/elasticsearch/api/requestParams";
|
||||
import {applyTimeFilter} from "../../v2-history/get_actions/functions";
|
||||
|
||||
async function getMissedBlocks(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const response = {
|
||||
stats: {
|
||||
by_producer: {}
|
||||
},
|
||||
events: []
|
||||
};
|
||||
const searchParams: Search<any> = {
|
||||
track_total_hits: true,
|
||||
index: fastify.manager.chain + "-logs-*",
|
||||
size: 100,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{term: {"type": "missed_blocks"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
"@timestamp": "desc"
|
||||
}
|
||||
}
|
||||
};
|
||||
const query: any = request.query;
|
||||
const response = {
|
||||
stats: {
|
||||
by_producer: {}
|
||||
},
|
||||
events: []
|
||||
};
|
||||
const searchParams: Search<any> = {
|
||||
track_total_hits: true,
|
||||
index: fastify.manager.chain + "-logs-*",
|
||||
size: 100,
|
||||
body: {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{term: {"type": "missed_blocks"}}
|
||||
]
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
"@timestamp": "desc"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let minBlocks = 0;
|
||||
if (request.query.min_blocks) {
|
||||
minBlocks = parseInt(request.query.min_blocks);
|
||||
searchParams.body.query.bool.must.push({range: {"missed_blocks.size": {gte: minBlocks}}})
|
||||
}
|
||||
let minBlocks = 0;
|
||||
if (query.min_blocks) {
|
||||
minBlocks = parseInt(query.min_blocks);
|
||||
searchParams.body.query.bool.must.push({range: {"missed_blocks.size": {gte: minBlocks}}})
|
||||
}
|
||||
|
||||
if (request.query.producer) {
|
||||
searchParams.body.query.bool.must.push({term: {"missed_blocks.producer": request.query.producer}});
|
||||
}
|
||||
if (query.producer) {
|
||||
searchParams.body.query.bool.must.push({term: {"missed_blocks.producer": query.producer}});
|
||||
}
|
||||
|
||||
applyTimeFilter(request.query, searchParams.body.query);
|
||||
applyTimeFilter(query, searchParams.body.query);
|
||||
|
||||
const apiResponse = await fastify.elastic.search(searchParams);
|
||||
apiResponse.body.hits.hits.forEach(v => {
|
||||
const ev = v._source;
|
||||
response.events.push({
|
||||
"@timestamp": ev["@timestamp"],
|
||||
...ev.missed_blocks
|
||||
});
|
||||
if (!response.stats.by_producer[ev.missed_blocks.producer]) {
|
||||
response.stats.by_producer[ev.missed_blocks.producer] = ev.missed_blocks.size;
|
||||
} else {
|
||||
response.stats.by_producer[ev.missed_blocks.producer] += ev.missed_blocks.size;
|
||||
}
|
||||
});
|
||||
return response;
|
||||
const apiResponse = await fastify.elastic.search(searchParams);
|
||||
apiResponse.body.hits.hits.forEach(v => {
|
||||
const ev = v._source;
|
||||
response.events.push({
|
||||
"@timestamp": ev["@timestamp"],
|
||||
...ev.missed_blocks
|
||||
});
|
||||
if (!response.stats.by_producer[ev.missed_blocks.producer]) {
|
||||
response.stats.by_producer[ev.missed_blocks.producer] = ev.missed_blocks.size;
|
||||
} else {
|
||||
response.stats.by_producer[ev.missed_blocks.producer] += ev.missed_blocks.size;
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
export function getMissedBlocksHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getMissedBlocks, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getMissedBlocks, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
|
||||
const percentiles = [1, 5, 25, 50, 75, 95, 99];
|
||||
|
||||
async function getResourceUsage(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
const query: any = request.query;
|
||||
const searchBody: any = {
|
||||
query: {
|
||||
bool: {
|
||||
must: [
|
||||
{term: {"act.account": request.query.code}},
|
||||
{term: {"act.name": request.query.action}},
|
||||
{term: {"act.account": query.code}},
|
||||
{term: {"act.name": query.action}},
|
||||
{term: {"action_ordinal": {"value": 1}}},
|
||||
{
|
||||
range: {
|
||||
@@ -69,7 +69,7 @@ async function getResourceUsage(fastify: FastifyInstance, request: FastifyReques
|
||||
}
|
||||
|
||||
export function getResourceUsageHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getResourceUsage, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,90 @@
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from "fastify";
|
||||
import {ServerResponse} from "http";
|
||||
import {connect} from "amqplib";
|
||||
import {timedQuery} from "../../../helpers/functions";
|
||||
import {getLastIndexedBlockWithTotalBlocks} from "../../../../helpers/common_functions";
|
||||
|
||||
async function checkRabbit(fastify: FastifyInstance) {
|
||||
try {
|
||||
const connection = await connect(fastify.manager.ampqUrl);
|
||||
await connection.close();
|
||||
return createHealth('RabbitMq', 'OK');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return createHealth('RabbitMq', 'Error');
|
||||
}
|
||||
try {
|
||||
const connection = await connect(fastify.manager.ampqUrl);
|
||||
await connection.close();
|
||||
return createHealth('RabbitMq', 'OK');
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return createHealth('RabbitMq', 'Error');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNodeos(fastify: FastifyInstance) {
|
||||
const rpc = fastify.manager.nodeosJsonRPC;
|
||||
try {
|
||||
const results = await rpc.get_info();
|
||||
if (results) {
|
||||
const diff = (new Date().getTime()) - (new Date(results.head_block_time + '+00:00').getTime());
|
||||
return createHealth('NodeosRPC', 'OK', {
|
||||
head_block_num: results.head_block_num,
|
||||
head_block_time: results.head_block_time,
|
||||
time_offset: diff,
|
||||
last_irreversible_block: results.last_irreversible_block_num,
|
||||
chain_id: results.chain_id
|
||||
});
|
||||
} else {
|
||||
return createHealth('NodeosRPC', 'Error');
|
||||
}
|
||||
} catch (e) {
|
||||
return createHealth('NodeosRPC', 'Error');
|
||||
}
|
||||
const rpc = fastify.manager.nodeosJsonRPC;
|
||||
try {
|
||||
const results = await rpc.get_info();
|
||||
if (results) {
|
||||
const diff = (new Date().getTime()) - (new Date(results.head_block_time + '+00:00').getTime());
|
||||
return createHealth('NodeosRPC', 'OK', {
|
||||
head_block_num: results.head_block_num,
|
||||
head_block_time: results.head_block_time,
|
||||
time_offset: diff,
|
||||
last_irreversible_block: results.last_irreversible_block_num,
|
||||
chain_id: results.chain_id
|
||||
});
|
||||
} else {
|
||||
return createHealth('NodeosRPC', 'Error');
|
||||
}
|
||||
} catch (e) {
|
||||
return createHealth('NodeosRPC', 'Error');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkElastic(fastify: FastifyInstance) {
|
||||
try {
|
||||
let esStatus = await fastify.elastic.cat.health({format: 'json', v: true});
|
||||
let indexedBlocks = await getLastIndexedBlockWithTotalBlocks(fastify.elastic, fastify.manager.chain);
|
||||
const data = {
|
||||
last_indexed_block: indexedBlocks[0],
|
||||
total_indexed_blocks: indexedBlocks[1],
|
||||
active_shards: esStatus.body[0]['active_shards_percent']
|
||||
};
|
||||
let stat = 'OK';
|
||||
esStatus.body.forEach(status => {
|
||||
if (status.status === 'yellow' && stat !== 'Error') {
|
||||
stat = 'Warning'
|
||||
} else if (status.status === 'red') {
|
||||
stat = 'Error'
|
||||
}
|
||||
});
|
||||
return createHealth('Elasticsearch', stat, data);
|
||||
} catch (e) {
|
||||
console.log(e, 'Elasticsearch Error');
|
||||
return createHealth('Elasticsearch', 'Error');
|
||||
}
|
||||
try {
|
||||
let esStatus = await fastify.elastic.cat.health({format: 'json', v: true});
|
||||
let indexedBlocks = await getLastIndexedBlockWithTotalBlocks(fastify.elastic, fastify.manager.chain);
|
||||
const data = {
|
||||
last_indexed_block: indexedBlocks[0],
|
||||
total_indexed_blocks: indexedBlocks[1] + 1,
|
||||
active_shards: esStatus.body[0]['active_shards_percent']
|
||||
};
|
||||
let stat = 'OK';
|
||||
esStatus.body.forEach(status => {
|
||||
if (status.status === 'yellow' && stat !== 'Error') {
|
||||
stat = 'Warning'
|
||||
} else if (status.status === 'red') {
|
||||
stat = 'Error'
|
||||
}
|
||||
});
|
||||
return createHealth('Elasticsearch', stat, data);
|
||||
} catch (e) {
|
||||
console.log(e, 'Elasticsearch Error');
|
||||
return createHealth('Elasticsearch', 'Error');
|
||||
}
|
||||
}
|
||||
|
||||
function createHealth(name: string, status, data?: any) {
|
||||
let time = Date.now();
|
||||
return {
|
||||
service: name,
|
||||
status: status,
|
||||
service_data: data,
|
||||
time: time
|
||||
}
|
||||
let time = Date.now();
|
||||
return {
|
||||
service: name,
|
||||
status: status,
|
||||
service_data: data,
|
||||
time: time
|
||||
}
|
||||
}
|
||||
|
||||
async function getHealthQuery(fastify: FastifyInstance, request: FastifyRequest) {
|
||||
let response = {
|
||||
version: fastify.manager.current_version,
|
||||
version_hash: fastify.manager.getServerHash(),
|
||||
host: fastify.manager.config.api.server_name,
|
||||
features: fastify.manager.config.features,
|
||||
health: []
|
||||
};
|
||||
response.health.push(await checkRabbit(fastify));
|
||||
response.health.push(await checkNodeos(fastify));
|
||||
response.health.push(await checkElastic(fastify));
|
||||
return response;
|
||||
let response = {
|
||||
version: fastify.manager.current_version,
|
||||
version_hash: fastify.manager.getServerHash(),
|
||||
host: fastify.manager.config.api.server_name,
|
||||
health: [],
|
||||
features: fastify.manager.config.features
|
||||
};
|
||||
response.health.push(await checkRabbit(fastify));
|
||||
response.health.push(await checkNodeos(fastify));
|
||||
response.health.push(await checkElastic(fastify));
|
||||
return response;
|
||||
}
|
||||
|
||||
export function healthHandler(fastify: FastifyInstance, route: string) {
|
||||
return async (request: FastifyRequest, reply: FastifyReply<ServerResponse>) => {
|
||||
reply.send(await timedQuery(getHealthQuery, fastify, request, route));
|
||||
}
|
||||
return async (request: FastifyRequest, reply: FastifyReply) => {
|
||||
reply.send(await timedQuery(getHealthQuery, fastify, request, route));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import {FastifyInstance, RouteSchema} from "fastify";
|
||||
import {FastifyInstance, FastifySchema} from "fastify";
|
||||
import {addApiRoute, getRouteName} from "../../../helpers/functions";
|
||||
import {healthHandler} from "./health";
|
||||
|
||||
export default function (fastify: FastifyInstance, opts: any, next) {
|
||||
const schema: RouteSchema = {
|
||||
tags: ['status'],
|
||||
summary: "API Service Health Report"
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
healthHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
const schema: FastifySchema = {
|
||||
tags: ['status'],
|
||||
summary: "API Service Health Report"
|
||||
};
|
||||
addApiRoute(
|
||||
fastify,
|
||||
'GET',
|
||||
getRouteName(__filename),
|
||||
healthHandler,
|
||||
schema
|
||||
);
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'fetch contract abi at specific block',
|
||||
summary: 'fetch abi at specific block',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"contract": {
|
||||
description: 'contract account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"block": {
|
||||
description: 'target block',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
"fetch": {
|
||||
description: 'should fetch the ABI',
|
||||
type: 'boolean'
|
||||
}
|
||||
},
|
||||
required: ["contract"]
|
||||
}
|
||||
};
|
||||
@@ -1,14 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get account data',
|
||||
summary: 'get account summary',
|
||||
tags: ['accounts', 'state'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'account name',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,132 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get actions based on notified account. this endpoint also accepts generic filters based on indexed fields' +
|
||||
' (e.g. act.authorization.actor=eosio or act.name=delegatebw), if included they will be combined with a AND operator',
|
||||
summary: 'get root actions',
|
||||
tags: ['actions', 'history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'notified account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"track": {
|
||||
description: 'total results to track (count) [number or true]',
|
||||
type: 'string'
|
||||
},
|
||||
"filter": {
|
||||
description: 'code:name filter',
|
||||
type: 'string',
|
||||
minLength: 3
|
||||
},
|
||||
"skip": {
|
||||
description: 'skip [n] actions (pagination)',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
},
|
||||
"limit": {
|
||||
description: 'limit of [n] actions per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
"sort": {
|
||||
description: 'sort direction',
|
||||
enum: ['desc', 'asc', '1', '-1'],
|
||||
type: 'string'
|
||||
},
|
||||
"after": {
|
||||
description: 'filter after specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601)',
|
||||
type: 'string'
|
||||
},
|
||||
"simple": {
|
||||
description: 'simplified output mode',
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"query_time": {
|
||||
type: "number"
|
||||
},
|
||||
"cached": {type: "boolean"},
|
||||
"lib": {
|
||||
type: "number"
|
||||
},
|
||||
"total": {
|
||||
type: "object",
|
||||
properties: {
|
||||
"value": {type: "number"},
|
||||
"relation": {type: "string"}
|
||||
}
|
||||
},
|
||||
"simple_actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"block": {type: "number"},
|
||||
"timestamp": {type: "string"},
|
||||
"irreversible": {type: "boolean"},
|
||||
"contract": {type: "string"},
|
||||
"action": {type: "string"},
|
||||
"actors": {type: "string"},
|
||||
"notified": {type: "string"},
|
||||
"transaction_id": {type: "string"},
|
||||
"data": {
|
||||
additionalProperties: true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"act": {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"name": {type: "string"}
|
||||
},
|
||||
additionalProperties: true
|
||||
},
|
||||
"cpu_usage_us": {type: "number"},
|
||||
"net_usage_words": {type: "number"},
|
||||
"account_ram_deltas": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"delta": {type: "number"}
|
||||
},
|
||||
additionalProperties: true
|
||||
}
|
||||
},
|
||||
"global_sequence": {type: "number"},
|
||||
"receiver": {type: 'string'},
|
||||
"@timestamp": {type: "string"},
|
||||
"block_num": {type: "number"},
|
||||
"producer": {type: "string"},
|
||||
"trx_id": {type: "string"},
|
||||
"parent": {type: "number"},
|
||||
"action_ordinal": {type: 'number'},
|
||||
"creator_action_ordinal": {type: 'number'},
|
||||
"notified": {type: "array", items: {type: "string"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get block range',
|
||||
summary: 'get block range',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"from": {
|
||||
description: 'starting block',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
"to": {
|
||||
description: 'last block',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get all accounts created by one creator',
|
||||
summary: 'get created accounts',
|
||||
tags: ['accounts', 'history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'creator account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"query_time": {
|
||||
type: "number"
|
||||
},
|
||||
"total": {
|
||||
type: "object",
|
||||
properties: {
|
||||
"value": {type: "number"},
|
||||
"relation": {type: "string"}
|
||||
}
|
||||
},
|
||||
"accounts": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
'name': {type: 'string'},
|
||||
'timestamp': {type: 'string'},
|
||||
'trx_id': {type: 'string'}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,41 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get account creator',
|
||||
summary: 'get account creator',
|
||||
tags: ['accounts', 'history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'created account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
type: "string"
|
||||
},
|
||||
"creator": {
|
||||
type: "string"
|
||||
},
|
||||
"timestamp": {
|
||||
type: "string"
|
||||
},
|
||||
"block_num": {
|
||||
type: "integer"
|
||||
},
|
||||
"trx_id": {
|
||||
type: "string"
|
||||
},
|
||||
"indirect_creator": {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get state deltas',
|
||||
summary: 'get state deltas',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"code": {
|
||||
description: 'contract account',
|
||||
type: 'string'
|
||||
},
|
||||
"scope": {
|
||||
description: 'table scope',
|
||||
type: 'string'
|
||||
},
|
||||
"table": {
|
||||
description: 'table name',
|
||||
type: 'string'
|
||||
},
|
||||
"payer": {
|
||||
description: 'payer account',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
module.exports = {
|
||||
GET: {
|
||||
description: 'get accounts by public key',
|
||||
summary: 'get accounts by public key',
|
||||
tags: ['accounts','state'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"public_key": {
|
||||
description: 'public key',
|
||||
type: 'string'
|
||||
},
|
||||
},
|
||||
required: ["public_key"]
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account_names": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
POST: {
|
||||
description: 'get accounts by public key',
|
||||
summary: 'get accounts by public key',
|
||||
tags: ['accounts','state'],
|
||||
body: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"public_key": {
|
||||
description: 'public key',
|
||||
type: 'string'
|
||||
},
|
||||
},
|
||||
required: ["public_key"]
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account_names": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,48 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get proposals',
|
||||
summary: 'get proposals',
|
||||
tags: ['state'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"proposer": {
|
||||
description: 'filter by proposer',
|
||||
type: 'string'
|
||||
},
|
||||
"proposal": {
|
||||
description: 'filter by proposal name',
|
||||
type: 'string'
|
||||
},
|
||||
"account": {
|
||||
description: 'filter by either requested or provided account',
|
||||
type: 'string'
|
||||
},
|
||||
"requested": {
|
||||
description: 'filter by requested account',
|
||||
type: 'string'
|
||||
},
|
||||
"provided": {
|
||||
description: 'filter by provided account',
|
||||
type: 'string'
|
||||
},
|
||||
"executed": {
|
||||
description: 'filter by execution status',
|
||||
type: 'boolean'
|
||||
},
|
||||
"track": {
|
||||
description: 'total results to track (count) [number or true]',
|
||||
type: 'string'
|
||||
},
|
||||
"skip": {
|
||||
description: 'skip [n] actions (pagination)',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
},
|
||||
"limit": {
|
||||
description: 'limit of [n] actions per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get tokens from account',
|
||||
summary: 'get tokens from account',
|
||||
tags: ['accounts', 'state'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'account',
|
||||
type: 'string'
|
||||
},
|
||||
},
|
||||
required: ["account"]
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"query_time": {type: "number"},
|
||||
"cached": {type: "boolean"},
|
||||
"account": {type: "string"},
|
||||
"tokens": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
"symbol": {type: "string"},
|
||||
"precision": {type: "number"},
|
||||
"amount": {type: "number"},
|
||||
"contract": {type: "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,52 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get all account that interacted with the source account provided',
|
||||
summary: 'get interactions based on transfers',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {
|
||||
description: 'source account',
|
||||
type: 'string'
|
||||
},
|
||||
"symbol": {
|
||||
description: 'token symbol',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 7
|
||||
},
|
||||
"contract": {
|
||||
description: 'token contract',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"direction": {
|
||||
description: 'search direction',
|
||||
enum: ['in', 'out', 'both'],
|
||||
type: 'string'
|
||||
},
|
||||
"min": {
|
||||
description: 'minimum value',
|
||||
type: 'number'
|
||||
},
|
||||
"max": {
|
||||
description: 'maximum value',
|
||||
type: 'number'
|
||||
},
|
||||
"limit": {
|
||||
description: 'query limit',
|
||||
type: 'number'
|
||||
},
|
||||
"after": {
|
||||
description: 'filter after specified date (ISO8601) or block number',
|
||||
type: 'string'
|
||||
},
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601) or block number',
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
required: ["account", "direction"]
|
||||
}
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get all actions belonging to the same transaction',
|
||||
summary: 'get transaction by id',
|
||||
tags: ['transactions','history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"id": {
|
||||
description: 'transaction id',
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
required: ["id"]
|
||||
}
|
||||
};
|
||||
@@ -1,96 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get token transfers utilizing the eosio.token standard',
|
||||
summary: 'get token transfers',
|
||||
tags: ['history'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"from": {
|
||||
description: 'source account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"to": {
|
||||
description: 'destination account',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"symbol": {
|
||||
description: 'token symbol',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 7
|
||||
},
|
||||
"contract": {
|
||||
description: 'token contract',
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 12
|
||||
},
|
||||
"skip": {
|
||||
description: 'skip [n] actions (pagination)',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
},
|
||||
"limit": {
|
||||
description: 'limit of [n] actions per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
},
|
||||
"after": {
|
||||
description: 'filter after specified date (ISO8601)',
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
},
|
||||
"before": {
|
||||
description: 'filter before specified date (ISO8601)',
|
||||
type: 'string',
|
||||
format: 'date-time'
|
||||
},
|
||||
},
|
||||
"anyOf": [
|
||||
{required: ["from"]},
|
||||
{required: ["to"]},
|
||||
{required: ["symbol"]},
|
||||
{required: ["contract"]}
|
||||
]
|
||||
},
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"action_count": {
|
||||
type: "number"
|
||||
},
|
||||
"total_amount": {
|
||||
type: "number"
|
||||
},
|
||||
"actions": {
|
||||
type: "array",
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"act": {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"account": {type: "string"},
|
||||
"name": {type: "string"}
|
||||
},
|
||||
additionalProperties: true
|
||||
},
|
||||
"@timestamp": {type: "string"},
|
||||
"block_num": {type: "number"},
|
||||
"producer": {type: "string"},
|
||||
"trx_id": {type: "string"},
|
||||
"parent": {type: "number"},
|
||||
"global_sequence": {type: "number"},
|
||||
"notified": {type: "array", items: {type: "string"}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
exports.GET = {
|
||||
description: 'get voters',
|
||||
summary: 'get voters',
|
||||
tags: ['accounts','state'],
|
||||
querystring: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
"producer": {
|
||||
description: 'filter by voted producer (comma separated)',
|
||||
type: 'string'
|
||||
},
|
||||
"skip": {
|
||||
description: 'skip [n] actions (pagination)',
|
||||
type: 'integer',
|
||||
minimum: 0
|
||||
},
|
||||
"limit": {
|
||||
description: 'limit of [n] actions per page',
|
||||
type: 'integer',
|
||||
minimum: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
module.exports = {
|
||||
getTransfersSchema: require('./get_transfers'),
|
||||
getAbiSnapshotSchema: require('./get_abi_snapshot'),
|
||||
getKeyAccountsSchema: require('./get_key_accounts'),
|
||||
getActionsSchema: require('./get_actions'),
|
||||
getTransactedAccountsSchema: require('./get_transacted_accounts'),
|
||||
getTransactionSchema: require('./get_transaction'),
|
||||
getCreatorSchema: require('./get_creator'),
|
||||
getTokensSchema: require('./get_tokens'),
|
||||
getDeltasSchema: require('./get_deltas'),
|
||||
getCreatedAccountsSchema: require('./get_created_accounts'),
|
||||
getVotersSchema: require('./get_voters'),
|
||||
getProposalsSchema: require('./get_proposals'),
|
||||
getBlocksSchema: require('./get_blocks'),
|
||||
getAccountSchema: require('./get_account')
|
||||
};
|
||||
+119
-86
@@ -2,103 +2,145 @@ import {hLog} from "../helpers/common_functions";
|
||||
import {ConfigurationModule} from "../modules/config";
|
||||
import {ConnectionManager} from "../connections/manager.class";
|
||||
import {HyperionConfig} from "../interfaces/hyperionConfig";
|
||||
import {IncomingMessage, Server, ServerResponse} from "http";
|
||||
import * as Fastify from 'fastify';
|
||||
import * as Redis from 'ioredis';
|
||||
import IORedis from 'ioredis';
|
||||
import fastify from 'fastify'
|
||||
import {registerPlugins} from "./plugins";
|
||||
import {AddressInfo} from "net";
|
||||
import {registerRoutes} from "./routes";
|
||||
import {generateOpenApiConfig} from "./config/open_api";
|
||||
import {createWriteStream, existsSync, mkdirSync, writeFileSync} from "fs";
|
||||
import {createWriteStream, existsSync, mkdirSync, readFileSync} from "fs";
|
||||
import {SocketManager} from "./socketManager";
|
||||
import got from "got";
|
||||
import {join} from "path";
|
||||
import * as io from 'socket.io-client';
|
||||
import {HyperionModuleLoader} from "../modules/loader";
|
||||
import {extendedActions} from "./routes/v2-history/get_actions/definitions";
|
||||
import {io, Socket} from "socket.io-client";
|
||||
import {CacheManager} from "./helpers/cacheManager";
|
||||
|
||||
import {bootstrap} from 'global-agent';
|
||||
bootstrap();
|
||||
|
||||
class HyperionApiServer {
|
||||
|
||||
private conf: HyperionConfig;
|
||||
private readonly manager: ConnectionManager;
|
||||
private readonly fastify: Fastify.FastifyInstance<Server, IncomingMessage, ServerResponse>;
|
||||
private hub: Socket;
|
||||
private readonly fastify;
|
||||
private readonly chain: string;
|
||||
socketManager: SocketManager;
|
||||
private readonly conf: HyperionConfig;
|
||||
private readonly manager: ConnectionManager;
|
||||
private readonly cacheManager: CacheManager;
|
||||
|
||||
private hub: SocketIOClient.Socket;
|
||||
socketManager: SocketManager;
|
||||
mLoader: HyperionModuleLoader;
|
||||
|
||||
constructor() {
|
||||
|
||||
const package_json = JSON.parse(readFileSync('./package.json').toString());
|
||||
hLog(`--------- Hyperion API ${package_json.version} ---------`);
|
||||
|
||||
const cm = new ConfigurationModule();
|
||||
this.conf = cm.config;
|
||||
this.chain = this.conf.settings.chain;
|
||||
process.title = `hyp-${this.chain}-api`;
|
||||
this.manager = new ConnectionManager(cm);
|
||||
this.manager.calculateServerHash();
|
||||
this.manager.getHyperionVersion();
|
||||
this.mLoader = new HyperionModuleLoader(cm);
|
||||
this.cacheManager = new CacheManager(this.conf);
|
||||
|
||||
if (!existsSync('./logs/' + this.chain)) {
|
||||
mkdirSync('./logs/' + this.chain, {recursive: true});
|
||||
}
|
||||
|
||||
const logStream = createWriteStream('./logs/' + this.chain + '/api.access.log');
|
||||
this.fastify = Fastify({
|
||||
|
||||
const loggerOpts = {
|
||||
stream: logStream,
|
||||
redact: ['req.headers.authorization'],
|
||||
level: 'info',
|
||||
prettyPrint: true,
|
||||
serializers: {
|
||||
res: (reply) => {
|
||||
return {
|
||||
statusCode: reply.statusCode
|
||||
};
|
||||
},
|
||||
req: (request) => {
|
||||
return {
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
ip: request.headers['x-real-ip']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.fastify = fastify({
|
||||
ignoreTrailingSlash: false,
|
||||
trustProxy: true,
|
||||
pluginTimeout: 5000,
|
||||
logger: this.conf.api.access_log ? {
|
||||
stream: logStream,
|
||||
redact: ['req.headers.authorization'],
|
||||
level: 'info',
|
||||
serializers: {
|
||||
res: (res) => {
|
||||
return {
|
||||
status: res.statusCode
|
||||
};
|
||||
},
|
||||
req: (req) => {
|
||||
return {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
ip: req.headers['x-real-ip']
|
||||
}
|
||||
}
|
||||
}
|
||||
} : false
|
||||
logger: this.conf.api.access_log ? loggerOpts : false
|
||||
});
|
||||
|
||||
this.fastify.decorate('cacheManager', this.cacheManager);
|
||||
|
||||
this.fastify.decorate('manager', this.manager);
|
||||
|
||||
if (this.conf.api.chain_api && this.conf.api.chain_api !== "") {
|
||||
this.fastify.decorate('chain_api', this.conf.api.chain_api);
|
||||
} else {
|
||||
this.fastify.decorate('chain_api', this.manager.conn.chains[this.chain].http);
|
||||
// import get_actions query params from custom modules
|
||||
const extendedActionsSet: Set<string> = new Set([...extendedActions]);
|
||||
for (const qPrefix of this.mLoader.extendedActions) {
|
||||
extendedActionsSet.add(qPrefix);
|
||||
}
|
||||
this.fastify.decorate('allowedActionQueryParamSet', extendedActionsSet);
|
||||
|
||||
if (this.conf.api.push_api && this.conf.api.push_api !== "") {
|
||||
// define chain api url for /v1/chain/ redirects
|
||||
let chainApiUrl: string = this.conf.api.chain_api;
|
||||
if (chainApiUrl === null || chainApiUrl === "") {
|
||||
chainApiUrl = this.manager.conn.chains[this.chain].http;
|
||||
}
|
||||
this.fastify.decorate('chain_api', chainApiUrl);
|
||||
|
||||
// define optional push api url for /v1/chain/push_transaction
|
||||
if (this.conf.api.push_api) {
|
||||
this.fastify.decorate('push_api', this.conf.api.push_api);
|
||||
}
|
||||
|
||||
console.log(`Chain API URL: ${this.fastify.chain_api}`);
|
||||
console.log(`Push API URL: ${this.fastify.push_api}`);
|
||||
hLog(`Chain API URL: "${this.fastify.chain_api}" | Push API URL: "${this.fastify.push_api}"`);
|
||||
|
||||
const ioRedisClient = new Redis(this.manager.conn.redis);
|
||||
const api_rate_limit = {
|
||||
max: 1000,
|
||||
whitelist: ['127.0.0.1'],
|
||||
timeWindow: '1 minute',
|
||||
redis: ioRedisClient
|
||||
};
|
||||
const ioRedisClient = new IORedis(this.manager.conn.redis);
|
||||
|
||||
const pluginParams = {
|
||||
fastify_elasticsearch: {
|
||||
client: this.manager.elasticsearchClient
|
||||
},
|
||||
fastify_redis: this.manager.conn.redis,
|
||||
fastify_eosjs: this.manager,
|
||||
} as any;
|
||||
|
||||
if (!this.conf.api.disable_rate_limit) {
|
||||
let rateLimiterWhitelist = ['127.0.0.1'];
|
||||
if (this.conf.api.rate_limit_allow && this.conf.api.rate_limit_allow.length > 0) {
|
||||
const tempSet = new Set<string>(['127.0.0.1', ...this.conf.api.rate_limit_allow]);
|
||||
rateLimiterWhitelist = [...tempSet];
|
||||
}
|
||||
let rateLimiterRPM = 1000;
|
||||
if (this.conf.api.rate_limit_rpm) {
|
||||
rateLimiterRPM = this.conf.api.rate_limit_rpm;
|
||||
}
|
||||
pluginParams.fastify_rate_limit = {
|
||||
max: rateLimiterRPM,
|
||||
allowList: rateLimiterWhitelist,
|
||||
timeWindow: '1 minute',
|
||||
redis: ioRedisClient
|
||||
}
|
||||
}
|
||||
|
||||
if (this.conf.features.streaming.enable) {
|
||||
this.activateStreaming();
|
||||
}
|
||||
|
||||
registerPlugins(this.fastify, {
|
||||
fastify_elasticsearch: {
|
||||
client: this.manager.elasticsearchClient
|
||||
},
|
||||
fastify_oas: generateOpenApiConfig(this.manager.config),
|
||||
fastify_rate_limit: api_rate_limit,
|
||||
fastify_redis: this.manager.conn.redis,
|
||||
fastify_eosjs: this.manager,
|
||||
});
|
||||
const docsConfig = generateOpenApiConfig(this.manager.config);
|
||||
if (docsConfig) {
|
||||
pluginParams.fastify_swagger = docsConfig;
|
||||
}
|
||||
|
||||
registerPlugins(this.fastify, pluginParams);
|
||||
|
||||
this.addGenericTypeParsing();
|
||||
}
|
||||
@@ -128,15 +170,15 @@ class HyperionApiServer {
|
||||
}
|
||||
|
||||
private addGenericTypeParsing() {
|
||||
this.fastify.addContentTypeParser('*', (req, done) => {
|
||||
this.fastify.addContentTypeParser('*', (request, payload, done) => {
|
||||
let data = '';
|
||||
req.on('data', chunk => {
|
||||
payload.on('data', chunk => {
|
||||
data += chunk;
|
||||
});
|
||||
req.on('end', () => {
|
||||
payload.on('end', () => {
|
||||
done(null, data);
|
||||
});
|
||||
req.on('error', (err) => {
|
||||
payload.on('error', (err) => {
|
||||
console.log('---- Content Parsing Error -----');
|
||||
console.log(err);
|
||||
});
|
||||
@@ -145,15 +187,24 @@ class HyperionApiServer {
|
||||
|
||||
async init() {
|
||||
|
||||
await this.fetchChainLogo();
|
||||
await this.mLoader.init();
|
||||
|
||||
// add custom plugin routes
|
||||
for (const plugin of this.mLoader.plugins) {
|
||||
if (plugin.hasApiRoutes) {
|
||||
hLog(`Adding routes for plugin: ${plugin.internalPluginName}`);
|
||||
plugin.addRoutes(this.fastify);
|
||||
plugin.chainName = this.chain;
|
||||
}
|
||||
}
|
||||
|
||||
registerRoutes(this.fastify);
|
||||
|
||||
// register documentation when ready
|
||||
this.fastify.ready().then(async () => {
|
||||
await this.fastify.oas();
|
||||
console.log(this.chain + ' api ready!');
|
||||
await this.fastify.swagger();
|
||||
}, (err) => {
|
||||
console.log('an error happened', err)
|
||||
hLog('an error happened', err)
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -161,28 +212,14 @@ class HyperionApiServer {
|
||||
host: this.conf.api.server_addr,
|
||||
port: this.conf.api.server_port
|
||||
});
|
||||
console.log(`server listening on ${(this.fastify.server.address() as AddressInfo).port}`);
|
||||
hLog(`${this.chain} hyperion api ready and listening on port ${(this.fastify.server.address() as AddressInfo).port}`);
|
||||
this.startHyperionHub();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
hLog(err);
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
async fetchChainLogo() {
|
||||
try {
|
||||
if (this.conf.api.chain_logo_url && this.conf.api.enable_explorer) {
|
||||
console.log(`Downloading chain logo from ${this.conf.api.chain_logo_url}...`);
|
||||
const chainLogo = await got(this.conf.api.chain_logo_url);
|
||||
const path = join(__dirname, '..', 'hyperion-explorer', 'dist', 'assets', this.chain + '_logo.png');
|
||||
writeFileSync(path, chainLogo.rawBody);
|
||||
this.conf.api.chain_logo_url = 'https://' + this.conf.api.server_name + '/v2/explore/assets/' + this.chain + '_logo.png';
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
startHyperionHub() {
|
||||
if (this.conf.hub) {
|
||||
const url = this.conf.hub.inform_url;
|
||||
@@ -190,17 +227,13 @@ class HyperionApiServer {
|
||||
this.hub = io(url, {
|
||||
query: {
|
||||
key: this.conf.hub.publisher_key,
|
||||
client_mode: false
|
||||
client_mode: 'false'
|
||||
}
|
||||
});
|
||||
this.hub.on('connect', () => {
|
||||
hLog(`Hyperion Hub connected!`);
|
||||
this.emitHubApiUpdate();
|
||||
});
|
||||
// this.hub.on('reconnect', () => {
|
||||
// hLog(`Reconnecting...`);
|
||||
// this.emitHubApiUpdate();
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +244,7 @@ class HyperionApiServer {
|
||||
location: this.conf.hub.location,
|
||||
chainId: this.manager.conn.chains[this.chain].chain_id,
|
||||
providerName: this.conf.api.provider_name,
|
||||
explorerEnabled: this.conf.api.enable_explorer,
|
||||
explorerEnabled: this.conf.plugins.explorer?.enabled,
|
||||
providerUrl: this.conf.api.provider_url,
|
||||
providerLogo: this.conf.api.provider_logo,
|
||||
chainLogo: this.conf.api.chain_logo_url,
|
||||
|
||||
+322
-315
@@ -1,377 +1,384 @@
|
||||
import {checkFilter, hLog} from '../helpers/common_functions';
|
||||
import * as sockets from 'socket.io';
|
||||
import * as IOClient from 'socket.io-client';
|
||||
import {Server, Socket} from 'socket.io';
|
||||
import {createAdapter} from 'socket.io-redis';
|
||||
import {io} from 'socket.io-client';
|
||||
import {FastifyInstance} from "fastify";
|
||||
import IORedis from "ioredis";
|
||||
|
||||
export interface StreamDeltasRequest {
|
||||
code: string;
|
||||
table: string;
|
||||
scope: string;
|
||||
payer: string;
|
||||
start_from: number | string;
|
||||
read_until: number | string;
|
||||
code: string;
|
||||
table: string;
|
||||
scope: string;
|
||||
payer: string;
|
||||
start_from: number | string;
|
||||
read_until: number | string;
|
||||
}
|
||||
|
||||
export interface RequestFilter {
|
||||
field: string;
|
||||
value: string;
|
||||
field: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface StreamActionsRequest {
|
||||
contract: string;
|
||||
account: string;
|
||||
action: string;
|
||||
filters: RequestFilter[];
|
||||
start_from: number | string;
|
||||
read_until: number | string;
|
||||
contract: string;
|
||||
account: string;
|
||||
action: string;
|
||||
filters: RequestFilter[];
|
||||
start_from: number | string;
|
||||
read_until: number | string;
|
||||
}
|
||||
|
||||
async function addBlockRangeOpts(data, search_body, fastify: FastifyInstance) {
|
||||
|
||||
let timeRange;
|
||||
let blockRange;
|
||||
let head;
|
||||
let timeRange;
|
||||
let blockRange;
|
||||
let head;
|
||||
|
||||
if (typeof data['start_from'] === 'string' && data['start_from'] !== '') {
|
||||
if (!timeRange) {
|
||||
timeRange = {"@timestamp": {}};
|
||||
}
|
||||
timeRange["@timestamp"]['gte'] = data['start_from'];
|
||||
}
|
||||
if (typeof data['start_from'] === 'string' && data['start_from'] !== '') {
|
||||
if (!timeRange) {
|
||||
timeRange = {"@timestamp": {}};
|
||||
}
|
||||
timeRange["@timestamp"]['gte'] = data['start_from'];
|
||||
}
|
||||
|
||||
if (typeof data['read_until'] === 'string' && data['read_until'] !== '') {
|
||||
if (!timeRange) {
|
||||
timeRange = {"@timestamp": {}};
|
||||
}
|
||||
timeRange["@timestamp"]['lte'] = data['read_until'];
|
||||
}
|
||||
if (typeof data['read_until'] === 'string' && data['read_until'] !== '') {
|
||||
if (!timeRange) {
|
||||
timeRange = {"@timestamp": {}};
|
||||
}
|
||||
timeRange["@timestamp"]['lte'] = data['read_until'];
|
||||
}
|
||||
|
||||
if (typeof data['start_from'] === 'number' && data['start_from'] !== 0) {
|
||||
if (!blockRange) {
|
||||
blockRange = {"block_num": {}};
|
||||
}
|
||||
if (data['start_from'] < 0) {
|
||||
if (!head) {
|
||||
head = (await fastify.eosjs.rpc.get_info()).head_block_num;
|
||||
}
|
||||
blockRange["block_num"]['gte'] = head + data['start_from'];
|
||||
} else {
|
||||
blockRange["block_num"]['gte'] = data['start_from'];
|
||||
}
|
||||
}
|
||||
if (typeof data['start_from'] === 'number' && data['start_from'] !== 0) {
|
||||
if (!blockRange) {
|
||||
blockRange = {"block_num": {}};
|
||||
}
|
||||
if (data['start_from'] < 0) {
|
||||
if (!head) {
|
||||
head = (await fastify.eosjs.rpc.get_info()).head_block_num;
|
||||
}
|
||||
blockRange["block_num"]['gte'] = head + data['start_from'];
|
||||
} else {
|
||||
blockRange["block_num"]['gte'] = data['start_from'];
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof data['read_until'] === 'number' && data['read_until'] !== 0) {
|
||||
if (!blockRange) {
|
||||
blockRange = {"block_num": {}};
|
||||
}
|
||||
if (data['read_until'] < 0) {
|
||||
if (!head) {
|
||||
head = (await fastify.eosjs.rpc.get_info()).head_block_num;
|
||||
}
|
||||
blockRange["block_num"]['lte'] = head + data['read_until'];
|
||||
} else {
|
||||
blockRange["block_num"]['lte'] = data['read_until'];
|
||||
}
|
||||
}
|
||||
if (typeof data['read_until'] === 'number' && data['read_until'] !== 0) {
|
||||
if (!blockRange) {
|
||||
blockRange = {"block_num": {}};
|
||||
}
|
||||
if (data['read_until'] < 0) {
|
||||
if (!head) {
|
||||
head = (await fastify.eosjs.rpc.get_info()).head_block_num;
|
||||
}
|
||||
blockRange["block_num"]['lte'] = head + data['read_until'];
|
||||
} else {
|
||||
blockRange["block_num"]['lte'] = data['read_until'];
|
||||
}
|
||||
}
|
||||
|
||||
if (timeRange) {
|
||||
search_body.query.bool.must.push({
|
||||
range: timeRange,
|
||||
});
|
||||
}
|
||||
if (blockRange) {
|
||||
search_body.query.bool.must.push({
|
||||
range: blockRange,
|
||||
});
|
||||
}
|
||||
if (timeRange) {
|
||||
search_body.query.bool.must.push({
|
||||
range: timeRange,
|
||||
});
|
||||
}
|
||||
if (blockRange) {
|
||||
search_body.query.bool.must.push({
|
||||
range: blockRange,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addTermMatch(data, search_body, field) {
|
||||
if (data[field] !== '*' && data[field] !== '') {
|
||||
const termQuery = {};
|
||||
termQuery[field] = data[field];
|
||||
search_body.query.bool.must.push({'term': termQuery});
|
||||
}
|
||||
if (data[field] !== '*' && data[field] !== '') {
|
||||
const termQuery = {};
|
||||
termQuery[field] = data[field];
|
||||
search_body.query.bool.must.push({'term': termQuery});
|
||||
}
|
||||
}
|
||||
|
||||
const deltaQueryFields = ['code', 'table', 'scope', 'payer'];
|
||||
|
||||
async function streamPastDeltas(fastify: FastifyInstance, socket, data) {
|
||||
const search_body = {
|
||||
query: {bool: {must: []}},
|
||||
sort: {block_num: 'asc'},
|
||||
};
|
||||
await addBlockRangeOpts(data, search_body, fastify);
|
||||
deltaQueryFields.forEach(f => {
|
||||
addTermMatch(data, search_body, f);
|
||||
});
|
||||
const responseQueue = [];
|
||||
let counter = 0;
|
||||
const init_response = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-delta-*',
|
||||
scroll: '30s',
|
||||
size: 20,
|
||||
body: search_body,
|
||||
});
|
||||
responseQueue.push(init_response);
|
||||
while (responseQueue.length) {
|
||||
const {body} = responseQueue.shift();
|
||||
counter += body['hits']['hits'].length;
|
||||
if (socket.connected) {
|
||||
socket.emit('message', {
|
||||
type: 'delta_trace',
|
||||
mode: 'history',
|
||||
messages: body['hits']['hits'].map(doc => doc._source),
|
||||
});
|
||||
} else {
|
||||
console.log('LOST CLIENT');
|
||||
break;
|
||||
}
|
||||
if (body['hits'].total.value === counter) {
|
||||
console.log(`${counter} past deltas streamed to ${socket.id}`);
|
||||
break;
|
||||
}
|
||||
const search_body = {
|
||||
query: {bool: {must: []}},
|
||||
sort: {block_num: 'asc'},
|
||||
};
|
||||
await addBlockRangeOpts(data, search_body, fastify);
|
||||
deltaQueryFields.forEach(f => {
|
||||
addTermMatch(data, search_body, f);
|
||||
});
|
||||
const responseQueue = [];
|
||||
let counter = 0;
|
||||
const init_response = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-delta-*',
|
||||
scroll: '30s',
|
||||
size: 20,
|
||||
body: search_body,
|
||||
});
|
||||
responseQueue.push(init_response);
|
||||
while (responseQueue.length) {
|
||||
const {body} = responseQueue.shift();
|
||||
counter += body['hits']['hits'].length;
|
||||
if (socket.connected) {
|
||||
socket.emit('message', {
|
||||
type: 'delta_trace',
|
||||
mode: 'history',
|
||||
messages: body['hits']['hits'].map(doc => doc._source),
|
||||
});
|
||||
} else {
|
||||
hLog('LOST CLIENT');
|
||||
break;
|
||||
}
|
||||
if (body['hits'].total.value === counter) {
|
||||
hLog(`${counter} past deltas streamed to ${socket.id}`);
|
||||
break;
|
||||
}
|
||||
|
||||
const next_response = await fastify.elastic.scroll({
|
||||
body: {
|
||||
scroll_id: body['_scroll_id'],
|
||||
scroll: '30s'
|
||||
}
|
||||
});
|
||||
responseQueue.push(next_response);
|
||||
}
|
||||
const next_response = await fastify.elastic.scroll({
|
||||
body: {
|
||||
scroll_id: body['_scroll_id'],
|
||||
scroll: '30s'
|
||||
}
|
||||
});
|
||||
responseQueue.push(next_response);
|
||||
}
|
||||
}
|
||||
|
||||
async function streamPastActions(fastify: FastifyInstance, socket, data) {
|
||||
const search_body = {
|
||||
query: {bool: {must: []}},
|
||||
sort: {global_sequence: 'asc'},
|
||||
};
|
||||
const search_body = {
|
||||
query: {bool: {must: []}},
|
||||
sort: {global_sequence: 'asc'},
|
||||
};
|
||||
|
||||
await addBlockRangeOpts(data, search_body, fastify);
|
||||
await addBlockRangeOpts(data, search_body, fastify);
|
||||
|
||||
if (data.account !== '') {
|
||||
search_body.query.bool.must.push({
|
||||
bool: {
|
||||
should: [
|
||||
{term: {'notified': data.account}},
|
||||
{term: {'act.authorization.actor': data.account}},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
if (data.account !== '') {
|
||||
search_body.query.bool.must.push({
|
||||
bool: {
|
||||
should: [
|
||||
{term: {'notified': data.account}},
|
||||
{term: {'act.authorization.actor': data.account}},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (data.contract !== '*' && data.contract !== '') {
|
||||
search_body.query.bool.must.push({'term': {'act.account': data.contract}});
|
||||
}
|
||||
if (data.contract !== '*' && data.contract !== '') {
|
||||
search_body.query.bool.must.push({'term': {'act.account': data.contract}});
|
||||
}
|
||||
|
||||
if (data.action !== '*' && data.action !== '') {
|
||||
search_body.query.bool.must.push({'term': {'act.name': data.action}});
|
||||
}
|
||||
if (data.action !== '*' && data.action !== '') {
|
||||
search_body.query.bool.must.push({'term': {'act.name': data.action}});
|
||||
}
|
||||
|
||||
const onDemandFilters = [];
|
||||
if (data.filters.length > 0) {
|
||||
data.filters.forEach(f => {
|
||||
if (f.field && f.value) {
|
||||
if (f.field.startsWith('@') && !f.field.startsWith('act.data')) {
|
||||
const _q = {};
|
||||
_q[f.field] = f.value;
|
||||
search_body.query.bool.must.push({'term': _q});
|
||||
} else {
|
||||
onDemandFilters.push(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const onDemandFilters = [];
|
||||
if (data.filters.length > 0) {
|
||||
data.filters.forEach(f => {
|
||||
if (f.field && f.value) {
|
||||
if (f.field.startsWith('@') && !f.field.startsWith('act.data')) {
|
||||
const _q = {};
|
||||
_q[f.field] = f.value;
|
||||
search_body.query.bool.must.push({'term': _q});
|
||||
} else {
|
||||
onDemandFilters.push(f);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const responseQueue = [];
|
||||
let counter = 0;
|
||||
const responseQueue = [];
|
||||
let counter = 0;
|
||||
|
||||
const init_response = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
scroll: '30s',
|
||||
size: 20,
|
||||
body: search_body,
|
||||
});
|
||||
responseQueue.push(init_response);
|
||||
while (responseQueue.length) {
|
||||
const {body} = responseQueue.shift();
|
||||
const enqueuedMessages = [];
|
||||
counter += body['hits']['hits'].length;
|
||||
for (const doc of body['hits']['hits']) {
|
||||
let allow = false;
|
||||
if (onDemandFilters.length > 0) {
|
||||
allow = onDemandFilters.every(filter => {
|
||||
return checkFilter(filter, doc._source);
|
||||
});
|
||||
} else {
|
||||
allow = true;
|
||||
}
|
||||
if (allow) {
|
||||
enqueuedMessages.push(doc._source);
|
||||
}
|
||||
}
|
||||
if (socket.connected) {
|
||||
socket.emit('message', {type: 'action_trace', mode: 'history', messages: enqueuedMessages});
|
||||
} else {
|
||||
console.log('LOST CLIENT');
|
||||
break;
|
||||
}
|
||||
if (body['hits'].total.value === counter) {
|
||||
console.log(`${counter} past actions streamed to ${socket.id}`);
|
||||
break;
|
||||
}
|
||||
if (init_response.body.hits.total.value < 1000) {
|
||||
const next_response = await fastify.elastic.scroll({
|
||||
body: {scroll_id: body['_scroll_id'], scroll: '30s'}
|
||||
});
|
||||
responseQueue.push(next_response);
|
||||
} else {
|
||||
console.log('Request too large!');
|
||||
socket.emit('message', {type: 'action_trace', mode: 'history', messages: []});
|
||||
}
|
||||
}
|
||||
const init_response = await fastify.elastic.search({
|
||||
index: fastify.manager.chain + '-action-*',
|
||||
scroll: '30s',
|
||||
size: 20,
|
||||
body: search_body,
|
||||
});
|
||||
responseQueue.push(init_response);
|
||||
while (responseQueue.length) {
|
||||
const {body} = responseQueue.shift();
|
||||
const enqueuedMessages = [];
|
||||
counter += body['hits']['hits'].length;
|
||||
for (const doc of body['hits']['hits']) {
|
||||
let allow = false;
|
||||
if (onDemandFilters.length > 0) {
|
||||
allow = onDemandFilters.every(filter => {
|
||||
return checkFilter(filter, doc._source);
|
||||
});
|
||||
} else {
|
||||
allow = true;
|
||||
}
|
||||
if (allow) {
|
||||
enqueuedMessages.push(doc._source);
|
||||
}
|
||||
}
|
||||
if (socket.connected) {
|
||||
socket.emit('message', {type: 'action_trace', mode: 'history', messages: enqueuedMessages});
|
||||
} else {
|
||||
hLog('LOST CLIENT');
|
||||
break;
|
||||
}
|
||||
if (body['hits'].total.value === counter) {
|
||||
hLog(`${counter} past actions streamed to ${socket.id}`);
|
||||
break;
|
||||
}
|
||||
if (init_response.body.hits.total.value < 1000) {
|
||||
const next_response = await fastify.elastic.scroll({
|
||||
body: {scroll_id: body['_scroll_id'], scroll: '30s'}
|
||||
});
|
||||
responseQueue.push(next_response);
|
||||
} else {
|
||||
hLog('Request too large!');
|
||||
socket.emit('message', {type: 'action_trace', mode: 'history', messages: []});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SocketManager {
|
||||
|
||||
private io;
|
||||
private relay;
|
||||
relay_restored = true;
|
||||
relay_down = false;
|
||||
private readonly url;
|
||||
private readonly server: FastifyInstance;
|
||||
private io: Server;
|
||||
private relay;
|
||||
relay_restored = true;
|
||||
relay_down = false;
|
||||
private readonly url;
|
||||
private readonly server: FastifyInstance;
|
||||
|
||||
constructor(fastify: FastifyInstance, url, redisOpts) {
|
||||
this.server = fastify;
|
||||
this.url = url;
|
||||
constructor(fastify: FastifyInstance, url, redisOptions) {
|
||||
this.server = fastify;
|
||||
this.url = url;
|
||||
|
||||
this.io = sockets(fastify.server, {
|
||||
transports: ['websocket', 'polling'],
|
||||
});
|
||||
this.io = new Server(fastify.server, {
|
||||
allowEIO3: true,
|
||||
transports: ['websocket', 'polling'],
|
||||
});
|
||||
|
||||
this.io.on('connection', (socket) => {
|
||||
const pubClient = new IORedis(redisOptions);
|
||||
const subClient = pubClient.duplicate();
|
||||
this.io.adapter(createAdapter({pubClient, subClient}));
|
||||
|
||||
if (socket.handshake.headers['x-forwarded-for']) {
|
||||
console.log(`[socket] ${socket.id} connected via ${socket.handshake.headers['x-forwarded-for']}`);
|
||||
}
|
||||
this.io.on('connection', (socket: Socket) => {
|
||||
|
||||
socket.emit('message', {
|
||||
event: 'handshake',
|
||||
chain: fastify.manager.chain,
|
||||
});
|
||||
if (socket.handshake.headers['x-forwarded-for']) {
|
||||
hLog(`[socket] ${socket.id} connected via ${socket.handshake.headers['x-forwarded-for']}`);
|
||||
}
|
||||
|
||||
if (this.relay) {
|
||||
this.relay.emit('event', {
|
||||
type: 'client_count',
|
||||
counter: Object.keys(this.io.sockets.connected).length,
|
||||
});
|
||||
}
|
||||
socket.emit('message', {
|
||||
event: 'handshake',
|
||||
chain: fastify.manager.chain,
|
||||
});
|
||||
|
||||
socket.on('delta_stream_request', async (data: StreamDeltasRequest, callback) => {
|
||||
if (typeof callback === 'function' && data) {
|
||||
try {
|
||||
if (data.start_from) {
|
||||
await streamPastDeltas(this.server, socket, data);
|
||||
}
|
||||
this.emitToRelay(data, 'delta_request', socket, callback);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (this.relay) {
|
||||
this.relay.emit('event', {
|
||||
type: 'client_count',
|
||||
counter: this.io.sockets.sockets.size,
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('action_stream_request', async (data: StreamActionsRequest, callback) => {
|
||||
if (typeof callback === 'function' && data) {
|
||||
try {
|
||||
if (data.start_from) {
|
||||
await streamPastActions(this.server, socket, data);
|
||||
}
|
||||
this.emitToRelay(data, 'action_request', socket, callback);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.on('delta_stream_request', async (data: StreamDeltasRequest, callback) => {
|
||||
if (typeof callback === 'function' && data) {
|
||||
try {
|
||||
if (data.start_from) {
|
||||
await streamPastDeltas(this.server, socket, data);
|
||||
}
|
||||
this.emitToRelay(data, 'delta_request', socket, callback);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', (reason) => {
|
||||
console.log(`[socket] ${socket.id} disconnected - ${reason}`);
|
||||
this.relay.emit('event', {
|
||||
type: 'client_disconnected',
|
||||
id: socket.id,
|
||||
reason,
|
||||
});
|
||||
});
|
||||
});
|
||||
console.log('Websocket manager loaded!');
|
||||
}
|
||||
socket.on('action_stream_request', async (data: StreamActionsRequest, callback) => {
|
||||
if (typeof callback === 'function' && data) {
|
||||
try {
|
||||
if (data.start_from) {
|
||||
await streamPastActions(this.server, socket, data);
|
||||
}
|
||||
this.emitToRelay(data, 'action_request', socket, callback);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
startRelay() {
|
||||
console.log(`starting relay - ${this.url}`);
|
||||
socket.on('disconnect', (reason) => {
|
||||
hLog(`[socket] ${socket.id} disconnected - ${reason}`);
|
||||
this.relay.emit('event', {
|
||||
type: 'client_disconnected',
|
||||
id: socket.id,
|
||||
reason,
|
||||
});
|
||||
});
|
||||
});
|
||||
hLog('Websocket manager loaded!');
|
||||
}
|
||||
|
||||
this.relay = IOClient(this.url, {path: '/router'});
|
||||
startRelay() {
|
||||
hLog(`starting relay - ${this.url}`);
|
||||
|
||||
this.relay.on('connect', () => {
|
||||
console.log('Relay Connected!');
|
||||
if (this.relay_down) {
|
||||
this.relay_restored = true;
|
||||
this.relay_down = false;
|
||||
this.io.emit('status', 'relay_restored');
|
||||
}
|
||||
});
|
||||
this.relay = io(this.url, {path: '/router'});
|
||||
|
||||
this.relay.on('disconnect', () => {
|
||||
console.log('Relay disconnected!');
|
||||
this.io.emit('status', 'relay_down');
|
||||
this.relay_down = true;
|
||||
this.relay_restored = false;
|
||||
});
|
||||
this.relay.on('connect', () => {
|
||||
hLog('Relay Connected!');
|
||||
if (this.relay_down) {
|
||||
this.relay_restored = true;
|
||||
this.relay_down = false;
|
||||
this.io.emit('status', 'relay_restored');
|
||||
}
|
||||
});
|
||||
|
||||
this.relay.on('delta', (traceData) => {
|
||||
this.emitToClient(traceData, 'delta_trace');
|
||||
});
|
||||
this.relay.on('disconnect', () => {
|
||||
hLog('Relay disconnected!');
|
||||
this.io.emit('status', 'relay_down');
|
||||
this.relay_down = true;
|
||||
this.relay_restored = false;
|
||||
});
|
||||
|
||||
this.relay.on('trace', (traceData) => {
|
||||
this.emitToClient(traceData, 'action_trace');
|
||||
});
|
||||
this.relay.on('delta', (traceData) => {
|
||||
this.emitToClient(traceData, 'delta_trace');
|
||||
});
|
||||
|
||||
// Relay LIB info to clients;
|
||||
this.relay.on('lib_update', (data) => {
|
||||
if (this.server.manager.conn.chains[this.server.manager.chain].chain_id === data.chain_id) {
|
||||
this.io.emit('lib_update', data);
|
||||
}
|
||||
});
|
||||
this.relay.on('trace', (traceData) => {
|
||||
this.emitToClient(traceData, 'action_trace');
|
||||
});
|
||||
|
||||
// Relay LIB info to clients;
|
||||
this.relay.on('fork_event', (data) => {
|
||||
console.log(data);
|
||||
if (this.server.manager.conn.chains[this.server.manager.chain].chain_id === data.chain_id) {
|
||||
this.io.emit('fork_event', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Relay LIB info to clients;
|
||||
this.relay.on('lib_update', (data) => {
|
||||
if (this.server.manager.conn.chains[this.server.manager.chain].chain_id === data.chain_id) {
|
||||
this.io.emit('lib_update', data);
|
||||
}
|
||||
});
|
||||
|
||||
emitToClient(traceData, type) {
|
||||
if (this.io.sockets.connected[traceData.client]) {
|
||||
this.io.sockets.connected[traceData.client].emit('message', {
|
||||
type: type,
|
||||
mode: 'live',
|
||||
message: traceData.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Relay LIB info to clients;
|
||||
this.relay.on('fork_event', (data) => {
|
||||
hLog(data);
|
||||
if (this.server.manager.conn.chains[this.server.manager.chain].chain_id === data.chain_id) {
|
||||
this.io.emit('fork_event', data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
emitToRelay(data, type, socket, callback) {
|
||||
if (this.relay.connected) {
|
||||
this.relay.emit('event', {
|
||||
type: type,
|
||||
client_socket: socket.id,
|
||||
request: data,
|
||||
}, (response) => {
|
||||
callback(response);
|
||||
});
|
||||
} else {
|
||||
callback('STREAMING_OFFLINE');
|
||||
}
|
||||
}
|
||||
emitToClient(traceData, type) {
|
||||
if (this.io.sockets.sockets.has(traceData.client)) {
|
||||
this.io.sockets.sockets.get(traceData.client).emit('message', {
|
||||
type: type,
|
||||
mode: 'live',
|
||||
message: traceData.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
emitToRelay(data, type, socket, callback) {
|
||||
if (this.relay.connected) {
|
||||
this.relay.emit('event', {
|
||||
type: type,
|
||||
client_socket: socket.id,
|
||||
request: data,
|
||||
}, (response) => {
|
||||
callback(response);
|
||||
});
|
||||
} else {
|
||||
callback('STREAMING_OFFLINE');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
git clone https://github.com/eosrio/hyperion-history-api.git || exit
|
||||
cd hyperion-history-api || exit
|
||||
bash ./install_env.sh
|
||||
+44
-22
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"api": {
|
||||
"enabled": true,
|
||||
"pm2_scaling": 1,
|
||||
"node_max_old_space_size": 1024,
|
||||
"chain_name": "EXAMPLE Chain",
|
||||
"server_addr": "127.0.0.1",
|
||||
"server_port": 7000,
|
||||
@@ -19,19 +22,48 @@
|
||||
"get_trx_actions": 200
|
||||
},
|
||||
"access_log": false,
|
||||
"enable_explorer": false,
|
||||
"chain_api_error_log": false,
|
||||
"custom_core_token": "",
|
||||
"enable_export_action": false,
|
||||
"disable_rate_limit": false,
|
||||
"rate_limit_rpm": 1000,
|
||||
"rate_limit_allow": [],
|
||||
"disable_tx_cache": false,
|
||||
"tx_cache_expiration_sec": 3600
|
||||
"tx_cache_expiration_sec": 3600,
|
||||
"v1_chain_cache": [
|
||||
{
|
||||
"path": "get_block",
|
||||
"ttl": 3000
|
||||
},
|
||||
{
|
||||
"path": "get_info",
|
||||
"ttl": 500
|
||||
}
|
||||
]
|
||||
},
|
||||
"indexer": {
|
||||
"enabled": true,
|
||||
"node_max_old_space_size": 4096,
|
||||
"start_on": 0,
|
||||
"stop_on": 0,
|
||||
"rewrite": false,
|
||||
"purge_queues": false,
|
||||
"live_reader": false,
|
||||
"live_only_mode": false,
|
||||
"abi_scan_mode": true,
|
||||
"fetch_block": true,
|
||||
"fetch_traces": true,
|
||||
"disable_reading": false,
|
||||
"disable_indexing": false,
|
||||
"process_deltas": true,
|
||||
"disable_delta_rm": true
|
||||
},
|
||||
"settings": {
|
||||
"preview": false,
|
||||
"chain": "eos",
|
||||
"eosio_alias": "eosio",
|
||||
"parser": "1.8",
|
||||
"auto_stop": 300,
|
||||
"parser": "2.1",
|
||||
"auto_stop": 0,
|
||||
"index_version": "v1",
|
||||
"debug": false,
|
||||
"bp_logs": false,
|
||||
@@ -39,12 +71,14 @@
|
||||
"ipc_debug_rate": 60000,
|
||||
"allow_custom_abi": false,
|
||||
"rate_monitoring": true,
|
||||
"max_ws_payload_kb": 256,
|
||||
"max_ws_payload_mb": 256,
|
||||
"ds_profiling": false,
|
||||
"auto_mode_switch": false,
|
||||
"hot_warm_policy": false,
|
||||
"custom_policy": "",
|
||||
"bypass_index_map": false
|
||||
"bypass_index_map": false,
|
||||
"index_partition_size": 10000000,
|
||||
"es_replicas": 0
|
||||
},
|
||||
"blacklists": {
|
||||
"actions": [],
|
||||
@@ -63,29 +97,16 @@
|
||||
"ds_pool_size": 1,
|
||||
"indexing_queues": 1,
|
||||
"ad_idx_queues": 1,
|
||||
"dyn_idx_queues": 1,
|
||||
"max_autoscale": 4,
|
||||
"batch_size": 5000,
|
||||
"resume_trigger": 5000,
|
||||
"auto_scale_trigger": 20000,
|
||||
"block_queue_limit": 10000,
|
||||
"max_queue_limit": 100000,
|
||||
"routing_mode": "heatmap",
|
||||
"routing_mode": "round_robin",
|
||||
"polling_interval": 10000
|
||||
},
|
||||
"indexer": {
|
||||
"start_on": 0,
|
||||
"stop_on": 0,
|
||||
"rewrite": false,
|
||||
"purge_queues": false,
|
||||
"live_reader": false,
|
||||
"live_only_mode": false,
|
||||
"abi_scan_mode": true,
|
||||
"fetch_block": true,
|
||||
"fetch_traces": true,
|
||||
"disable_reading": false,
|
||||
"disable_indexing": false,
|
||||
"process_deltas": true
|
||||
},
|
||||
"features": {
|
||||
"streaming": {
|
||||
"enable": false,
|
||||
@@ -109,5 +130,6 @@
|
||||
"read": 50,
|
||||
"block": 100,
|
||||
"index": 500
|
||||
}
|
||||
},
|
||||
"plugins": {}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"amqp": {
|
||||
"host": "127.0.0.1:5672",
|
||||
"api": "127.0.0.1:15672",
|
||||
"user": "username",
|
||||
"pass": "password",
|
||||
"vhost": "hyperion"
|
||||
},
|
||||
"elasticsearch": {
|
||||
"host": "127.0.0.1:9200",
|
||||
"ingest_nodes": [
|
||||
"127.0.0.1:9200"
|
||||
],
|
||||
"user": "elastic",
|
||||
"pass": "password"
|
||||
},
|
||||
"redis": {
|
||||
"host": "127.0.0.1",
|
||||
"port": "6379"
|
||||
},
|
||||
"chains": {
|
||||
"eos": {
|
||||
"name": "EOS Mainnet",
|
||||
"chain_id": "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906",
|
||||
"http": "http://127.0.0.1:8888",
|
||||
"ship": "ws://127.0.0.1:8080",
|
||||
"WS_ROUTER_PORT": 7001,
|
||||
"WS_ROUTER_HOST": "127.0.0.1"
|
||||
},
|
||||
"eos2": {
|
||||
"name": "EOS Mainnet",
|
||||
"chain_id": "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906",
|
||||
"http": "http://127.0.0.1:8888",
|
||||
"ship": "ws://127.0.0.1:8080",
|
||||
"WS_ROUTER_PORT": 7001,
|
||||
"WS_ROUTER_HOST": "127.0.0.1"
|
||||
},
|
||||
"demo": {
|
||||
"name": "Demo Chain",
|
||||
"chain_id": "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906",
|
||||
"http": "http://api.eosrio.io",
|
||||
"ship": "ws://200.201.191.228:58013",
|
||||
"WS_ROUTER_PORT": 57201,
|
||||
"WS_ROUTER_HOST": "192.168.0.1"
|
||||
},
|
||||
"demo2": {
|
||||
"name": "Demo Chain",
|
||||
"chain_id": "aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906",
|
||||
"http": "http://api.eosrio.io",
|
||||
"ship": "ws://200.201.191.228:58013",
|
||||
"WS_ROUTER_PORT": 57201,
|
||||
"WS_ROUTER_HOST": "192.168.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -9,8 +9,8 @@ export async function createConnection(config): Promise<Connection> {
|
||||
debugLog("[AMQP] connection established");
|
||||
return conn;
|
||||
} catch (e) {
|
||||
console.log("[AMQP] failed to connect!");
|
||||
console.log(e.message);
|
||||
hLog("[AMQP] failed to connect!");
|
||||
hLog(e.message);
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
return await createConnection(config);
|
||||
}
|
||||
@@ -34,8 +34,8 @@ async function createChannels(connection) {
|
||||
const confirmChannel = await connection.createConfirmChannel();
|
||||
return [channel, confirmChannel];
|
||||
} catch (e) {
|
||||
console.log("[AMQP] failed to create channels");
|
||||
console.error(e);
|
||||
hLog("[AMQP] failed to create channels");
|
||||
hLog(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ export async function amqpConnect(onReconnect, config, onClose) {
|
||||
export async function checkQueueSize(q_name, config) {
|
||||
try {
|
||||
const v = encodeURIComponent(config.vhost);
|
||||
const apiUrl = `http://${config.api}/api/queues/${v}/${encodeURIComponent(q_name)}`;
|
||||
const apiUrl = `${config.protocol}://${config.api}/api/queues/${v}/${encodeURIComponent(q_name)}`;
|
||||
const opts = {
|
||||
username: config.user,
|
||||
password: config.pass
|
||||
@@ -78,13 +78,13 @@ export async function checkQueueSize(q_name, config) {
|
||||
const data = await got.get(apiUrl, opts).json() as any;
|
||||
return data.messages;
|
||||
} catch (e) {
|
||||
hLog('[WARNING] Checking queue size failed, HTTP API is not ready!');
|
||||
if (e instanceof HTTPError) {
|
||||
if(e.response.body) {
|
||||
hLog(`[WARNING] Checking queue size failed! - ${e.message}`);
|
||||
if (e.response && e.response.body) {
|
||||
if (e instanceof HTTPError) {
|
||||
hLog(e.response.body);
|
||||
} else {
|
||||
hLog(JSON.stringify(e.response.body, null, 2));
|
||||
}
|
||||
} else {
|
||||
hLog(JSON.stringify(e, null, 2));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+148
-134
@@ -1,5 +1,5 @@
|
||||
import {ConfigurationModule} from "../modules/config";
|
||||
import {JsonRpc} from "eosjs/dist";
|
||||
import {JsonRpc} from "eosjs";
|
||||
import got from "got";
|
||||
import {Client} from '@elastic/elasticsearch'
|
||||
import {HyperionConnections} from "../interfaces/hyperionConnections";
|
||||
@@ -12,153 +12,167 @@ import {hLog} from "../helpers/common_functions";
|
||||
|
||||
export class ConnectionManager {
|
||||
|
||||
config: HyperionConfig;
|
||||
conn: HyperionConnections;
|
||||
config: HyperionConfig;
|
||||
conn: HyperionConnections;
|
||||
|
||||
chain: string;
|
||||
last_commit_hash: string;
|
||||
current_version: string;
|
||||
chain: string;
|
||||
last_commit_hash: string;
|
||||
current_version: string;
|
||||
|
||||
private esIngestClients: Client[];
|
||||
private esIngestClient: Client;
|
||||
private readonly esIngestClients: Client[];
|
||||
private esIngestClient: Client;
|
||||
|
||||
constructor(private cm: ConfigurationModule) {
|
||||
this.config = cm.config;
|
||||
this.conn = cm.connections;
|
||||
this.chain = this.config.settings.chain;
|
||||
this.esIngestClients = [];
|
||||
this.prepareESClient();
|
||||
this.prepareIngestClients();
|
||||
}
|
||||
constructor(private cm: ConfigurationModule) {
|
||||
this.config = cm.config;
|
||||
this.conn = cm.connections;
|
||||
if (!this.conn.amqp.protocol) {
|
||||
this.conn.amqp.protocol = 'http';
|
||||
}
|
||||
this.chain = this.config.settings.chain;
|
||||
this.esIngestClients = [];
|
||||
this.prepareESClient();
|
||||
this.prepareIngestClients();
|
||||
}
|
||||
|
||||
get nodeosJsonRPC() {
|
||||
// @ts-ignore
|
||||
return new JsonRpc(this.conn.chains[this.chain].http, {fetch});
|
||||
}
|
||||
get nodeosJsonRPC() {
|
||||
// @ts-ignore
|
||||
return new JsonRpc(this.conn.chains[this.chain].http, {fetch});
|
||||
}
|
||||
|
||||
async purgeQueues() {
|
||||
hLog(`Purging all ${this.chain} queues!`);
|
||||
const apiUrl = `http://${this.conn.amqp.api}`;
|
||||
const getAllQueuesFromVHost = apiUrl + `/api/queues/%2F${this.conn.amqp.vhost}`;
|
||||
const opts = {
|
||||
username: this.conn.amqp.user,
|
||||
password: this.conn.amqp.pass
|
||||
};
|
||||
let result;
|
||||
try {
|
||||
const data = await got(getAllQueuesFromVHost, opts);
|
||||
if (data) {
|
||||
result = JSON.parse(data.body);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.error('failed to connect to rabbitmq http api');
|
||||
process.exit(1);
|
||||
}
|
||||
if (result) {
|
||||
for (const queue of result) {
|
||||
if (queue.name.startsWith(this.chain + ":")) {
|
||||
const msg_count = parseInt(queue.messages);
|
||||
if (msg_count > 0) {
|
||||
try {
|
||||
await got.delete(apiUrl + `/api/queues/%2F${this.conn.amqp.vhost}/${queue.name}/contents`, opts);
|
||||
hLog(`${queue.messages} messages deleted on queue ${queue.name}`);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.error('failed to connect to rabbitmq http api');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async purgeQueues() {
|
||||
hLog(`Purging all ${this.chain} queues!`);
|
||||
const apiUrl = `http://${this.conn.amqp.api}`;
|
||||
const vHost = encodeURIComponent(this.conn.amqp.vhost);
|
||||
const getAllQueuesFromVHost = apiUrl + `/api/queues/${vHost}`;
|
||||
const opts = {
|
||||
username: this.conn.amqp.user,
|
||||
password: this.conn.amqp.pass
|
||||
};
|
||||
let result;
|
||||
try {
|
||||
const data = await got(getAllQueuesFromVHost, opts);
|
||||
if (data) {
|
||||
result = JSON.parse(data.body);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.error('failed to connect to rabbitmq http api');
|
||||
process.exit(1);
|
||||
}
|
||||
if (result) {
|
||||
for (const queue of result) {
|
||||
if (queue.name.startsWith(this.chain + ":")) {
|
||||
const msg_count = parseInt(queue.messages);
|
||||
if (msg_count > 0) {
|
||||
try {
|
||||
await got.delete(apiUrl + `/api/queues/${vHost}/${queue.name}/contents`, opts);
|
||||
hLog(`${queue.messages} messages deleted on queue ${queue.name}`);
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.error('failed to connect to rabbitmq http api');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareESClient() {
|
||||
let es_url;
|
||||
const _es = this.conn.elasticsearch;
|
||||
if (!_es.protocol) {
|
||||
_es.protocol = 'http';
|
||||
}
|
||||
if (_es.user !== '') {
|
||||
es_url = `${_es.protocol}://${_es.user}:${_es.pass}@${_es.host}`;
|
||||
} else {
|
||||
es_url = `${_es.protocol}://${_es.host}`
|
||||
}
|
||||
this.esIngestClient = new Client({
|
||||
node: es_url,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
});
|
||||
}
|
||||
prepareESClient() {
|
||||
let es_url;
|
||||
const _es = this.conn.elasticsearch;
|
||||
if (!_es.protocol) {
|
||||
_es.protocol = 'http';
|
||||
}
|
||||
if (_es.user !== '') {
|
||||
es_url = `${_es.protocol}://${_es.user}:${_es.pass}@${_es.host}`;
|
||||
} else {
|
||||
es_url = `${_es.protocol}://${_es.host}`
|
||||
}
|
||||
this.esIngestClient = new Client({
|
||||
node: es_url,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get elasticsearchClient() {
|
||||
return this.esIngestClient;
|
||||
}
|
||||
get elasticsearchClient() {
|
||||
return this.esIngestClient;
|
||||
}
|
||||
|
||||
prepareIngestClients() {
|
||||
const _es = this.conn.elasticsearch;
|
||||
if (!_es.protocol) {
|
||||
_es.protocol = 'http';
|
||||
}
|
||||
if (_es.ingest_nodes) {
|
||||
if (_es.ingest_nodes.length > 0) {
|
||||
for (const node of _es.ingest_nodes) {
|
||||
let es_url;
|
||||
if (_es.user !== '') {
|
||||
es_url = `${_es.protocol}://${_es.user}:${_es.pass}@${node}`;
|
||||
} else {
|
||||
es_url = `${_es.protocol}://${node}`
|
||||
}
|
||||
this.esIngestClients.push(new Client({
|
||||
node: es_url,
|
||||
pingTimeout: 100,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
prepareIngestClients() {
|
||||
const _es = this.conn.elasticsearch;
|
||||
if (!_es.protocol) {
|
||||
_es.protocol = 'http';
|
||||
}
|
||||
if (_es.ingest_nodes) {
|
||||
if (_es.ingest_nodes.length > 0) {
|
||||
for (const node of _es.ingest_nodes) {
|
||||
let es_url;
|
||||
if (_es.user !== '') {
|
||||
es_url = `${_es.protocol}://${_es.user}:${_es.pass}@${node}`;
|
||||
} else {
|
||||
es_url = `${_es.protocol}://${node}`
|
||||
}
|
||||
this.esIngestClients.push(new Client({
|
||||
node: es_url,
|
||||
pingTimeout: 100,
|
||||
ssl: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get ingestClients() {
|
||||
if (this.esIngestClients.length > 0) {
|
||||
return this.esIngestClients;
|
||||
} else {
|
||||
return [this.esIngestClient];
|
||||
}
|
||||
}
|
||||
get ingestClients() {
|
||||
if (this.esIngestClients.length > 0) {
|
||||
return this.esIngestClients;
|
||||
} else {
|
||||
return [this.esIngestClient];
|
||||
}
|
||||
}
|
||||
|
||||
async createAMQPChannels(onReconnect, onClose) {
|
||||
return await amqpConnect(onReconnect, this.conn.amqp, onClose);
|
||||
}
|
||||
async createAMQPChannels(onReconnect, onClose) {
|
||||
return await amqpConnect(onReconnect, this.conn.amqp, onClose);
|
||||
}
|
||||
|
||||
async checkQueueSize(queue) {
|
||||
return await checkQueueSize(queue, this.conn.amqp);
|
||||
}
|
||||
async checkQueueSize(queue) {
|
||||
return await checkQueueSize(queue, this.conn.amqp);
|
||||
}
|
||||
|
||||
get shipClient(): StateHistorySocket {
|
||||
return new StateHistorySocket(this.conn.chains[this.config.settings.chain]['ship'], this.config.settings.max_ws_payload_kb);
|
||||
}
|
||||
get shipClient(): StateHistorySocket {
|
||||
return new StateHistorySocket(this.conn.chains[this.config.settings.chain]['ship'], this.config.settings.max_ws_payload_mb);
|
||||
}
|
||||
|
||||
get ampqUrl() {
|
||||
return getAmpqUrl(this.conn.amqp);
|
||||
}
|
||||
get ampqUrl() {
|
||||
return getAmpqUrl(this.conn.amqp);
|
||||
}
|
||||
|
||||
calculateServerHash() {
|
||||
exec('git rev-parse HEAD', (err, stdout) => {
|
||||
console.log('Last commit hash on this branch is:', stdout);
|
||||
this.last_commit_hash = stdout.trim();
|
||||
});
|
||||
}
|
||||
calculateServerHash() {
|
||||
exec('git rev-parse HEAD', (err, stdout) => {
|
||||
if (err) {
|
||||
// hLog(`\n ${err.message}\n >>> Failed to check last commit hash. Version hash will be "custom"`);
|
||||
hLog(`Failed to check last commit hash. Version hash will be "custom"`);
|
||||
this.last_commit_hash = 'custom';
|
||||
} else {
|
||||
hLog('Last commit hash on this branch is:', stdout);
|
||||
this.last_commit_hash = stdout.trim();
|
||||
}
|
||||
this.getHyperionVersion();
|
||||
});
|
||||
}
|
||||
|
||||
getServerHash() {
|
||||
return this.last_commit_hash;
|
||||
}
|
||||
getServerHash() {
|
||||
return this.last_commit_hash;
|
||||
}
|
||||
|
||||
getHyperionVersion() {
|
||||
this.current_version = require('../package.json').version
|
||||
}
|
||||
getHyperionVersion() {
|
||||
this.current_version = require('../package.json').version;
|
||||
if (this.last_commit_hash === 'custom') {
|
||||
this.current_version = this.current_version + '-dirty';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,49 @@
|
||||
import {debugLog, hLog} from "../helpers/common_functions";
|
||||
|
||||
const WebSocket = require('ws');
|
||||
import WebSocket from 'ws';
|
||||
|
||||
export class StateHistorySocket {
|
||||
private ws;
|
||||
private readonly shipUrl;
|
||||
private readonly max_payload_kb;
|
||||
private ws;
|
||||
private readonly shipUrl;
|
||||
private readonly max_payload_mb;
|
||||
|
||||
constructor(ship_url, max_payload_kb) {
|
||||
this.shipUrl = ship_url;
|
||||
if (max_payload_kb) {
|
||||
this.max_payload_kb = max_payload_kb;
|
||||
} else {
|
||||
this.max_payload_kb = 256;
|
||||
}
|
||||
}
|
||||
constructor(ship_url, max_payload_mb) {
|
||||
this.shipUrl = ship_url;
|
||||
if (max_payload_mb) {
|
||||
this.max_payload_mb = max_payload_mb;
|
||||
} else {
|
||||
this.max_payload_mb = 256;
|
||||
}
|
||||
}
|
||||
|
||||
connect(onMessage, onDisconnect, onError, onConnected) {
|
||||
debugLog(`Connecting to ${this.shipUrl}...`);
|
||||
this.ws = new WebSocket(this.shipUrl, null, {
|
||||
perMessageDeflate: false,
|
||||
maxPayload: this.max_payload_kb * 1024 * 1024,
|
||||
});
|
||||
this.ws.on('open', () => {
|
||||
hLog('Websocket connected!');
|
||||
if (onConnected) {
|
||||
onConnected();
|
||||
}
|
||||
});
|
||||
this.ws.on('message', onMessage);
|
||||
this.ws.on('close', () => {
|
||||
hLog('Websocket disconnected!');
|
||||
onDisconnect();
|
||||
});
|
||||
this.ws.on('error', (err) => {
|
||||
hLog(`${this.shipUrl} :: ${err.message}`);
|
||||
});
|
||||
}
|
||||
connect(onMessage, onDisconnect, onError, onConnected) {
|
||||
|
||||
close() {
|
||||
this.ws.close();
|
||||
}
|
||||
debugLog(`Connecting to ${this.shipUrl}...`);
|
||||
this.ws = new WebSocket(this.shipUrl, {
|
||||
perMessageDeflate: false,
|
||||
maxPayload: this.max_payload_mb * 1024 * 1024,
|
||||
});
|
||||
this.ws.on('open', () => {
|
||||
hLog('Websocket connected!');
|
||||
if (onConnected) {
|
||||
onConnected();
|
||||
}
|
||||
});
|
||||
this.ws.on('message', (data) => onMessage(data));
|
||||
this.ws.on('close', () => {
|
||||
hLog('Websocket disconnected!');
|
||||
onDisconnect();
|
||||
});
|
||||
this.ws.on('error', (err) => {
|
||||
hLog(`${this.shipUrl} :: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
send(payload) {
|
||||
this.ws.send(payload);
|
||||
}
|
||||
close() {
|
||||
this.ws.close();
|
||||
}
|
||||
|
||||
send(payload) {
|
||||
this.ws.send(payload);
|
||||
}
|
||||
}
|
||||
|
||||
+232
-228
@@ -1,260 +1,264 @@
|
||||
const AbiDefinitions = {
|
||||
version: "eosio::abi/1.1",
|
||||
structs: [
|
||||
version: 'eosio::abi/1.1',
|
||||
structs: [
|
||||
{
|
||||
name: 'extensions_entry',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: "extensions_entry",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "tag",
|
||||
type: "uint16"
|
||||
},
|
||||
{
|
||||
name: "value",
|
||||
type: "bytes"
|
||||
}
|
||||
]
|
||||
name: 'tag',
|
||||
type: 'uint16',
|
||||
},
|
||||
{
|
||||
name: "type_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "new_type_name",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'value',
|
||||
type: 'bytes',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'type_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'new_type_name',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "field_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'field_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "struct_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "base",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "fields",
|
||||
type: "field_def[]"
|
||||
}
|
||||
]
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'struct_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "action_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "name"
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "ricardian_contract",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'base',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "table_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "name"
|
||||
},
|
||||
{
|
||||
name: "index_type",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "key_names",
|
||||
type: "string[]"
|
||||
},
|
||||
{
|
||||
name: "key_types",
|
||||
type: "string[]"
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'fields',
|
||||
type: 'field_def[]',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'action_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'name',
|
||||
},
|
||||
{
|
||||
name: "clause_pair",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "id",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "body",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "error_message",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "error_code",
|
||||
type: "uint64"
|
||||
},
|
||||
{
|
||||
name: "error_msg",
|
||||
type: "string"
|
||||
}
|
||||
]
|
||||
name: 'ricardian_contract',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'table_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'name',
|
||||
},
|
||||
{
|
||||
name: "variant_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "types",
|
||||
type: "string[]"
|
||||
}
|
||||
]
|
||||
name: 'index_type',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: "abi_def",
|
||||
base: "",
|
||||
fields: [
|
||||
{
|
||||
name: "version",
|
||||
type: "string"
|
||||
},
|
||||
{
|
||||
name: "types",
|
||||
type: "type_def[]"
|
||||
},
|
||||
{
|
||||
name: "structs",
|
||||
type: "struct_def[]"
|
||||
},
|
||||
{
|
||||
name: "actions",
|
||||
type: "action_def[]"
|
||||
},
|
||||
{
|
||||
name: "tables",
|
||||
type: "table_def[]"
|
||||
},
|
||||
{
|
||||
name: "ricardian_clauses",
|
||||
type: "clause_pair[]"
|
||||
},
|
||||
{
|
||||
name: "error_messages",
|
||||
type: "error_message[]"
|
||||
},
|
||||
{
|
||||
name: "abi_extensions",
|
||||
type: "extensions_entry[]"
|
||||
},
|
||||
{
|
||||
name: "variants",
|
||||
type: "variant_def[]$"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
name: 'key_names',
|
||||
type: 'string[]',
|
||||
},
|
||||
{
|
||||
name: 'key_types',
|
||||
type: 'string[]',
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'clause_pair',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'body',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'error_message',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'error_code',
|
||||
type: 'uint64',
|
||||
},
|
||||
{
|
||||
name: 'error_msg',
|
||||
type: 'string',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'variant_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'types',
|
||||
type: 'string[]',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'abi_def',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'version',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
name: 'types',
|
||||
type: 'type_def[]',
|
||||
},
|
||||
{
|
||||
name: 'structs',
|
||||
type: 'struct_def[]',
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
type: 'action_def[]',
|
||||
},
|
||||
{
|
||||
name: 'tables',
|
||||
type: 'table_def[]',
|
||||
},
|
||||
{
|
||||
name: 'ricardian_clauses',
|
||||
type: 'clause_pair[]',
|
||||
},
|
||||
{
|
||||
name: 'error_messages',
|
||||
type: 'error_message[]',
|
||||
},
|
||||
{
|
||||
name: 'abi_extensions',
|
||||
type: 'extensions_entry[]',
|
||||
},
|
||||
{
|
||||
name: 'variants',
|
||||
type: 'variant_def[]$',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const RexAbi = {
|
||||
version: "eosio::abi/1.1",
|
||||
types: [],
|
||||
structs: [
|
||||
version: 'eosio::abi/1.1',
|
||||
types: [],
|
||||
structs: [
|
||||
{
|
||||
name: 'buyresult',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: "buyresult",
|
||||
base: "",
|
||||
fields: [{
|
||||
name: "rex_received",
|
||||
type: "asset"
|
||||
}
|
||||
]
|
||||
name: 'rex_received',
|
||||
type: 'asset',
|
||||
},
|
||||
],
|
||||
}, {
|
||||
name: 'orderresult',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: 'owner',
|
||||
type: 'name',
|
||||
}, {
|
||||
name: "orderresult",
|
||||
base: "",
|
||||
fields: [{
|
||||
name: "owner",
|
||||
type: "name"
|
||||
}, {
|
||||
name: "proceeds",
|
||||
type: "asset"
|
||||
}
|
||||
]
|
||||
}, {
|
||||
name: "rentresult",
|
||||
base: "",
|
||||
fields: [{
|
||||
name: "rented_tokens",
|
||||
type: "asset"
|
||||
}
|
||||
]
|
||||
name: 'proceeds',
|
||||
type: 'asset',
|
||||
},
|
||||
],
|
||||
}, {
|
||||
name: 'rentresult',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: "sellresult",
|
||||
base: "",
|
||||
fields: [{
|
||||
name: "proceeds",
|
||||
type: "asset"
|
||||
}]
|
||||
}
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
name: "buyresult",
|
||||
type: "buyresult",
|
||||
ricardian_contract: ""
|
||||
name: 'rented_tokens',
|
||||
type: 'asset',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'sellresult',
|
||||
base: '',
|
||||
fields: [
|
||||
{
|
||||
name: "orderresult",
|
||||
type: "orderresult",
|
||||
ricardian_contract: ""
|
||||
},
|
||||
{
|
||||
name: "rentresult",
|
||||
type: "rentresult",
|
||||
ricardian_contract: ""
|
||||
},
|
||||
{
|
||||
name: "sellresult",
|
||||
type: "sellresult",
|
||||
ricardian_contract: ""
|
||||
}
|
||||
]
|
||||
name: 'proceeds',
|
||||
type: 'asset',
|
||||
}],
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
name: 'buyresult',
|
||||
type: 'buyresult',
|
||||
ricardian_contract: '',
|
||||
},
|
||||
{
|
||||
name: 'orderresult',
|
||||
type: 'orderresult',
|
||||
ricardian_contract: '',
|
||||
},
|
||||
{
|
||||
name: 'rentresult',
|
||||
type: 'rentresult',
|
||||
ricardian_contract: '',
|
||||
},
|
||||
{
|
||||
name: 'sellresult',
|
||||
type: 'sellresult',
|
||||
ricardian_contract: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = {AbiDefinitions, RexAbi};
|
||||
|
||||
@@ -1,38 +1,51 @@
|
||||
function addIndexer(chainName) {
|
||||
return {
|
||||
script: './launcher.js',
|
||||
name: chainName + '-indexer',
|
||||
namespace: chainName,
|
||||
interpreter: 'node',
|
||||
interpreter_args: ['--max-old-space-size=4096', '--trace-deprecation'],
|
||||
autorestart: false,
|
||||
kill_timeout: 3600,
|
||||
watch: false,
|
||||
time: true,
|
||||
env: {
|
||||
CONFIG_JSON: 'chains/' + chainName + '.config.json',
|
||||
TRACE_LOGS: 'false',
|
||||
},
|
||||
};
|
||||
function interpreterArgs(heap) {
|
||||
const arr = ['--trace-deprecation', '--trace-warnings'];
|
||||
if (heap) {
|
||||
arr.push('--max-old-space-size=' + heap);
|
||||
} else {
|
||||
arr.push('--max-old-space-size=2048');
|
||||
}
|
||||
if (process.env.INSPECT) {
|
||||
arr.push('--inspect');
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
function addApiServer(chainName, threads) {
|
||||
return {
|
||||
script: './api/server.js',
|
||||
name: chainName + '-api',
|
||||
namespace: chainName,
|
||||
node_args: ['--trace-deprecation'],
|
||||
exec_mode: 'cluster',
|
||||
merge_logs: true,
|
||||
instances: threads,
|
||||
autorestart: true,
|
||||
exp_backoff_restart_delay: 100,
|
||||
watch: false,
|
||||
time: true,
|
||||
env: {
|
||||
CONFIG_JSON: 'chains/' + chainName + '.config.json',
|
||||
},
|
||||
};
|
||||
function addIndexer(chainName, heap) {
|
||||
return {
|
||||
script: './launcher.js',
|
||||
name: chainName + '-indexer',
|
||||
namespace: chainName,
|
||||
interpreter: 'node',
|
||||
interpreter_args: interpreterArgs(heap),
|
||||
autorestart: false,
|
||||
kill_timeout: 3600,
|
||||
watch: false,
|
||||
time: true,
|
||||
env: {
|
||||
CONFIG_JSON: 'chains/' + chainName + '.config.json',
|
||||
TRACE_LOGS: 'false',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function addApiServer(chainName, threads, heap) {
|
||||
return {
|
||||
script: './api/server.js',
|
||||
name: chainName + '-api',
|
||||
namespace: chainName,
|
||||
node_args: interpreterArgs(heap),
|
||||
exec_mode: 'cluster',
|
||||
merge_logs: true,
|
||||
instances: threads,
|
||||
autorestart: true,
|
||||
exp_backoff_restart_delay: 100,
|
||||
watch: false,
|
||||
time: true,
|
||||
env: {
|
||||
CONFIG_JSON: 'chains/' + chainName + '.config.json',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {addIndexer, addApiServer};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {IlmPutLifecycle} from "@elastic/elasticsearch/api/requestParams";
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export const ILPs: IlmPutLifecycle[] = [
|
||||
{
|
||||
policy: "hyperion-rollover",
|
||||
|
||||
+431
-428
@@ -1,9 +1,7 @@
|
||||
import {ConfigurationModule} from "../modules/config";
|
||||
|
||||
const shards = 2;
|
||||
const replicas = 0;
|
||||
const refresh = "1s";
|
||||
let defaultLifecyclePolicy = "200G";
|
||||
|
||||
export * from './index-lifecycle-policies';
|
||||
|
||||
@@ -15,507 +13,512 @@ const compression = "best_compression";
|
||||
const cm = new ConfigurationModule();
|
||||
const chain = cm.config.settings.chain;
|
||||
|
||||
if (cm.config.settings.hot_warm_policy) {
|
||||
defaultLifecyclePolicy = "hyperion-rollover";
|
||||
}
|
||||
|
||||
if(cm.config.settings.custom_policy) {
|
||||
defaultLifecyclePolicy = cm.config.settings.custom_policy;
|
||||
// update number of replicas if set to larger than 0
|
||||
let replicas = 0;
|
||||
if (cm.config.settings.es_replicas) {
|
||||
replicas = cm.config.settings.es_replicas;
|
||||
}
|
||||
|
||||
const defaultIndexSettings = {
|
||||
"index": {
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"codec": compression
|
||||
}
|
||||
"index": {
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"codec": compression
|
||||
}
|
||||
};
|
||||
|
||||
const actionSettings = {
|
||||
index: {
|
||||
lifecycle: {
|
||||
"name": defaultLifecyclePolicy,
|
||||
"rollover_alias": chain + "-action"
|
||||
},
|
||||
codec: compression,
|
||||
refresh_interval: refresh,
|
||||
number_of_shards: shards * 2,
|
||||
number_of_replicas: replicas,
|
||||
sort: {
|
||||
field: "global_sequence",
|
||||
order: "desc"
|
||||
}
|
||||
}
|
||||
index: {
|
||||
codec: compression,
|
||||
refresh_interval: refresh,
|
||||
number_of_shards: shards * 2,
|
||||
number_of_replicas: replicas,
|
||||
sort: {
|
||||
field: "global_sequence",
|
||||
order: "desc"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// actionSettings.index["lifecycle"] = {
|
||||
// "name": defaultLifecyclePolicy,
|
||||
// "rollover_alias": chain + "-action"
|
||||
// };
|
||||
|
||||
if (cm.config.settings.hot_warm_policy) {
|
||||
actionSettings["routing"] = {"allocation": {"exclude": {"data": "warm"}}};
|
||||
actionSettings["routing"] = {"allocation": {"exclude": {"data": "warm"}}};
|
||||
}
|
||||
|
||||
export const action = {
|
||||
order: 0,
|
||||
index_patterns: [
|
||||
chain + "-action-*"
|
||||
],
|
||||
settings: actionSettings,
|
||||
mappings: {
|
||||
properties: {
|
||||
"@timestamp": {"type": "date"},
|
||||
"ds_error": {"type": "boolean"},
|
||||
"global_sequence": {"type": "long"},
|
||||
"account_ram_deltas.delta": {"type": "integer"},
|
||||
"account_ram_deltas.account": {"type": "keyword"},
|
||||
"act.authorization.permission": {"enabled": false},
|
||||
"act.authorization.actor": {"type": "keyword"},
|
||||
"act.account": {"type": "keyword"},
|
||||
"act.name": {"type": "keyword"},
|
||||
"act.data": {"enabled": false},
|
||||
"block_num": {"type": "long"},
|
||||
"action_ordinal": {"type": "long"},
|
||||
"creator_action_ordinal": {"type": "long"},
|
||||
"cpu_usage_us": {"type": "integer"},
|
||||
"net_usage_words": {"type": "integer"},
|
||||
"code_sequence": {"type": "integer"},
|
||||
"abi_sequence": {"type": "integer"},
|
||||
"trx_id": {"type": "keyword"},
|
||||
"producer": {"type": "keyword"},
|
||||
"notified": {"type": "keyword"},
|
||||
"signatures": {"enabled": false},
|
||||
"inline_count": {"type": "short"},
|
||||
"max_inline": {"type": "short"},
|
||||
"inline_filtered": {"type": "boolean"},
|
||||
"receipts": {
|
||||
"properties": {
|
||||
"global_sequence": {"type": "long"},
|
||||
"recv_sequence": {"type": "long"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"auth_sequence": {
|
||||
"properties": {
|
||||
"account": {"type": "keyword"},
|
||||
"sequence": {"type": "long"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
order: 0,
|
||||
index_patterns: [
|
||||
chain + "-action-*"
|
||||
],
|
||||
settings: actionSettings,
|
||||
mappings: {
|
||||
properties: {
|
||||
"@timestamp": {"type": "date"},
|
||||
"ds_error": {"type": "boolean"},
|
||||
"global_sequence": {"type": "long"},
|
||||
"account_ram_deltas.delta": {"type": "integer"},
|
||||
"account_ram_deltas.account": {"type": "keyword"},
|
||||
"act.authorization.permission": {"enabled": false},
|
||||
"act.authorization.actor": {"type": "keyword"},
|
||||
"act.account": {"type": "keyword"},
|
||||
"act.name": {"type": "keyword"},
|
||||
"act.data": {"enabled": false},
|
||||
"block_num": {"type": "long"},
|
||||
"block_id": {"type": "keyword"},
|
||||
"action_ordinal": {"type": "long"},
|
||||
"creator_action_ordinal": {"type": "long"},
|
||||
"cpu_usage_us": {"type": "integer"},
|
||||
"net_usage_words": {"type": "integer"},
|
||||
"code_sequence": {"type": "integer"},
|
||||
"abi_sequence": {"type": "integer"},
|
||||
"trx_id": {"type": "keyword"},
|
||||
"producer": {"type": "keyword"},
|
||||
"notified": {"type": "keyword"},
|
||||
"signatures": {"enabled": false},
|
||||
"inline_count": {"type": "short"},
|
||||
"max_inline": {"type": "short"},
|
||||
"inline_filtered": {"type": "boolean"},
|
||||
"receipts": {
|
||||
"properties": {
|
||||
"global_sequence": {"type": "long"},
|
||||
"recv_sequence": {"type": "long"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"auth_sequence": {
|
||||
"properties": {
|
||||
"account": {"type": "keyword"},
|
||||
"sequence": {"type": "long"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::newaccount
|
||||
"@newaccount": {
|
||||
"properties": {
|
||||
"active": {"type": "object"},
|
||||
"owner": {"type": "object"},
|
||||
"newact": {"type": "keyword"}
|
||||
}
|
||||
},
|
||||
// eosio::newaccount
|
||||
"@newaccount": {
|
||||
"properties": {
|
||||
"active": {"type": "object"},
|
||||
"owner": {"type": "object"},
|
||||
"newact": {"type": "keyword"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::updateauth
|
||||
"@updateauth": {
|
||||
"properties": {
|
||||
"permission": {"type": "keyword"},
|
||||
"parent": {"type": "keyword"},
|
||||
"auth": {"type": "object"}
|
||||
}
|
||||
},
|
||||
// eosio::updateauth
|
||||
"@updateauth": {
|
||||
"properties": {
|
||||
"permission": {"type": "keyword"},
|
||||
"parent": {"type": "keyword"},
|
||||
"auth": {"type": "object"}
|
||||
}
|
||||
},
|
||||
|
||||
// *::transfer
|
||||
"@transfer": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"to": {"type": "keyword"},
|
||||
"amount": {"type": "float"},
|
||||
"symbol": {"type": "keyword"},
|
||||
"memo": {"type": "text"}
|
||||
}
|
||||
},
|
||||
// *::transfer
|
||||
"@transfer": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"to": {"type": "keyword"},
|
||||
"amount": {"type": "float"},
|
||||
"symbol": {"type": "keyword"},
|
||||
"memo": {"type": "text"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::unstaketorex
|
||||
"@unstaketorex": {
|
||||
"properties": {
|
||||
"owner": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
// eosio::unstaketorex
|
||||
"@unstaketorex": {
|
||||
"properties": {
|
||||
"owner": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::buyrex
|
||||
"@buyrex": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
// eosio::buyrex
|
||||
"@buyrex": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::buyram
|
||||
"@buyram": {
|
||||
"properties": {
|
||||
"payer": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"quant": {"type": "float"}
|
||||
}
|
||||
},
|
||||
// eosio::buyram
|
||||
"@buyram": {
|
||||
"properties": {
|
||||
"payer": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"quant": {"type": "float"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::buyrambytes
|
||||
"@buyrambytes": {
|
||||
"properties": {
|
||||
"payer": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"bytes": {"type": "long"}
|
||||
}
|
||||
},
|
||||
// eosio::buyrambytes
|
||||
"@buyrambytes": {
|
||||
"properties": {
|
||||
"payer": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"bytes": {"type": "long"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::delegatebw
|
||||
"@delegatebw": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"stake_cpu_quantity": {"type": "float"},
|
||||
"stake_net_quantity": {"type": "float"},
|
||||
"transfer": {"type": "boolean"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
// eosio::delegatebw
|
||||
"@delegatebw": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"stake_cpu_quantity": {"type": "float"},
|
||||
"stake_net_quantity": {"type": "float"},
|
||||
"transfer": {"type": "boolean"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
},
|
||||
|
||||
// eosio::undelegatebw
|
||||
"@undelegatebw": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"unstake_cpu_quantity": {"type": "float"},
|
||||
"unstake_net_quantity": {"type": "float"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// eosio::undelegatebw
|
||||
"@undelegatebw": {
|
||||
"properties": {
|
||||
"from": {"type": "keyword"},
|
||||
"receiver": {"type": "keyword"},
|
||||
"unstake_cpu_quantity": {"type": "float"},
|
||||
"unstake_net_quantity": {"type": "float"},
|
||||
"amount": {"type": "float"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deltaSettings = {
|
||||
"index": {
|
||||
"lifecycle": {
|
||||
"name": defaultLifecyclePolicy,
|
||||
"rollover_alias": chain + "-delta"
|
||||
},
|
||||
"codec": compression,
|
||||
"number_of_shards": shards * 2,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": ["block_num", "scope", "primary_key"],
|
||||
"sort.order": ["desc", "asc", "asc"]
|
||||
}
|
||||
index: {
|
||||
codec: compression,
|
||||
number_of_shards: shards * 2,
|
||||
refresh_interval: refresh,
|
||||
number_of_replicas: replicas,
|
||||
sort: {
|
||||
field: ["block_num", "scope", "primary_key"],
|
||||
order: ["desc", "asc", "asc"]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// deltaSettings.index["lifecycle"] = {
|
||||
// "name": defaultLifecyclePolicy,
|
||||
// "rollover_alias": chain + "-delta"
|
||||
// };
|
||||
|
||||
if (cm.config.settings.hot_warm_policy) {
|
||||
deltaSettings["routing"] = {"allocation": {"exclude": {"data": "warm"}}};
|
||||
deltaSettings["routing"] = {"allocation": {"exclude": {"data": "warm"}}};
|
||||
}
|
||||
|
||||
export const delta = {
|
||||
"index_patterns": [chain + "-delta-*"],
|
||||
"settings": deltaSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"index_patterns": [chain + "-delta-*"],
|
||||
"settings": deltaSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
|
||||
// base fields
|
||||
"@timestamp": {"type": "date"},
|
||||
"ds_error": {"type": "boolean"},
|
||||
"block_id": {"type": "keyword"},
|
||||
"block_num": {"type": "long"},
|
||||
"deleted_at": {"type": "long"},
|
||||
"code": {"type": "keyword"},
|
||||
"scope": {"type": "keyword"},
|
||||
"table": {"type": "keyword"},
|
||||
"payer": {"type": "keyword"},
|
||||
"primary_key": {"type": "keyword"},
|
||||
"data": {"enabled": false},
|
||||
"value": {"enabled": false},
|
||||
// base fields
|
||||
"@timestamp": {"type": "date"},
|
||||
"present": {"type": "byte"},
|
||||
"ds_error": {"type": "boolean"},
|
||||
"block_id": {"type": "keyword"},
|
||||
"block_num": {"type": "long"},
|
||||
"deleted_at": {"type": "long"},
|
||||
"code": {"type": "keyword"},
|
||||
"scope": {"type": "keyword"},
|
||||
"table": {"type": "keyword"},
|
||||
"payer": {"type": "keyword"},
|
||||
"primary_key": {"type": "keyword"},
|
||||
"data": {"enabled": false},
|
||||
"value": {"enabled": false},
|
||||
|
||||
// eosio.msig::approvals
|
||||
"@approvals.proposal_name": {"type": "keyword"},
|
||||
"@approvals.provided_approvals": {"type": "object"},
|
||||
"@approvals.requested_approvals": {"type": "object"},
|
||||
// eosio.msig::approvals
|
||||
"@approvals.proposal_name": {"type": "keyword"},
|
||||
"@approvals.provided_approvals": {"type": "object"},
|
||||
"@approvals.requested_approvals": {"type": "object"},
|
||||
|
||||
// eosio.msig::proposal
|
||||
"@proposal.proposal_name": {"type": "keyword"},
|
||||
"@proposal.transaction": {"enabled": false},
|
||||
// eosio.msig::proposal
|
||||
"@proposal.proposal_name": {"type": "keyword"},
|
||||
"@proposal.transaction": {"enabled": false},
|
||||
|
||||
// *::accounts
|
||||
"@accounts.amount": {"type": "float"},
|
||||
"@accounts.symbol": {"type": "keyword"},
|
||||
// *::accounts
|
||||
"@accounts.amount": {"type": "float"},
|
||||
"@accounts.symbol": {"type": "keyword"},
|
||||
|
||||
// eosio::voters
|
||||
"@voters.is_proxy": {"type": "boolean"},
|
||||
"@voters.producers": {"type": "keyword"},
|
||||
"@voters.last_vote_weight": {"type": "double"},
|
||||
"@voters.proxied_vote_weight": {"type": "double"},
|
||||
"@voters.staked": {"type": "float"},
|
||||
"@voters.proxy": {"type": "keyword"},
|
||||
// eosio::voters
|
||||
"@voters.is_proxy": {"type": "boolean"},
|
||||
"@voters.producers": {"type": "keyword"},
|
||||
"@voters.last_vote_weight": {"type": "double"},
|
||||
"@voters.proxied_vote_weight": {"type": "double"},
|
||||
"@voters.staked": {"type": "float"},
|
||||
"@voters.proxy": {"type": "keyword"},
|
||||
|
||||
// eosio::producers
|
||||
"@producers.total_votes": {"type": "double"},
|
||||
"@producers.is_active": {"type": "boolean"},
|
||||
"@producers.unpaid_blocks": {"type": "long"},
|
||||
// eosio::producers
|
||||
"@producers.total_votes": {"type": "double"},
|
||||
"@producers.is_active": {"type": "boolean"},
|
||||
"@producers.unpaid_blocks": {"type": "long"},
|
||||
|
||||
// eosio::global
|
||||
"@global": {
|
||||
"properties": {
|
||||
"last_name_close": {"type": "date"},
|
||||
"last_pervote_bucket_fill": {"type": "date"},
|
||||
"last_producer_schedule_update": {"type": "date"},
|
||||
"perblock_bucket": {"type": "double"},
|
||||
"pervote_bucket": {"type": "double"},
|
||||
"total_activated_stake": {"type": "double"},
|
||||
"total_voteshare_change_rate": {"type": "double"},
|
||||
"total_unpaid_voteshare": {"type": "double"},
|
||||
"total_producer_vote_weight": {"type": "double"},
|
||||
"total_ram_bytes_reserved": {"type": "long"},
|
||||
"total_ram_stake": {"type": "long"},
|
||||
"total_unpaid_blocks": {"type": "long"},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// eosio::global
|
||||
"@global": {
|
||||
"properties": {
|
||||
"last_name_close": {"type": "date"},
|
||||
"last_pervote_bucket_fill": {"type": "date"},
|
||||
"last_producer_schedule_update": {"type": "date"},
|
||||
"perblock_bucket": {"type": "double"},
|
||||
"pervote_bucket": {"type": "double"},
|
||||
"total_activated_stake": {"type": "double"},
|
||||
"total_voteshare_change_rate": {"type": "double"},
|
||||
"total_unpaid_voteshare": {"type": "double"},
|
||||
"total_producer_vote_weight": {"type": "double"},
|
||||
"total_ram_bytes_reserved": {"type": "long"},
|
||||
"total_ram_stake": {"type": "long"},
|
||||
"total_unpaid_blocks": {"type": "long"},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const abi = {
|
||||
"index_patterns": [chain + "-abi-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {"type": "date"},
|
||||
"block": {"type": "long"},
|
||||
"account": {"type": "keyword"},
|
||||
"abi": {"enabled": false},
|
||||
"abi_hex": {"enabled": false},
|
||||
"actions": {"type": "keyword"},
|
||||
"tables": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-abi-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {"type": "date"},
|
||||
"block": {"type": "long"},
|
||||
"account": {"type": "keyword"},
|
||||
"abi": {"enabled": false},
|
||||
"abi_hex": {"enabled": false},
|
||||
"actions": {"type": "keyword"},
|
||||
"tables": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const permissionLink = {
|
||||
"index_patterns": [chain + "-link-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"present": {"type": "boolean"},
|
||||
"account": {"type": "keyword"},
|
||||
"code": {"type": "keyword"},
|
||||
"action": {"type": "keyword"},
|
||||
"permission": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-link-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"present": {"type": "byte"},
|
||||
"account": {"type": "keyword"},
|
||||
"code": {"type": "keyword"},
|
||||
"action": {"type": "keyword"},
|
||||
"permission": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const permission = {
|
||||
"index_patterns": [chain + "-perm-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "boolean"},
|
||||
"owner": {"type": "keyword"},
|
||||
"name": {"type": "keyword"},
|
||||
"parent": {"type": "keyword"},
|
||||
"last_updated": {"type": "date"},
|
||||
"auth": {"type": "object"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-perm-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "byte"},
|
||||
"owner": {"type": "keyword"},
|
||||
"name": {"type": "keyword"},
|
||||
"parent": {"type": "keyword"},
|
||||
"last_updated": {"type": "date"},
|
||||
"auth": {"type": "object"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const resourceLimits = {
|
||||
"index_patterns": [chain + "-reslimits-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"owner": {"type": "keyword"},
|
||||
"total_weight": {"type": "long"},
|
||||
"net_weight": {"type": "long"},
|
||||
"cpu_weight": {"type": "long"},
|
||||
"ram_bytes": {"type": "long"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-reslimits-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"owner": {"type": "keyword"},
|
||||
"total_weight": {"type": "long"},
|
||||
"net_weight": {"type": "long"},
|
||||
"cpu_weight": {"type": "long"},
|
||||
"ram_bytes": {"type": "long"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const generatedTransaction = {
|
||||
"index_patterns": [chain + "-gentrx-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"sender": {"type": "keyword"},
|
||||
"sender_id": {"type": "keyword"},
|
||||
"payer": {"type": "keyword"},
|
||||
"trx_id": {"type": "keyword"},
|
||||
"actions": {"enabled": false},
|
||||
"packed_trx": {"enabled": false}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-gentrx-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"sender": {"type": "keyword"},
|
||||
"sender_id": {"type": "keyword"},
|
||||
"payer": {"type": "keyword"},
|
||||
"trx_id": {"type": "keyword"},
|
||||
"actions": {"enabled": false},
|
||||
"packed_trx": {"enabled": false}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const failedTransaction = {
|
||||
"index_patterns": [chain + "-trxerr-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"status": {"type": "short"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-trxerr-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"status": {"type": "short"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const resourceUsage = {
|
||||
"index_patterns": [chain + "-userres-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"owner": {"type": "keyword"},
|
||||
"net_used": {"type": "long"},
|
||||
"net_total": {"type": "long"},
|
||||
"net_pct": {"type": "float"},
|
||||
"cpu_used": {"type": "long"},
|
||||
"cpu_total": {"type": "long"},
|
||||
"cpu_pct": {"type": "float"},
|
||||
"ram": {"type": "long"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-userres-*"],
|
||||
"settings": defaultIndexSettings,
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"@timestamp": {"type": "date"},
|
||||
"owner": {"type": "keyword"},
|
||||
"net_used": {"type": "long"},
|
||||
"net_total": {"type": "long"},
|
||||
"net_pct": {"type": "float"},
|
||||
"cpu_used": {"type": "long"},
|
||||
"cpu_total": {"type": "long"},
|
||||
"cpu_pct": {"type": "float"},
|
||||
"ram": {"type": "long"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export const logs = {
|
||||
"index_patterns": [chain + "-logs-*"],
|
||||
"settings": defaultIndexSettings
|
||||
"index_patterns": [chain + "-logs-*"],
|
||||
"settings": defaultIndexSettings
|
||||
};
|
||||
|
||||
export const block = {
|
||||
"index_patterns": [chain + "-block-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "block_num",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {"type": "date"},
|
||||
"block_num": {"type": "long"},
|
||||
"block_id": {"type": "keyword"},
|
||||
"prev_id": {"type": "keyword"},
|
||||
"producer": {"type": "keyword"},
|
||||
"new_producers.producers.block_signing_key": {"enabled": false},
|
||||
"new_producers.producers.producer_name": {"type": "keyword"},
|
||||
"new_producers.version": {"type": "long"},
|
||||
"schedule_version": {"type": "double"},
|
||||
"cpu_usage": {"type": "integer"},
|
||||
"net_usage": {"type": "integer"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-block-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "block_num",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"@timestamp": {"type": "date"},
|
||||
"block_num": {"type": "long"},
|
||||
"block_id": {"type": "keyword"},
|
||||
"prev_id": {"type": "keyword"},
|
||||
"producer": {"type": "keyword"},
|
||||
"new_producers.producers.block_signing_key": {"enabled": false},
|
||||
"new_producers.producers.producer_name": {"type": "keyword"},
|
||||
"new_producers.version": {"type": "long"},
|
||||
"schedule_version": {"type": "double"},
|
||||
"cpu_usage": {"type": "integer"},
|
||||
"net_usage": {"type": "integer"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const tableProposals = {
|
||||
"index_patterns": [chain + "-table-proposals-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "block_num",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "boolean"},
|
||||
"proposal_name": {"type": "keyword"},
|
||||
"requested_approvals": {"type": "object"},
|
||||
"provided_approvals": {"type": "object"},
|
||||
"executed": {"type": "boolean"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-table-proposals-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "block_num",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "byte"},
|
||||
"proposal_name": {"type": "keyword"},
|
||||
"requested_approvals": {"type": "object"},
|
||||
"provided_approvals": {"type": "object"},
|
||||
"executed": {"type": "boolean"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const tableAccounts = {
|
||||
"index_patterns": [chain + "-table-accounts-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "amount",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "boolean"},
|
||||
"code": {"type": "keyword"},
|
||||
"scope": {"type": "keyword"},
|
||||
"amount": {"type": "float"},
|
||||
"symbol": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-table-accounts-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "amount",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"present": {"type": "byte"},
|
||||
"code": {"type": "keyword"},
|
||||
"scope": {"type": "keyword"},
|
||||
"amount": {"type": "float"},
|
||||
"symbol": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export const tableDelBand = {
|
||||
"index_patterns": [chain + "-table-delband-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "total_weight",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"from": {"type": "keyword"},
|
||||
"to": {"type": "keyword"},
|
||||
"total_weight": {"type": "float"},
|
||||
"net_weight": {"type": "float"},
|
||||
"cpu_weight": {"type": "float"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-table-delband-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "total_weight",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"from": {"type": "keyword"},
|
||||
"to": {"type": "keyword"},
|
||||
"total_weight": {"type": "float"},
|
||||
"net_weight": {"type": "float"},
|
||||
"cpu_weight": {"type": "float"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const tableVoters = {
|
||||
"index_patterns": [chain + "-table-voters-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "last_vote_weight",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"voter": {"type": "keyword"},
|
||||
"producers": {"type": "keyword"},
|
||||
"last_vote_weight": {"type": "double"},
|
||||
"is_proxy": {"type": "boolean"},
|
||||
"proxied_vote_weight": {"type": "double"},
|
||||
"staked": {"type": "double"},
|
||||
"proxy": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
"index_patterns": [chain + "-table-voters-*"],
|
||||
"settings": {
|
||||
"index": {
|
||||
"codec": compression,
|
||||
"number_of_shards": shards,
|
||||
"refresh_interval": refresh,
|
||||
"number_of_replicas": replicas,
|
||||
"sort.field": "last_vote_weight",
|
||||
"sort.order": "desc"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"properties": {
|
||||
"block_num": {"type": "long"},
|
||||
"voter": {"type": "keyword"},
|
||||
"producers": {"type": "keyword"},
|
||||
"last_vote_weight": {"type": "double"},
|
||||
"is_proxy": {"type": "boolean"},
|
||||
"proxied_vote_weight": {"type": "double"},
|
||||
"staked": {"type": "double"},
|
||||
"proxy": {"type": "keyword"}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
cluster.name: docker-cluster
|
||||
network.host: 0.0.0.0
|
||||
bootstrap.memory_lock: true
|
||||
discovery.type: single-node
|
||||
node.name: es01
|
||||
@@ -0,0 +1,2 @@
|
||||
-Xms8g
|
||||
-Xmx8g
|
||||
+5
-11
@@ -1,11 +1,5 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y && apt-get autoremove && apt-get install -y wget netcat
|
||||
|
||||
RUN wget -nv https://github.com/eosio/eos/releases/download/v2.0.5/eosio_2.0.5-1-ubuntu-18.04_amd64.deb
|
||||
RUN apt-get install -y ./eosio_2.0.5-1-ubuntu-18.04_amd64.deb
|
||||
|
||||
RUN useradd -m -s /bin/bash eosio
|
||||
USER eosio
|
||||
|
||||
EXPOSE 8080
|
||||
FROM ubuntu:20.04
|
||||
LABEL version="2.1.0" description="EOSIO 2.1.0"
|
||||
RUN apt-get update && apt-get install -y wget
|
||||
RUN wget -nv https://github.com/EOSIO/eos/releases/download/v2.1.0/eosio_2.1.0-1-ubuntu-20.04_amd64.deb
|
||||
RUN apt-get install -y ./eosio_2.1.0-1-ubuntu-20.04_amd64.deb && rm -f ./eosio_2.1.0-1-ubuntu-20.04_amd64.deb
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
# Enable block production, even if the chain is stale. (eosio::producer_plugin)
|
||||
### PRODUCER PLUGIN ###
|
||||
enable-stale-production = true
|
||||
|
||||
# ID of producer controlled by this node (e.g. inita; may specify multiple times) (eosio::producer_plugin)
|
||||
producer-name = eosio
|
||||
|
||||
# print contract's output to console (eosio::chain_plugin)
|
||||
contracts-console = true
|
||||
|
||||
# The local IP and port to listen for incoming http connections; set blank to disable. (eosio::http_plugin)
|
||||
http-server-address = 0.0.0.0:8888
|
||||
|
||||
# If set to false, then any incoming "Host" header is considered valid (eosio::http_plugin)
|
||||
http-validate-host = false
|
||||
|
||||
# enable trace history (eosio::state_history_plugin)
|
||||
### STATE HISTORY PLUGIN ###
|
||||
plugin = eosio::state_history_plugin
|
||||
state-history-endpoint = 0.0.0.0:8080
|
||||
trace-history = true
|
||||
|
||||
# enable chain state history (eosio::state_history_plugin)
|
||||
chain-state-history = true
|
||||
|
||||
# the endpoint upon which to listen for incoming connections. Caution: only expose this port to your internal network. (eosio::state_history_plugin)
|
||||
state-history-endpoint = 0.0.0.0:8080
|
||||
|
||||
# Plugin(s) to enable, may be specified multiple times
|
||||
plugin = eosio::producer_api_plugin
|
||||
### CHAIN API PLUGIN ###
|
||||
plugin = eosio::chain_api_plugin
|
||||
plugin = eosio::state_history_plugin
|
||||
http-server-address = 0.0.0.0:8888
|
||||
http-validate-host = false
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update && apt-get upgrade -y && apt-get autoremove && apt-get install -y build-essential git curl netcat
|
||||
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
||||
RUN apt-get install -y nodejs && npm install pm2 -g && git clone https://github.com/eosrio/hyperion-history-api.git
|
||||
|
||||
RUN useradd -m -s /bin/bash hyperion && chown -R hyperion:hyperion /hyperion-history-api
|
||||
USER hyperion
|
||||
FROM node:16.5
|
||||
RUN npm install pm2 -g
|
||||
RUN git clone https://github.com/eosrio/hyperion-history-api.git
|
||||
WORKDIR /hyperion-history-api
|
||||
|
||||
RUN npm install
|
||||
|
||||
EXPOSE 7001
|
||||
|
||||
RUN git checkout dev-3.3
|
||||
RUN npm install --production
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
|
||||
# Except this file
|
||||
!.gitignore
|
||||
@@ -0,0 +1,4 @@
|
||||
default_vhost = hyperion
|
||||
default_user = hyperion
|
||||
default_pass = hyp123456
|
||||
cluster_name = hyp-docker
|
||||
@@ -1,111 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
redis:
|
||||
container_name: redis
|
||||
image: redis:5.0.9-alpine
|
||||
restart: on-failure
|
||||
networks:
|
||||
- hyperion
|
||||
|
||||
rabbitmq:
|
||||
container_name: rabbitmq
|
||||
image: rabbitmq:3.8.3-management
|
||||
restart: on-failure
|
||||
environment:
|
||||
- RABBITMQ_DEFAULT_USER=username
|
||||
- RABBITMQ_DEFAULT_PASS=password
|
||||
- RABBITMQ_DEFAULT_VHOST=/hyperion
|
||||
volumes:
|
||||
- ./rabbitmq/data:/var/lib/rabbitmq
|
||||
ports:
|
||||
- 15672:15672
|
||||
networks:
|
||||
- hyperion
|
||||
|
||||
elasticsearch:
|
||||
container_name: elasticsearch
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.7.1
|
||||
restart: on-failure
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- cluster.name=es-cluster
|
||||
- node.name=es01
|
||||
- bootstrap.memory_lock=true
|
||||
- xpack.security.enabled=true
|
||||
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
|
||||
- ELASTIC_USERNAME=elastic
|
||||
- ELASTIC_PASSWORD=password
|
||||
volumes:
|
||||
- ./elasticsearch/data:/usr/share/elasticsearch/data
|
||||
ports:
|
||||
- 9200:9200
|
||||
networks:
|
||||
- hyperion
|
||||
|
||||
kibana:
|
||||
container_name: kibana
|
||||
image: docker.elastic.co/kibana/kibana:7.7.1
|
||||
restart: on-failure
|
||||
environment:
|
||||
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||||
- ELASTICSEARCH_USERNAME=elastic
|
||||
- ELASTICSEARCH_PASSWORD=password
|
||||
ports:
|
||||
- 5601:5601
|
||||
networks:
|
||||
- hyperion
|
||||
depends_on:
|
||||
- elasticsearch
|
||||
|
||||
eosio-node:
|
||||
container_name: eosio-node
|
||||
image: eosrio/hyperion:eosio-2.0.5
|
||||
volumes:
|
||||
- ./eosio/data/:/home/eosio/data/
|
||||
- ./eosio/config/:/home/eosio/config/
|
||||
- ./scripts/:/home/eosio/scripts/
|
||||
ports:
|
||||
- 8888:8888
|
||||
networks:
|
||||
- hyperion
|
||||
command: bash -c "/home/eosio/scripts/run-nodeos.sh ${SCRIPT:-false} ${SNAPSHOT:-""}"
|
||||
|
||||
hyperion-indexer:
|
||||
container_name: hyperion-indexer
|
||||
image: eosrio/hyperion:hyperion-3.1.0-beta.3
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- elasticsearch
|
||||
- redis
|
||||
- rabbitmq
|
||||
- eosio-node
|
||||
volumes:
|
||||
- ./hyperion/config/connections.json:/hyperion-history-api/connections.json
|
||||
- ./hyperion/config/ecosystem.config.js:/hyperion-history-api/ecosystem.config.js
|
||||
- ./hyperion/config/chains/:/hyperion-history-api/chains/
|
||||
- ./scripts/:/home/hyperion/scripts/
|
||||
networks:
|
||||
- hyperion
|
||||
command: bash -c "/home/hyperion/scripts/run-hyperion.sh ${SCRIPT:-false} eos-indexer"
|
||||
|
||||
hyperion-api:
|
||||
container_name: hyperion-api
|
||||
image: eosrio/hyperion:hyperion-3.0
|
||||
restart: on-failure
|
||||
ports:
|
||||
- 7000:7000
|
||||
depends_on:
|
||||
- hyperion-indexer
|
||||
volumes:
|
||||
- ./hyperion/config/connections.json:/hyperion-history-api/connections.json
|
||||
- ./hyperion/config/ecosystem.config.js:/hyperion-history-api/ecosystem.config.js
|
||||
- ./hyperion/config/chains/:/hyperion-history-api/chains/
|
||||
- ./scripts/:/home/hyperion/scripts/
|
||||
networks:
|
||||
- hyperion
|
||||
command: bash -c "/home/hyperion/scripts/run-hyperion.sh ${SCRIPT:-false} eos-api"
|
||||
|
||||
networks:
|
||||
hyperion:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,23 @@
|
||||
const {addApiServer, addIndexer} = require('./definitions/ecosystem_settings');
|
||||
const {readdirSync, readFileSync} = require("fs");
|
||||
const path = require('path');
|
||||
|
||||
const apps = [];
|
||||
const chainsRoot = path.join(path.resolve(), 'chains');
|
||||
readdirSync(chainsRoot)
|
||||
.filter(f => f.endsWith('.config.json'))
|
||||
.forEach(value => {
|
||||
const configFile = readFileSync(path.join(chainsRoot, value))
|
||||
const config = JSON.parse(configFile.toString());
|
||||
const chainName = config.settings.chain;
|
||||
if (config.api.enabled) {
|
||||
const apiHeap = config.api.node_max_old_space_size;
|
||||
apps.push(addApiServer(chainName, config.api.pm2_scaling, apiHeap));
|
||||
}
|
||||
if (config.indexer.enabled) {
|
||||
const indexerHeap = config.indexer.node_max_old_space_size;
|
||||
apps.push(addIndexer(chainName, indexerHeap));
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {apps};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user