Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hack up BoomPoW v2 support #131

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
347 changes: 206 additions & 141 deletions README.md

Large diffs are not rendered by default.

9,211 changes: 9,187 additions & 24 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"express-ipfilter": "^1.2.0",
"express-slow-down": "^1.4.0",
"fs": "0.0.1-security",
"graphql": "^16.6.0",
"graphql-request": "^4.3.0",
"helmet": "^4.1.1",
"http": "0.0.0",
"https": "^1.0.0",
Expand Down
5 changes: 0 additions & 5 deletions pow_creds.json.default
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
{
"dpow": {
"user": "user",
"key": "key"
},
"bpow": {
"user": "user",
"key": "key"
},
"work_server": {
Expand Down
1 change: 0 additions & 1 deletion settings.json.default
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"use_websocket": false,
"allow_websocket_all": false,
"use_cors": true,
"use_dpow": false,
"use_bpow": false,
"use_work_server": false,
"use_work_peers": false,
Expand Down
1 change: 0 additions & 1 deletion src/__test__/proxy_file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ const expectedSettingsWithFile = [
'Use IP blacklist: true',
'Use token system: false',
'Use websocket system: false',
'Use dPoW: false',
'Use bPoW: false',
'Use work server: false',
'Use work peers: false',
Expand Down
4 changes: 0 additions & 4 deletions src/pow-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import ProxySettings from "./proxy-settings";
import * as Fs from "fs";

interface UserKeyPair {
user: string;
key: string;
}

Expand All @@ -12,7 +11,6 @@ interface ServerPair {
}

export interface PowSettings {
dpow?: UserKeyPair
bpow?: UserKeyPair
work_server?: ServerPair
}
Expand All @@ -22,15 +20,13 @@ export function readPowSettings(path: string, settings: ProxySettings): PowSetti
try {
const readSettings: PowSettings = JSON.parse(Fs.readFileSync(path, 'utf-8'))
return {
dpow: settings.use_dpow ? readSettings.dpow : undefined,
bpow: settings.use_bpow ? readSettings.bpow : undefined,
work_server: settings.use_work_server ? readSettings.work_server : undefined,
}
}
catch(e) {
console.log("Could not read pow_creds.json", e)
return {
dpow: undefined,
bpow: undefined,
work_server: undefined,
}
Expand Down
8 changes: 2 additions & 6 deletions src/proxy-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,9 @@ export default interface ProxySettings {
allow_websocket_all: boolean;
// If allowing users to subscribe to ALL accounts (more traffic)
use_cors: boolean;
// if allow work_generate to be done by dPoW instead of local node. Work will consume 10 token points. If "difficulty" is not provided with the work_generate request the "default send difficulty" will be used. (The priority order is bpow > dpow > work server. If all three are set to false, it will use the node to generate work) (requires work_generate in allowed_commands and credentials to be set in pow_creds.json)
use_dpow: boolean;
// if allow work_generate to be done by BoomPoW intead of local node. Work will consume 10 token points. If "difficulty" is not provided with the work_generate request the "default send difficulty" will be used. (The priority order is bpow > dpow > work server. If all three are set to false, it will use the node to generate work) (requires work_generate in allowed_commands and credentials to be set in pow_creds.json)
// if allow work_generate to be done by BoomPoW intead of local node. Work will consume 10 token points. If "difficulty" is not provided with the work_generate request the "default send difficulty" will be used. (The priority order is bpow > work server. If all three are set to false, it will use the node to generate work) (requires work_generate in allowed_commands and credentials to be set in pow_creds.json)
use_bpow: boolean;
// if allow work_generate to be done by external work server instead of local node. Work will consume 10 token points. If "difficulty" is not provided with the work_generate request the "default send difficulty" will be used. (The priority order is bpow > dpow > work server. If all three are set to false, it will use the node to generate work) (requires work_generate in allowed_commands)
// if allow work_generate to be done by external work server instead of local node. Work will consume 10 token points. If "difficulty" is not provided with the work_generate request the "default send difficulty" will be used. (The priority order is bpow > work server. If all three are set to false, it will use the node to generate work) (requires work_generate in allowed_commands)
use_work_server: boolean;
// if allow work_generate implicitly add "use_peers": "true" to the request to use work_peers configured in the nano node.
use_work_peers: boolean;
Expand Down Expand Up @@ -139,7 +137,6 @@ export function proxyLogSettings(logger: (...data: any[]) => void, settings: Pro
logger("Use IP blacklist: " + settings.use_ip_blacklist)
logger("Use token system: " + settings.use_tokens)
logger("Use websocket system: " + settings.use_websocket)
logger("Use dPoW: " + settings.use_dpow)
logger("Use bPoW: " + settings.use_bpow)
logger("Use work server: " + settings.use_work_server)
logger("Use work peers: " + settings.use_work_peers)
Expand Down Expand Up @@ -204,7 +201,6 @@ export function readProxySettings(settingsPath: string): ProxySettings {
use_websocket: false,
allow_websocket_all: false,
use_cors: true,
use_dpow: false,
use_bpow: false,
use_work_server: false,
use_work_peers: false,
Expand Down
76 changes: 24 additions & 52 deletions src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ const mynano_ninja_url = 'https://mynano.ninja/api/accounts/verified'
//const price_url2 = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?id=1567'
//const CMC_API_KEY = 'xxx'
const API_TIMEOUT: number = 20000 // 10sec timeout for calling http APIs
// Banano base
const work_threshold_banano: string = 'fffffe0000000000'
const work_threshold_default: string = 'fffffff800000000'
const work_default_timeout: number = 10 // x sec timeout before trying next delegated work method (only when use_dpow or use_bpow)
const bpow_url: string = 'https://bpow.banano.cc/service/'
const dpow_url: string = 'https://dpow.nanocenter.org/service/'
const work_default_timeout: number = 10 // x sec timeout before trying next delegated work method (only when use_bpow)
const work_token_cost = 10 // work_generate will consume x token points
let ws: ReconnectingWebSocket | null = null
let global_tracked_accounts: string[] = [] // the accounts to track in websocket (synced with database)
Expand Down Expand Up @@ -148,6 +148,8 @@ if (settings.use_tokens) {
})
}

const boompowClient = Tools.newBoomPowClient(powSettings.bpow?.key || '')

async function checkOldOrders() {
let now = Math.floor(Date.now()/1000)
// get all orders older than 60min
Expand Down Expand Up @@ -674,11 +676,10 @@ async function processRequest(query: ProxyRPCRequest, req: Request, res: Respons
}
}

// Handle work generate via dpow and/or bpow
if (query.action === 'work_generate' && (settings.use_dpow || settings.use_bpow || settings.use_work_server || settings.use_work_peers)) {
// Handle work generate via bpow
if (query.action === 'work_generate' && (settings.use_bpow || settings.use_work_server || settings.use_work_peers)) {
if (query.hash) {
let bpow_failed = false
let dpow_failed = false
// Set difficulty to SEND default if it was not defined
if (!query.difficulty) {
query.difficulty = work_threshold_default;
Expand All @@ -688,21 +689,29 @@ async function processRequest(query: ProxyRPCRequest, req: Request, res: Respons
query.timeout = work_default_timeout
}

if (settings.use_work_peers && !settings.use_bpow && !settings.use_dpow && !settings.use_work_server) {
//Only add use_peers when _NOT_ using any of bpow or dpow.
if (settings.use_work_peers && !settings.use_bpow && !settings.use_work_server) {
//Only add use_peers when _NOT_ using any of bpow.
query.use_peers = "true"
}

// Try bpow first
if (settings.use_bpow && powSettings.bpow) {
// ! TODO v2
logThis("Requesting work using bpow with diff: " + query.difficulty, log_levels.info)
query.user = powSettings.bpow.user
query.api_key = powSettings.bpow.key

try {
let data: ProcessDataResponse = await Tools.postData(query, bpow_url, work_default_timeout*1000*2)
const reqDiff = multiplierFromDifficulty(query.difficulty, work_threshold_banano)
let resp = await Tools.workGenerateBoompow(boompowClient, query.hash, parseInt(reqDiff))
const data: ProcessDataResponse = {
work: resp,
difficulty: query.difficulty,
multiplier: reqDiff,
tokens_total: 0

}
data.difficulty = query.difficulty
data.multiplier = multiplierFromDifficulty(data.difficulty, work_threshold_default)
data.multiplier = reqDiff
if (tokens_left != null) {
data.tokens_total = tokens_left
}
Expand All @@ -712,9 +721,7 @@ async function processRequest(query: ProxyRPCRequest, req: Request, res: Respons
}
if ((data.error) || !(data.work)) {
bpow_failed = true
if (!settings.use_dpow) {
return res.json(appendRateLimiterStatus(res, data)) // forward error if not retrying with dpow
}
return res.json(appendRateLimiterStatus(res, data))
}
else if (data.work) {
return res.json(appendRateLimiterStatus(res, data)) // sending back successful json response
Expand All @@ -723,47 +730,12 @@ async function processRequest(query: ProxyRPCRequest, req: Request, res: Respons
catch(err) {
bpow_failed = true
logThis("Bpow connection error: " + err.toString(), log_levels.warning)
if (!settings.use_dpow) {
return res.status(500).json({error: err.toString()})
}
}
}
// Use dpow only if not already used bpow or bpow timed out
if ((!settings.use_bpow || bpow_failed) && settings.use_dpow && powSettings.dpow) {
logThis("Requesting work using dpow with diff: " + query.difficulty, log_levels.info)
query.user = powSettings.dpow.user
query.api_key = powSettings.dpow.key

try {
let data: ProcessDataResponse = await Tools.postData(query, dpow_url, work_default_timeout*1000*2)
data.difficulty = query.difficulty
data.multiplier = multiplierFromDifficulty(data.difficulty, work_threshold_default)
if (tokens_left != null) {
data.tokens_total = tokens_left
}
if (data.error) {
logThis("dPoW failed: " + data.error, log_levels.warning)
}
if ((data.error) || !(data.work)) {
dpow_failed = true
if (!settings.use_work_server) {
return res.json(appendRateLimiterStatus(res, data)) // forward error if not retrying with work server
}
}
else if (data.work) {
return res.json(appendRateLimiterStatus(res, data)) // sending back json response (regardless if timeout error)
}
}
catch(err) {
dpow_failed = true
logThis("Dpow connection error: " + err.toString(), log_levels.warning)
if (!settings.use_work_server) {
return res.status(500).json({error: err.toString()})
return res.status(500).json({error: err.toString()})
}
}
}
// Use work server only if not already used bpow/dpow or bpow/dpow timed out
if (((!settings.use_bpow && !settings.use_dpow) || (bpow_failed || dpow_failed)) && settings.use_work_server && powSettings.work_server) {
// Use work server only if not already used bpow or bpow timed out
if (((!settings.use_bpow) || (bpow_failed)) && settings.use_work_server && powSettings.work_server) {
logThis("Requesting work using work server with diff: " + query.difficulty, log_levels.info)

try {
Expand Down
2 changes: 1 addition & 1 deletion src/token-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Fs from "fs";

export interface TokenSettings {
// the work server for doing PoW (the node can be used as well, for example http://127.0.0.1:7076, but enable_control is needed in the node config)
// To use bpow or dpow, just point the server to itself such as http://127.0.0.1:9950/proxy (requires bpow/dpow to be configured and work_generate as allowed command)
// To use bpow, just point the server to itself such as http://127.0.0.1:9950/proxy (requires bpow to be configured and work_generate as allowed command)
work_server: string
// Nano per token
token_price: number
Expand Down
26 changes: 26 additions & 0 deletions src/tools.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import Fetch, {Response} from 'node-fetch'
import BigInt from 'big-integer'
import * as Nano from 'nanocurrency'
import { GraphQLClient, gql } from 'graphql-request'
const Dec = require('bigdecimal') //https://github.com/iriscouch/bigdecimal.js
const RemoveTrailingZeros = require('remove-trailing-zeros')

// GQLClient
export const newBoomPowClient = (apikey: string) => new GraphQLClient('https://boompow.banano.cc/graphql', {
headers: {
Authorization: apikey,
},
})

// Custom error class
class APIError extends Error {

Expand Down Expand Up @@ -59,6 +67,24 @@ export async function postData<ResponseData>(data: any, server: string, timeout
})
return await promise // return promise result when finished instead of returning the promise itself, to avoid nested ".then"
}

// Post workGenerate mutation
const workGenerate = gql`
mutation workGenerate($hash:String!, $difficultyMultiplier: Int!) {
workGenerate(input:{hash:$hash, difficultyMultiplier:$difficultyMultiplier})
}
`

export const workGenerateBoompow = async (client:GraphQLClient, hash: string, difficultyMultiplier: number): Promise<string> => {
const variables = {
hash,
difficultyMultiplier,
}
const data = await client.request(workGenerate, variables)

return data?.workGenerate
}

// Check if a string is a valid JSON
export function isValidJson(obj: any) {
if (obj != null) {
Expand Down