mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 03:25:09 +00:00
customize off chain data: use custom listener as docker container
This commit is contained in:
parent
c02c49be55
commit
dbabd42c9e
27 changed files with 5089 additions and 0 deletions
4
off_chain_data/addMarbles.json
Normal file
4
off_chain_data/addMarbles.json
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"nextMarbleNumber": 120,
|
||||||
|
"numberMarblesToAdd": 20
|
||||||
|
}
|
||||||
87
off_chain_data/addMarbles2.js
Normal file
87
off_chain_data/addMarbles2.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* addMarbles.js will add random sample data to blockchain.
|
||||||
|
*
|
||||||
|
* $ node addMarbles.js
|
||||||
|
*
|
||||||
|
* addMarbles will add 10 marbles by default with a starting marble name of "marble100".
|
||||||
|
* Additional marbles will be added by incrementing the number at the end of the marble name.
|
||||||
|
*
|
||||||
|
* The properties for adding marbles are stored in addMarbles.json. This file will be created
|
||||||
|
* during the first execution of the utility if it does not exist. The utility can be run
|
||||||
|
* multiple times without changing the properties. The nextMarbleNumber will be incremented and
|
||||||
|
* stored in the JSON file.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "nextMarbleNumber": 100,
|
||||||
|
* "numberMarblesToAdd": 10
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets, Gateway } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const addMarblesConfigFile = path.resolve(__dirname, 'addMarbles.json');
|
||||||
|
|
||||||
|
const colors=[ 'blue', 'red', 'yellow', 'green', 'white', 'purple' ];
|
||||||
|
const owners=[ 'tom', 'fred', 'julie', 'james', 'janet', 'henry', 'alice', 'marie', 'sam', 'debra', 'nancy'];
|
||||||
|
const sizes=[ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ];
|
||||||
|
const docType='marble'
|
||||||
|
|
||||||
|
const config = require('./config.json');
|
||||||
|
const channelid = config.channelid;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse the connection profile. This would be the path to the file downloaded
|
||||||
|
// from the IBM Blockchain Platform operational console.
|
||||||
|
const ccpPath = path.resolve(__dirname, '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
||||||
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Configure a wallet. This wallet must already be primed with an identity that
|
||||||
|
// the application can use to interact with the peer node.
|
||||||
|
const walletPath = path.resolve(__dirname, 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
|
||||||
|
// Create a new gateway, and connect to the gateway peer node(s). The identity
|
||||||
|
// specified must already exist in the specified wallet.
|
||||||
|
const gateway = new Gateway();
|
||||||
|
await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
// Get the network channel that the smart contract is deployed to.
|
||||||
|
const network = await gateway.getNetwork(channelid);
|
||||||
|
|
||||||
|
// Get the smart contract from the network channel.
|
||||||
|
const contract = network.getContract('marbles');
|
||||||
|
|
||||||
|
var randomColor = Math.floor(Math.random() * (6));
|
||||||
|
var randomOwner = Math.floor(Math.random() * (11));
|
||||||
|
var randomSize = Math.floor(Math.random() * (10));
|
||||||
|
|
||||||
|
// Submit the 'initMarble' transaction to the smart contract, and wait for it
|
||||||
|
// to be committed to the ledger.
|
||||||
|
var counter = Math.floor(Math.random() * (10000));
|
||||||
|
await contract.submitTransaction('initMarble', docType+counter, colors[randomColor], ''+sizes[randomSize], owners[randomOwner]);
|
||||||
|
console.log("Adding marble: " + docType + counter + " owner:" + owners[randomOwner] + " color:" + colors[randomColor] + " size:" + '' + sizes[randomSize] );
|
||||||
|
|
||||||
|
await gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to submit transaction: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
55
off_chain_data/enrollAdmin.tmp.js
Normal file
55
off_chain_data/enrollAdmin.tmp.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const FabricCAServices = require('fabric-ca-client');
|
||||||
|
const { Wallets, X509WalletMixin } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
// load the network configuration
|
||||||
|
const ccpPath = path.resolve(__dirname, 'files', 'connection-org1.json');
|
||||||
|
let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Create a new CA client for interacting with the CA.
|
||||||
|
const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
|
||||||
|
const ca = new FabricCAServices(caURL);
|
||||||
|
|
||||||
|
// Create a new file system based wallet for managing identities.
|
||||||
|
const walletPath = path.join(process.cwd(), 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
console.log(`Wallet path: ${walletPath}`);
|
||||||
|
|
||||||
|
// Check to see if we've already enrolled the admin user.
|
||||||
|
const adminExists = await wallet.get('admin');
|
||||||
|
if (adminExists) {
|
||||||
|
console.log('An identity for the admin user "admin" already exists in the wallet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enroll the admin user, and import the new identity into the wallet.
|
||||||
|
const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' });
|
||||||
|
const x509Identity = {
|
||||||
|
credentials: {
|
||||||
|
certificate: enrollment.certificate,
|
||||||
|
privateKey: enrollment.key.toBytes(),
|
||||||
|
},
|
||||||
|
mspId: 'Org1MSP',
|
||||||
|
type: 'X.509',
|
||||||
|
};
|
||||||
|
await wallet.put('admin', x509Identity);
|
||||||
|
console.log('Successfully enrolled admin user "admin" and imported it into the wallet');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to enroll admin user "admin": ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
11
off_chain_data/listener/Dockerfile
Normal file
11
off_chain_data/listener/Dockerfile
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
FROM node:8.17.0
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD ["node", "blockEventListener.js"]
|
||||||
87
off_chain_data/listener/addMarbles.js
Normal file
87
off_chain_data/listener/addMarbles.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* addMarbles.js will add random sample data to blockchain.
|
||||||
|
*
|
||||||
|
* $ node addMarbles.js
|
||||||
|
*
|
||||||
|
* addMarbles will add 10 marbles by default with a starting marble name of "marble100".
|
||||||
|
* Additional marbles will be added by incrementing the number at the end of the marble name.
|
||||||
|
*
|
||||||
|
* The properties for adding marbles are stored in addMarbles.json. This file will be created
|
||||||
|
* during the first execution of the utility if it does not exist. The utility can be run
|
||||||
|
* multiple times without changing the properties. The nextMarbleNumber will be incremented and
|
||||||
|
* stored in the JSON file.
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "nextMarbleNumber": 100,
|
||||||
|
* "numberMarblesToAdd": 10
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets, Gateway } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const addMarblesConfigFile = path.resolve(__dirname, 'addMarbles.json');
|
||||||
|
|
||||||
|
const colors=[ 'blue', 'red', 'yellow', 'green', 'white', 'purple' ];
|
||||||
|
const owners=[ 'tom', 'fred', 'julie', 'james', 'janet', 'henry', 'alice', 'marie', 'sam', 'debra', 'nancy'];
|
||||||
|
const sizes=[ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ];
|
||||||
|
const docType='marble'
|
||||||
|
|
||||||
|
const config = require('./config.json');
|
||||||
|
const channelid = config.channelid;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Parse the connection profile. This would be the path to the file downloaded
|
||||||
|
// from the IBM Blockchain Platform operational console.
|
||||||
|
const ccpPath = path.resolve(__dirname, '..', '..', 'test-network','organizations','peerOrganizations','org1.example.com', 'connection-org1.json');
|
||||||
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Configure a wallet. This wallet must already be primed with an identity that
|
||||||
|
// the application can use to interact with the peer node.
|
||||||
|
const walletPath = path.resolve(__dirname, 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
|
||||||
|
// Create a new gateway, and connect to the gateway peer node(s). The identity
|
||||||
|
// specified must already exist in the specified wallet.
|
||||||
|
const gateway = new Gateway();
|
||||||
|
await gateway.connect(ccp, { wallet, identity: 'listenerUser', discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
// Get the network channel that the smart contract is deployed to.
|
||||||
|
const network = await gateway.getNetwork(channelid);
|
||||||
|
|
||||||
|
// Get the smart contract from the network channel.
|
||||||
|
const contract = network.getContract('marbles');
|
||||||
|
|
||||||
|
var randomColor = Math.floor(Math.random() * (6));
|
||||||
|
var randomOwner = Math.floor(Math.random() * (11));
|
||||||
|
var randomSize = Math.floor(Math.random() * (10));
|
||||||
|
|
||||||
|
// Submit the 'initMarble' transaction to the smart contract, and wait for it
|
||||||
|
// to be committed to the ledger.
|
||||||
|
var counter = Math.floor(Math.random() * (10000));
|
||||||
|
await contract.submitTransaction('initMarble', docType+counter, colors[randomColor], ''+sizes[randomSize], owners[randomOwner]);
|
||||||
|
console.log("Adding marble: " + docType + counter + " owner:" + owners[randomOwner] + " color:" + colors[randomColor] + " size:" + '' + sizes[randomSize] );
|
||||||
|
|
||||||
|
await gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to submit transaction: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
183
off_chain_data/listener/blockEventListener.js
Normal file
183
off_chain_data/listener/blockEventListener.js
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
blockEventListener.js is an nodejs application to listen for block events from
|
||||||
|
a specified channel.
|
||||||
|
|
||||||
|
Configuration is stored in config.json:
|
||||||
|
|
||||||
|
{
|
||||||
|
"peer_name": "peer0.org1.example.com",
|
||||||
|
"channelid": "mychannel",
|
||||||
|
"use_couchdb":false,
|
||||||
|
"couchdb_address": "http://admin:adminpw@localhost:5990"
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_name: target peer for the listener
|
||||||
|
channelid: channel name for block events
|
||||||
|
use_couchdb: if set to true, events will be stored in a local couchdb
|
||||||
|
couchdb_address: local address for an off chain couchdb database
|
||||||
|
|
||||||
|
Note: If use_couchdb is set to false, only a local log of events will be stored.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
node bockEventListener.js
|
||||||
|
|
||||||
|
The block event listener will log events received to the console and write event blocks to
|
||||||
|
a log file based on the channelid and chaincode name.
|
||||||
|
|
||||||
|
The event listener stores the next block to retrieve in a file named nextblock.txt. This file
|
||||||
|
is automatically created and initialized to zero if it does not exist.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets, Gateway } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const couchdbutil = require('./couchdbutil.js');
|
||||||
|
const blockProcessing = require('./blockProcessing.js');
|
||||||
|
|
||||||
|
const config = require('./config.json');
|
||||||
|
const channelid = config.channelid;
|
||||||
|
const peer_name = config.peer_name;
|
||||||
|
const use_couchdb = config.use_couchdb;
|
||||||
|
const couchdb_address = config.couchdb_address;
|
||||||
|
|
||||||
|
const configPath = path.resolve(__dirname, 'nextblock.txt');
|
||||||
|
|
||||||
|
const nano = require('nano')(couchdb_address);
|
||||||
|
|
||||||
|
// simple map to hold blocks for processing
|
||||||
|
class BlockMap {
|
||||||
|
constructor() {
|
||||||
|
this.list = []
|
||||||
|
}
|
||||||
|
get(key) {
|
||||||
|
key = parseInt(key, 10).toString();
|
||||||
|
return this.list[`block${key}`];
|
||||||
|
}
|
||||||
|
set(key, value) {
|
||||||
|
this.list[`block${key}`] = value;
|
||||||
|
}
|
||||||
|
remove(key) {
|
||||||
|
key = parseInt(key, 10).toString();
|
||||||
|
delete this.list[`block${key}`];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ProcessingMap = new BlockMap()
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// initialize the next block to be 0
|
||||||
|
let nextBlock = 0;
|
||||||
|
|
||||||
|
// check to see if there is a next block already defined
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
// read file containing the next block to read
|
||||||
|
nextBlock = fs.readFileSync(configPath, 'utf8');
|
||||||
|
} else {
|
||||||
|
// store the next block as 0
|
||||||
|
fs.writeFileSync(configPath, parseInt(nextBlock, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new file system based wallet for managing identities.
|
||||||
|
const walletPath = path.join(process.cwd(), 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
console.log(`Wallet path: ${walletPath}`);
|
||||||
|
|
||||||
|
// Check to see if we've already enrolled the user.
|
||||||
|
const userExists = await wallet.get('listenerUser');
|
||||||
|
if (!userExists) {
|
||||||
|
console.log('An identity for the user "listenerUser" does not exist in the wallet');
|
||||||
|
console.log('Run the enrollUser.js application before retrying');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the connection profile. This would be the path to the file downloaded
|
||||||
|
// from the IBM Blockchain Platform operational console.
|
||||||
|
const ccpPath = path.resolve(__dirname, 'files', 'connection-org1.json');
|
||||||
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
// Create a new gateway for connecting to our peer node.
|
||||||
|
const gateway = new Gateway();
|
||||||
|
await gateway.connect(ccp, { wallet, identity: 'listenerUser', discovery: { enabled: true, asLocalhost: false } });
|
||||||
|
|
||||||
|
// Get the network (channel) our contract is deployed to.
|
||||||
|
const network = await gateway.getNetwork('mychannel');
|
||||||
|
|
||||||
|
const listener = await network.addBlockListener(
|
||||||
|
async (event) => {
|
||||||
|
// Add the block to the processing map by block number
|
||||||
|
const blockNum = event.blockNumber.low;
|
||||||
|
const block = event.blockData;
|
||||||
|
await ProcessingMap.set(block.header.number, block);
|
||||||
|
|
||||||
|
console.log(`Added block ${blockNum} to ProcessingMap`)
|
||||||
|
},
|
||||||
|
// set the starting block for the listener
|
||||||
|
{ filtered: false, startBlock: parseInt(nextBlock, 10) }
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Listening for block events, nextblock: ${nextBlock}`);
|
||||||
|
|
||||||
|
// start processing, looking for entries in the ProcessingMap
|
||||||
|
processPendingBlocks(ProcessingMap);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to evaluate transaction: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// listener function to check for blocks in the ProcessingMap
|
||||||
|
async function processPendingBlocks(ProcessingMap) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
|
||||||
|
// get the next block number from nextblock.txt
|
||||||
|
let nextBlockNumber = fs.readFileSync(configPath, 'utf8');
|
||||||
|
let processBlock;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
// get the next block to process from the ProcessingMap
|
||||||
|
processBlock = ProcessingMap.get(nextBlockNumber)
|
||||||
|
if (processBlock == undefined) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("call block processing event")
|
||||||
|
try {
|
||||||
|
await blockProcessing.processBlockEvent(channelid, processBlock, use_couchdb, nano)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to process block: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if successful, remove the block from the ProcessingMap
|
||||||
|
ProcessingMap.remove(nextBlockNumber);
|
||||||
|
|
||||||
|
// increment the next block number to the next block
|
||||||
|
fs.writeFileSync(configPath, parseInt(nextBlockNumber, 10) + 1)
|
||||||
|
|
||||||
|
// retrive the next block number to process
|
||||||
|
nextBlockNumber = fs.readFileSync(configPath, 'utf8');
|
||||||
|
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
processPendingBlocks(ProcessingMap);
|
||||||
|
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
216
off_chain_data/listener/blockProcessing.js
Normal file
216
off_chain_data/listener/blockProcessing.js
Normal file
|
|
@ -0,0 +1,216 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const couchdbutil = require('./couchdbutil.js');
|
||||||
|
|
||||||
|
const configPath = path.resolve(__dirname, 'nextblock.txt');
|
||||||
|
|
||||||
|
exports.processBlockEvent = async function (channelname, block, use_couchdb, nano) {
|
||||||
|
|
||||||
|
return new Promise((async (resolve, reject) => {
|
||||||
|
|
||||||
|
// reject the block if the block number is not defined
|
||||||
|
if (block.header.number == undefined) {
|
||||||
|
reject(new Error('Undefined block number'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockNumber = block.header.number
|
||||||
|
|
||||||
|
console.log(`------------------------------------------------`);
|
||||||
|
console.log(`Block Number: ${blockNumber}`);
|
||||||
|
|
||||||
|
// reject if the data is not set
|
||||||
|
if (block.data.data == undefined) {
|
||||||
|
reject(new Error('Data block is not defined'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataArray = block.data.data;
|
||||||
|
|
||||||
|
// transaction filter for each transaction in dataArray
|
||||||
|
const txSuccess = block.metadata.metadata[2];
|
||||||
|
|
||||||
|
for (var dataItem in dataArray) {
|
||||||
|
|
||||||
|
// reject if a timestamp is not set
|
||||||
|
if (dataArray[dataItem].payload.header.channel_header.timestamp == undefined) {
|
||||||
|
reject(new Error('Transaction timestamp is not defined'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx may be rejected at commit stage by peers
|
||||||
|
// only valid transactions (code=0) update the word state and off-chain db
|
||||||
|
// filter through valid tx, refer below for list of error codes
|
||||||
|
// https://github.com/hyperledger/fabric-sdk-node/blob/release-1.4/fabric-client/lib/protos/peer/transaction.proto
|
||||||
|
if (txSuccess[dataItem] !== 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = dataArray[dataItem].payload.header.channel_header.timestamp;
|
||||||
|
|
||||||
|
// continue to next tx if no actions are set
|
||||||
|
if (dataArray[dataItem].payload.data.actions == undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actions are stored as an array. In Fabric 1.4.3 only one
|
||||||
|
// action exists per tx so we may simply use actions[0]
|
||||||
|
// in case Fabric adds support for multiple actions
|
||||||
|
// a for loop is used for demonstration
|
||||||
|
const actions = dataArray[dataItem].payload.data.actions;
|
||||||
|
|
||||||
|
// iterate through all actions
|
||||||
|
for (var actionItem in actions) {
|
||||||
|
|
||||||
|
// reject if a chaincode id is not defined
|
||||||
|
if (actions[actionItem].payload.chaincode_proposal_payload.input.chaincode_spec.chaincode_id.name == undefined) {
|
||||||
|
reject(new Error('Chaincode name is not defined'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const chaincodeID = actions[actionItem].payload.chaincode_proposal_payload.input.chaincode_spec.chaincode_id.name
|
||||||
|
|
||||||
|
// reject if there is no readwrite set
|
||||||
|
if (actions[actionItem].payload.action.proposal_response_payload.extension.results.ns_rwset == undefined) {
|
||||||
|
reject(new Error('No readwrite set is defined'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const rwSet = actions[actionItem].payload.action.proposal_response_payload.extension.results.ns_rwset
|
||||||
|
|
||||||
|
for (var record in rwSet) {
|
||||||
|
|
||||||
|
// ignore lscc events
|
||||||
|
if (rwSet[record].namespace != 'lscc') {
|
||||||
|
// create object to store properties
|
||||||
|
const writeObject = new Object();
|
||||||
|
writeObject.blocknumber = blockNumber;
|
||||||
|
writeObject.chaincodeid = chaincodeID;
|
||||||
|
writeObject.timestamp = timestamp;
|
||||||
|
writeObject.values = rwSet[record].rwset.writes;
|
||||||
|
|
||||||
|
console.log(`Transaction Timestamp: ${writeObject.timestamp}`);
|
||||||
|
console.log(`ChaincodeID: ${writeObject.chaincodeid}`);
|
||||||
|
console.log(writeObject.values);
|
||||||
|
|
||||||
|
const logfilePath = path.resolve(__dirname, 'nextblock.txt');
|
||||||
|
|
||||||
|
// send the object to a log file
|
||||||
|
fs.appendFileSync(channelname + '_' + chaincodeID + '.log', JSON.stringify(writeObject) + "\n");
|
||||||
|
|
||||||
|
// if couchdb is configured, then write to couchdb
|
||||||
|
if (use_couchdb) {
|
||||||
|
try {
|
||||||
|
await writeValuesToCouchDBP(nano, channelname, writeObject);
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// update the nextblock.txt file to retrieve the next block
|
||||||
|
fs.writeFileSync(configPath, parseInt(blockNumber, 10) + 1)
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeValuesToCouchDBP(nano, channelname, writeObject) {
|
||||||
|
|
||||||
|
return new Promise((async (resolve, reject) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// define the database for saving block events by key - this emulates world state
|
||||||
|
const dbname = channelname + '_' + writeObject.chaincodeid;
|
||||||
|
// define the database for saving all block events - this emulates history
|
||||||
|
const historydbname = channelname + '_' + writeObject.chaincodeid + '_history';
|
||||||
|
// set values to the array of values received
|
||||||
|
const values = writeObject.values;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var sequence in values) {
|
||||||
|
let keyvalue =
|
||||||
|
values[
|
||||||
|
sequence
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
keyvalue.is_delete ==
|
||||||
|
true
|
||||||
|
) {
|
||||||
|
await couchdbutil.deleteRecord(
|
||||||
|
nano,
|
||||||
|
dbname,
|
||||||
|
keyvalue.key
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
isJSON(
|
||||||
|
keyvalue.value
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// insert or update value by key - this emulates world state behavior
|
||||||
|
await couchdbutil.writeToCouchDB(
|
||||||
|
nano,
|
||||||
|
dbname,
|
||||||
|
keyvalue.key,
|
||||||
|
JSON.parse(
|
||||||
|
keyvalue.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add additional fields for history
|
||||||
|
keyvalue.timestamp =
|
||||||
|
writeObject.timestamp;
|
||||||
|
keyvalue.blocknumber = parseInt(
|
||||||
|
writeObject.blocknumber,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
keyvalue.sequence = parseInt(
|
||||||
|
sequence,
|
||||||
|
10
|
||||||
|
);
|
||||||
|
|
||||||
|
await couchdbutil.writeToCouchDB(
|
||||||
|
nano,
|
||||||
|
historydbname,
|
||||||
|
null,
|
||||||
|
keyvalue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to write to couchdb: ${error}`);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function isJSON(value) {
|
||||||
|
try {
|
||||||
|
JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
7
off_chain_data/listener/config.json
Normal file
7
off_chain_data/listener/config.json
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"peer_name": "peer0.org1.example.com",
|
||||||
|
"channelid": "mychannel",
|
||||||
|
"use_couchdb":true,
|
||||||
|
"create_history_log":true,
|
||||||
|
"couchdb_address": "http://admin:adminpw@offchaindb:5984"
|
||||||
|
}
|
||||||
111
off_chain_data/listener/couchdbutil.js
Normal file
111
off_chain_data/listener/couchdbutil.js
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
exports.createDatabaseIfNotExists = function (nano, dbname) {
|
||||||
|
|
||||||
|
return new Promise((async (resolve, reject) => {
|
||||||
|
await nano.db.get(dbname, async function (err, body) {
|
||||||
|
if (err) {
|
||||||
|
if (err.statusCode == 404) {
|
||||||
|
await nano.db.create(dbname, function (err, body) {
|
||||||
|
if (!err) {
|
||||||
|
resolve(true);
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.writeToCouchDB = async function (nano, dbname, key, value) {
|
||||||
|
|
||||||
|
return new Promise((async (resolve, reject) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createDatabaseIfNotExists(nano, dbname);
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = nano.use(dbname);
|
||||||
|
|
||||||
|
// If a key is not specified, then this is an insert
|
||||||
|
if (key == null) {
|
||||||
|
db.insert(value, async function (err, body, header) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// If a key is specified, then attempt to retrieve the record by key
|
||||||
|
db.get(key, async function (err, body) {
|
||||||
|
// parse the value
|
||||||
|
const updateValue = value;
|
||||||
|
// if the record was found, then update the revision to allow the update
|
||||||
|
if (err == null) {
|
||||||
|
updateValue._rev = body._rev
|
||||||
|
}
|
||||||
|
// update or insert the value
|
||||||
|
db.insert(updateValue, key, async function (err, body, header) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
exports.deleteRecord = async function (nano, dbname, key) {
|
||||||
|
|
||||||
|
return new Promise((async (resolve, reject) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createDatabaseIfNotExists(nano, dbname);
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = nano.use(dbname);
|
||||||
|
|
||||||
|
// If a key is specified, then attempt to retrieve the record by key
|
||||||
|
db.get(key, async function (err, body) {
|
||||||
|
|
||||||
|
// if the record was found, then update the revision to allow the update
|
||||||
|
if (err == null) {
|
||||||
|
|
||||||
|
let revision = body._rev
|
||||||
|
|
||||||
|
// update or insert the value
|
||||||
|
db.destroy(key, revision, async function (err, body, header) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(true);
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
26
off_chain_data/listener/docker-compose.yml
Normal file
26
off_chain_data/listener/docker-compose.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net_test:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
offchaindb:
|
||||||
|
container_name: offchaindb
|
||||||
|
image: couchdb:3.1
|
||||||
|
environment:
|
||||||
|
- COUCHDB_USER=admin
|
||||||
|
- COUCHDB_PASSWORD=adminpw
|
||||||
|
ports:
|
||||||
|
- "5990:5984"
|
||||||
|
networks:
|
||||||
|
- net_test
|
||||||
|
|
||||||
|
offchainlistener:
|
||||||
|
container_name: block-listener
|
||||||
|
restart: always
|
||||||
|
build: .
|
||||||
|
networks:
|
||||||
|
- net_test
|
||||||
|
depends_on:
|
||||||
|
- offchaindb
|
||||||
55
off_chain_data/listener/enrollAdmin.js
Normal file
55
off_chain_data/listener/enrollAdmin.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const FabricCAServices = require('fabric-ca-client');
|
||||||
|
const { Wallets, X509WalletMixin } = require('fabric-network');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
// load the network configuration
|
||||||
|
const ccpPath = path.resolve(__dirname, 'files', 'connection-org1.json');
|
||||||
|
let ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Create a new CA client for interacting with the CA.
|
||||||
|
const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
|
||||||
|
const ca = new FabricCAServices(caURL);
|
||||||
|
|
||||||
|
// Create a new file system based wallet for managing identities.
|
||||||
|
const walletPath = path.join(process.cwd(), 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
console.log(`Wallet path: ${walletPath}`);
|
||||||
|
|
||||||
|
// Check to see if we've already enrolled the admin user.
|
||||||
|
const adminExists = await wallet.get('admin');
|
||||||
|
if (adminExists) {
|
||||||
|
console.log('An identity for the admin user "admin" already exists in the wallet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enroll the admin user, and import the new identity into the wallet.
|
||||||
|
const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' });
|
||||||
|
const x509Identity = {
|
||||||
|
credentials: {
|
||||||
|
certificate: enrollment.certificate,
|
||||||
|
privateKey: enrollment.key.toBytes(),
|
||||||
|
},
|
||||||
|
mspId: 'Org1MSP',
|
||||||
|
type: 'X.509',
|
||||||
|
};
|
||||||
|
await wallet.put('admin', x509Identity);
|
||||||
|
console.log('Successfully enrolled admin user "admin" and imported it into the wallet');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to enroll admin user "admin": ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "test-network-org1",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"client": {
|
||||||
|
"organization": "Org1",
|
||||||
|
"connection": {
|
||||||
|
"timeout": {
|
||||||
|
"peer": {
|
||||||
|
"endorser": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizations": {
|
||||||
|
"Org1": {
|
||||||
|
"mspid": "Org1MSP",
|
||||||
|
"peers": [
|
||||||
|
"peer0.org1.example.com"
|
||||||
|
],
|
||||||
|
"certificateAuthorities": [
|
||||||
|
"ca.org1.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peers": {
|
||||||
|
"peer0.org1.example.com": {
|
||||||
|
"url": "grpcs://localhost:7051",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": "-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIURi+1fFD+OW/8hkW3oGqvmyrKe5cwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTIzOTAwWhcNMzUwODMwMTIzOTAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA8o\nTFVDKJAYRFhme5CIe4NoUC6dBA2n0wu00gdU4wxYN3t5jgvNfND9rbz69IxcTx25\nA56qWnG6M8TxP3oCKyOjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBTwjphcJEd3xvAI/OpZXDCd/aoMxDAKBggqhkjOPQQD\nAgNHADBEAiAu63pJTw5kr7ua/MDVIcZ+eoqVDbyRVku5yp8ETwyxiwIgGfiyOJEm\nMIeKkhyNrYSwqCLUR0wUp+H9dagC9X18MDI=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"grpcOptions": {
|
||||||
|
"ssl-target-name-override": "peer0.org1.example.com",
|
||||||
|
"hostnameOverride": "peer0.org1.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificateAuthorities": {
|
||||||
|
"ca.org1.example.com": {
|
||||||
|
"url": "https://localhost:7054",
|
||||||
|
"caName": "ca-org1",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": ["-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIURi+1fFD+OW/8hkW3oGqvmyrKe5cwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTIzOTAwWhcNMzUwODMwMTIzOTAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA8o\nTFVDKJAYRFhme5CIe4NoUC6dBA2n0wu00gdU4wxYN3t5jgvNfND9rbz69IxcTx25\nA56qWnG6M8TxP3oCKyOjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBTwjphcJEd3xvAI/OpZXDCd/aoMxDAKBggqhkjOPQQD\nAgNHADBEAiAu63pJTw5kr7ua/MDVIcZ+eoqVDbyRVku5yp8ETwyxiwIgGfiyOJEm\nMIeKkhyNrYSwqCLUR0wUp+H9dagC9X18MDI=\n-----END CERTIFICATE-----\n"]
|
||||||
|
},
|
||||||
|
"httpOptions": {
|
||||||
|
"verify": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
off_chain_data/listener/files/connection-org1.json
Normal file
49
off_chain_data/listener/files/connection-org1.json
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "test-network-org1",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"client": {
|
||||||
|
"organization": "Org1",
|
||||||
|
"connection": {
|
||||||
|
"timeout": {
|
||||||
|
"peer": {
|
||||||
|
"endorser": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizations": {
|
||||||
|
"Org1": {
|
||||||
|
"mspid": "Org1MSP",
|
||||||
|
"peers": [
|
||||||
|
"peer0.org1.example.com"
|
||||||
|
],
|
||||||
|
"certificateAuthorities": [
|
||||||
|
"ca.org1.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peers": {
|
||||||
|
"peer0.org1.example.com": {
|
||||||
|
"url": "grpcs://peer0.org1.example.com:7051",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": "-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIURi+1fFD+OW/8hkW3oGqvmyrKe5cwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTIzOTAwWhcNMzUwODMwMTIzOTAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA8o\nTFVDKJAYRFhme5CIe4NoUC6dBA2n0wu00gdU4wxYN3t5jgvNfND9rbz69IxcTx25\nA56qWnG6M8TxP3oCKyOjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBTwjphcJEd3xvAI/OpZXDCd/aoMxDAKBggqhkjOPQQD\nAgNHADBEAiAu63pJTw5kr7ua/MDVIcZ+eoqVDbyRVku5yp8ETwyxiwIgGfiyOJEm\nMIeKkhyNrYSwqCLUR0wUp+H9dagC9X18MDI=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"grpcOptions": {
|
||||||
|
"ssl-target-name-override": "peer0.org1.example.com",
|
||||||
|
"hostnameOverride": "peer0.org1.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificateAuthorities": {
|
||||||
|
"ca.org1.example.com": {
|
||||||
|
"url": "https://ca_org{ORG}:7054",
|
||||||
|
"caName": "ca-org1",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": ["-----BEGIN CERTIFICATE-----\nMIICJjCCAc2gAwIBAgIURi+1fFD+OW/8hkW3oGqvmyrKe5cwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTIzOTAwWhcNMzUwODMwMTIzOTAw\nWjBwMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExDzANBgNV\nBAcTBkR1cmhhbTEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEcMBoGA1UEAxMT\nY2Eub3JnMS5leGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA8o\nTFVDKJAYRFhme5CIe4NoUC6dBA2n0wu00gdU4wxYN3t5jgvNfND9rbz69IxcTx25\nA56qWnG6M8TxP3oCKyOjRTBDMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAG\nAQH/AgEBMB0GA1UdDgQWBBTwjphcJEd3xvAI/OpZXDCd/aoMxDAKBggqhkjOPQQD\nAgNHADBEAiAu63pJTw5kr7ua/MDVIcZ+eoqVDbyRVku5yp8ETwyxiwIgGfiyOJEm\nMIeKkhyNrYSwqCLUR0wUp+H9dagC9X18MDI=\n-----END CERTIFICATE-----\n"]
|
||||||
|
},
|
||||||
|
"httpOptions": {
|
||||||
|
"verify": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "test-network-org2",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"client": {
|
||||||
|
"organization": "Org2",
|
||||||
|
"connection": {
|
||||||
|
"timeout": {
|
||||||
|
"peer": {
|
||||||
|
"endorser": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizations": {
|
||||||
|
"Org2": {
|
||||||
|
"mspid": "Org2MSP",
|
||||||
|
"peers": [
|
||||||
|
"peer0.org2.example.com"
|
||||||
|
],
|
||||||
|
"certificateAuthorities": [
|
||||||
|
"ca.org2.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peers": {
|
||||||
|
"peer0.org2.example.com": {
|
||||||
|
"url": "grpcs://localhost:9051",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": "-----BEGIN CERTIFICATE-----\nMIICHzCCAcWgAwIBAgIUGt1JOlE1zJ31uO7t8ed5n4d7V0YwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yMDA5MDIxMjM5MDBaFw0zNTA4MzAxMjM5MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATrGlQj2uW/FXjr\nZcrSzHMY4yHNyeEmfMJfKWyzrOpHIbVcUDfvmOlyUwmWN9hNrELw8V8I+I1LKdtx\nE4mcha0To0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUa06sGaGWJeWPZK42DmgCaXrkrCMwCgYIKoZIzj0EAwIDSAAwRQIh\nAMvQVC/bz0fchGBLzJaaUan3EnHyCsgJ+3jXefKwRT4aAiArRMyTkFJtcUP428YR\n/4qB0m37B+GiMHgrjZCGBNmVDA==\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"grpcOptions": {
|
||||||
|
"ssl-target-name-override": "peer0.org2.example.com",
|
||||||
|
"hostnameOverride": "peer0.org2.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificateAuthorities": {
|
||||||
|
"ca.org2.example.com": {
|
||||||
|
"url": "https://localhost:8054",
|
||||||
|
"caName": "ca-org2",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": ["-----BEGIN CERTIFICATE-----\nMIICHzCCAcWgAwIBAgIUGt1JOlE1zJ31uO7t8ed5n4d7V0YwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yMDA5MDIxMjM5MDBaFw0zNTA4MzAxMjM5MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATrGlQj2uW/FXjr\nZcrSzHMY4yHNyeEmfMJfKWyzrOpHIbVcUDfvmOlyUwmWN9hNrELw8V8I+I1LKdtx\nE4mcha0To0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUa06sGaGWJeWPZK42DmgCaXrkrCMwCgYIKoZIzj0EAwIDSAAwRQIh\nAMvQVC/bz0fchGBLzJaaUan3EnHyCsgJ+3jXefKwRT4aAiArRMyTkFJtcUP428YR\n/4qB0m37B+GiMHgrjZCGBNmVDA==\n-----END CERTIFICATE-----\n"]
|
||||||
|
},
|
||||||
|
"httpOptions": {
|
||||||
|
"verify": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
49
off_chain_data/listener/files/connection-org2.json
Normal file
49
off_chain_data/listener/files/connection-org2.json
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "test-network-org2",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"client": {
|
||||||
|
"organization": "Org2",
|
||||||
|
"connection": {
|
||||||
|
"timeout": {
|
||||||
|
"peer": {
|
||||||
|
"endorser": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizations": {
|
||||||
|
"Org2": {
|
||||||
|
"mspid": "Org2MSP",
|
||||||
|
"peers": [
|
||||||
|
"peer0.org2.example.com"
|
||||||
|
],
|
||||||
|
"certificateAuthorities": [
|
||||||
|
"ca.org2.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peers": {
|
||||||
|
"peer0.org2.example.com": {
|
||||||
|
"url": "grpcs://peer0.org2.example.com:9051",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": "-----BEGIN CERTIFICATE-----\nMIICHzCCAcWgAwIBAgIUGt1JOlE1zJ31uO7t8ed5n4d7V0YwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yMDA5MDIxMjM5MDBaFw0zNTA4MzAxMjM5MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATrGlQj2uW/FXjr\nZcrSzHMY4yHNyeEmfMJfKWyzrOpHIbVcUDfvmOlyUwmWN9hNrELw8V8I+I1LKdtx\nE4mcha0To0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUa06sGaGWJeWPZK42DmgCaXrkrCMwCgYIKoZIzj0EAwIDSAAwRQIh\nAMvQVC/bz0fchGBLzJaaUan3EnHyCsgJ+3jXefKwRT4aAiArRMyTkFJtcUP428YR\n/4qB0m37B+GiMHgrjZCGBNmVDA==\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"grpcOptions": {
|
||||||
|
"ssl-target-name-override": "peer0.org2.example.com",
|
||||||
|
"hostnameOverride": "peer0.org2.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificateAuthorities": {
|
||||||
|
"ca.org2.example.com": {
|
||||||
|
"url": "https://ca_org{ORG}:8054",
|
||||||
|
"caName": "ca-org2",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": ["-----BEGIN CERTIFICATE-----\nMIICHzCCAcWgAwIBAgIUGt1JOlE1zJ31uO7t8ed5n4d7V0YwCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCVUsxEjAQBgNVBAgTCUhhbXBzaGlyZTEQMA4GA1UEBxMHSHVy\nc2xleTEZMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEcMBoGA1UEAxMTY2Eub3Jn\nMi5leGFtcGxlLmNvbTAeFw0yMDA5MDIxMjM5MDBaFw0zNTA4MzAxMjM5MDBaMGwx\nCzAJBgNVBAYTAlVLMRIwEAYDVQQIEwlIYW1wc2hpcmUxEDAOBgNVBAcTB0h1cnNs\nZXkxGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2NhLm9yZzIu\nZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATrGlQj2uW/FXjr\nZcrSzHMY4yHNyeEmfMJfKWyzrOpHIbVcUDfvmOlyUwmWN9hNrELw8V8I+I1LKdtx\nE4mcha0To0UwQzAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAd\nBgNVHQ4EFgQUa06sGaGWJeWPZK42DmgCaXrkrCMwCgYIKoZIzj0EAwIDSAAwRQIh\nAMvQVC/bz0fchGBLzJaaUan3EnHyCsgJ+3jXefKwRT4aAiArRMyTkFJtcUP428YR\n/4qB0m37B+GiMHgrjZCGBNmVDA==\n-----END CERTIFICATE-----\n"]
|
||||||
|
},
|
||||||
|
"httpOptions": {
|
||||||
|
"verify": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3783
off_chain_data/listener/package-lock.json
generated
Normal file
3783
off_chain_data/listener/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
45
off_chain_data/listener/package.json
Normal file
45
off_chain_data/listener/package.json
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"name": "offchaindata",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Offchain Data application implemented in JavaScript",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8",
|
||||||
|
"npm": ">=5"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint .",
|
||||||
|
"pretest": "npm run lint",
|
||||||
|
"test": "nyc mocha --recursive"
|
||||||
|
},
|
||||||
|
"engineStrict": true,
|
||||||
|
"author": "Hyperledger",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"fabric-ca-client": "^2.1.0",
|
||||||
|
"fabric-network": "^2.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"chai": "^4.2.0",
|
||||||
|
"eslint": "^5.9.0",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"nyc": "^13.1.0",
|
||||||
|
"sinon": "^7.1.1",
|
||||||
|
"sinon-chai": "^3.3.0"
|
||||||
|
},
|
||||||
|
"nyc": {
|
||||||
|
"exclude": [
|
||||||
|
"coverage/**",
|
||||||
|
"test/**"
|
||||||
|
],
|
||||||
|
"reporter": [
|
||||||
|
"text-summary",
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"all": true,
|
||||||
|
"check-coverage": true,
|
||||||
|
"statements": 100,
|
||||||
|
"branches": 100,
|
||||||
|
"functions": 100,
|
||||||
|
"lines": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
75
off_chain_data/listener/registerUser.js
Normal file
75
off_chain_data/listener/registerUser.js
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets, Gateway, X509WalletMixin } = require('fabric-network');
|
||||||
|
const FabricCAServices = require('fabric-ca-client');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
// load the network configuration
|
||||||
|
const ccpPath = path.resolve(__dirname, 'files', 'connection-org1.json');
|
||||||
|
const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
|
||||||
|
|
||||||
|
// Create a new CA client for interacting with the CA.
|
||||||
|
const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
|
||||||
|
const ca = new FabricCAServices(caURL);
|
||||||
|
|
||||||
|
// Create a new file system based wallet for managing identities.
|
||||||
|
const walletPath = path.join(process.cwd(), 'wallet');
|
||||||
|
const wallet = await Wallets.newFileSystemWallet(walletPath);
|
||||||
|
console.log(`Wallet path: ${walletPath}`);
|
||||||
|
|
||||||
|
// Check to see if we've already enrolled the user.
|
||||||
|
const userExists = await wallet.get('listenerUser');
|
||||||
|
if (userExists) {
|
||||||
|
console.log('An identity for the user "listenerUser" already exists in the wallet');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if we've already enrolled the admin user.
|
||||||
|
const adminIdentity = await wallet.get('admin');
|
||||||
|
if (!adminIdentity) {
|
||||||
|
console.log('An identity for the admin user "admin" does not exist in the wallet');
|
||||||
|
console.log('Run the enrollAdmin.js application before retrying');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a user object for authenticating with the CA
|
||||||
|
const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
|
||||||
|
const adminUser = await provider.getUserContext(adminIdentity, 'admin');
|
||||||
|
|
||||||
|
// Register the user, enroll the user, and import the new identity into the wallet.
|
||||||
|
const secret = await ca.register({
|
||||||
|
affiliation: 'org1.department1',
|
||||||
|
enrollmentID: 'listenerUser',
|
||||||
|
role: 'client'
|
||||||
|
}, adminUser);
|
||||||
|
const enrollment = await ca.enroll({
|
||||||
|
enrollmentID: 'listenerUser',
|
||||||
|
enrollmentSecret: secret
|
||||||
|
});
|
||||||
|
const x509Identity = {
|
||||||
|
credentials: {
|
||||||
|
certificate: enrollment.certificate,
|
||||||
|
privateKey: enrollment.key.toBytes(),
|
||||||
|
},
|
||||||
|
mspId: 'Org1MSP',
|
||||||
|
type: 'X.509',
|
||||||
|
};
|
||||||
|
await wallet.put('listenerUser', x509Identity);
|
||||||
|
console.log('Successfully registered and enrolled admin user "listenerUser" and imported it into the wallet');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to register user "listenerUser": ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
1
off_chain_data/listener/wallet/admin.id
Normal file
1
off_chain_data/listener/wallet/admin.id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"credentials":{"certificate":"-----BEGIN CERTIFICATE-----\nMIIB8jCCAZmgAwIBAgIUZK86p6mQmWUJzhv4EoY5EeGObM4wCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTI0MDAwWhcNMjEwOTAyMTI0NTAw\nWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZIzj0C\nAQYIKoZIzj0DAQcDQgAE8ANxIy5O+BU02DiF5KRMKdSmQHntJNLjKIuTMWAoZjNp\nB+ylEP4h8174OSL122lOz+j+U1NKWWSANn9MsYn2qqNgMF4wDgYDVR0PAQH/BAQD\nAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKt5l3uuPT/Q5CnezAQKaMhCEvSy\nMB8GA1UdIwQYMBaAFPCOmFwkR3fG8Aj86llcMJ39qgzEMAoGCCqGSM49BAMCA0cA\nMEQCIEpd5ipyrnWYEknfg/ydYSGTwug/SS3smTDaK0R9rZI4AiAtBO9WnfsGgBGd\ntNpJg18QdM8TH6OJglypwI2Q0H/A+Q==\n-----END CERTIFICATE-----\n","privateKey":"-----BEGIN PRIVATE KEY-----\r\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgHLXHn0jU6ZRzkdtb\r\nMaZ1VnlruDE0crtGAyaVQ6INEpOhRANCAATwA3EjLk74FTTYOIXkpEwp1KZAee0k\r\n0uMoi5MxYChmM2kH7KUQ/iHzXvg5IvXbaU7P6P5TU0pZZIA2f0yxifaq\r\n-----END PRIVATE KEY-----\r\n"},"mspId":"Org1MSP","type":"X.509","version":1}
|
||||||
1
off_chain_data/listener/wallet/listenerUser.id
Normal file
1
off_chain_data/listener/wallet/listenerUser.id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"credentials":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICjDCCAjSgAwIBAgIUOAhHDicFm3H63ACoYBVis78SoREwCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTI0MDAwWhcNMjEwOTAyMTI0NTAw\nWjBJMTAwDQYDVQQLEwZjbGllbnQwCwYDVQQLEwRvcmcxMBIGA1UECxMLZGVwYXJ0\nbWVudDExFTATBgNVBAMTDGxpc3RlbmVyVXNlcjBZMBMGByqGSM49AgEGCCqGSM49\nAwEHA0IABMvQv3LC0SBoUd0Qzulpi5smB5sX5++vK98irqcRYmcr8l3smHCDmbS4\n0/VYvp6QnQSC18zyHOf5Wt0ZS6cPzrOjgdIwgc8wDgYDVR0PAQH/BAQDAgeAMAwG\nA1UdEwEB/wQCMAAwHQYDVR0OBBYEFDZjFQb5/b8qp8nj9z6fLZ2hqjcNMB8GA1Ud\nIwQYMBaAFPCOmFwkR3fG8Aj86llcMJ39qgzEMG8GCCoDBAUGBwgBBGN7ImF0dHJz\nIjp7ImhmLkFmZmlsaWF0aW9uIjoib3JnMS5kZXBhcnRtZW50MSIsImhmLkVucm9s\nbG1lbnRJRCI6Imxpc3RlbmVyVXNlciIsImhmLlR5cGUiOiJjbGllbnQifX0wCgYI\nKoZIzj0EAwIDRgAwQwIgVZo7YqK1DCyPPSu/nfz8VWqP+g9fYrkUPqvig/AfDb8C\nH3rP+wzF09o0ddH91+TUegflXFceRaPB8uXgz07sYLE=\n-----END CERTIFICATE-----\n","privateKey":"-----BEGIN PRIVATE KEY-----\r\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgyn/2qcvazRXWCnLk\r\nNNx51uBeDAck6w2z0VgLAGBPOpahRANCAATL0L9ywtEgaFHdEM7paYubJgebF+fv\r\nryvfIq6nEWJnK/Jd7Jhwg5m0uNP1WL6ekJ0EgtfM8hzn+VrdGUunD86z\r\n-----END PRIVATE KEY-----\r\n"},"mspId":"Org1MSP","type":"X.509","version":1}
|
||||||
|
|
@ -16,5 +16,12 @@ popd
|
||||||
rm -rf wallet
|
rm -rf wallet
|
||||||
rm -rf addMarbles.json mychannel_marbles.log mychannel__lifecycle.log nextblock.txt
|
rm -rf addMarbles.json mychannel_marbles.log mychannel__lifecycle.log nextblock.txt
|
||||||
|
|
||||||
|
pushd listener
|
||||||
|
docker-compose down
|
||||||
|
rm -rf wallet
|
||||||
|
rm -rf mychannel_marbles.log mychannel__lifecycle.log nextblock.txt
|
||||||
|
rm files/*
|
||||||
|
popd
|
||||||
|
|
||||||
docker stop offchaindb
|
docker stop offchaindb
|
||||||
docker rm offchaindb
|
docker rm offchaindb
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,36 @@ pushd ../test-network
|
||||||
./network.sh down
|
./network.sh down
|
||||||
./network.sh up createChannel -ca -s couchdb
|
./network.sh up createChannel -ca -s couchdb
|
||||||
|
|
||||||
|
# COPY CPP to my working dir
|
||||||
|
cp organizations/peerOrganizations/org1.example.com/connection-org1.json ../off_chain_data/listener/files/
|
||||||
|
cp organizations/peerOrganizations/org2.example.com/connection-org2.json ../off_chain_data/listener/files/
|
||||||
|
cp organizations/peerOrganizations/org1.example.com/connection-org1-for-docker.json ../off_chain_data/listener/files/
|
||||||
|
cp organizations/peerOrganizations/org2.example.com/connection-org2-for-docker.json ../off_chain_data/listener/files/
|
||||||
|
|
||||||
|
#echo Starting offchain database
|
||||||
|
#docker run --publish 5990:5984 --detach -e COUCHDB_USER=admin -e COUCHDB_PASSWORD=adminpw --name offchaindb couchdb
|
||||||
|
|
||||||
|
popd
|
||||||
|
pushd listener
|
||||||
|
node enrollAdmin.js
|
||||||
|
node registerUser.js
|
||||||
|
|
||||||
|
mv files/connection-org1.json files/connection-org1-for-localhost.json
|
||||||
|
mv files/connection-org2.json files/connection-org2-for-localhost.json
|
||||||
|
|
||||||
|
mv files/connection-org1-for-docker.json files/connection-org1.json
|
||||||
|
mv files/connection-org2-for-docker.json files/connection-org2.json
|
||||||
|
|
||||||
|
docker-compose up -d --build
|
||||||
|
popd
|
||||||
|
pushd ../test-network
|
||||||
|
|
||||||
|
#echo Creating admin and user wallet
|
||||||
|
#popd
|
||||||
|
#node enrollAdmin.js
|
||||||
|
#node registerUser.js
|
||||||
|
#pushd ../test-network
|
||||||
|
|
||||||
export PATH=${PWD}/../bin:${PWD}:$PATH
|
export PATH=${PWD}/../bin:${PWD}:$PATH
|
||||||
export FABRIC_CFG_PATH=${PWD}/../config
|
export FABRIC_CFG_PATH=${PWD}/../config
|
||||||
|
|
||||||
|
|
|
||||||
1
off_chain_data/wallet/admin.id
Normal file
1
off_chain_data/wallet/admin.id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"credentials":{"certificate":"-----BEGIN CERTIFICATE-----\nMIIB8jCCAZmgAwIBAgIUD5BT6A6tbOI1DV6+o5n8e12I8Q0wCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTI0MzAwWhcNMjEwOTAyMTI0ODAw\nWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZIzj0C\nAQYIKoZIzj0DAQcDQgAE3Q1CrssZsPsi2kBpAA1ET9zc3lLpPyyGSQkm81UfUSL9\n7HldsNSAGhR4RAsnGWPQTtlIOMCN2vMnIxGVLIXQlaNgMF4wDgYDVR0PAQH/BAQD\nAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFGKRYFnAJqVfRKGfpL8cL+pJt2bG\nMB8GA1UdIwQYMBaAFPCOmFwkR3fG8Aj86llcMJ39qgzEMAoGCCqGSM49BAMCA0cA\nMEQCIAJE27zMnqeInlRS6+RBQiDKxLu9vc3zkLhe8jaQSvCBAiAL3hNPpvLS99yG\nZUTFtmtBE2cqnPlrQzQyIjEWDyDwAQ==\n-----END CERTIFICATE-----\n","privateKey":"-----BEGIN PRIVATE KEY-----\r\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgs2sdp3R/tqHt8KrG\r\nO5Bf6mVQBqw+SuBZ3x6oVIQLJ6ahRANCAATdDUKuyxmw+yLaQGkADURP3NzeUuk/\r\nLIZJCSbzVR9RIv3seV2w1IAaFHhECycZY9BO2Ug4wI3a8ycjEZUshdCV\r\n-----END PRIVATE KEY-----\r\n"},"mspId":"Org1MSP","type":"X.509","version":1}
|
||||||
1
off_chain_data/wallet/appUser.id
Normal file
1
off_chain_data/wallet/appUser.id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"credentials":{"certificate":"-----BEGIN CERTIFICATE-----\nMIIChDCCAiqgAwIBAgIUD75+CCeBRhGk9EcXWlBXWUw0du0wCgYIKoZIzj0EAwIw\ncDELMAkGA1UEBhMCVVMxFzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQH\nEwZEdXJoYW0xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTAyMTI0MzAwWhcNMjEwOTAyMTI0ODAw\nWjBEMTAwDQYDVQQLEwZjbGllbnQwCwYDVQQLEwRvcmcxMBIGA1UECxMLZGVwYXJ0\nbWVudDExEDAOBgNVBAMTB2FwcFVzZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\nAATQC3qTQuhSTQPX9qypSot6gDzt8KdNstH8ZbH9TVQswdkgTMqMhO822X0iWTo2\nPsK7wCFWpDaTq0rm3KlYYvyho4HNMIHKMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMB\nAf8EAjAAMB0GA1UdDgQWBBRCSGtwMHSB9NN1Pv1mCNqTqnxCcjAfBgNVHSMEGDAW\ngBTwjphcJEd3xvAI/OpZXDCd/aoMxDBqBggqAwQFBgcIAQReeyJhdHRycyI6eyJo\nZi5BZmZpbGlhdGlvbiI6Im9yZzEuZGVwYXJ0bWVudDEiLCJoZi5FbnJvbGxtZW50\nSUQiOiJhcHBVc2VyIiwiaGYuVHlwZSI6ImNsaWVudCJ9fTAKBggqhkjOPQQDAgNI\nADBFAiEAr+l/dAPgMtXPz+5CMDWUDesa9spsksjwr0u8vRtWxv8CIC+QrZNHeJId\n9N1Ubdav4bMzgntBtLCamhMm5TnE4AaP\n-----END CERTIFICATE-----\n","privateKey":"-----BEGIN PRIVATE KEY-----\r\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgaLfWkqiyUsKH0ZZm\r\nKIAQ+XgEoWIGs1ZDfiFDigtrbKKhRANCAATQC3qTQuhSTQPX9qypSot6gDzt8KdN\r\nstH8ZbH9TVQswdkgTMqMhO822X0iWTo2PsK7wCFWpDaTq0rm3KlYYvyh\r\n-----END PRIVATE KEY-----\r\n"},"mspId":"Org1MSP","type":"X.509","version":1}
|
||||||
|
|
@ -15,6 +15,17 @@ function json_ccp {
|
||||||
organizations/ccp-template.json
|
organizations/ccp-template.json
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function json_ccp_for_docker {
|
||||||
|
local PP=$(one_line_pem $4)
|
||||||
|
local CP=$(one_line_pem $5)
|
||||||
|
sed -e "s/\${ORG}/$1/" \
|
||||||
|
-e "s/\${P0PORT}/$2/" \
|
||||||
|
-e "s/\${CAPORT}/$3/" \
|
||||||
|
-e "s#\${PEERPEM}#$PP#" \
|
||||||
|
-e "s#\${CAPEM}#$CP#" \
|
||||||
|
organizations/ccp-template-for-docker.json
|
||||||
|
}
|
||||||
|
|
||||||
function yaml_ccp {
|
function yaml_ccp {
|
||||||
local PP=$(one_line_pem $4)
|
local PP=$(one_line_pem $4)
|
||||||
local CP=$(one_line_pem $5)
|
local CP=$(one_line_pem $5)
|
||||||
|
|
@ -33,6 +44,7 @@ PEERPEM=organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.exampl
|
||||||
CAPEM=organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
|
CAPEM=organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
|
||||||
|
|
||||||
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.json
|
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.json
|
||||||
|
echo "$(json_ccp_for_docker $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1-for-docker.json
|
||||||
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.yaml
|
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.yaml
|
||||||
|
|
||||||
ORG=2
|
ORG=2
|
||||||
|
|
@ -42,4 +54,5 @@ PEERPEM=organizations/peerOrganizations/org2.example.com/tlsca/tlsca.org2.exampl
|
||||||
CAPEM=organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem
|
CAPEM=organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem
|
||||||
|
|
||||||
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.json
|
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.json
|
||||||
|
echo "$(json_ccp_for_docker $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2-for-docker.json
|
||||||
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.yaml
|
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.yaml
|
||||||
|
|
|
||||||
45
test-network/organizations/ccp-generate.sh.original
Executable file
45
test-network/organizations/ccp-generate.sh.original
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function one_line_pem {
|
||||||
|
echo "`awk 'NF {sub(/\\n/, ""); printf "%s\\\\\\\n",$0;}' $1`"
|
||||||
|
}
|
||||||
|
|
||||||
|
function json_ccp {
|
||||||
|
local PP=$(one_line_pem $4)
|
||||||
|
local CP=$(one_line_pem $5)
|
||||||
|
sed -e "s/\${ORG}/$1/" \
|
||||||
|
-e "s/\${P0PORT}/$2/" \
|
||||||
|
-e "s/\${CAPORT}/$3/" \
|
||||||
|
-e "s#\${PEERPEM}#$PP#" \
|
||||||
|
-e "s#\${CAPEM}#$CP#" \
|
||||||
|
organizations/ccp-template.json
|
||||||
|
}
|
||||||
|
|
||||||
|
function yaml_ccp {
|
||||||
|
local PP=$(one_line_pem $4)
|
||||||
|
local CP=$(one_line_pem $5)
|
||||||
|
sed -e "s/\${ORG}/$1/" \
|
||||||
|
-e "s/\${P0PORT}/$2/" \
|
||||||
|
-e "s/\${CAPORT}/$3/" \
|
||||||
|
-e "s#\${PEERPEM}#$PP#" \
|
||||||
|
-e "s#\${CAPEM}#$CP#" \
|
||||||
|
organizations/ccp-template.yaml | sed -e $'s/\\\\n/\\\n /g'
|
||||||
|
}
|
||||||
|
|
||||||
|
ORG=1
|
||||||
|
P0PORT=7051
|
||||||
|
CAPORT=7054
|
||||||
|
PEERPEM=organizations/peerOrganizations/org1.example.com/tlsca/tlsca.org1.example.com-cert.pem
|
||||||
|
CAPEM=organizations/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem
|
||||||
|
|
||||||
|
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.json
|
||||||
|
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org1.example.com/connection-org1.yaml
|
||||||
|
|
||||||
|
ORG=2
|
||||||
|
P0PORT=9051
|
||||||
|
CAPORT=8054
|
||||||
|
PEERPEM=organizations/peerOrganizations/org2.example.com/tlsca/tlsca.org2.example.com-cert.pem
|
||||||
|
CAPEM=organizations/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem
|
||||||
|
|
||||||
|
echo "$(json_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.json
|
||||||
|
echo "$(yaml_ccp $ORG $P0PORT $CAPORT $PEERPEM $CAPEM)" > organizations/peerOrganizations/org2.example.com/connection-org2.yaml
|
||||||
49
test-network/organizations/ccp-template-for-docker.json
Executable file
49
test-network/organizations/ccp-template-for-docker.json
Executable file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"name": "test-network-org${ORG}",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"client": {
|
||||||
|
"organization": "Org${ORG}",
|
||||||
|
"connection": {
|
||||||
|
"timeout": {
|
||||||
|
"peer": {
|
||||||
|
"endorser": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"organizations": {
|
||||||
|
"Org${ORG}": {
|
||||||
|
"mspid": "Org${ORG}MSP",
|
||||||
|
"peers": [
|
||||||
|
"peer0.org${ORG}.example.com"
|
||||||
|
],
|
||||||
|
"certificateAuthorities": [
|
||||||
|
"ca.org${ORG}.example.com"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peers": {
|
||||||
|
"peer0.org${ORG}.example.com": {
|
||||||
|
"url": "grpcs://peer0.org${ORG}.example.com:${P0PORT}",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": "${PEERPEM}"
|
||||||
|
},
|
||||||
|
"grpcOptions": {
|
||||||
|
"ssl-target-name-override": "peer0.org${ORG}.example.com",
|
||||||
|
"hostnameOverride": "peer0.org${ORG}.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"certificateAuthorities": {
|
||||||
|
"ca.org${ORG}.example.com": {
|
||||||
|
"url": "https://ca_org{ORG}:${CAPORT}",
|
||||||
|
"caName": "ca-org${ORG}",
|
||||||
|
"tlsCACerts": {
|
||||||
|
"pem": ["${CAPEM}"]
|
||||||
|
},
|
||||||
|
"httpOptions": {
|
||||||
|
"verify": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue