Skip to content

Commit

Permalink
Merge pull request #306 from Bitcoin-com/stage
Browse files Browse the repository at this point in the history
v2.2.6
  • Loading branch information
cgcardona authored Feb 18, 2019
2 parents 6d41726 + 5fe3d61 commit 3657838
Show file tree
Hide file tree
Showing 28 changed files with 168 additions and 546 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ Now you need to start REST and pass in the following environment variables
- ZEROMQ_URL - The IP address of your full BCH node
- NETWORK - mainnet or testnet depending on which network you're using
- BITDB_URL - mainnet or testnet BITDB URL
- RATE_LIMIT_MAX_REQUESTS (optional) - Rate limit per route per minute. Defaults to 60. Set to 0 to disable rate limit.

Here's how the final command would look

Expand Down
4 changes: 4 additions & 0 deletions dist/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var express = require("express");
// Middleware
var route_ratelimit_1 = require("./middleware/route-ratelimit");
var path = require("path");
var logger = require("morgan");
var cookieParser = require("cookie-parser");
Expand Down Expand Up @@ -89,6 +91,8 @@ app.use("/" + v1prefix + "/" + "util", utilV1);
app.use("/" + v1prefix + "/" + "dataRetrieval", dataRetrievalV1);
app.use("/" + v1prefix + "/" + "payloadCreation", payloadCreationV1);
app.use("/" + v1prefix + "/" + "slp", slpV1);
// Rate limit on all v2 routes
app.use("/" + v2prefix + "/", route_ratelimit_1.routeRateLimit);
app.use("/", indexV2);
app.use("/" + v2prefix + "/" + "health-check", healthCheckV2);
app.use("/" + v2prefix + "/" + "address", addressV2.router);
Expand Down
36 changes: 36 additions & 0 deletions dist/middleware/route-ratelimit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var RateLimit = require("express-rate-limit");
// Set max requests per minute
var maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 60;
// Unique route mapped to its rate limit
var uniqueRateLimits = {};
var routeRateLimit = function (req, res, next) {
// Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS
if (maxRequests === 0)
return next();
// TODO: Auth: Set or disable rate limit if authenticated user
// Current route
var path = req.baseUrl + req.path;
var route = req.method + path.split("/").slice(0, 4).join("/");
// Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({
windowMs: 60 * 1000,
delayMs: 0,
max: maxRequests,
handler: function (req, res /*next*/) {
res.format({
json: function () {
res.status(500).json({
error: "Too many requests. Limits are 60 requests per minute."
});
}
});
}
});
}
// Call rate limit for this route
uniqueRateLimits[route](req, res, next);
};
exports.routeRateLimit = routeRateLimit;
10 changes: 5 additions & 5 deletions dist/public/bitcoin-com-mainnet-rest-v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@
"openapi": "3.0.0",
"info": {
"description": "rest.bitcoin.com is the REST layer for Bitcoin.com's Cloud. More info: [developer.bitcoin.com/rest](https://developer.bitcoin.com/rest). Chatroom [geni.us/CashDev](http://geni.us/CashDev)",
"version": "2.2.5",
"version": "2.2.6",
"title": "REST",
"license": {
"name": "MIT",
Expand Down Expand Up @@ -1569,7 +1569,7 @@
],
"summary": "Block details single",
"description": "Details about a single block by height",
"operationId": "etailsHeightSingle",
"operationId": "detailsHeightSingle",
"parameters": [
{
"name": "height",
Expand Down Expand Up @@ -2629,21 +2629,21 @@
}
}
},
"/slp/address/convert/{address}": {
"/slp/convert/{address}": {
"get": {
"tags": [
"slp"
],
"summary": "convert address to slpAddr, cashAddr and legacy",
"description": "convert address to slpAddr, cashAddr and legacy",
"operationId": "addressConvert",
"operationId": "convert",
"parameters": [
{
"name": "address",
"in": "path",
"description": "The slp address",
"required": true,
"example": "simpleledger:qz9tzs6d5097ejpg279rg0rnlhz546q4fsnck9wh5m",
"example": "",
"schema": {
"type": "string"
}
Expand Down
10 changes: 5 additions & 5 deletions dist/public/bitcoin-com-testnet-rest-v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,7 @@
"openapi": "3.0.0",
"info": {
"description": "trest.bitcoin.com is the REST layer for Bitcoin.com's Cloud. More info: [developer.bitcoin.com/rest](https://developer.bitcoin.com/rest). Chatroom [geni.us/CashDev](http://geni.us/CashDev)",
"version": "2.2.5",
"version": "2.2.6",
"title": "REST",
"license": {
"name": "MIT",
Expand Down Expand Up @@ -1569,7 +1569,7 @@
],
"summary": "Block details single",
"description": "Details about a single block by height",
"operationId": "etailsHeightSingle",
"operationId": "detailsHeightSingle",
"parameters": [
{
"name": "height",
Expand Down Expand Up @@ -2629,21 +2629,21 @@
}
}
},
"/slp/address/convert/{address}": {
"/slp/convert/{address}": {
"get": {
"tags": [
"slp"
],
"summary": "convert address to slpAddr, cashAddr and legacy",
"description": "convert address to slpAddr, cashAddr and legacy",
"operationId": "addressConvert",
"operationId": "convert",
"parameters": [
{
"name": "address",
"in": "path",
"description": "The slp address",
"required": true,
"example": "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5",
"example": "",
"schema": {
"type": "string"
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rest.bitcoin.com",
"version": "2.2.5",
"version": "2.2.6",
"description": "REST API for Bitcoin.com's Cloud",
"author": "Gabriel Cardona <[email protected]>",
"contributors": [
Expand Down
5 changes: 5 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { Socket } from "net"

import * as express from "express"

// Middleware
import { routeRateLimit } from "./middleware/route-ratelimit"

const path = require("path")
const logger = require("morgan")
const cookieParser = require("cookie-parser")
Expand Down Expand Up @@ -130,6 +133,8 @@ app.use(`/${v1prefix}/` + `dataRetrieval`, dataRetrievalV1)
app.use(`/${v1prefix}/` + `payloadCreation`, payloadCreationV1)
app.use(`/${v1prefix}/` + `slp`, slpV1)

// Rate limit on all v2 routes
app.use(`/${v2prefix}/`, routeRateLimit)
app.use("/", indexV2)
app.use(`/${v2prefix}/` + `health-check`, healthCheckV2)
app.use(`/${v2prefix}/` + `address`, addressV2.router)
Expand Down
48 changes: 48 additions & 0 deletions src/middleware/route-ratelimit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as express from "express"
const RateLimit = require("express-rate-limit")

// Set max requests per minute
const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) : 60

// Unique route mapped to its rate limit
const uniqueRateLimits: any = {}

const routeRateLimit = function(
req: express.Request,
res: express.Response,
next: express.NextFunction
) {
// Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS
if (maxRequests === 0) return next()

// TODO: Auth: Set or disable rate limit if authenticated user

// Current route
const path = req.baseUrl + req.path
const route = req.method + path.split("/").slice(0,4).join("/")

// Create new RateLimit if none exists for this route
if (!uniqueRateLimits[route]) {
uniqueRateLimits[route] = new RateLimit({
windowMs: 60 * 1000, // 1 minute window
delayMs: 0, // disable delaying - full speed until the max limit is reached
max: maxRequests, // start blocking after maxRequests
handler: function(req: express.Request, res: express.Response /*next*/) {
res.format({
json: function() {
res.status(500).json({
error: "Too many requests. Limits are 60 requests per minute."
})
}
})
}
})
}

// Call rate limit for this route
uniqueRateLimits[route](req, res, next)
}

export {
routeRateLimit
}
65 changes: 9 additions & 56 deletions src/routes/v2/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ const routeUtils = require("./route-utils")

//const router = express.Router()
const router: express.Router = express.Router()
const RateLimit = require("express-rate-limit")

// Used for processing error messages before sending them to the user.
const util = require("util")
Expand All @@ -25,66 +24,20 @@ const FREEMIUM_INPUT_SIZE = 20
// https://github.com/bitpay/insight-api#notes-on-upgrading-from-v03
const PAGE_SIZE = 1000

interface IRLConfig {
[addressRateLimit1: string]: any
addressRateLimit2: any
addressRateLimit3: any
addressRateLimit4: any
addressRateLimit5: any
addressRateLimit6: any
addressRateLimit7: any
addressRateLimit8: any
addressRateLimit9: any
addressRateLimit10: any
}

const config: IRLConfig = {
addressRateLimit1: undefined,
addressRateLimit2: undefined,
addressRateLimit3: undefined,
addressRateLimit4: undefined,
addressRateLimit5: undefined,
addressRateLimit6: undefined,
addressRateLimit7: undefined,
addressRateLimit8: undefined,
addressRateLimit9: undefined,
addressRateLimit10: undefined
}

let i = 1
while (i < 11) {
config[`addressRateLimit${i}`] = new RateLimit({
windowMs: 60000, // 1 hour window
delayMs: 0, // disable delaying - full speed until the max limit is reached
max: 60, // start blocking after 60 requests
handler: function(req: express.Request, res: express.Response /*next*/) {
res.format({
json: function() {
res.status(500).json({
error: "Too many requests. Limits are 60 requests per minute."
})
}
})
}
})
i++
}

// Connect the route endpoints to their handler functions.
router.get("/", config.addressRateLimit1, root)
router.get("/details/:address", config.addressRateLimit2, detailsSingle)
router.post("/details", config.addressRateLimit3, detailsBulk)
router.post("/utxo", config.addressRateLimit4, utxoBulk)
router.get("/utxo/:address", config.addressRateLimit5, utxoSingle)
router.post("/unconfirmed", config.addressRateLimit6, unconfirmedBulk)
router.get("/unconfirmed/:address", config.addressRateLimit7, unconfirmedSingle)
router.get("/", root)
router.get("/details/:address", detailsSingle)
router.post("/details", detailsBulk)
router.post("/utxo", utxoBulk)
router.get("/utxo/:address", utxoSingle)
router.post("/unconfirmed", unconfirmedBulk)
router.get("/unconfirmed/:address", unconfirmedSingle)
router.get(
"/transactions/:address",
config.addressRateLimit8,
transactionsSingle
)
router.post("/transactions", config.addressRateLimit9, transactionsBulk)
router.get("/fromXPub/:xpub", config.addressRateLimit10, fromXPubSingle)
router.post("/transactions", transactionsBulk)
router.get("/fromXPub/:xpub", fromXPubSingle)

// Root API endpoint. Simply acknowledges that it exists.
function root(
Expand Down
46 changes: 4 additions & 42 deletions src/routes/v2/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,14 @@ const router: express.Router = express.Router()

const FREEMIUM_INPUT_SIZE = 20

const RateLimit = require("express-rate-limit")

interface IRLConfig {
[blockRateLimit1: string]: any
blockRateLimit2: any
blockRateLimit3: any
blockRateLimit4: any
blockRateLimit5: any
}

const config: IRLConfig = {
blockRateLimit1: undefined,
blockRateLimit2: undefined,
blockRateLimit3: undefined,
blockRateLimit4: undefined,
blockRateLimit5: undefined
}

let i = 1
while (i < 6) {
config[`blockRateLimit${i}`] = new RateLimit({
windowMs: 60000, // 1 hour window
delayMs: 0, // disable delaying - full speed until the max limit is reached
max: 60, // start blocking after 60 requests
handler: (req: express.Request, res: express.Response /*next*/) => {
res.format({
json: () => {
res.status(500).json({
error: "Too many requests. Limits are 60 requests per minute."
})
}
})
}
})
i++
}

router.get("/", config.blockRateLimit1, root)
router.get("/detailsByHash/:hash", config.blockRateLimit2, detailsByHashSingle)
router.post("/detailsByHash", config.blockRateLimit3, detailsByHashBulk)
router.get("/", root)
router.get("/detailsByHash/:hash", detailsByHashSingle)
router.post("/detailsByHash", detailsByHashBulk)
router.get(
"/detailsByHeight/:height",
config.blockRateLimit4,
detailsByHeightSingle
)
router.post("/detailsByHeight", config.blockRateLimit5, detailsByHeightBulk)
router.post("/detailsByHeight", detailsByHeightBulk)

function root(
req: express.Request,
Expand Down
Loading

0 comments on commit 3657838

Please sign in to comment.