Implemented remaining stubs for api

This commit is contained in:
Kirill Fedoseev 2019-11-09 15:11:39 +03:00
parent e832874891
commit 6b43434f53
7 changed files with 201 additions and 83 deletions

View File

@ -6,10 +6,8 @@ WORKDIR /api-server
COPY --from=testnet-binaries /binaries/cli/testnet/${BNC_VERSION}/linux/tbnbcli ./
COPY ./package.json ./
RUN echo 12345678 | ./tbnbcli keys add key
RUN npm install
EXPOSE 8080
COPY ./index.js ./
ENTRYPOINT ["node", "./index.js"]
ENTRYPOINT ["./tbnbcli", "api-server", "--chain-id", "Binance-Dev", "--node", "http://node:26657", "--laddr", "tcp://0.0.0.0:8080"]

View File

@ -1,75 +0,0 @@
const fs = require('fs')
const express = require('express')
const axios = require('axios')
const { execSync } = require('child_process')
const rpcClient = axios.create({
baseURL: process.env.FOREIGN_RPC_URL,
timeout: 10000
})
async function delay(ms) {
await new Promise((res) => setTimeout(res, ms))
}
async function retry(getPromise, n = -1, sleep = 3000) {
while (n) {
try {
return await getPromise()
} catch (e) {
console.debug(`Promise failed, retrying, ${n - 1} attempts left`)
await delay(sleep)
// eslint-disable-next-line no-param-reassign
n -= 1
}
}
return null
}
async function sendRpcRequest(subUrl, method, params) {
console.trace(`Request to ${subUrl}, method ${method}, params `, params)
const response = await retry(() => rpcClient.post(subUrl, {
jsonrpc: '2.0',
method,
params,
id: 1
}))
console.trace('Response, ', response.data)
return response.data
}
const app = express()
// GET
// /api/v1/tx/:hash
// /api/v1/time
// /api/v1/transactions
// ?address=a&side=RECEIVE&txAsset=FOREIGN_ASSET&txType=TRANSFER&startTime=111&endTime=222
// /api/v1/account/:account
// /api/v1/account/:account/sequence
// POST
// /api/v1/broadcast?sync=true
app.get('/api/v1/tx/:hash', async (req, res) => {
try {
const {
tx, hash, height, result
} = JSON.parse(
execSync(`./tbnbcli tx ${req.params.hash} --node "http://node:26657" --chain-id Binance-Dev`)
)
res.send({
code: 0,
hash,
height,
log: result.log,
ok: true,
tx
})
} catch (e) {
res.status(404).end()
}
})
app.listen(8000, () => {
console.log('Listening on port 8000')
})

View File

@ -10,15 +10,25 @@ services:
volumes:
- 'marketdata:/root/.bnbchaind/marketdata'
api-server:
build: ./api-server
build: api-server
image: bnc-api-server
networks:
- binance_rpc_net
ports:
- '8080:8080'
http-api:
build: http-api
image: bnc-http-api
environment:
FOREIGN_RPC_URL: 'http://node:26657'
FOREIGN_API_SERVER_URL: 'http://api-server:8080'
networks:
- binance_net
- binance_rpc_net
ports:
- '8000:8000'
volumes:
- 'marketdata:/api-server/marketdata'
- 'marketdata:/http-api/marketdata'
networks:
binance_net:
external: true

View File

@ -0,0 +1,15 @@
FROM node:10.16.0-alpine
ARG BNC_VERSION=0.6.2
WORKDIR /http-api
COPY --from=testnet-binaries /binaries/cli/testnet/${BNC_VERSION}/linux/tbnbcli ./
COPY ./package.json ./
RUN npm install
COPY ./index.js ./parser.js ./
ENTRYPOINT ["node", "./index.js"]

View File

@ -0,0 +1,125 @@
const { execSync } = require('child_process')
const express = require('express')
const axios = require('axios')
const BN = require('bignumber.js')
const createParser = require('./parser')
const rpcClient = axios.create({
baseURL: process.env.FOREIGN_RPC_URL,
timeout: 10000
})
const apiClient = axios.create({
baseURL: process.env.FOREIGN_API_SERVER_URL,
timeout: 10000
})
const transfers = []
const parser = createParser('/http-api/marketdata/marketdata.json', 20 * 1024)
parser.eventEmitter.on('object', (obj) => {
obj.Transfers.forEach((event) => {
// eslint-disable-next-line no-param-reassign
event.Timestamp = Math.ceil(obj.Timestamp / 1000)
transfers.push(event)
})
})
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
function wrap(f) {
return async (req, res) => {
try {
await f(req, res)
} catch (e) {
res.status(404).end()
}
}
}
async function handleTx(req, res) {
const {
tx, hash, height, result
} = JSON.parse(
execSync(`./tbnbcli tx ${req.params.hash} --node "http://node:26657" --chain-id Binance-Dev`)
)
res.send({
code: 0,
hash,
height,
log: result.log,
ok: true,
tx
})
}
async function handleTransactions(req, res) {
// eslint-disable-next-line no-empty
while (parser.update()) {}
const {
address, side, txAsset, txType, startTime, endTime
} = req.query
if (txType !== 'TRANSFER' || side !== 'RECEIVE') {
res.status(400).send('Given parameters are not supported')
}
const filtered = transfers.filter((event) => event.Timestamp >= parseInt(startTime, 10)
&& event.Timestamp <= parseInt(endTime, 10)
&& event.To.length === 1
&& event.To[0].Addr === address
&& event.To[0].Coins.length === 1
&& event.To[0].Coins[0].denom === txAsset)
res.send({
tx: filtered.map((tx) => ({
txHash: tx.TxHash,
memo: tx.Memo,
value: new BN(tx.To[0].Coins[0].amount).dividedBy(10 ** 8).toFixed(8, 3)
})),
total: filtered.length
})
}
async function handleTime(req, res) {
const response = (await rpcClient.get('/status')).data
res.send({
ap_time: response.result.sync_info.latest_block_time,
block_time: response.result.sync_info.latest_block_time
})
}
async function handleAccount(req, res) {
const response = (await apiClient.get(`/api/v1/account/${req.params.account}`)).data
res.send(response)
}
async function handleAccountSequence(req, res) {
const response = (await apiClient.get(`/api/v1/account/${req.params.account}`)).data
res.send(response.sequence.toString())
}
async function handleBroadcast(req, res) {
if (req.query.sync !== 'true') {
res.status(400).send('Async broadcast is not supported')
} else {
const response = (await rpcClient.get('/broadcast_tx_sync', {
params: {
tx: req.body
}
})).data
res.send(response.result)
}
}
app.get('/api/v1/tx/:hash', wrap(handleTx))
app.get('/api/v1/time', wrap(handleTime))
app.get('/api/v1/transactions', wrap(handleTransactions))
app.get('/api/v1/account/:account', wrap(handleAccount))
app.get('/api/v1/account/:account/sequence', wrap(handleAccountSequence))
app.post('/api/v1/broadcast', wrap(handleBroadcast))
app.listen(8000, () => {
console.log('Listening on port 8000')
})

View File

@ -3,7 +3,8 @@
"version": "0.0.1",
"dependencies": {
"express": "4.17.1",
"axios": "0.19.0"
"axios": "0.19.0",
"bignumber.js": "9.0.0"
},
"engines": {
"node": ">=10.6.0"

View File

@ -0,0 +1,44 @@
const events = require('events')
const fs = require('fs')
function createParser(file, bufferSize) {
const buf = Buffer.alloc(bufferSize)
const eventEmitter = new events.EventEmitter()
let fd
let position = 0
let end = 0
return {
update() {
if (!fd) {
try {
fd = fs.openSync(file, 'r')
} catch (e) {
return 0
}
}
const bytesRead = fs.readSync(fd, buf, position, buf.length - position, null)
for (let i = position; i < position + bytesRead; i += 1) {
if (buf[i] === 10) {
const obj = buf.slice(end, i)
end = i + 1
eventEmitter.emit('object', JSON.parse(obj))
}
}
position += bytesRead
if (buf.length - position < bufferSize / 2) {
buf.copy(buf, 0, end, position)
position -= end
end = 0
}
return bytesRead
},
close() {
fs.closeSync(fd)
},
eventEmitter
}
}
module.exports = createParser