From 483705f89196be77d0e71b870074eb2ca38126de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Neboj=C5=A1a=20Obradovi=C4=87?= Date: Fri, 17 Aug 2018 17:02:22 +0200 Subject: [PATCH] Version 1.3.8 (#471) * Catchup with develop --- modules/Blockchain.js | 4 +- modules/Blockchain/Ethereum/Transactions.js | 29 +- modules/Blockchain/Ethereum/index.js | 5 +- modules/EventEmitter.js | 24 +- .../command/dc/dc-escrow-cancel-command.js | 1 + .../dh/dh-data-read-request-free-command.js | 167 +++++++ ...dh-offer-replication-parameters-command.js | 2 +- .../dh-read-data-location-request-command.js | 10 +- .../dv/dv-data-read-request-command.js | 8 + .../dv/dv-data-read-response-free-command.js | 146 ++++++ modules/controller/dh-controller.js | 15 + modules/controller/dv-controller.js | 36 ++ modules/importer.js | 3 +- ot-node.js | 14 +- package-lock.json | 2 +- package.json | 2 +- postman/OriginTrail.postman_collection.json | 466 ++++++++++++++++++ postman/ot-env.postman_environment.json | 95 ++++ 18 files changed, 998 insertions(+), 31 deletions(-) create mode 100644 modules/command/dh/dh-data-read-request-free-command.js create mode 100644 modules/command/dv/dv-data-read-response-free-command.js create mode 100644 postman/OriginTrail.postman_collection.json create mode 100644 postman/ot-env.postman_environment.json diff --git a/modules/Blockchain.js b/modules/Blockchain.js index 0e07232e4d..a4b73451e6 100644 --- a/modules/Blockchain.js +++ b/modules/Blockchain.js @@ -154,8 +154,8 @@ class Blockchain { * @param {number} - importId * @returns {Promise} */ - cancelEscrow(dhWallet, importId) { - return this.blockchain.cancelEscrow(dhWallet, importId); + cancelEscrow(dhWallet, importId, dhIsSender) { + return this.blockchain.cancelEscrow(dhWallet, importId, dhIsSender); } /** diff --git a/modules/Blockchain/Ethereum/Transactions.js b/modules/Blockchain/Ethereum/Transactions.js index ced7764ac5..9ee1fb2cae 100644 --- a/modules/Blockchain/Ethereum/Transactions.js +++ b/modules/Blockchain/Ethereum/Transactions.js @@ -22,15 +22,25 @@ class Transactions { this.queue = new Queue((async (args, cb) => { const { transaction, future } = args; try { - const delta = (Date.now() - this.lastTransactionTime); - if (delta < 2000) { - await sleep.sleep(2000); - } - const result = await this._sendTransaction(transaction); - if (result.status === '0x0') { - future.reject(result); - } else { - future.resolve(result); + for (let i = 0; i < 3; i += 1) { + try { + // eslint-disable-next-line no-await-in-loop + const result = await this._sendTransaction(transaction); + if (result.status === '0x0') { + future.reject(result); + break; + } else { + future.resolve(result); + break; + } + } catch (error) { + this.log.trace(`Nonce too low / underpriced detected. Retrying. ${error.toString()}`); + if (!error.toString().includes('nonce too low') && !error.toString().includes('underpriced')) { + throw new Error(error); + } + // eslint-disable-next-line no-await-in-loop + await sleep.sleep(2000); + } } } catch (e) { future.reject(e); @@ -71,6 +81,7 @@ class Transactions { this.log.warn(`ETH balance running low! Your balance: ${currentBalance.toString()} wei, while minimum required is: ${requiredAmount.toString()} wei`); } + this.log.trace(`Sending transaction to blockchain, nonce ${newTransaction.options.nonce}, balance is ${currentBalance.toString()}`); return this.web3.eth.sendSignedTransaction(`0x${serializedTx}`); } diff --git a/modules/Blockchain/Ethereum/index.js b/modules/Blockchain/Ethereum/index.js index b6b4eb246e..8b548e7ac0 100644 --- a/modules/Blockchain/Ethereum/index.js +++ b/modules/Blockchain/Ethereum/index.js @@ -368,7 +368,7 @@ class Ethereum { * @param {number} - importId * @returns {Promise} */ - cancelEscrow(dhWallet, importId) { + cancelEscrow(dhWallet, importId, dhIsSender) { const options = { gasLimit: this.web3.utils.toHex(this.config.gas_limit), gasPrice: this.web3.utils.toHex(this.config.gas_price), @@ -380,8 +380,9 @@ class Ethereum { this.escrowContractAbi, 'cancelEscrow', [ - dhWallet, importId, + dhWallet, + dhIsSender, ], options, ); diff --git a/modules/EventEmitter.js b/modules/EventEmitter.js index d70e3f1b25..d1e3501f63 100644 --- a/modules/EventEmitter.js +++ b/modules/EventEmitter.js @@ -135,7 +135,7 @@ class EventEmitter { } data.response.send(res); }).catch((error) => { - logger.error(`Failed to get trail for query ${data.query}`); + logger.error(`Failed to get trail for query ${JSON.stringify(data.query)}`); notifyError(error); data.response.status(500); data.response.send({ @@ -167,7 +167,7 @@ class EventEmitter { }); this._on('api-get-imports', (data) => { - logger.info(`Get imports triggered with query ${data.query}`); + logger.info(`Get imports triggered with query ${JSON.stringify(data.query)}`); product.getImports(data.query).then((res) => { if (res.length === 0) { data.response.status(204); @@ -176,7 +176,7 @@ class EventEmitter { } data.response.send(res); }).catch((error) => { - logger.error(`Failed to get imports for query ${data.query}`); + logger.error(`Failed to get imports for query ${JSON.stringify(data.query)}`); notifyError(error); data.response.status(500); data.response.send({ @@ -186,7 +186,7 @@ class EventEmitter { }); this._on('api-query', (data) => { - logger.info(`Get veritces triggered with query ${data.query}`); + logger.info(`Get veritces triggered with query ${JSON.stringify(data.query)}`); product.getVertices(data.query).then((res) => { if (res.length === 0) { data.response.status(204); @@ -195,11 +195,11 @@ class EventEmitter { } data.response.send(res); }).catch((error) => { - logger.error(`Failed to get vertices for query ${data.query}`); + logger.error(`Failed to get vertices for query ${JSON.stringify(data.query)}`); notifyError(error); data.response.status(500); data.response.send({ - message: `Failed to get vertices for query ${data.query}`, + message: `Failed to get vertices for query ${JSON.stringify(data.query)}`, }); }); }); @@ -225,10 +225,10 @@ class EventEmitter { blockchain.getRootHash(dcWallet, importId).then((res) => { data.response.send(res); }).catch((err) => { - logger.error(`Failed to get root hash for query ${data.query}`); + logger.error(`Failed to get root hash for query ${JSON.stringify(data.query)}`); notifyError(err); data.response.status(500); - data.response.send(`Failed to get root hash for query ${data.query}`); // TODO rethink about status codes + data.response.send(`Failed to get root hash for query ${JSON.stringify(data.query)}`); // TODO rethink about status codes }); }); @@ -345,7 +345,7 @@ class EventEmitter { import_id, root_hash, total_documents, - wallet, + wallet, // TODO: Sender's wallet is ignored for now. vertices, } = response; @@ -355,7 +355,7 @@ class EventEmitter { .create({ import_id, root_hash, - data_provider_wallet: wallet, + data_provider_wallet: config.node_wallet, import_timestamp: new Date(), total_documents, data_size: dataSize, @@ -931,7 +931,7 @@ class EventEmitter { logger.warn(returnMessage); return; } - await dhService.handleDataReadRequest(message); + await dhController.handleDataReadRequestFree(message); }); // async @@ -951,7 +951,7 @@ class EventEmitter { } try { - await dvService.handleDataReadResponse(message); + await dvController.handleDataReadResponseFree(message); } catch (error) { logger.warn(`Failed to process data read response. ${error}.`); notifyError(error); diff --git a/modules/command/dc/dc-escrow-cancel-command.js b/modules/command/dc/dc-escrow-cancel-command.js index ff626e5276..30154481d9 100644 --- a/modules/command/dc/dc-escrow-cancel-command.js +++ b/modules/command/dc/dc-escrow-cancel-command.js @@ -20,6 +20,7 @@ class DCEscrowCancelCommand extends Command { await this.blockchain.cancelEscrow( dhWallet, importId, + false, ); await this.network.kademlia().sendVerifyImportResponse({ status: 'fail', diff --git a/modules/command/dh/dh-data-read-request-free-command.js b/modules/command/dh/dh-data-read-request-free-command.js new file mode 100644 index 0000000000..df97d02610 --- /dev/null +++ b/modules/command/dh/dh-data-read-request-free-command.js @@ -0,0 +1,167 @@ +const Models = require('../../../models/index'); +const Command = require('../command'); + +const BN = require('bn.js'); +const Utilities = require('../../Utilities'); +const ImportUtilities = require('../../ImportUtilities'); +const Graph = require('../../Graph'); + +/** + * Free read request command. + */ +class DHDataReadRequestFreeCommand extends Command { + constructor(ctx) { + super(ctx); + this.logger = ctx.logger; + this.graphStorage = ctx.graphStorage; + this.config = ctx.config; + this.web3 = ctx.web3; + this.network = ctx.network; + this.notifyError = ctx.notifyError; + } + + /** + * Executes command and produces one or more events + * @param command + * @param transaction + */ + async execute(command, transaction) { + const { + message, + } = command.data; + + /* + message: { + id: REPLY_ID, + import_id: IMPORT_ID, + wallet: DH_WALLET, + nodeId: KAD_ID + } + */ + + // TODO in order to avoid getting a different import. + const { + nodeId, wallet, id, import_id, + } = message; + try { + // Check is it mine offer. + const networkReplyModel = await Models.network_replies.find({ where: { id } }); + if (!networkReplyModel) { + throw Error(`Couldn't find reply with ID ${id}.`); + } + + const offer = networkReplyModel.data; + + if (networkReplyModel.receiver_wallet !== wallet && + networkReplyModel.receiver_identity) { + throw Error('Sorry not your read request'); + } + + // TODO: Only one import ID used. Later we'll support replication from multiple imports. + // eslint-disable-next-line + const importId = import_id; + + const verticesPromise = this.graphStorage.findVerticesByImportId(importId); + const edgesPromise = this.graphStorage.findEdgesByImportId(importId); + + const values = await Promise.all([verticesPromise, edgesPromise]); + const vertices = values[0]; + const edges = values[1]; + + ImportUtilities.unpackKeys(vertices, edges); + + const dataInfo = await Models.data_info.findOne({ + where: { + import_id: importId, + }, + }); + + if (!dataInfo) { + throw Error(`Failed to get data info for import ID ${importId}.`); + } + + ImportUtilities.deleteInternal(vertices); + + // Get replication key and then encrypt data. + const holdingDataModel = await Models.holding_data.find({ where: { id: importId } }); + + if (holdingDataModel) { + const holdingData = holdingDataModel.get({ plain: true }); + const dataPublicKey = holdingData.data_public_key; + const replicationPrivateKey = holdingData.distribution_private_key; + + Graph.decryptVertices( + vertices.filter(vertex => vertex.vertex_type !== 'CLASS'), + dataPublicKey, + ); + } + + /* + dataReadResponseObject = { + message: { + id: REPLY_ID + wallet: DH_WALLET, + nodeId: KAD_ID + agreementStatus: CONFIRMED/REJECTED, + data_provider_wallet, + encryptedData: { … } + }, + messageSignature: { + c: …, + r: …, + s: … + } + } + */ + + const replyMessage = { + id, + wallet: this.config.node_wallet, + nodeId: this.config.identity, + data_provider_wallet: dataInfo.data_provider_wallet, + agreementStatus: 'CONFIRMED', + data: { + vertices, + edges, + }, + import_id: importId, // TODO: Temporal. Remove it. + }; + const dataReadResponseObject = { + message: replyMessage, + messageSignature: Utilities.generateRsvSignature( + JSON.stringify(replyMessage), + this.web3, + this.config.node_private_key, + ), + }; + + await this.network.kademlia().sendDataReadResponse(dataReadResponseObject, nodeId); + } catch (e) { + const errorMessage = `Failed to process data read request. ${e}.`; + this.logger.warn(errorMessage); + this.notifyError(e); + await this.network.kademlia().sendDataReadResponse({ + status: 'FAIL', + message: errorMessage, + }, nodeId); + } + + return Command.empty(); + } + + /** + * Builds default command + * @param map + * @returns {{add, data: *, delay: *, deadline: *}} + */ + default(map) { + const command = { + name: 'dhDataReadRequestFreeCommand', + transactional: true, + }; + Object.assign(command, map); + return command; + } +} + +module.exports = DHDataReadRequestFreeCommand; diff --git a/modules/command/dh/dh-offer-replication-parameters-command.js b/modules/command/dh/dh-offer-replication-parameters-command.js index 01535e4356..20a16c251b 100644 --- a/modules/command/dh/dh-offer-replication-parameters-command.js +++ b/modules/command/dh/dh-offer-replication-parameters-command.js @@ -30,7 +30,7 @@ class DHOfferReplicationParametersCommand extends Command { } = command.data; const encryptedVertices = importResult.vertices.filter(vertex => vertex.vertex_type !== 'CLASS'); - ImportUtilities.sort(encryptedVertices, '_dc_key'); + ImportUtilities.sort(encryptedVertices); const litigationBlocks = Challenge.getBlocks(encryptedVertices, 32); const litigationBlocksMerkleTree = new MerkleTree(litigationBlocks); const litigationRootHash = litigationBlocksMerkleTree.getRoot(); diff --git a/modules/command/dh/dh-read-data-location-request-command.js b/modules/command/dh/dh-read-data-location-request-command.js index 52bb0697b6..d30ea7f652 100644 --- a/modules/command/dh/dh-read-data-location-request-command.js +++ b/modules/command/dh/dh-read-data-location-request-command.js @@ -65,6 +65,14 @@ class DHReadDataLocationRequestCommand extends Command { const nodeId = this.config.identity; const dataPrice = 100000; // TODO add to configuration + // TODO: Temporarily allow sending raw data (free read). + // Merge with all imports + imports.forEach((importId) => { + if (!replicatedImportIds.includes(importId)) { + replicatedImportIds.push(importId); + } + }); + const dataInfos = await Models.data_info.findAll({ where: { import_id: { @@ -83,7 +91,7 @@ class DHReadDataLocationRequestCommand extends Command { }); if (importObjects.length === 0) { - this.logger.warn(`Zero import size for IDs ${JSON.stringify(replicatedImportIds)}.`); + this.logger.trace(`Didn't find imports for query ${JSON.stringify(msgQuery)}.`); return Command.empty(); } diff --git a/modules/command/dv/dv-data-read-request-command.js b/modules/command/dv/dv-data-read-request-command.js index 6f0d5e0b84..2c8e744918 100644 --- a/modules/command/dv/dv-data-read-request-command.js +++ b/modules/command/dv/dv-data-read-request-command.js @@ -38,6 +38,14 @@ class DVDataReadRequestCommand extends Command { } */ + const dataInfo = await Models.data_info.findOne({ + where: { import_id: importId }, + }); + if (dataInfo) { + this.logger.trace(`I've already stored data for import ID ${importId}. Purchase ignored.`); + return Command.empty(); + } + const offer = await Models.network_query_responses.findOne({ where: { query_id: queryId, diff --git a/modules/command/dv/dv-data-read-response-free-command.js b/modules/command/dv/dv-data-read-response-free-command.js new file mode 100644 index 0000000000..36ae205bd6 --- /dev/null +++ b/modules/command/dv/dv-data-read-response-free-command.js @@ -0,0 +1,146 @@ +const bytes = require('utf8-length'); + +const Models = require('../../../models/index'); +const Command = require('../command'); +const ImportUtilities = require('../../ImportUtilities'); + +/** + * Handles data read response for free. + */ +class DVDataReadResponseFreeCommand extends Command { + constructor(ctx) { + super(ctx); + this.logger = ctx.logger; + this.config = ctx.config; + this.network = ctx.network; + this.web3 = ctx.web3; + this.blockchain = ctx.blockchain; + this.remoteControl = ctx.remoteControl; + this.notifyError = ctx.notifyError; + this.importer = ctx.importer; + } + + /** + * Executes command and produces one or more events + * @param command + * @param transaction + */ + async execute(command, transaction) { + const { + message, + } = command.data; + + + /* + message: { + id: REPLY_ID + wallet: DH_WALLET, + data_provider_wallet: DC_WALLET, + nodeId: KAD_ID + agreementStatus: CONFIRMED/REJECTED, + data: { … } + importId: IMPORT_ID, // Temporal. Remove it. + }, + */ + + // Is it the chosen one? + const replyId = message.id; + const { + import_id: importId, + data_provider_wallet: dcWallet, + wallet: dhWallet, + } = message; + + // Find the particular reply. + const networkQueryResponse = await Models.network_query_responses.findOne({ + where: { reply_id: replyId }, + }); + + if (!networkQueryResponse) { + throw Error(`Didn't find query reply with ID ${replyId}.`); + } + + const networkQuery = await Models.network_queries.findOne({ + where: { id: networkQueryResponse.query_id }, + }); + + if (message.agreementStatus !== 'CONFIRMED') { + networkQuery.status = 'REJECTED'; + await networkQuery.save({ fields: ['status'] }); + throw Error('Read not confirmed'); + } + + // Calculate root hash and check is it the same on the SC. + const { vertices, edges } = message.data; + + const fingerprint = await this.blockchain.getRootHash(dcWallet, importId); + + if (!fingerprint) { + const errorMessage = `Couldn't not find fingerprint for Dc ${dcWallet} and import ID ${importId}`; + this.logger.warn(errorMessage); + networkQuery.status = 'FAILED'; + await networkQuery.save({ fields: ['status'] }); + throw errorMessage; + } + + const merkle = await ImportUtilities.merkleStructure(vertices.filter(vertex => + vertex.vertex_type !== 'CLASS'), edges); + const rootHash = merkle.tree.getRoot(); + + if (fingerprint !== rootHash) { + const errorMessage = `Fingerprint root hash doesn't match with one from data. Root hash ${rootHash}, first DH ${dhWallet}, import ID ${importId}`; + this.logger.warn(errorMessage); + networkQuery.status = 'FAILED'; + await networkQuery.save({ fields: ['status'] }); + throw errorMessage; + } + + try { + await this.importer.importJSON({ + vertices: message.data.vertices, + edges: message.data.edges, + import_id: importId, + wallet: dcWallet, + }, true); + } catch (error) { + this.logger.warn(`Failed to import JSON. ${error}.`); + this.notifyError(error); + networkQuery.status = 'FAILED'; + await networkQuery.save({ fields: ['status'] }); + return Command.empty(); + } + + this.logger.info(`Import ID ${importId} imported successfully.`); + this.remoteControl.readNotification(`Import ID ${importId} imported successfully.`); + + const dataSize = bytes(JSON.stringify(vertices)); + await Models.data_info.create({ + import_id: importId, + total_documents: vertices.length, + root_hash: rootHash, + data_provider_wallet: dcWallet, + import_timestamp: new Date(), + data_size: dataSize, + }); + + return Command.empty(); + } + + /** + * Builds default DVDataReadRequestCommand + * @param map + * @returns {{add, data: *, delay: *, deadline: *}} + */ + default(map) { + const command = { + name: 'dvDataReadResponseFreeCommand', + delay: 0, + deadline_at: Date.now() + (5 * 60 * 1000), + transactional: false, + }; + Object.assign(command, map); + return command; + } +} + +module.exports = DVDataReadResponseFreeCommand; diff --git a/modules/controller/dh-controller.js b/modules/controller/dh-controller.js index a4b9829ab4..034787ba31 100644 --- a/modules/controller/dh-controller.js +++ b/modules/controller/dh-controller.js @@ -86,6 +86,21 @@ class DHController { }, }); } + + /** + * Sends dhDataReadRequestFreeCommand to the queue. + * @param message Message received from network + * @returns {Promise} + */ + async handleDataReadRequestFree(message) { + await this.commandExecutor.add({ + name: 'dhDataReadRequestFreeCommand', + transactional: false, + data: { + message, + }, + }); + } } module.exports = DHController; diff --git a/modules/controller/dv-controller.js b/modules/controller/dv-controller.js index 27400d0590..41692d2c12 100644 --- a/modules/controller/dv-controller.js +++ b/modules/controller/dv-controller.js @@ -99,6 +99,42 @@ class DVController { transactional: false, }); } + async handleDataReadResponseFree(message) { + /* + message: { + id: REPLY_ID + wallet: DH_WALLET, + nodeId: KAD_ID + agreementStatus: CONFIRMED/REJECTED, + encryptedData: { … } + importId: IMPORT_ID, // Temporal. Remove it. + }, + */ + + // Is it the chosen one? + const replyId = message.id; + + // Find the particular reply. + const networkQueryResponse = await Models.network_query_responses.findOne({ + where: { reply_id: replyId }, + }); + + if (!networkQueryResponse) { + throw Error(`Didn't find query reply with ID ${replyId}.`); + } + + const networkQuery = await Models.network_queries.findOne({ + where: { id: networkQueryResponse.query_id }, + }); + await this.commandExecutor.add({ + name: 'dvDataReadResponseFreeCommand', + delay: 0, + data: { + message, + }, + transactional: false, + }); + } } module.exports = DVController; diff --git a/modules/importer.js b/modules/importer.js index b2c050f7ac..7e9ed25c0c 100644 --- a/modules/importer.js +++ b/modules/importer.js @@ -144,7 +144,8 @@ class Importer { edges = Graph.sortVertices(edges); vertices = Graph.sortVertices(vertices); - const merkle = await ImportUtilities.merkleStructure(vertices, edges); + const merkle = await ImportUtilities.merkleStructure(vertices.filter(vertex => + vertex.vertex_type !== 'CLASS'), edges); this.log.info(`Import id: ${import_id}`); this.log.info(`Import hash: ${merkle.tree.getRoot()}`); diff --git a/ot-node.js b/ot-node.js index ee19604d86..4cc7c96fc3 100644 --- a/ot-node.js +++ b/ot-node.js @@ -17,6 +17,7 @@ const RemoteControl = require('./modules/RemoteControl'); const corsMiddleware = require('restify-cors-middleware'); const BN = require('bn.js'); const bugsnag = require('bugsnag'); +const ip = require('ip'); const awilix = require('awilix'); @@ -546,7 +547,13 @@ class OTNode { server.pre(cors.preflight); server.use(cors.actual); - server.listen(parseInt(config.node_rpc_port, 10), config.node_rpc_ip, () => { + // TODO: Temp solution to listen all adapters in local net. + let serverListenAddress = config.node_rpc_ip; + if (ip.isLoopback(serverListenAddress)) { + serverListenAddress = '0.0.0.0'; + } + + server.listen(parseInt(config.node_rpc_port, 10), serverListenAddress, () => { log.notify(`API exposed at ${server.url}`); }); @@ -564,6 +571,11 @@ class OTNode { const request_ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; const remote_access = config.remote_access_whitelist; + // TODO: Temp solution for local network. Ignore whitelist. + if (ip.isLoopback(config.node_rpc_ip)) { + return true; + } + if (!remote_access.includes(request_ip)) { res.status(403); res.send({ diff --git a/package-lock.json b/package-lock.json index 8080ecc232..54f6ed917f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "origintrail-node", - "version": "1.3.5", + "version": "1.3.8", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b98f81c94a..2912f7f9ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "origintrail-node", - "version": "1.3.5", + "version": "1.3.8", "description": "OriginTrail node", "main": ".eslintrc.js", "config": { diff --git a/postman/OriginTrail.postman_collection.json b/postman/OriginTrail.postman_collection.json new file mode 100644 index 0000000000..267e2b794d --- /dev/null +++ b/postman/OriginTrail.postman_collection.json @@ -0,0 +1,466 @@ +{ + "info": { + "_postman_id": "ccb96309-f511-4335-99ed-9d23fad79a03", + "name": "OriginTrail", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "/api/import", + "event": [ + { + "listen": "test", + "script": { + "id": "ed9219f5-1661-4e6f-b15b-dcc3532d1792", + "type": "text/javascript", + "exec": [ + "var data = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"import_id\", data.import_id);" + ] + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "data_id", + "value": "0x653fc4bca63f6de06a1bd47a2434545db4f95af3c1d1d6593aff72b4a11bbba5", + "type": "text", + "disabled": true + }, + { + "key": "importtype", + "value": "{{importtype}}", + "type": "text" + }, + { + "key": "importfile", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/api/import?", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "import" + ], + "query": [ + { + "key": "data_id", + "value": "0x174ff26aa6d6c6be0e8cbcb1d14a1f82924f6afa9425dfdf16b971e12b3bbd04", + "disabled": true + } + ] + }, + "description": "https://docs.origintrail.io/en/latest/introduction-to-api.html#api-import-post" + }, + "response": [] + }, + { + "name": "/api/trail", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{baseUrl}}/api/trail?vertex_type={{vertex_type}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "trail" + ], + "query": [ + { + "key": "uid", + "value": "{{uid}}", + "disabled": true + }, + { + "key": "vertex_type", + "value": "{{vertex_type}}" + } + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-trail-get" + }, + "response": [] + }, + { + "name": "/api/replication", + "event": [ + { + "listen": "test", + "script": { + "id": "2b457da8-3339-415a-b249-e34366d01bf3", + "type": "text/javascript", + "exec": [ + "var data = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"replication_id\", data.replication_id);" + ] + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "import_id", + "value": "{{import_id}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/api/replication", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "replication" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-replication-post" + }, + "response": [] + }, + { + "name": "/api/fingerprint", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{baseUrl}}/api/fingerprint?dc_wallet={{dc_wallet}}&import_id={{import_id}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "fingerprint" + ], + "query": [ + { + "key": "dc_wallet", + "value": "{{dc_wallet}}" + }, + { + "key": "import_id", + "value": "{{import_id}}" + } + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-fingerprint-get" + }, + "response": [] + }, + { + "name": "/api/replication/:{replication_id}", + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "import_id", + "value": "0xa3f2b70718bbc5f4a136fda16ef77e5af568c33e1ccfeabee979cc90d88553b1", + "type": "text" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/api/replication/{{replication_id}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "replication", + "{{replication_id}}" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-replication-replication-id-get" + }, + "response": [] + }, + { + "name": "/api/query/network", + "event": [ + { + "listen": "test", + "script": { + "id": "ff7307dc-b908-49e3-9fa9-e468c8af9344", + "type": "text/javascript", + "exec": [ + "var data = JSON.parse(responseBody);", + "postman.setEnvironmentVariable(\"query_id\", data.query_id);" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\":\n [\n {\n \"path\": \"vertex_type\",\n \"value\": \"ACTOR\",\n \"opcode\": \"EQ\"\n }\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/query/network", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "query", + "network" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-network-post" + }, + "response": [] + }, + { + "name": "/api/query/{query_id}/responses", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{baseUrl}}/api/query/{{query_id}}/responses", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "query", + "{{query_id}}", + "responses" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-query-id-responses-get" + }, + "response": [] + }, + { + "name": "api/query/network/{query_param} ", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{baseUrl}}api/query/network/{query_param}", + "host": [ + "{{baseUrl}}api" + ], + "path": [ + "query", + "network", + "{query_param}" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-network-query-param-get" + }, + "response": [] + }, + { + "name": "/api/read/network", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "query_id", + "value": "{{query_id}}", + "sessionValue": "{{query_id}}", + "type": "text" + }, + { + "key": "reply_id", + "value": "99090be7-91f3-4a34-93e6-dbb632c0ef10", + "sessionValue": "99090be7-91f3-4a34-93e6-dbb632c0ef10", + "type": "text" + }, + { + "key": "import_id", + "value": "{{import_id}}", + "sessionValue": "{{import_id}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/api/read/network", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "read", + "network" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-read-network-post" + }, + "response": [] + }, + { + "name": "/api/query/local", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\":\n [\n {\n \"path\": \"vertex_type\",\n \"value\": \"BATCH\",\n \"opcode\": \"EQ\"\n }\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/query/local", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "query", + "local" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-local-post" + }, + "response": [] + }, + { + "name": "/api/query/local/import", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"query\":\n [\n {\n \"path\": \"vertex_type\",\n \"value\": \"BATCH\",\n \"opcode\": \"EQ\"\n }\n ]\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/query/local/import", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "query", + "local", + "import" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-local-import-post" + }, + "response": [] + }, + { + "name": "/api/query/local/import:{import_id}", + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "{{baseUrl}}/api/query/local/import/{{import_id}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "query", + "local", + "import", + "{{import_id}}" + ] + }, + "description": "http://docs.origintrail.io/en/latest/introduction-to-api.html#api-query-local-import-import-id-get" + }, + "response": [] + }, + { + "name": "/api/deposit", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{ \n\t\"atrac_amount\": 10\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/deposit", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "deposit" + ] + } + }, + "response": [] + }, + { + "name": "/api/withdraw", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{ \n\t\"atrac_amount\": 10\n\t\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/withdraw", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "withdraw" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/postman/ot-env.postman_environment.json b/postman/ot-env.postman_environment.json new file mode 100644 index 0000000000..5a0167dcb6 --- /dev/null +++ b/postman/ot-env.postman_environment.json @@ -0,0 +1,95 @@ +{ + "id": "f298bc4a-a9b5-431c-a9b2-9a1516084e77", + "name": "ot-env", + "values": [ + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "http://{{NODE_IP}}:{{NODE_PORT}}", + "key": "baseUrl", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "127.0.0.1", + "key": "NODE_IP", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "8900", + "key": "NODE_PORT", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "0xcb6fd5f21505b42c070f62e82b9d643ec1d0af20ea05719eb5f6b259ecb06e08", + "key": "import_id", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "GS1", + "key": "importtype", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "urn:ot:object:actor:id:Hospital1", + "key": "uid", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "20029bc7-9346-43c3-a7e0-16c77e1ecaa3", + "key": "replication_id", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "0xAC13a2D4cCD1d7Ba29517F96b4eD84D652be5EbD", + "key": "dc_wallet", + "enabled": true + }, + { + "description": { + "content": "", + "type": "text/plain" + }, + "value": "BATCH", + "key": "vertex_type", + "enabled": true + }, + { + "value": "8760d911-2584-4331-8e1c-99492e51d623", + "key": "query_id", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2018-08-15T06:59:52.554Z", + "_postman_exported_using": "Postman/6.2.4" +} \ No newline at end of file