diff --git a/TODO.txt b/TODO.txt index 7ec5061..b2d817b 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,6 +2,6 @@ - Proposal with spent input should be tagged as invalid or removed - Cron job to broadcast accepted txps that failed to broadcast (we may need to track broadcast attempts for this). - Payment protocol - +- Automatically create ./db directory - check parameters for KEY at storage diff --git a/app.js b/app.js new file mode 100644 index 0000000..85ba2e2 --- /dev/null +++ b/app.js @@ -0,0 +1,187 @@ +'use strict'; + +var _ = require('lodash'); +var async = require('async'); +var log = require('npmlog'); +var CopayServer = require('./lib/server'); +var express = require('express'); +var querystring = require('querystring'); + +log.debug = log.verbose; +log.level = 'debug'; + +var POST_LIMIT = 1024 * 100 /* Max POST 100 kb */ ; + +CopayServer.initialize(); + + +var app = express(); +app.use(function(req, res, next) { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, DELETE'); + res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type,Authorization'); + next(); +}); +var allowCORS = function(req, res, next) { + if ('OPTIONS' == req.method) { + res.send(200); + res.end(); + return; + } + next(); +} +app.use(allowCORS); + +var port = process.env.COPAY_PORT || 3001; +var router = express.Router(); + +function returnError(err, res) { + if (err instanceof CopayServer.ClientError) { + var status = (err.code == 'NOTAUTHORIZED') ? 401 : 400; + res.status(status).json({ + code: err.code, + error: err.message, + }).end(); + } else { + var code, message; + if (_.isObject(err)) { + code = err.code; + message = err.message; + } + res.status(code || 500).json({ + error: message || err.toString(), + }).end(); + } +}; + +function getCredentials(req) { + var identity = req.header('x-identity'); + if (!identity) return; + + return { + copayerId: identity, + }; +}; + +function getServerWithAuth(req, res, cb) { + var credentials = getCredentials(req); + + CopayServer.getInstanceWithAuth({ + copayerId: credentials.copayerId, + message: 'hello world!', + signature: '3045022100addd20e5413865d65d561ad2979f2289a40d52594b1f804840babd9a63e4ebbf02204b86285e1fcab02df772e7a1325fc4b511ecad79a8f80a2bd1ad8bfa858ac3d4', + }, function(err, server) { + if (err) return returnError(err, res); + return cb(server); + }); +}; + +function authenticate() { + return true; +}; + +function parsePost(req, res, cb) { + var queryData = ''; + req.on('data', function(data) { + queryData += data; + if (queryData.length > POST_LIMIT) { + queryData = ''; + res.writeHead(413, { + 'Content-Type': 'text/plain' + }); + res.end(); + req.connection.destroy(); + } + }).on('end', function() { + try { + var params = JSON.parse(queryData); + cb(params); + } catch (ex) { + returnError({ + code: 400, + message: 'Unable to parse request' + }, res); + } + }); +}; + +router.post('/v1/wallets/', function(req, res) { + parsePost(req, res, function(params) { + var server = CopayServer.getInstance(); + server.createWallet(params, function(err, wallet) { + if (err) returnError(err, res); + + res.json(wallet); + }); + }); +}); + +router.post('/v1/wallets/:id/join/', function(req, res) { + parsePost(req, res, function(params) { + params.walletId = req.params['id']; + var server = CopayServer.getInstance(); + server.joinWallet(params, function(err) { + if (err) returnError(err, res); + + res.end(); + }); + }); +}); + +router.get('/v1/wallets/', function(req, res) { + var credentials = getCredentials(req); + + CopayServer.getInstanceWithAuth(getCredentials(req) { + copayerId: credentials.copayerId, + message: 'hello world!', + signature: '3045022100addd20e5413865d65d561ad2979f2289a40d52594b1f804840babd9a63e4ebbf02204b86285e1fcab02df772e7a1325fc4b511ecad79a8f80a2bd1ad8bfa858ac3d4', + }, function(err, server) { + if (err) return returnError(err, res); + server.getWallet({}, function(err, wallet) { + if (err) returnError(err, res); + res.json(wallet); + }); + }); +}); + +router.post('/v1/addresses/', function(req, res) { + parsePost(req, res, function(params) { + getServerWithAuth(req, res, function(server) { + server.createAddress(params, function(err, address) { + if (err) returnError(err, res); + res.json(address); + }); + }); + }); +}); + +router.get('/v1/addresses/', function(req, res) { + getServerWithAuth(req, res, function(server) { + server.getAddresses({}, function(err, addresses) { + if (err) returnError(err, res); + res.json(addresses); + }); + }); +}); + +router.get('/v1/balance/', function(req, res) { + getServerWithAuth(req, res, function(server) { + server.getBalance({}, function(err, balance) { + if (err) returnError(err, res); + res.json(balance); + }); + }); +}); + +// TODO: DEBUG only! +router.get('/v1/dump', function(req, res) { + var server = CopayServer.getInstance(); + server.storage._dump(function() { + res.end(); + }); +}); + +app.use('/copay/api', router); + +app.listen(port); +console.log('Copay service running on port ' + port); diff --git a/lib/server.js b/lib/server.js index bf07053..0c7fd35 100644 --- a/lib/server.js +++ b/lib/server.js @@ -48,6 +48,10 @@ CopayServer.initialize = function(opts) { initialized = true; }; +CopayServer.getInstance = function() { + return new CopayServer(); +}; + /** * Gets an instance of the server after authenticating the copayer. * @param {Object} opts @@ -62,10 +66,10 @@ CopayServer.getInstanceWithAuth = function(opts, cb) { var server = new CopayServer(); server.storage.fetchCopayerLookup(opts.copayerId, function(err, copayer) { if (err) return cb(err); - if (!copayer) return cb('Copayer not found'); + if (!copayer) return cb(new ClientError('NOTAUTHORIZED', 'Copayer not found')); var isValid = server._verifySignature(opts.message, opts.signature, copayer.signingPubKey); - if (!isValid) return cb('Invalid signature'); + if (!isValid) return cb(new ClientError('NOTAUTHORIZED', 'Invalid signature')); server.copayerId = opts.copayerId; server.walletId = copayer.walletId; @@ -653,5 +657,5 @@ CopayServer.prototype.getTxs = function(opts, cb) { - module.exports = CopayServer; +module.exports.ClientError = ClientError; diff --git a/package.json b/package.json index 30ce41f..06f0978 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "async": "^0.9.0", "bitcore": "*", "bitcore-explorers": "^0.9.1", + "commander": "^2.6.0", "express": "^4.10.0", "inherits": "^2.0.1", "leveldown": "^0.10.0", @@ -27,7 +28,8 @@ "lodash": "^2.4.1", "npmlog": "^0.1.1", "preconditions": "^1.0.7", - "uuid":"*" + "request": "^2.53.0", + "uuid": "*" }, "devDependencies": { "chai": "^1.9.1",