mirror of
https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-service.git
synced 2026-09-21 16:52:03 -07:00
feat(broadcast): Added endpoint for REST and JSON APIs
This commit is contained in:
@@ -59,21 +59,10 @@ class BCHRPC {
|
||||
case 'utxos':
|
||||
await this.rateLimit.limiter(rpcData.from)
|
||||
return await this.utxos(rpcData)
|
||||
//
|
||||
// case 'getUser':
|
||||
// user = await this.validators.ensureUser(rpcData)
|
||||
// await this.rateLimit.limiter(rpcData.from)
|
||||
// return await this.getUser(rpcData, user)
|
||||
//
|
||||
// case 'updateUser':
|
||||
// user = await this.validators.ensureTargetUserOrAdmin(rpcData)
|
||||
// await this.rateLimit.limiter(rpcData.from)
|
||||
// return await this.updateUser(rpcData, user)
|
||||
//
|
||||
// case 'deleteUser':
|
||||
// user = await this.validators.ensureTargetUserOrAdmin(rpcData)
|
||||
// await this.rateLimit.limiter(rpcData.from)
|
||||
// return await this.deleteUser(rpcData, user)
|
||||
|
||||
case 'broadcast':
|
||||
await this.rateLimit.limiter(rpcData.from)
|
||||
return await this.broadcast(rpcData)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error in BCHRPC/rpcRouter()')
|
||||
@@ -204,7 +193,44 @@ class BCHRPC {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO create deleteUser()
|
||||
/**
|
||||
* @api {JSON} /bch Broadcast
|
||||
* @apiPermission public
|
||||
* @apiName Broadcast
|
||||
* @apiGroup JSON BCH
|
||||
* @apiDescription Broadcast a transaction to the BCH network.
|
||||
* The transaction should be encoded as a hexidecimal string.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "broadcast", "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000"}}
|
||||
*
|
||||
*/
|
||||
async broadcast (rpcData) {
|
||||
try {
|
||||
// console.log('createUser rpcData: ', rpcData)
|
||||
|
||||
const hex = rpcData.payload.params.hex
|
||||
|
||||
const data = await this.bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
const retObj = data
|
||||
retObj.status = 200
|
||||
|
||||
return retObj
|
||||
} catch (err) {
|
||||
// console.error('Error in createUser()')
|
||||
// throw err
|
||||
|
||||
// Return an error response
|
||||
return {
|
||||
success: false,
|
||||
status: 422,
|
||||
message: err.message,
|
||||
endpoint: 'broadcast'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BCHRPC
|
||||
|
||||
@@ -14,7 +14,7 @@ class RateLimit {
|
||||
// Set default rate limit options.
|
||||
this.defaultOptions = {
|
||||
interval: { min: 1 },
|
||||
max: 1000, // 1000 RPM while prototyping.
|
||||
max: 600, // 600 RPM while prototyping.
|
||||
onLimitReached: this.onLimitReached
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ class RateLimit {
|
||||
onLimitReached () {
|
||||
try {
|
||||
const error = new Error() // Establish provided options as the default options.
|
||||
error.message = 'Too many requests, please try again later.'
|
||||
error.message = 'Too many requests, please slow down your requests.'
|
||||
error.status = 429
|
||||
throw error
|
||||
} catch (error) {
|
||||
|
||||
@@ -143,6 +143,45 @@ class BCHRESTController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} /bch/broadcast Broadcast
|
||||
* @apiName Broadcast
|
||||
* @apiGroup REST BCH
|
||||
* @apiDescription Broadcast a transaction to the BCH network.
|
||||
* The transaction should be encoded as a hexidecimal string.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* curl -H "Content-Type: application/json" -X POST -d '{ "hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0704ffff001d0104ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac00000000" }' localhost:5001/bch/broadcast
|
||||
*
|
||||
* @apiSuccessExample {json} Success-Response:
|
||||
* HTTP/1.1 200 OK
|
||||
* {
|
||||
* success:true,
|
||||
* data: <data>
|
||||
* }
|
||||
*
|
||||
* @apiError UnprocessableEntity Missing required parameters
|
||||
*
|
||||
* @apiErrorExample {json} Error-Response:
|
||||
* HTTP/1.1 422 Unprocessable Entity
|
||||
* {
|
||||
* "status": 422,
|
||||
* "error": "Unprocessable Entity"
|
||||
* }
|
||||
*/
|
||||
async broadcast (ctx) {
|
||||
try {
|
||||
const hex = ctx.request.body.hex
|
||||
|
||||
const txid = await _this.bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
ctx.body = txid
|
||||
} catch (err) {
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
|
||||
@@ -48,6 +48,7 @@ class BCHRouter {
|
||||
this.router.post('/transactions', this.bchRESTController.transactions)
|
||||
this.router.post('/balance', this.bchRESTController.balance)
|
||||
this.router.post('/utxos', this.bchRESTController.utxos)
|
||||
this.router.post('/broadcast', this.bchRESTController.broadcast)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(this.router.routes())
|
||||
|
||||
@@ -69,7 +69,7 @@ describe('#rate-limit', () => {
|
||||
assert.equal(error.status, 429)
|
||||
assert.include(
|
||||
error.message,
|
||||
'Too many requests, please try again later.'
|
||||
'Too many requests, please slow down your requests.'
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -101,7 +101,7 @@ describe('#rate-limit', () => {
|
||||
} catch (error) {
|
||||
assert.include(
|
||||
error.message,
|
||||
'Too many requests, please try again later.'
|
||||
'Too many requests, please slow down your requests.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -118,6 +118,25 @@ describe('#BCHRPC', () => {
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should route to the broadcast method', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'broadcast').resolves(true)
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'broadcast'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
rpcData.from = 'Origin request'
|
||||
|
||||
const result = await uut.bchRouter(rpcData)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return 500 status on routing issue', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut, 'transactions').rejects(new Error('test error'))
|
||||
@@ -284,4 +303,54 @@ describe('#BCHRPC', () => {
|
||||
assert.equal(response.endpoint, 'utxos')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(uut.bchjs.RawTransactions, 'sendRawTransaction')
|
||||
.resolves({ success: true })
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'broadcast',
|
||||
addresses: 'testData'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.broadcast(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, true)
|
||||
assert.equal(response.status, 200)
|
||||
})
|
||||
|
||||
it('should return an error for invalid input', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.RawTransactions, 'sendRawTransaction')
|
||||
.rejects(new Error('Invalid data'))
|
||||
|
||||
// Generate the parsed data that the main router would pass to this
|
||||
// endpoint.
|
||||
const id = uid()
|
||||
const rpcCall = jsonrpc.request(id, 'bch', {
|
||||
endpoint: 'broadcast',
|
||||
addresses: 'testHex'
|
||||
})
|
||||
const jsonStr = JSON.stringify(rpcCall, null, 2)
|
||||
const rpcData = jsonrpc.parse(jsonStr)
|
||||
|
||||
const response = await uut.broadcast(rpcData)
|
||||
// console.log('response: ', response)
|
||||
|
||||
assert.equal(response.success, false)
|
||||
assert.equal(response.status, 422)
|
||||
assert.equal(response.message, 'Invalid data')
|
||||
assert.equal(response.endpoint, 'broadcast')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -163,6 +163,42 @@ describe('#BCH-REST-Router', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#broadcast', () => {
|
||||
it('should return data from bchjs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(uut.bchjs.RawTransactions, 'sendRawTransaction')
|
||||
.resolves({ success: true })
|
||||
|
||||
ctx.request.body = {
|
||||
hex: 'testData'
|
||||
}
|
||||
|
||||
await uut.broadcast(ctx)
|
||||
// console.log('ctx.body: ', ctx.body)
|
||||
|
||||
assert.equal(ctx.body.success, true)
|
||||
})
|
||||
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.bchjs.RawTransactions, 'sendRawTransaction')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
ctx.request.body = {
|
||||
hex: 'testData'
|
||||
}
|
||||
|
||||
await uut.broadcast(ctx)
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#handleError', () => {
|
||||
it('should pass an error message', () => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user