├── README.md └── food-traceability ├── README.md ├── app ├── app.js ├── build.sh ├── config.js ├── config.json ├── config │ ├── network-config.yaml │ ├── org1.yaml │ ├── org2.yaml │ └── org3.yaml ├── lib │ ├── create-channel.js │ ├── helper.js │ ├── install-chaincode.js │ ├── instantiate-chaincode.js │ ├── invoke-transaction.js │ ├── join-channel.js │ └── query.js └── package.json ├── chaincode └── src │ ├── README.md │ ├── dairyfarm │ ├── cc_dairyfarm.go │ └── cc_dairyfarm_test.go │ ├── machining │ ├── cc_machining.go │ └── cc_machining_test.go │ └── salesterminal │ ├── cc_salesterminal.go │ └── cc_salesterminal_test.go └── fabric_raft ├── .env ├── READEME.md ├── base ├── docker-compose-base.yaml └── peer-base.yaml ├── bin ├── configtxgen ├── configtxlator └── cryptogen ├── cc.sh ├── chaincode └── chaincode_example02 │ ├── go │ └── chaincode_example02.go │ └── node │ ├── chaincode_example02.js │ └── package.json ├── configtx.yaml ├── crypto-config.yaml ├── docker-compose-cli.yaml ├── genesis.sh ├── scripts └── official.sh └── updown.sh /README.md: -------------------------------------------------------------------------------- 1 | #### fabric实战项目 2 | -------------------------------------------------------------------------------- /food-traceability/README.md: -------------------------------------------------------------------------------- 1 | ### fabric案例实战教程 2 | #### 搭建网络 3 | * 下载源码`git clone https://github.com/Jalins/fabricase.git` 4 | 5 | * cd 进入`fabric_raft`目录 6 | 7 | * 编写configtx.yaml,cryptogen.yaml,base目录下的docker-compose-base.yaml以及peer-base.yaml 文件,还有docker-compose-cli.yaml文件 8 | 9 | * 生产启动网络所需的证书及创始区块以及通道配置文件。 10 | 11 | ```shell 12 | bash ./updown.sh up 13 | ``` 14 | 15 | * 创建channel以及安装实例化链码 16 | 17 | ```shell 18 | bash ./cc.sh 19 | ``` 20 | 21 | * 完成网络 22 | 23 | 博客教程地址: 24 | 25 | #### 编写chaincode 26 | * 创建 chaincode 27 | * 在 src 下编写chaincode源代码,每个目录一个chaincode 28 | * 相应目录下编写测试用例 29 | * 相应目录下测试: go test -v ..._test.go ...go 30 | * 测试成功,完成chaincode 31 | 32 | 博客教程地址: 33 | 34 | #### SDK开发 35 | 36 | ##### 1.node-sdk-api 37 | 38 | 博客教程地址: 39 | 40 | ##### 2.go-sdk-api 41 | 42 | 博客教程地址: -------------------------------------------------------------------------------- /food-traceability/app/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | var log4js = require('log4js'); 18 | var logger = log4js.getLogger('SampleWebApp'); 19 | var express = require('express'); 20 | var session = require('express-session'); 21 | var cookieParser = require('cookie-parser'); 22 | var bodyParser = require('body-parser'); 23 | var http = require('http'); 24 | var util = require('util'); 25 | var app = express(); 26 | var expressJWT = require('express-jwt'); 27 | var jwt = require('jsonwebtoken'); 28 | var bearerToken = require('express-bearer-token'); 29 | var cors = require('cors'); 30 | 31 | require('./config.js'); 32 | var hfc = require('fabric-client'); 33 | 34 | var helper = require('./lib/helper.js'); 35 | var createChannel = require('./lib/create-channel.js'); 36 | var join = require('./lib/join-channel.js'); 37 | var install = require('./lib/install-chaincode.js'); 38 | var instantiate = require('./lib/instantiate-chaincode.js'); 39 | var invoke = require('./lib/invoke-transaction.js'); 40 | var query = require('./lib/query.js'); 41 | var host = process.env.HOST || hfc.getConfigSetting('host'); 42 | var port = process.env.PORT || hfc.getConfigSetting('port'); 43 | /////////////////////////////////////////////////////////////////////////////// 44 | //////////////////////////////// SET CONFIGURATONS //////////////////////////// 45 | /////////////////////////////////////////////////////////////////////////////// 46 | app.options('*', cors()); 47 | app.use(cors()); 48 | //support parsing of application/json type post data 49 | app.use(bodyParser.json()); 50 | //support parsing of application/x-www-form-urlencoded post data 51 | app.use(bodyParser.urlencoded({ 52 | extended: false 53 | })); 54 | // set secret variable 55 | app.set('secret', 'thisismysecret'); 56 | app.use(expressJWT({ 57 | secret: 'thisismysecret' 58 | }).unless({ 59 | path: ['/users'] 60 | })); 61 | app.use(bearerToken()); 62 | app.use(function(req, res, next) { 63 | logger.debug(' ------>>>>>> new request for %s',req.originalUrl); 64 | if (req.originalUrl.indexOf('/users') >= 0) { 65 | return next(); 66 | } 67 | 68 | var token = req.token; 69 | jwt.verify(token, app.get('secret'), function(err, decoded) { 70 | if (err) { 71 | res.send({ 72 | success: false, 73 | message: 'Failed to authenticate token. Make sure to include the ' + 74 | 'token returned from /users call in the authorization header ' + 75 | ' as a Bearer token' 76 | }); 77 | return; 78 | } else { 79 | // add the decoded user name and org name to the request object 80 | // for the downstream code to use 81 | req.username = decoded.username; 82 | req.orgname = decoded.orgName; 83 | logger.debug(util.format('Decoded from JWT token: username - %s, orgname - %s', decoded.username, decoded.orgName)); 84 | return next(); 85 | } 86 | }); 87 | }); 88 | 89 | /////////////////////////////////////////////////////////////////////////////// 90 | //////////////////////////////// START SERVER ///////////////////////////////// 91 | /////////////////////////////////////////////////////////////////////////////// 92 | var server = http.createServer(app).listen(port, function() {}); 93 | logger.info('****************** SERVER STARTED ************************'); 94 | logger.info('*************** http://%s:%s ******************',host,port); 95 | server.timeout = 240000; 96 | 97 | function getErrorMessage(field) { 98 | var response = { 99 | success: false, 100 | message: field + ' field is missing or Invalid in the request' 101 | }; 102 | return response; 103 | } 104 | 105 | /////////////////////////////////////////////////////////////////////////////// 106 | ///////////////////////// REST ENDPOINTS START HERE /////////////////////////// 107 | /////////////////////////////////////////////////////////////////////////////// 108 | // Register and enroll user 109 | app.post('/users', async function(req, res) { 110 | var username = req.body.username; 111 | var orgName = req.body.orgName; 112 | logger.debug('End point : /users'); 113 | logger.debug('User name : ' + username); 114 | logger.debug('Org name : ' + orgName); 115 | if (!username) { 116 | res.json(getErrorMessage('\'username\'')); 117 | return; 118 | } 119 | if (!orgName) { 120 | res.json(getErrorMessage('\'orgName\'')); 121 | return; 122 | } 123 | var token = jwt.sign({ 124 | exp: Math.floor(Date.now() / 1000) + parseInt(hfc.getConfigSetting('jwt_expiretime')), 125 | username: username, 126 | orgName: orgName 127 | }, app.get('secret')); 128 | let response = await helper.getRegisteredUser(username, orgName, true); 129 | logger.debug('-- returned from registering the username %s for organization %s',username,orgName); 130 | if (response && typeof response !== 'string') { 131 | logger.debug('Successfully registered the username %s for organization %s',username,orgName); 132 | response.token = token; 133 | res.json(response); 134 | } else { 135 | logger.debug('Failed to register the username %s for organization %s with::%s',username,orgName,response); 136 | res.json({success: false, message: response}); 137 | } 138 | 139 | }); 140 | // Create Channel 141 | app.post('/channels', async function(req, res) { 142 | logger.info('<<<<<<<<<<<<<<<<< C R E A T E C H A N N E L >>>>>>>>>>>>>>>>>'); 143 | logger.debug('End point : /channels'); 144 | var channelName = req.body.channelName; 145 | var channelConfigPath = req.body.channelConfigPath; 146 | logger.debug('Channel name : ' + channelName); 147 | logger.debug('channelConfigPath : ' + channelConfigPath); //../artifacts/channel/mychannel.tx 148 | if (!channelName) { 149 | res.json(getErrorMessage('\'channelName\'')); 150 | return; 151 | } 152 | if (!channelConfigPath) { 153 | res.json(getErrorMessage('\'channelConfigPath\'')); 154 | return; 155 | } 156 | 157 | let message = await createChannel.createChannel(channelName, channelConfigPath, req.username, req.orgname); 158 | res.send(message); 159 | }); 160 | // Join Channel 161 | app.post('/channels/:channelName/peers', async function(req, res) { 162 | logger.info('<<<<<<<<<<<<<<<<< J O I N C H A N N E L >>>>>>>>>>>>>>>>>'); 163 | var channelName = req.params.channelName; 164 | var peers = req.body.peers; 165 | logger.debug('channelName : ' + channelName); 166 | logger.debug('peers : ' + peers); 167 | logger.debug('username :' + req.username); 168 | logger.debug('orgname:' + req.orgname); 169 | 170 | if (!channelName) { 171 | res.json(getErrorMessage('\'channelName\'')); 172 | return; 173 | } 174 | if (!peers || peers.length == 0) { 175 | res.json(getErrorMessage('\'peers\'')); 176 | return; 177 | } 178 | 179 | let message = await join.joinChannel(channelName, peers, req.username, req.orgname); 180 | res.send(message); 181 | }); 182 | // Install chaincode on target peers 183 | app.post('/chaincodes', async function(req, res) { 184 | logger.debug('==================== INSTALL CHAINCODE =================='); 185 | var peers = req.body.peers; 186 | var chaincodeName = req.body.chaincodeName; 187 | var chaincodePath = req.body.chaincodePath; 188 | var chaincodeVersion = req.body.chaincodeVersion; 189 | var chaincodeType = req.body.chaincodeType; 190 | logger.debug('peers : ' + peers); // target peers list 191 | logger.debug('chaincodeName : ' + chaincodeName); 192 | logger.debug('chaincodePath : ' + chaincodePath); 193 | logger.debug('chaincodeVersion : ' + chaincodeVersion); 194 | logger.debug('chaincodeType : ' + chaincodeType); 195 | if (!peers || peers.length == 0) { 196 | res.json(getErrorMessage('\'peers\'')); 197 | return; 198 | } 199 | if (!chaincodeName) { 200 | res.json(getErrorMessage('\'chaincodeName\'')); 201 | return; 202 | } 203 | if (!chaincodePath) { 204 | res.json(getErrorMessage('\'chaincodePath\'')); 205 | return; 206 | } 207 | if (!chaincodeVersion) { 208 | res.json(getErrorMessage('\'chaincodeVersion\'')); 209 | return; 210 | } 211 | if (!chaincodeType) { 212 | res.json(getErrorMessage('\'chaincodeType\'')); 213 | return; 214 | } 215 | let message = await install.installChaincode(peers, chaincodeName, chaincodePath, chaincodeVersion, chaincodeType, req.username, req.orgname) 216 | res.send(message);}); 217 | // Instantiate chaincode on target peers 218 | app.post('/channels/:channelName/chaincodes', async function(req, res) { 219 | logger.debug('==================== INSTANTIATE CHAINCODE =================='); 220 | var peers = req.body.peers; 221 | var chaincodeName = req.body.chaincodeName; 222 | var chaincodeVersion = req.body.chaincodeVersion; 223 | var channelName = req.params.channelName; 224 | var chaincodeType = req.body.chaincodeType; 225 | var fcn = req.body.fcn; 226 | var args = req.body.args; 227 | logger.debug('peers : ' + peers); 228 | logger.debug('channelName : ' + channelName); 229 | logger.debug('chaincodeName : ' + chaincodeName); 230 | logger.debug('chaincodeVersion : ' + chaincodeVersion); 231 | logger.debug('chaincodeType : ' + chaincodeType); 232 | logger.debug('fcn : ' + fcn); 233 | logger.debug('args : ' + args); 234 | if (!chaincodeName) { 235 | res.json(getErrorMessage('\'chaincodeName\'')); 236 | return; 237 | } 238 | if (!chaincodeVersion) { 239 | res.json(getErrorMessage('\'chaincodeVersion\'')); 240 | return; 241 | } 242 | if (!channelName) { 243 | res.json(getErrorMessage('\'channelName\'')); 244 | return; 245 | } 246 | if (!chaincodeType) { 247 | res.json(getErrorMessage('\'chaincodeType\'')); 248 | return; 249 | } 250 | if (!args) { 251 | res.json(getErrorMessage('\'args\'')); 252 | return; 253 | } 254 | 255 | let message = await instantiate.instantiateChaincode(peers, channelName, chaincodeName, chaincodeVersion, chaincodeType, fcn, args, req.username, req.orgname); 256 | res.send(message); 257 | }); 258 | // Invoke transaction on chaincode on target peers 259 | app.post('/channels/:channelName/chaincodes/:chaincodeName', async function(req, res) { 260 | logger.debug('==================== INVOKE ON CHAINCODE =================='); 261 | var peers = req.body.peers; 262 | var chaincodeName = req.params.chaincodeName; 263 | var channelName = req.params.channelName; 264 | var fcn = req.body.fcn; 265 | var args = req.body.args; 266 | logger.debug('channelName : ' + channelName); 267 | logger.debug('chaincodeName : ' + chaincodeName); 268 | logger.debug('fcn : ' + fcn); 269 | logger.debug('args : ' + args); 270 | if (!chaincodeName) { 271 | res.json(getErrorMessage('\'chaincodeName\'')); 272 | return; 273 | } 274 | if (!channelName) { 275 | res.json(getErrorMessage('\'channelName\'')); 276 | return; 277 | } 278 | if (!fcn) { 279 | res.json(getErrorMessage('\'fcn\'')); 280 | return; 281 | } 282 | if (!args) { 283 | res.json(getErrorMessage('\'args\'')); 284 | return; 285 | } 286 | 287 | let message = await invoke.invokeChaincode(peers, channelName, chaincodeName, fcn, args, req.username, req.orgname); 288 | res.send(message); 289 | }); 290 | // Query on chaincode on target peers 291 | app.get('/channels/:channelName/chaincodes/:chaincodeName', async function(req, res) { 292 | logger.debug('==================== QUERY BY CHAINCODE =================='); 293 | var channelName = req.params.channelName; 294 | var chaincodeName = req.params.chaincodeName; 295 | let args = req.query.args; 296 | let fcn = req.query.fcn; 297 | let peer = req.query.peer; 298 | 299 | logger.debug('channelName : ' + channelName); 300 | logger.debug('chaincodeName : ' + chaincodeName); 301 | logger.debug('fcn : ' + fcn); 302 | logger.debug('args : ' + args); 303 | 304 | if (!chaincodeName) { 305 | res.json(getErrorMessage('\'chaincodeName\'')); 306 | return; 307 | } 308 | if (!channelName) { 309 | res.json(getErrorMessage('\'channelName\'')); 310 | return; 311 | } 312 | if (!fcn) { 313 | res.json(getErrorMessage('\'fcn\'')); 314 | return; 315 | } 316 | if (!args) { 317 | res.json(getErrorMessage('\'args\'')); 318 | return; 319 | } 320 | args = args.replace(/'/g, '"'); 321 | args = JSON.parse(args); 322 | logger.debug(args); 323 | 324 | let message = await query.queryChaincode(peer, channelName, chaincodeName, args, fcn, req.username, req.orgname); 325 | res.send(message); 326 | }); 327 | // Query Get Block by BlockNumber 328 | app.get('/channels/:channelName/blocks/:blockId', async function(req, res) { 329 | logger.debug('==================== GET BLOCK BY NUMBER =================='); 330 | let blockId = req.params.blockId; 331 | let peer = req.query.peer; 332 | logger.debug('channelName : ' + req.params.channelName); 333 | logger.debug('BlockID : ' + blockId); 334 | logger.debug('Peer : ' + peer); 335 | if (!blockId) { 336 | res.json(getErrorMessage('\'blockId\'')); 337 | return; 338 | } 339 | 340 | let message = await query.getBlockByNumber(peer, req.params.channelName, blockId, req.username, req.orgname); 341 | res.send(message); 342 | }); 343 | // Query Get Transaction by Transaction ID 344 | app.get('/channels/:channelName/transactions/:trxnId', async function(req, res) { 345 | logger.debug('================ GET TRANSACTION BY TRANSACTION_ID ======================'); 346 | logger.debug('channelName : ' + req.params.channelName); 347 | let trxnId = req.params.trxnId; 348 | let peer = req.query.peer; 349 | if (!trxnId) { 350 | res.json(getErrorMessage('\'trxnId\'')); 351 | return; 352 | } 353 | 354 | let message = await query.getTransactionByID(peer, req.params.channelName, trxnId, req.username, req.orgname); 355 | res.send(message); 356 | }); 357 | // Query Get Block by Hash 358 | app.get('/channels/:channelName/blocks', async function(req, res) { 359 | logger.debug('================ GET BLOCK BY HASH ======================'); 360 | logger.debug('channelName : ' + req.params.channelName); 361 | let hash = req.query.hash; 362 | let peer = req.query.peer; 363 | if (!hash) { 364 | res.json(getErrorMessage('\'hash\'')); 365 | return; 366 | } 367 | 368 | let message = await query.getBlockByHash(peer, req.params.channelName, hash, req.username, req.orgname); 369 | res.send(message); 370 | }); 371 | //Query for Channel Information 372 | app.get('/channels/:channelName', async function(req, res) { 373 | logger.debug('================ GET CHANNEL INFORMATION ======================'); 374 | logger.debug('channelName : ' + req.params.channelName); 375 | let peer = req.query.peer; 376 | 377 | let message = await query.getChainInfo(peer, req.params.channelName, req.username, req.orgname); 378 | res.send(message); 379 | }); 380 | //Query for Channel instantiated chaincodes 381 | app.get('/channels/:channelName/chaincodes', async function(req, res) { 382 | logger.debug('================ GET INSTANTIATED CHAINCODES ======================'); 383 | logger.debug('channelName : ' + req.params.channelName); 384 | let peer = req.query.peer; 385 | 386 | let message = await query.getInstalledChaincodes(peer, req.params.channelName, 'instantiated', req.username, req.orgname); 387 | res.send(message); 388 | }); 389 | // Query to fetch all Installed/instantiated chaincodes 390 | app.get('/chaincodes', async function(req, res) { 391 | var peer = req.query.peer; 392 | var installType = req.query.type; 393 | logger.debug('================ GET INSTALLED CHAINCODES ======================'); 394 | 395 | let message = await query.getInstalledChaincodes(peer, null, 'installed', req.username, req.orgname) 396 | res.send(message); 397 | }); 398 | // Query to fetch channels 399 | app.get('/channels', async function(req, res) { 400 | logger.debug('================ GET CHANNELS ======================'); 401 | logger.debug('peer: ' + req.query.peer); 402 | var peer = req.query.peer; 403 | if (!peer) { 404 | res.json(getErrorMessage('\'peer\'')); 405 | return; 406 | } 407 | 408 | let message = await query.getChannels(peer, req.username, req.orgname); 409 | res.send(message); 410 | }); 411 | -------------------------------------------------------------------------------- /food-traceability/app/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp. All Rights Reserved. 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | LANGUAGE="golang" 9 | CHAINCODE_VERSION="v0" 10 | CC_SRC_PATH1="dairyfarm" 11 | CHAINCODE_NAME1="dairyfarm" 12 | CC_SRC_PATH2="machining" 13 | CHAINCODE_NAME2="machining" 14 | CC_SRC_PATH3="salesterminal" 15 | CHAINCODE_NAME3="salesterminal" 16 | 17 | jq --version > /dev/null 2>&1 18 | if [ $? -ne 0 ]; then 19 | echo "Please Install 'jq' https://stedolan.github.io/jq/ to execute this script" 20 | echo 21 | exit 1 22 | fi 23 | 24 | echo "POST request Enroll on Org1 ..." 25 | echo 26 | ORG1_TOKEN=$(curl -s -X POST \ 27 | http://localhost:8000/users \ 28 | -H "content-type: application/x-www-form-urlencoded" \ 29 | -d 'username=Jim&orgName=Org1') 30 | echo $ORG1_TOKEN 31 | ORG1_TOKEN=$(echo $ORG1_TOKEN | jq ".token" | sed "s/\"//g") 32 | echo 33 | echo "ORG1 token is $ORG1_TOKEN" 34 | echo 35 | 36 | echo "POST request Enroll on Org2 ..." 37 | echo 38 | ORG2_TOKEN=$(curl -s -X POST \ 39 | http://localhost:8000/users \ 40 | -H "content-type: application/x-www-form-urlencoded" \ 41 | -d 'username=Jim&orgName=Org2') 42 | echo $ORG2_TOKEN 43 | ORG2_TOKEN=$(echo $ORG2_TOKEN | jq ".token" | sed "s/\"//g") 44 | echo 45 | echo "ORG2 token is $ORG2_TOKEN" 46 | echo 47 | 48 | echo "POST request Enroll on Org3 ..." 49 | echo 50 | ORG3_TOKEN=$(curl -s -X POST \ 51 | http://localhost:8000/users \ 52 | -H "content-type: application/x-www-form-urlencoded" \ 53 | -d 'username=Jim&orgName=Org3') 54 | echo $ORG3_TOKEN 55 | ORG3_TOKEN=$(echo $ORG3_TOKEN | jq ".token" | sed "s/\"//g") 56 | echo 57 | echo "ORG3 token is $ORG3_TOKEN" 58 | echo 59 | 60 | 61 | echo "POST request Create channel mychannel ..." 62 | echo 63 | curl -s -X POST \ 64 | http://localhost:8000/channels \ 65 | -H "authorization: Bearer $ORG1_TOKEN" \ 66 | -H "content-type: application/json" \ 67 | -d '{ 68 | "channelName":"mychannel", 69 | "channelConfigPath":"../../network/channel/artifacts/mychannel.tx" 70 | }' 71 | echo 72 | echo 73 | sleep 5 74 | 75 | echo "POST request Join channel on Org1" 76 | echo 77 | curl -s -X POST \ 78 | http://localhost:8000/channels/mychannel/peers \ 79 | -H "authorization: Bearer $ORG1_TOKEN" \ 80 | -H "content-type: application/json" \ 81 | -d '{ 82 | "peers": ["peer0.org1.example.com","peer1.org1.example.com"] 83 | }' 84 | echo 85 | echo 86 | 87 | echo "POST request Join channel on Org2" 88 | echo 89 | curl -s -X POST \ 90 | http://localhost:8000/channels/mychannel/peers \ 91 | -H "authorization: Bearer $ORG2_TOKEN" \ 92 | -H "content-type: application/json" \ 93 | -d '{ 94 | "peers": ["peer0.org2.example.com","peer1.org2.example.com"] 95 | }' 96 | echo 97 | echo 98 | 99 | 100 | echo "POST request Join channel on Org3" 101 | echo 102 | curl -s -X POST \ 103 | http://localhost:8000/channels/mychannel/peers \ 104 | -H "authorization: Bearer $ORG3_TOKEN" \ 105 | -H "content-type: application/json" \ 106 | -d '{ 107 | "peers": ["peer0.org3.example.com","peer1.org3.example.com"] 108 | }' 109 | echo 110 | echo 111 | 112 | 113 | echo "POST Install chaincode on Org1" 114 | echo 115 | curl -s -X POST \ 116 | http://localhost:8000/chaincodes \ 117 | -H "authorization: Bearer $ORG1_TOKEN" \ 118 | -H "content-type: application/json" \ 119 | -d "{ 120 | \"peers\": [\"peer0.org1.example.com\",\"peer1.org1.example.com\"], 121 | \"chaincodeName\":\"$CHAINCODE_NAME1\", 122 | \"chaincodePath\":\"$CC_SRC_PATH1\", 123 | \"chaincodeType\": \"$LANGUAGE\", 124 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 125 | }" 126 | echo 127 | echo 128 | 129 | echo "POST Install chaincode on Org1" 130 | echo 131 | curl -s -X POST \ 132 | http://localhost:8000/chaincodes \ 133 | -H "authorization: Bearer $ORG1_TOKEN" \ 134 | -H "content-type: application/json" \ 135 | -d "{ 136 | \"peers\": [\"peer0.org1.example.com\",\"peer1.org1.example.com\"], 137 | \"chaincodeName\":\"$CHAINCODE_NAME2\", 138 | \"chaincodePath\":\"$CC_SRC_PATH2\", 139 | \"chaincodeType\": \"$LANGUAGE\", 140 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 141 | }" 142 | echo 143 | echo 144 | 145 | echo "POST Install chaincode on Org1" 146 | echo 147 | curl -s -X POST \ 148 | http://localhost:8000/chaincodes \ 149 | -H "authorization: Bearer $ORG1_TOKEN" \ 150 | -H "content-type: application/json" \ 151 | -d "{ 152 | \"peers\": [\"peer0.org1.example.com\",\"peer1.org1.example.com\"], 153 | \"chaincodeName\":\"$CHAINCODE_NAME3\", 154 | \"chaincodePath\":\"$CC_SRC_PATH3\", 155 | \"chaincodeType\": \"$LANGUAGE\", 156 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 157 | }" 158 | echo 159 | echo 160 | 161 | 162 | echo "POST Install chaincode on Org2" 163 | echo 164 | curl -s -X POST \ 165 | http://localhost:8000/chaincodes \ 166 | -H "authorization: Bearer $ORG2_TOKEN" \ 167 | -H "content-type: application/json" \ 168 | -d "{ 169 | \"peers\": [\"peer0.org2.example.com\",\"peer1.org2.example.com\"], 170 | \"chaincodeName\":\"$CHAINCODE_NAME1\", 171 | \"chaincodePath\":\"$CC_SRC_PATH1\", 172 | \"chaincodeType\": \"$LANGUAGE\", 173 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 174 | }" 175 | echo 176 | echo 177 | 178 | echo "POST Install chaincode on Org2" 179 | echo 180 | curl -s -X POST \ 181 | http://localhost:8000/chaincodes \ 182 | -H "authorization: Bearer $ORG2_TOKEN" \ 183 | -H "content-type: application/json" \ 184 | -d "{ 185 | \"peers\": [\"peer0.org2.example.com\",\"peer1.org2.example.com\"], 186 | \"chaincodeName\":\"$CHAINCODE_NAME2\", 187 | \"chaincodePath\":\"$CC_SRC_PATH2\", 188 | \"chaincodeType\": \"$LANGUAGE\", 189 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 190 | }" 191 | echo 192 | echo 193 | 194 | echo "POST Install chaincode on Org2" 195 | echo 196 | curl -s -X POST \ 197 | http://localhost:8000/chaincodes \ 198 | -H "authorization: Bearer $ORG2_TOKEN" \ 199 | -H "content-type: application/json" \ 200 | -d "{ 201 | \"peers\": [\"peer0.org2.example.com\",\"peer1.org2.example.com\"], 202 | \"chaincodeName\":\"$CHAINCODE_NAME3\", 203 | \"chaincodePath\":\"$CC_SRC_PATH3\", 204 | \"chaincodeType\": \"$LANGUAGE\", 205 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 206 | }" 207 | echo 208 | echo 209 | 210 | 211 | echo "POST Install chaincode on Org3" 212 | echo 213 | curl -s -X POST \ 214 | http://localhost:8000/chaincodes \ 215 | -H "authorization: Bearer $ORG3_TOKEN" \ 216 | -H "content-type: application/json" \ 217 | -d "{ 218 | \"peers\": [\"peer0.org3.example.com\",\"peer1.org3.example.com\"], 219 | \"chaincodeName\":\"$CHAINCODE_NAME1\", 220 | \"chaincodePath\":\"$CC_SRC_PATH1\", 221 | \"chaincodeType\": \"$LANGUAGE\", 222 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 223 | }" 224 | echo 225 | echo 226 | 227 | echo "POST Install chaincode on Org3" 228 | echo 229 | curl -s -X POST \ 230 | http://localhost:8000/chaincodes \ 231 | -H "authorization: Bearer $ORG3_TOKEN" \ 232 | -H "content-type: application/json" \ 233 | -d "{ 234 | \"peers\": [\"peer0.org3.example.com\",\"peer1.org3.example.com\"], 235 | \"chaincodeName\":\"$CHAINCODE_NAME2\", 236 | \"chaincodePath\":\"$CC_SRC_PATH2\", 237 | \"chaincodeType\": \"$LANGUAGE\", 238 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 239 | }" 240 | echo 241 | echo 242 | 243 | echo "POST Install chaincode on Org3" 244 | echo 245 | curl -s -X POST \ 246 | http://localhost:8000/chaincodes \ 247 | -H "authorization: Bearer $ORG3_TOKEN" \ 248 | -H "content-type: application/json" \ 249 | -d "{ 250 | \"peers\": [\"peer0.org3.example.com\",\"peer1.org3.example.com\"], 251 | \"chaincodeName\":\"$CHAINCODE_NAME3\", 252 | \"chaincodePath\":\"$CC_SRC_PATH3\", 253 | \"chaincodeType\": \"$LANGUAGE\", 254 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\" 255 | }" 256 | echo 257 | echo 258 | 259 | echo "POST instantiate chaincode on peer1 of Org1" 260 | echo 261 | curl -s -X POST \ 262 | http://localhost:8000/channels/mychannel/chaincodes \ 263 | -H "authorization: Bearer $ORG1_TOKEN" \ 264 | -H "content-type: application/json" \ 265 | -d "{ 266 | \"chaincodeName\":\"$CHAINCODE_NAME1\", 267 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\", 268 | \"chaincodeType\": \"$LANGUAGE\", 269 | \"args\":[\"\"] 270 | }" 271 | echo 272 | echo 273 | 274 | echo "POST instantiate chaincode on peer1 of Org2" 275 | echo 276 | curl -s -X POST \ 277 | http://localhost:8000/channels/mychannel/chaincodes \ 278 | -H "authorization: Bearer $ORG2_TOKEN" \ 279 | -H "content-type: application/json" \ 280 | -d "{ 281 | \"chaincodeName\":\"$CHAINCODE_NAME2\", 282 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\", 283 | \"chaincodeType\": \"$LANGUAGE\", 284 | \"args\":[\"\"] 285 | }" 286 | echo 287 | echo 288 | 289 | echo "POST instantiate chaincode on peer1 of Org3" 290 | echo 291 | curl -s -X POST \ 292 | http://localhost:8000/channels/mychannel/chaincodes \ 293 | -H "authorization: Bearer $ORG3_TOKEN" \ 294 | -H "content-type: application/json" \ 295 | -d "{ 296 | \"chaincodeName\":\"$CHAINCODE_NAME3\", 297 | \"chaincodeVersion\":\"$CHAINCODE_VERSION\", 298 | \"chaincodeType\": \"$LANGUAGE\", 299 | \"args\":[\"\"] 300 | }" 301 | echo 302 | echo 303 | 304 | 305 | 306 | echo "success !!!" 307 | -------------------------------------------------------------------------------- /food-traceability/app/config.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var path = require('path'); 3 | var hfc = require('fabric-client'); 4 | 5 | var file = 'network-config%s.yaml'; 6 | 7 | var env = process.env.TARGET_NETWORK; 8 | if (env) 9 | file = util.format(file, '-' + env); 10 | else 11 | file = util.format(file, ''); 12 | // indicate to the application where the setup file is located so it able 13 | // to have the hfc load it to initalize the fabric client instance 14 | hfc.setConfigSetting('network-connection-profile-path',path.join(__dirname, 'config' ,file)); 15 | hfc.setConfigSetting('Org1-connection-profile-path',path.join(__dirname, 'config', 'org1.yaml')); 16 | hfc.setConfigSetting('Org2-connection-profile-path',path.join(__dirname, 'config', 'org2.yaml')); 17 | hfc.setConfigSetting('Org3-connection-profile-path',path.join(__dirname, 'config', 'org3.yaml')); 18 | 19 | // some other settings the application might need to know 20 | hfc.addConfigFile(path.join(__dirname, 'config.json')); 21 | -------------------------------------------------------------------------------- /food-traceability/app/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "host":"localhost", 3 | "port":"8000", 4 | "jwt_expiretime": "36000", 5 | "channelName":"mychannel", 6 | "CC_SRC_PATH":"../../chaincode", 7 | "eventWaitTime":"30000", 8 | "admins":[ 9 | { 10 | "username":"admin", 11 | "secret":"adminpw" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /food-traceability/app/config/network-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # The network connection profile provides client applications the information about the target 4 | # blockchain network that are necessary for the applications to interact with it. These are all 5 | # knowledge that must be acquired from out-of-band sources. This file provides such a source. 6 | # 7 | name: "my-dapp" 8 | 9 | # 10 | # Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming 11 | # in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave 12 | # them for the applications to process. This is a mechanism for different components of an application 13 | # to exchange information that are not part of the standard schema described below. In particular, 14 | # the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to 15 | # determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with. 16 | # 17 | x-type: "hlfv1" 18 | 19 | # 20 | # Describe what the target network is/does. 21 | # 22 | description: "Fabric Network" 23 | 24 | # 25 | # Schema version of the content. Used by the SDK to apply the corresponding parsing rules. 26 | # 27 | version: "1.0" 28 | 29 | # 30 | # The client section will be added on a per org basis see org1.yaml and org2.yaml 31 | # 32 | #client: 33 | 34 | # 35 | # [Optional]. But most apps would have this section so that channel objects can be constructed 36 | # based on the content below. If an app is creating channels, then it likely will not need this 37 | # section. 38 | # 39 | channels: 40 | # name of the channel 41 | mychannel: 42 | # Required. list of orderers designated by the application to use for transactions on this 43 | # channel. This list can be a result of access control ("org1" can only access "ordererA"), or 44 | # operational decisions to share loads from applications among the orderers. The values must 45 | # be "names" of orgs defined under "organizations/peers" 46 | orderers: 47 | - orderer0.example.com 48 | - orderer1.example.com 49 | - orderer2.example.com 50 | 51 | # Required. list of peers from participating orgs 52 | peers: 53 | peer0.org1.example.com: 54 | # [Optional]. will this peer be sent transaction proposals for endorsement? The peer must 55 | # have the chaincode installed. The app can also use this property to decide which peers 56 | # to send the chaincode install request. Default: true 57 | endorsingPeer: true 58 | 59 | # [Optional]. will this peer be sent query proposals? The peer must have the chaincode 60 | # installed. The app can also use this property to decide which peers to send the 61 | # chaincode install request. Default: true 62 | chaincodeQuery: true 63 | 64 | # [Optional]. will this peer be sent query proposals that do not require chaincodes, like 65 | # queryBlock(), queryTransaction(), etc. Default: true 66 | ledgerQuery: true 67 | 68 | # [Optional]. will this peer be the target of the SDK's listener registration? All peers can 69 | # produce events but the app typically only needs to connect to one to listen to events. 70 | # Default: true 71 | eventSource: true 72 | 73 | peer1.org1.example.com: 74 | endorsingPeer: false 75 | chaincodeQuery: true 76 | ledgerQuery: true 77 | eventSource: false 78 | 79 | peer0.org2.example.com: 80 | endorsingPeer: true 81 | chaincodeQuery: true 82 | ledgerQuery: true 83 | eventSource: true 84 | 85 | peer1.org2.example.com: 86 | endorsingPeer: false 87 | chaincodeQuery: true 88 | ledgerQuery: true 89 | eventSource: false 90 | 91 | peer0.org3.example.com: 92 | endorsingPeer: true 93 | chaincodeQuery: true 94 | ledgerQuery: true 95 | eventSource: true 96 | 97 | peer1.org3.example.com: 98 | endorsingPeer: false 99 | chaincodeQuery: true 100 | ledgerQuery: true 101 | eventSource: false 102 | 103 | # [Optional]. what chaincodes are expected to exist on this channel? The application can use 104 | # this information to validate that the target peers are in the expected state by comparing 105 | # this list with the query results of getInstalledChaincodes() and getInstantiatedChaincodes() 106 | chaincodes: 107 | # the format follows the "cannonical name" of chaincodes by fabric code 108 | - mycc:v0 109 | 110 | 111 | # 112 | # list of participating organizations in this network 113 | # 114 | organizations: 115 | Org1: 116 | mspid: Org1MSP 117 | 118 | peers: 119 | - peer0.org1.example.com 120 | - peer1.org1.example.com 121 | 122 | # [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based 123 | # network. Typically certificates provisioning is done in a separate process outside of the 124 | # runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for 125 | # dynamic certificate management (enroll, revoke, re-enroll). The following section is only for 126 | # Fabric-CA servers. 127 | certificateAuthorities: 128 | - ca-org1 129 | 130 | # [Optional]. If the application is going to make requests that are reserved to organization 131 | # administrators, including creating/updating channels, installing/instantiating chaincodes, it 132 | # must have access to the admin identity represented by the private key and signing certificate. 133 | # Both properties can be the PEM string or local path to the PEM file. Note that this is mainly for 134 | # convenience in development mode, production systems should not expose sensitive information 135 | # this way. The SDK should allow applications to set the org admin identity via APIs, and only use 136 | # this route as an alternative when it exists. 137 | adminPrivateKey: 138 | path: ../network/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/keystore/23e66fee0dbe604f3c23f17227aabdcf0ee56aba29000afb2213f5997fe22064_sk 139 | signedCert: 140 | path: ../network/channel/crypto-config/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp/signcerts/Admin@org1.example.com-cert.pem 141 | 142 | # the profile will contain public information about organizations other than the one it belongs to. 143 | # These are necessary information to make transaction lifecycles work, including MSP IDs and 144 | # peers with a public URL to send transaction proposals. The file will not contain private 145 | # information reserved for members of the organization, such as admin key and certificate, 146 | # fabric-ca registrar enroll ID and secret, etc. 147 | Org2: 148 | mspid: Org2MSP 149 | peers: 150 | - peer0.org2.example.com 151 | - peer1.org2.example.com 152 | certificateAuthorities: 153 | - ca-org2 154 | adminPrivateKey: 155 | path: ../network/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/keystore/7bcac812a1a2d3949e6bf3c0c9de0a005dc45fac4e7d67c3d0631287a465c7da_sk 156 | signedCert: 157 | path: ../network/channel/crypto-config/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp/signcerts/Admin@org2.example.com-cert.pem 158 | 159 | Org3: 160 | mspid: Org3MSP 161 | peers: 162 | - peer0.org3.example.com 163 | - peer1.org3.example.com 164 | certificateAuthorities: 165 | - ca-org3 166 | adminPrivateKey: 167 | path: ../network/channel/crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/keystore/72d5f1f2cbd43c5656bfbc97c51859e97b6f33ce82bdc38b0931189d16265cbd_sk 168 | signedCert: 169 | path: ../network/channel/crypto-config/peerOrganizations/org3.example.com/users/Admin@org3.example.com/msp/signcerts/Admin@org3.example.com-cert.pem 170 | 171 | 172 | 173 | # 174 | # List of orderers to send transaction and channel create/update requests to. For the time 175 | # being only one orderer is needed. If more than one is defined, which one get used by the 176 | # SDK is implementation specific. Consult each SDK's documentation for its handling of orderers. 177 | # 178 | orderers: 179 | orderer0.example.com: 180 | url: grpcs://localhost:7050 181 | 182 | # these are standard properties defined by the gRPC library 183 | # they will be passed in as-is to gRPC client constructor 184 | grpcOptions: 185 | ssl-target-name-override: orderer0.example.com 186 | 187 | tlsCACerts: 188 | path: ../network/channel/crypto-config/ordererOrganizations/example.com/orderers/orderer0.example.com/tls/ca.crt 189 | 190 | orderer1.example.com: 191 | url: grpcs://localhost:8050 192 | 193 | # these are standard properties defined by the gRPC library 194 | # they will be passed in as-is to gRPC client constructor 195 | grpcOptions: 196 | ssl-target-name-override: orderer1.example.com 197 | 198 | tlsCACerts: 199 | path: ../network/channel/crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 200 | 201 | orderer2.example.com: 202 | url: grpcs://localhost:9050 203 | 204 | # these are standard properties defined by the gRPC library 205 | # they will be passed in as-is to gRPC client constructor 206 | grpcOptions: 207 | ssl-target-name-override: orderer2.example.com 208 | 209 | tlsCACerts: 210 | path: ../network/channel/crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/ca.crt 211 | 212 | # 213 | # List of peers to send various requests to, including endorsement, query 214 | # and event listener registration. 215 | # 216 | peers: 217 | peer0.org1.example.com: 218 | # this URL is used to send endorsement and query requests 219 | url: grpcs://localhost:7051 220 | eventUrl: grpcs://localhost:7053 221 | grpcOptions: 222 | ssl-target-name-override: peer0.org1.example.com 223 | tlsCACerts: 224 | path: ../network/channel/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 225 | 226 | peer1.org1.example.com: 227 | url: grpcs://localhost:7056 228 | eventUrl: grpcs://localhost:7058 229 | grpcOptions: 230 | ssl-target-name-override: peer1.org1.example.com 231 | tlsCACerts: 232 | path: ../network/channel/crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls/ca.crt 233 | 234 | peer0.org2.example.com: 235 | url: grpcs://localhost:8051 236 | eventUrl: grpcs://localhost:8053 237 | grpcOptions: 238 | ssl-target-name-override: peer0.org2.example.com 239 | tlsCACerts: 240 | path: ../network/channel/crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 241 | 242 | peer1.org2.example.com: 243 | url: grpcs://localhost:8056 244 | eventUrl: grpcs://localhost:8058 245 | grpcOptions: 246 | ssl-target-name-override: peer1.org2.example.com 247 | tlsCACerts: 248 | path: ../network/channel/crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls/ca.crt 249 | 250 | peer0.org3.example.com: 251 | url: grpcs://localhost:9051 252 | eventUrl: grpcs://localhost:9053 253 | grpcOptions: 254 | ssl-target-name-override: peer0.org3.example.com 255 | tlsCACerts: 256 | path: ../network/channel/crypto-config/peerOrganizations/org3.example.com/peers/peer0.org3.example.com/tls/ca.crt 257 | 258 | peer1.org3.example.com: 259 | url: grpcs://localhost:9056 260 | eventUrl: grpcs://localhost:9058 261 | grpcOptions: 262 | ssl-target-name-override: peer1.org3.example.com 263 | tlsCACerts: 264 | path: ../network/channel/crypto-config/peerOrganizations/org3.example.com/peers/peer1.org3.example.com/tls/ca.crt 265 | 266 | # 267 | # Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows 268 | # certificate management to be done via REST APIs. Application may choose to use a standard 269 | # Certificate Authority instead of Fabric-CA, in which case this section would not be specified. 270 | # 271 | certificateAuthorities: 272 | ca-org1: 273 | url: https://localhost:7054 274 | # the properties specified under this object are passed to the 'http' client verbatim when 275 | # making the request to the Fabric-CA server 276 | httpOptions: 277 | verify: false 278 | tlsCACerts: 279 | path: ../network/channel/crypto-config/peerOrganizations/org1.example.com/ca/ca.org1.example.com-cert.pem 280 | 281 | # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is 282 | # needed to enroll and invoke new users. 283 | registrar: 284 | - enrollId: admin 285 | enrollSecret: adminpw 286 | # [Optional] The optional name of the CA. 287 | caName: ca-org1 288 | 289 | ca-org2: 290 | url: https://localhost:8054 291 | httpOptions: 292 | verify: false 293 | tlsCACerts: 294 | path: ../network/channel/crypto-config/peerOrganizations/org2.example.com/ca/ca.org2.example.com-cert.pem 295 | registrar: 296 | - enrollId: admin 297 | enrollSecret: adminpw 298 | # [Optional] The optional name of the CA. 299 | caName: ca-org2 300 | 301 | ca-org3: 302 | url: https://localhost:9054 303 | httpOptions: 304 | verify: false 305 | tlsCACerts: 306 | path: ../network/channel/crypto-config/peerOrganizations/org3.example.com/ca/ca.org3.example.com-cert.pem 307 | registrar: 308 | - enrollId: admin 309 | enrollSecret: adminpw 310 | # [Optional] The optional name of the CA. 311 | caName: ca-org3 312 | -------------------------------------------------------------------------------- /food-traceability/app/config/org1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # The network connection profile provides client applications the information about the target 4 | # blockchain network that are necessary for the applications to interact with it. These are all 5 | # knowledge that must be acquired from out-of-band sources. This file provides such a source. 6 | # 7 | name: "my-dapp-org1" 8 | 9 | # 10 | # Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming 11 | # in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave 12 | # them for the applications to process. This is a mechanism for different components of an application 13 | # to exchange information that are not part of the standard schema described below. In particular, 14 | # the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to 15 | # determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with. 16 | # 17 | x-type: "hlfv1" 18 | 19 | # 20 | # Describe what the target network is/does. 21 | # 22 | description: "Fabric Network Org1" 23 | 24 | # 25 | # Schema version of the content. Used by the SDK to apply the corresponding parsing rules. 26 | # 27 | version: "1.0" 28 | 29 | # 30 | # The client section is SDK-specific. The sample below is for the node.js SDK 31 | # 32 | client: 33 | # Which organization does this application instance belong to? The value must be the name of an org 34 | # defined under "organizations" 35 | organization: Org1 36 | 37 | # Some SDKs support pluggable KV stores, the properties under "credentialStore" 38 | # are implementation specific 39 | credentialStore: 40 | # [Optional]. Specific to FileKeyValueStore.js or similar implementations in other SDKs. Can be others 41 | # if using an alternative impl. For instance, CouchDBKeyValueStore.js would require an object 42 | # here for properties like url, db name, etc. 43 | path: "./keystore/fabric-client-kv-org1/users" 44 | 45 | # [Optional]. Specific to the CryptoSuite implementation. Software-based implementations like 46 | # CryptoSuite_ECDSA_AES.js in node SDK requires a key store. PKCS#11 based implementations does 47 | # not. 48 | cryptoStore: 49 | # Specific to the underlying KeyValueStore that backs the crypto key store. 50 | path: "./keystore/fabric-client-kv-org1" 51 | 52 | # [Optional]. Specific to Composer environment 53 | wallet: wallet-name 54 | -------------------------------------------------------------------------------- /food-traceability/app/config/org2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # The network connection profile provides client applications the information about the target 4 | # blockchain network that are necessary for the applications to interact with it. These are all 5 | # knowledge that must be acquired from out-of-band sources. This file provides such a source. 6 | # 7 | name: "my-dapp-org2" 8 | 9 | # 10 | # Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming 11 | # in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave 12 | # them for the applications to process. This is a mechanism for different components of an application 13 | # to exchange information that are not part of the standard schema described below. In particular, 14 | # the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to 15 | # determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with. 16 | # 17 | x-type: "hlfv1" 18 | 19 | # 20 | # Describe what the target network is/does. 21 | # 22 | description: "Fabric Network Org2" 23 | 24 | # 25 | # Schema version of the content. Used by the SDK to apply the corresponding parsing rules. 26 | # 27 | version: "1.0" 28 | 29 | # 30 | # The client section is SDK-specific. The sample below is for the node.js SDK 31 | # 32 | client: 33 | # Which organization does this application instance belong to? The value must be the name of an org 34 | # defined under "organizations" 35 | organization: Org2 36 | 37 | # Some SDKs support pluggable KV stores, the properties under "credentialStore" 38 | # are implementation specific 39 | credentialStore: 40 | # [Optional]. Specific to FileKeyValueStore.js or similar implementations in other SDKs. Can be others 41 | # if using an alternative impl. For instance, CouchDBKeyValueStore.js would require an object 42 | # here for properties like url, db name, etc. 43 | path: "./keystore/fabric-client-kv-org2/users" 44 | 45 | # [Optional]. Specific to the CryptoSuite implementation. Software-based implementations like 46 | # CryptoSuite_ECDSA_AES.js in node SDK requires a key store. PKCS#11 based implementations does 47 | # not. 48 | cryptoStore: 49 | # Specific to the underlying KeyValueStore that backs the crypto key store. 50 | path: "./keystore/fabric-client-kv-org2" 51 | 52 | # [Optional]. Specific to Composer environment 53 | wallet: wallet-name 54 | -------------------------------------------------------------------------------- /food-traceability/app/config/org3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # The network connection profile provides client applications the information about the target 4 | # blockchain network that are necessary for the applications to interact with it. These are all 5 | # knowledge that must be acquired from out-of-band sources. This file provides such a source. 6 | # 7 | name: "my-dapp-org3" 8 | 9 | # 10 | # Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming 11 | # in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave 12 | # them for the applications to process. This is a mechanism for different components of an application 13 | # to exchange information that are not part of the standard schema described below. In particular, 14 | # the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to 15 | # determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with. 16 | # 17 | x-type: "hlfv1" 18 | 19 | # 20 | # Describe what the target network is/does. 21 | # 22 | description: "Fabric Network Org3" 23 | 24 | # 25 | # Schema version of the content. Used by the SDK to apply the corresponding parsing rules. 26 | # 27 | version: "1.0" 28 | 29 | # 30 | # The client section is SDK-specific. The sample below is for the node.js SDK 31 | # 32 | client: 33 | # Which organization does this application instance belong to? The value must be the name of an org 34 | # defined under "organizations" 35 | organization: Org3 36 | 37 | # Some SDKs support pluggable KV stores, the properties under "credentialStore" 38 | # are implementation specific 39 | credentialStore: 40 | # [Optional]. Specific to FileKeyValueStore.js or similar implementations in other SDKs. Can be others 41 | # if using an alternative impl. For instance, CouchDBKeyValueStore.js would require an object 42 | # here for properties like url, db name, etc. 43 | path: "./keystore/fabric-client-kv-org3/users" 44 | 45 | # [Optional]. Specific to the CryptoSuite implementation. Software-based implementations like 46 | # CryptoSuite_ECDSA_AES.js in node SDK requires a key store. PKCS#11 based implementations does 47 | # not. 48 | cryptoStore: 49 | # Specific to the underlying KeyValueStore that backs the crypto key store. 50 | path: "./keystore/fabric-client-kv-org3" 51 | 52 | # [Optional]. Specific to Composer environment 53 | wallet: wallet-name 54 | -------------------------------------------------------------------------------- /food-traceability/app/lib/create-channel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var util = require('util'); 17 | var fs = require('fs'); 18 | var path = require('path'); 19 | 20 | var helper = require('./helper.js'); 21 | var logger = helper.getLogger('Create-Channel'); 22 | //Attempt to send a request to the orderer with the sendTransaction method 23 | var createChannel = async function(channelName, channelConfigPath, username, orgName) { 24 | logger.debug('\n====== Creating Channel \'' + channelName + '\' ======\n'); 25 | try { 26 | // first setup the client for this org 27 | var client = await helper.getClientForOrg(orgName); 28 | logger.debug('Successfully got the fabric client for the organization "%s"', orgName); 29 | 30 | // read in the envelope for the channel config raw bytes 31 | var envelope = fs.readFileSync(path.join(__dirname, channelConfigPath)); 32 | // extract the channel config bytes from the envelope to be signed 33 | var channelConfig = client.extractChannelConfig(envelope); 34 | 35 | //Acting as a client in the given organization provided with "orgName" param 36 | // sign the channel config bytes as "endorsement", this is required by 37 | // the orderer's channel creation policy 38 | // this will use the admin identity assigned to the client when the connection profile was loaded 39 | let signature = client.signChannelConfig(channelConfig); 40 | 41 | let request = { 42 | config: channelConfig, 43 | signatures: [signature], 44 | name: channelName, 45 | txId: client.newTransactionID(true) // get an admin based transactionID 46 | }; 47 | 48 | // send to orderer 49 | var response = await client.createChannel(request) 50 | logger.debug(' response ::%j', response); 51 | if (response && response.status === 'SUCCESS') { 52 | logger.debug('Successfully created the channel.'); 53 | let response = { 54 | success: true, 55 | message: 'Channel \'' + channelName + '\' created Successfully' 56 | }; 57 | return response; 58 | } else { 59 | logger.error('\n!!!!!!!!! Failed to create the channel \'' + channelName + 60 | '\' !!!!!!!!!\n\n'); 61 | throw new Error('Failed to create the channel \'' + channelName + '\''); 62 | } 63 | } catch (err) { 64 | logger.error('Failed to initialize the channel: ' + err.stack ? err.stack : err); 65 | throw new Error('Failed to initialize the channel: ' + err.toString()); 66 | } 67 | }; 68 | 69 | exports.createChannel = createChannel; 70 | -------------------------------------------------------------------------------- /food-traceability/app/lib/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | var log4js = require('log4js'); 18 | var logger = log4js.getLogger('Helper'); 19 | logger.setLevel('DEBUG'); 20 | 21 | var path = require('path'); 22 | var util = require('util'); 23 | var copService = require('fabric-ca-client'); 24 | 25 | var hfc = require('fabric-client'); 26 | hfc.setLogger(logger); 27 | var ORGS = hfc.getConfigSetting('network-config'); 28 | 29 | var clients = {}; 30 | var channels = {}; 31 | var caClients = {}; 32 | 33 | var sleep = async function (sleep_time_ms) { 34 | return new Promise(resolve => setTimeout(resolve, sleep_time_ms)); 35 | } 36 | 37 | async function getClientForOrg (userorg, username) { 38 | logger.debug('getClientForOrg - ****** START %s %s', userorg, username) 39 | // get a fabric client loaded with a connection profile for this org 40 | let config = '-connection-profile-path'; 41 | 42 | // build a client context and load it with a connection profile 43 | // lets only load the network settings and save the client for later 44 | let client = hfc.loadFromConfig(hfc.getConfigSetting('network'+config)); 45 | 46 | // This will load a connection profile over the top of the current one one 47 | // since the first one did not have a client section and the following one does 48 | // nothing will actually be replaced. 49 | // This will also set an admin identity because the organization defined in the 50 | // client section has one defined 51 | client.loadFromConfig(hfc.getConfigSetting(userorg+config)); 52 | 53 | // this will create both the state store and the crypto store based 54 | // on the settings in the client section of the connection profile 55 | await client.initCredentialStores(); 56 | 57 | // The getUserContext call tries to get the user from persistence. 58 | // If the user has been saved to persistence then that means the user has 59 | // been registered and enrolled. If the user is found in persistence 60 | // the call will then assign the user to the client object. 61 | if(username) { 62 | let user = await client.getUserContext(username, true); 63 | if(!user) { 64 | throw new Error(util.format('User was not found :', username)); 65 | } else { 66 | logger.debug('User %s was found to be registered and enrolled', username); 67 | } 68 | } 69 | logger.debug('getClientForOrg - ****** END %s %s \n\n', userorg, username) 70 | 71 | return client; 72 | } 73 | 74 | var getRegisteredUser = async function(username, userOrg, isJson) { 75 | try { 76 | var client = await getClientForOrg(userOrg); 77 | logger.debug('Successfully initialized the credential stores'); 78 | // client can now act as an agent for organization Org1 79 | // first check to see if the user is already enrolled 80 | var user = await client.getUserContext(username, true); 81 | if (user && user.isEnrolled()) { 82 | logger.info('Successfully loaded member from persistence'); 83 | } else { 84 | // user was not enrolled, so we will need an admin user object to register 85 | logger.info('User %s was not enrolled, so we will need an admin user object to register',username); 86 | var admins = hfc.getConfigSetting('admins'); 87 | let adminUserObj = await client.setUserContext({username: admins[0].username, password: admins[0].secret}); 88 | let caClient = client.getCertificateAuthority(); 89 | let secret = await caClient.register({ 90 | enrollmentID: username, 91 | affiliation: userOrg.toLowerCase() + '.department1' 92 | }, adminUserObj); 93 | logger.debug('Successfully got the secret for user %s',username); 94 | user = await client.setUserContext({username:username, password:secret}); 95 | logger.debug('Successfully enrolled username %s and setUserContext on the client object', username); 96 | } 97 | if(user && user.isEnrolled) { 98 | if (isJson && isJson === true) { 99 | var response = { 100 | success: true, 101 | secret: user._enrollmentSecret, 102 | message: username + ' enrolled Successfully', 103 | }; 104 | return response; 105 | } 106 | } else { 107 | throw new Error('User was not enrolled '); 108 | } 109 | } catch(error) { 110 | logger.error('Failed to get registered user: %s with error: %s', username, error.toString()); 111 | return 'failed '+error.toString(); 112 | } 113 | 114 | }; 115 | 116 | 117 | var setupChaincodeDeploy = function() { 118 | process.env.GOPATH = path.join(__dirname, hfc.getConfigSetting('CC_SRC_PATH')); 119 | }; 120 | 121 | var getLogger = function(moduleName) { 122 | var logger = log4js.getLogger(moduleName); 123 | logger.setLevel('DEBUG'); 124 | return logger; 125 | }; 126 | 127 | exports.getClientForOrg = getClientForOrg; 128 | exports.getLogger = getLogger; 129 | exports.setupChaincodeDeploy = setupChaincodeDeploy; 130 | exports.getRegisteredUser = getRegisteredUser; 131 | -------------------------------------------------------------------------------- /food-traceability/app/lib/install-chaincode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | var path = require('path'); 18 | var fs = require('fs'); 19 | var util = require('util'); 20 | var config = require('../config.json'); 21 | var helper = require('./helper.js'); 22 | var logger = helper.getLogger('install-chaincode'); 23 | var tx_id = null; 24 | 25 | var installChaincode = async function(peers, chaincodeName, chaincodePath, 26 | chaincodeVersion, chaincodeType, username, org_name) { 27 | logger.debug('\n\n============ Install chaincode on organizations ============\n'); 28 | helper.setupChaincodeDeploy(); 29 | let error_message = null; 30 | try { 31 | logger.info('Calling peers in organization "%s" to join the channel', org_name); 32 | 33 | // first setup the client for this org 34 | var client = await helper.getClientForOrg(org_name, username); 35 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 36 | 37 | tx_id = client.newTransactionID(true); //get an admin transactionID 38 | var request = { 39 | targets: peers, 40 | chaincodePath: chaincodePath, 41 | chaincodeId: chaincodeName, 42 | chaincodeVersion: chaincodeVersion, 43 | chaincodeType: chaincodeType 44 | }; 45 | let results = await client.installChaincode(request); 46 | // the returned object has both the endorsement results 47 | // and the actual proposal, the proposal will be needed 48 | // later when we send a transaction to the orederer 49 | var proposalResponses = results[0]; 50 | var proposal = results[1]; 51 | 52 | // lets have a look at the responses to see if they are 53 | // all good, if good they will also include signatures 54 | // required to be committed 55 | var all_good = true; 56 | for (var i in proposalResponses) { 57 | let one_good = false; 58 | if (proposalResponses && proposalResponses[i].response && 59 | proposalResponses[i].response.status === 200) { 60 | one_good = true; 61 | logger.info('install proposal was good'); 62 | } else { 63 | logger.error('install proposal was bad %j',proposalResponses.toJSON()); 64 | } 65 | all_good = all_good & one_good; 66 | } 67 | if (all_good) { 68 | logger.info('Successfully sent install Proposal and received ProposalResponse'); 69 | } else { 70 | error_message = 'Failed to send install Proposal or receive valid response. Response null or status is not 200' 71 | logger.error(error_message); 72 | } 73 | } catch(error) { 74 | logger.error('Failed to install due to error: ' + error.stack ? error.stack : error); 75 | error_message = error.toString(); 76 | } 77 | 78 | if (!error_message) { 79 | let message = util.format('Successfully install chaincode'); 80 | logger.info(message); 81 | // build a response to send back to the REST caller 82 | let response = { 83 | success: true, 84 | message: message 85 | }; 86 | return response; 87 | } else { 88 | let message = util.format('Failed to install due to:%s',error_message); 89 | logger.error(message); 90 | throw new Error(message); 91 | } 92 | }; 93 | exports.installChaincode = installChaincode; 94 | -------------------------------------------------------------------------------- /food-traceability/app/lib/instantiate-chaincode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | var path = require('path'); 18 | var fs = require('fs'); 19 | var util = require('util'); 20 | var hfc = require('fabric-client'); 21 | var helper = require('./helper.js'); 22 | var logger = helper.getLogger('instantiate-chaincode'); 23 | 24 | var instantiateChaincode = async function(peers, channelName, chaincodeName, chaincodeVersion, functionName, chaincodeType, args, username, org_name) { 25 | logger.debug('\n\n============ Instantiate chaincode on channel ' + channelName + 26 | ' ============\n'); 27 | var error_message = null; 28 | 29 | try { 30 | // first setup the client for this org 31 | var client = await helper.getClientForOrg(org_name, username); 32 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 33 | var channel = client.getChannel(channelName); 34 | if(!channel) { 35 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 36 | logger.error(message); 37 | throw new Error(message); 38 | } 39 | var tx_id = client.newTransactionID(true); // Get an admin based transactionID 40 | // An admin based transactionID will 41 | // indicate that admin identity should 42 | // be used to sign the proposal request. 43 | // will need the transaction ID string for the event registration later 44 | var deployId = tx_id.getTransactionID(); 45 | 46 | // send proposal to endorser 47 | var request = { 48 | targets : peers, 49 | chaincodeId: chaincodeName, 50 | chaincodeType: chaincodeType, 51 | chaincodeVersion: chaincodeVersion, 52 | args: args, 53 | txId: tx_id 54 | }; 55 | 56 | if (functionName) 57 | request.fcn = functionName; 58 | 59 | let results = await channel.sendInstantiateProposal(request, 60000); //instantiate takes much longer 60 | 61 | // the returned object has both the endorsement results 62 | // and the actual proposal, the proposal will be needed 63 | // later when we send a transaction to the orderer 64 | var proposalResponses = results[0]; 65 | var proposal = results[1]; 66 | 67 | // lets have a look at the responses to see if they are 68 | // all good, if good they will also include signatures 69 | // required to be committed 70 | var all_good = true; 71 | for (var i in proposalResponses) { 72 | let one_good = false; 73 | if (proposalResponses && proposalResponses[i].response && 74 | proposalResponses[i].response.status === 200) { 75 | one_good = true; 76 | logger.info('instantiate proposal was good'); 77 | } else { 78 | logger.error('instantiate proposal was bad'); 79 | } 80 | all_good = all_good & one_good; 81 | } 82 | 83 | if (all_good) { 84 | logger.info(util.format( 85 | 'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', 86 | proposalResponses[0].response.status, proposalResponses[0].response.message, 87 | proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature)); 88 | 89 | // wait for the channel-based event hub to tell us that the 90 | // instantiate transaction was committed on the peer 91 | var promises = []; 92 | let event_hubs = channel.getChannelEventHubsForOrg(); 93 | logger.debug('found %s eventhubs for this organization %s',event_hubs.length, org_name); 94 | event_hubs.forEach((eh) => { 95 | let instantiateEventPromise = new Promise((resolve, reject) => { 96 | logger.debug('instantiateEventPromise - setting up event'); 97 | let event_timeout = setTimeout(() => { 98 | let message = 'REQUEST_TIMEOUT:' + eh.getPeerAddr(); 99 | logger.error(message); 100 | eh.disconnect(); 101 | }, 60000); 102 | eh.registerTxEvent(deployId, (tx, code, block_num) => { 103 | logger.info('The chaincode instantiate transaction has been committed on peer %s',eh.getPeerAddr()); 104 | logger.info('Transaction %s has status of %s in blocl %s', tx, code, block_num); 105 | clearTimeout(event_timeout); 106 | 107 | if (code !== 'VALID') { 108 | let message = until.format('The chaincode instantiate transaction was invalid, code:%s',code); 109 | logger.error(message); 110 | reject(new Error(message)); 111 | } else { 112 | let message = 'The chaincode instantiate transaction was valid.'; 113 | logger.info(message); 114 | resolve(message); 115 | } 116 | }, (err) => { 117 | clearTimeout(event_timeout); 118 | logger.error(err); 119 | reject(err); 120 | }, 121 | // the default for 'unregister' is true for transaction listeners 122 | // so no real need to set here, however for 'disconnect' 123 | // the default is false as most event hubs are long running 124 | // in this use case we are using it only once 125 | {unregister: true, disconnect: true} 126 | ); 127 | eh.connect(); 128 | }); 129 | promises.push(instantiateEventPromise); 130 | }); 131 | 132 | var orderer_request = { 133 | txId: tx_id, // must include the transaction id so that the outbound 134 | // transaction to the orderer will be signed by the admin 135 | // id as was the proposal above, notice that transactionID 136 | // generated above was based on the admin id not the current 137 | // user assigned to the 'client' instance. 138 | proposalResponses: proposalResponses, 139 | proposal: proposal 140 | }; 141 | var sendPromise = channel.sendTransaction(orderer_request); 142 | // put the send to the orderer last so that the events get registered and 143 | // are ready for the orderering and committing 144 | promises.push(sendPromise); 145 | let results = await Promise.all(promises); 146 | logger.debug(util.format('------->>> R E S P O N S E : %j', results)); 147 | let response = results.pop(); // orderer results are last in the results 148 | if (response.status === 'SUCCESS') { 149 | logger.info('Successfully sent transaction to the orderer.'); 150 | } else { 151 | error_message = util.format('Failed to order the transaction. Error code: %s',response.status); 152 | logger.debug(error_message); 153 | } 154 | 155 | // now see what each of the event hubs reported 156 | for(let i in results) { 157 | let event_hub_result = results[i]; 158 | let event_hub = event_hubs[i]; 159 | logger.debug('Event results for event hub :%s',event_hub.getPeerAddr()); 160 | if(typeof event_hub_result === 'string') { 161 | logger.debug(event_hub_result); 162 | } else { 163 | if(!error_message) error_message = event_hub_result.toString(); 164 | logger.debug(event_hub_result.toString()); 165 | } 166 | } 167 | } else { 168 | error_message = util.format('Failed to send Proposal and receive all good ProposalResponse'); 169 | logger.debug(error_message); 170 | } 171 | } catch (error) { 172 | logger.error('Failed to send instantiate due to error: ' + error.stack ? error.stack : error); 173 | error_message = error.toString(); 174 | } 175 | 176 | if (!error_message) { 177 | let message = util.format( 178 | 'Successfully instantiate chaingcode in organization %s to the channel \'%s\'', 179 | org_name, channelName); 180 | logger.info(message); 181 | // build a response to send back to the REST caller 182 | let response = { 183 | success: true, 184 | message: message 185 | }; 186 | return response; 187 | } else { 188 | let message = util.format('Failed to instantiate. cause:%s',error_message); 189 | logger.error(message); 190 | throw new Error(message); 191 | } 192 | }; 193 | exports.instantiateChaincode = instantiateChaincode; 194 | -------------------------------------------------------------------------------- /food-traceability/app/lib/invoke-transaction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 'use strict'; 17 | var path = require('path'); 18 | var fs = require('fs'); 19 | var util = require('util'); 20 | var hfc = require('fabric-client'); 21 | var config = require('../config.json'); 22 | var helper = require('./helper.js'); 23 | var logger = helper.getLogger('invoke-chaincode'); 24 | 25 | var invokeChaincode = async function(peerNames, channelName, chaincodeName, fcn, args, username, org_name) { 26 | logger.debug(util.format('\n============ invoke transaction on channel %s ============\n', channelName)); 27 | var error_message = null; 28 | var tx_id_string = null; 29 | try { 30 | // first setup the client for this org 31 | var client = await helper.getClientForOrg(org_name, username); 32 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 33 | var channel = client.getChannel(channelName); 34 | if(!channel) { 35 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 36 | logger.error(message); 37 | throw new Error(message); 38 | } 39 | var tx_id = client.newTransactionID(); 40 | // will need the transaction ID string for the event registration later 41 | tx_id_string = tx_id.getTransactionID(); 42 | 43 | // send proposal to endorser 44 | var request = { 45 | targets: peerNames, 46 | chaincodeId: chaincodeName, 47 | fcn: fcn, 48 | args: args, 49 | chainId: channelName, 50 | txId: tx_id 51 | }; 52 | 53 | let results = await channel.sendTransactionProposal(request); 54 | 55 | // the returned object has both the endorsement results 56 | // and the actual proposal, the proposal will be needed 57 | // later when we send a transaction to the orderer 58 | var proposalResponses = results[0]; 59 | var proposal = results[1]; 60 | 61 | // lets have a look at the responses to see if they are 62 | // all good, if good they will also include signatures 63 | // required to be committed 64 | var all_good = true; 65 | for (var i in proposalResponses) { 66 | let one_good = false; 67 | if (proposalResponses && proposalResponses[i].response && 68 | proposalResponses[i].response.status === 200) { 69 | one_good = true; 70 | logger.info('invoke chaincode proposal was good'); 71 | } else { 72 | logger.error('invoke chaincode proposal was bad'); 73 | } 74 | all_good = all_good & one_good; 75 | } 76 | 77 | if (all_good) { 78 | logger.info(util.format( 79 | 'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', 80 | proposalResponses[0].response.status, proposalResponses[0].response.message, 81 | proposalResponses[0].response.payload, proposalResponses[0].endorsement.signature)); 82 | 83 | // wait for the channel-based event hub to tell us 84 | // that the commit was good or bad on each peer in our organization 85 | var promises = []; 86 | let event_hubs = channel.getChannelEventHubsForOrg(); 87 | event_hubs.forEach((eh) => { 88 | logger.debug('invokeEventPromise - setting up event'); 89 | let invokeEventPromise = new Promise((resolve, reject) => { 90 | let event_timeout = setTimeout(() => { 91 | let message = 'REQUEST_TIMEOUT:' + eh.getPeerAddr(); 92 | logger.error(message); 93 | eh.disconnect(); 94 | }, config.eventWaitTime); 95 | eh.registerTxEvent(tx_id_string, (tx, code, block_num) => { 96 | logger.info('The chaincode invoke chaincode transaction has been committed on peer %s',eh.getPeerAddr()); 97 | logger.info('Transaction %s has status of %s in blocl %s', tx, code, block_num); 98 | clearTimeout(event_timeout); 99 | 100 | if (code !== 'VALID') { 101 | let message = util.format('The invoke chaincode transaction was invalid, code:%s',code); 102 | logger.error(message); 103 | reject(new Error(message)); 104 | } else { 105 | let message = 'The invoke chaincode transaction was valid.'; 106 | logger.info(message); 107 | resolve(message); 108 | } 109 | }, (err) => { 110 | clearTimeout(event_timeout); 111 | logger.error(err); 112 | reject(err); 113 | }, 114 | // the default for 'unregister' is true for transaction listeners 115 | // so no real need to set here, however for 'disconnect' 116 | // the default is false as most event hubs are long running 117 | // in this use case we are using it only once 118 | {unregister: true, disconnect: true} 119 | ); 120 | eh.connect(); 121 | }); 122 | promises.push(invokeEventPromise); 123 | }); 124 | 125 | var orderer_request = { 126 | txId: tx_id, 127 | proposalResponses: proposalResponses, 128 | proposal: proposal 129 | }; 130 | var sendPromise = channel.sendTransaction(orderer_request); 131 | // put the send to the orderer last so that the events get registered and 132 | // are ready for the orderering and committing 133 | promises.push(sendPromise); 134 | let results = await Promise.all(promises); 135 | logger.debug(util.format('------->>> R E S P O N S E : %j', results)); 136 | let response = results.pop(); // orderer results are last in the results 137 | if (response.status === 'SUCCESS') { 138 | logger.info('Successfully sent transaction to the orderer.'); 139 | } else { 140 | error_message = util.format('Failed to order the transaction. Error code: %s',response.status); 141 | logger.debug(error_message); 142 | } 143 | 144 | // now see what each of the event hubs reported 145 | for(let i in results) { 146 | let event_hub_result = results[i]; 147 | let event_hub = event_hubs[i]; 148 | logger.debug('Event results for event hub :%s',event_hub.getPeerAddr()); 149 | if(typeof event_hub_result === 'string') { 150 | logger.debug(event_hub_result); 151 | } else { 152 | if(!error_message) error_message = event_hub_result.toString(); 153 | logger.debug(event_hub_result.toString()); 154 | } 155 | } 156 | } else { 157 | error_message = util.format('Failed to send Proposal and receive all good ProposalResponse'); 158 | logger.debug(error_message); 159 | } 160 | } catch (error) { 161 | logger.error('Failed to invoke due to error: ' + error.stack ? error.stack : error); 162 | error_message = error.toString(); 163 | } 164 | 165 | if (!error_message) { 166 | let message = util.format( 167 | 'Successfully invoked the chaincode %s to the channel \'%s\' for transaction ID: %s', 168 | org_name, channelName, tx_id_string); 169 | logger.info(message); 170 | 171 | return tx_id_string; 172 | } else { 173 | let message = util.format('Failed to invoke chaincode. cause:%s',error_message); 174 | logger.error(message); 175 | throw new Error(message); 176 | } 177 | }; 178 | 179 | exports.invokeChaincode = invokeChaincode; 180 | -------------------------------------------------------------------------------- /food-traceability/app/lib/join-channel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the 'License'); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an 'AS IS' BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var util = require('util'); 17 | var path = require('path'); 18 | var fs = require('fs'); 19 | 20 | var helper = require('./helper.js'); 21 | var logger = helper.getLogger('Join-Channel'); 22 | 23 | /* 24 | * Have an organization join a channel 25 | */ 26 | var joinChannel = async function(channel_name, peers, username, org_name) { 27 | logger.debug('\n\n============ Join Channel start ============\n') 28 | var error_message = null; 29 | var all_eventhubs = []; 30 | try { 31 | logger.info('Calling peers in organization "%s" to join the channel', org_name); 32 | 33 | // first setup the client for this org 34 | var client = await helper.getClientForOrg(org_name, username); 35 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 36 | var channel = client.getChannel(channel_name); 37 | if(!channel) { 38 | let message = util.format('Channel %s was not defined in the connection profile', channel_name); 39 | logger.error(message); 40 | throw new Error(message); 41 | } 42 | 43 | // next step is to get the genesis_block from the orderer, 44 | // the starting point for the channel that we want to join 45 | let request = { 46 | txId : client.newTransactionID(true) //get an admin based transactionID 47 | }; 48 | let genesis_block = await channel.getGenesisBlock(request); 49 | 50 | // tell each peer to join and wait 10 seconds 51 | // for the channel to be created on each peer 52 | var promises = []; 53 | promises.push(new Promise(resolve => setTimeout(resolve, 10000))); 54 | 55 | let join_request = { 56 | targets: peers, //using the peer names which only is allowed when a connection profile is loaded 57 | txId: client.newTransactionID(true), //get an admin based transactionID 58 | block: genesis_block 59 | }; 60 | let join_promise = channel.joinChannel(join_request); 61 | promises.push(join_promise); 62 | let results = await Promise.all(promises); 63 | logger.debug(util.format('Join Channel R E S P O N S E : %j', results)); 64 | 65 | // lets check the results of sending to the peers which is 66 | // last in the results array 67 | let peers_results = results.pop(); 68 | // then each peer results 69 | for(let i in peers_results) { 70 | let peer_result = peers_results[i]; 71 | if(peer_result.response && peer_result.response.status == 200) { 72 | logger.info('Successfully joined peer to the channel %s',channel_name); 73 | } else { 74 | let message = util.format('Failed to joined peer to the channel %s',channel_name); 75 | error_message = message; 76 | logger.error(message); 77 | } 78 | } 79 | } catch(error) { 80 | logger.error('Failed to join channel due to error: ' + error.stack ? error.stack : error); 81 | error_message = error.toString(); 82 | } 83 | 84 | // need to shutdown open event streams 85 | all_eventhubs.forEach((eh) => { 86 | eh.disconnect(); 87 | }); 88 | 89 | if (!error_message) { 90 | let message = util.format( 91 | 'Successfully joined peers in organization %s to the channel:%s', 92 | org_name, channel_name); 93 | logger.info(message); 94 | // build a response to send back to the REST caller 95 | let response = { 96 | success: true, 97 | message: message 98 | }; 99 | return response; 100 | } else { 101 | let message = util.format('Failed to join all peers to channel. cause:%s',error_message); 102 | logger.error(message); 103 | throw new Error(message); 104 | } 105 | }; 106 | exports.joinChannel = joinChannel; 107 | -------------------------------------------------------------------------------- /food-traceability/app/lib/query.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 IBM All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | var path = require('path'); 17 | var fs = require('fs'); 18 | var util = require('util'); 19 | var hfc = require('fabric-client'); 20 | var helper = require('./helper.js'); 21 | var logger = helper.getLogger('Query'); 22 | 23 | var queryChaincode = async function(peer, channelName, chaincodeName, args, fcn, username, org_name) { 24 | try { 25 | // first setup the client for this org 26 | var client = await helper.getClientForOrg(org_name, username); 27 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 28 | var channel = client.getChannel(channelName); 29 | if(!channel) { 30 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 31 | logger.error(message); 32 | throw new Error(message); 33 | } 34 | 35 | // send query 36 | var request = { 37 | targets : [peer], //queryByChaincode allows for multiple targets 38 | chaincodeId: chaincodeName, 39 | fcn: fcn, 40 | args: args 41 | }; 42 | let response_payloads = await channel.queryByChaincode(request); 43 | if (response_payloads) { 44 | for (let i = 0; i < response_payloads.length; i++) { 45 | logger.info(args[0]+' now has ' + response_payloads[i].toString('utf8') + 46 | ' after the move'); 47 | } 48 | return args[0]+' now has ' + response_payloads[0].toString('utf8') + 49 | ' after the move'; 50 | } else { 51 | logger.error('response_payloads is null'); 52 | return 'response_payloads is null'; 53 | } 54 | } catch(error) { 55 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 56 | return error.toString(); 57 | } 58 | }; 59 | var getBlockByNumber = async function(peer, channelName, blockNumber, username, org_name) { 60 | try { 61 | // first setup the client for this org 62 | var client = await helper.getClientForOrg(org_name, username); 63 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 64 | var channel = client.getChannel(channelName); 65 | if(!channel) { 66 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 67 | logger.error(message); 68 | throw new Error(message); 69 | } 70 | 71 | let response_payload = await channel.queryBlock(parseInt(blockNumber, peer)); 72 | if (response_payload) { 73 | logger.debug(response_payload); 74 | return response_payload; 75 | } else { 76 | logger.error('response_payload is null'); 77 | return 'response_payload is null'; 78 | } 79 | } catch(error) { 80 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 81 | return error.toString(); 82 | } 83 | }; 84 | var getTransactionByID = async function(peer, channelName, trxnID, username, org_name) { 85 | try { 86 | // first setup the client for this org 87 | var client = await helper.getClientForOrg(org_name, username); 88 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 89 | var channel = client.getChannel(channelName); 90 | if(!channel) { 91 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 92 | logger.error(message); 93 | throw new Error(message); 94 | } 95 | 96 | let response_payload = await channel.queryTransaction(trxnID, peer); 97 | if (response_payload) { 98 | logger.debug(response_payload); 99 | return response_payload; 100 | } else { 101 | logger.error('response_payload is null'); 102 | return 'response_payload is null'; 103 | } 104 | } catch(error) { 105 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 106 | return error.toString(); 107 | } 108 | }; 109 | var getBlockByHash = async function(peer, channelName, hash, username, org_name) { 110 | try { 111 | // first setup the client for this org 112 | var client = await helper.getClientForOrg(org_name, username); 113 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 114 | var channel = client.getChannel(channelName); 115 | if(!channel) { 116 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 117 | logger.error(message); 118 | throw new Error(message); 119 | } 120 | 121 | let response_payload = await channel.queryBlockByHash(Buffer.from(hash), peer); 122 | if (response_payload) { 123 | logger.debug(response_payload); 124 | return response_payload; 125 | } else { 126 | logger.error('response_payload is null'); 127 | return 'response_payload is null'; 128 | } 129 | } catch(error) { 130 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 131 | return error.toString(); 132 | } 133 | }; 134 | var getChainInfo = async function(peer, channelName, username, org_name) { 135 | try { 136 | // first setup the client for this org 137 | var client = await helper.getClientForOrg(org_name, username); 138 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 139 | var channel = client.getChannel(channelName); 140 | if(!channel) { 141 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 142 | logger.error(message); 143 | throw new Error(message); 144 | } 145 | 146 | let response_payload = await channel.queryInfo(peer); 147 | if (response_payload) { 148 | logger.debug(response_payload); 149 | return response_payload; 150 | } else { 151 | logger.error('response_payload is null'); 152 | return 'response_payload is null'; 153 | } 154 | } catch(error) { 155 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 156 | return error.toString(); 157 | } 158 | }; 159 | //getInstalledChaincodes 160 | var getInstalledChaincodes = async function(peer, channelName, type, username, org_name) { 161 | try { 162 | // first setup the client for this org 163 | var client = await helper.getClientForOrg(org_name, username); 164 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 165 | 166 | let response = null 167 | if (type === 'installed') { 168 | response = await client.queryInstalledChaincodes(peer, true); //use the admin identity 169 | } else { 170 | var channel = client.getChannel(channelName); 171 | if(!channel) { 172 | let message = util.format('Channel %s was not defined in the connection profile', channelName); 173 | logger.error(message); 174 | throw new Error(message); 175 | } 176 | response = await channel.queryInstantiatedChaincodes(peer, true); //use the admin identity 177 | } 178 | if (response) { 179 | if (type === 'installed') { 180 | logger.debug('<<< Installed Chaincodes >>>'); 181 | } else { 182 | logger.debug('<<< Instantiated Chaincodes >>>'); 183 | } 184 | var details = []; 185 | for (let i = 0; i < response.chaincodes.length; i++) { 186 | logger.debug('name: ' + response.chaincodes[i].name + ', version: ' + 187 | response.chaincodes[i].version + ', path: ' + response.chaincodes[i].path 188 | ); 189 | details.push('name: ' + response.chaincodes[i].name + ', version: ' + 190 | response.chaincodes[i].version + ', path: ' + response.chaincodes[i].path 191 | ); 192 | } 193 | return details; 194 | } else { 195 | logger.error('response is null'); 196 | return 'response is null'; 197 | } 198 | } catch(error) { 199 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 200 | return error.toString(); 201 | } 202 | }; 203 | var getChannels = async function(peer, username, org_name) { 204 | try { 205 | // first setup the client for this org 206 | var client = await helper.getClientForOrg(org_name, username); 207 | logger.debug('Successfully got the fabric client for the organization "%s"', org_name); 208 | 209 | let response = await client.queryChannels(peer); 210 | if (response) { 211 | logger.debug('<<< channels >>>'); 212 | var channelNames = []; 213 | for (let i = 0; i < response.channels.length; i++) { 214 | channelNames.push('channel id: ' + response.channels[i].channel_id); 215 | } 216 | logger.debug(channelNames); 217 | return response; 218 | } else { 219 | logger.error('response_payloads is null'); 220 | return 'response_payloads is null'; 221 | } 222 | } catch(error) { 223 | logger.error('Failed to query due to error: ' + error.stack ? error.stack : error); 224 | return error.toString(); 225 | } 226 | }; 227 | 228 | exports.queryChaincode = queryChaincode; 229 | exports.getBlockByNumber = getBlockByNumber; 230 | exports.getTransactionByID = getTransactionByID; 231 | exports.getBlockByHash = getBlockByHash; 232 | exports.getChainInfo = getChainInfo; 233 | exports.getInstalledChaincodes = getInstalledChaincodes; 234 | exports.getChannels = getChannels; 235 | -------------------------------------------------------------------------------- /food-traceability/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-dapp", 3 | "version": "1.0.0", 4 | "description": "A example node program to demonstrate using node.js SDK APIs", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js" 8 | }, 9 | "keywords": [ 10 | "fabric-client sample app", 11 | "node sample", 12 | "v1.0 fabric nodesdk sample" 13 | ], 14 | "engines": { 15 | "node": ">=8.9.4 <9.0", 16 | "npm": ">=5.6.0 <6.0" 17 | }, 18 | "license": "Apache-2.0", 19 | "dependencies": { 20 | "@google-cloud/vision": "^0.22.1", 21 | "body-parser": "^1.17.1", 22 | "cookie-parser": "^1.4.3", 23 | "cors": "^2.8.3", 24 | "express": "^4.15.2", 25 | "express-bearer-token": "^2.1.0", 26 | "express-jwt": "^5.1.0", 27 | "express-session": "^1.15.2", 28 | "fabric-ca-client": "~1.2.0", 29 | "fabric-client": "~1.2.0", 30 | "fs-extra": "^2.0.0", 31 | "jsonwebtoken": "^7.3.0", 32 | "log4js": "^0.6.38" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/README.md: -------------------------------------------------------------------------------- 1 | ### api 2 | #### Dairyfarm Chaincode 3 | * addDairyFarm 方法 4 | 方法描述:添加奶牛场 5 | 参数:(string,string) 6 | 参数描述:第一个奶牛场id,第二个为奶牛场名 7 | 例子:F001,奶牛场1 8 | 9 | * addCow 方法 10 | 方法描述:添加奶牛 11 | 参数:(string) 12 | 参数描述:为奶牛结构体json字符串 13 | 例子:{"farmId":"F001","healthy":true,"quarantine":true,"feedSource":"国产","stutas":0} 14 | 15 | | 字段 | 类型 |含义| 16 | |:----:|:----:|:----:| 17 | | farmId | string | 奶牛场id | 18 | | healthy | bool | 健康状态 | 19 | | quarantine | bool | 检疫状态 | 20 | | feedSource | string | 食物来源 | 21 | | stutas | int | 状态:0正常,1死亡 | 22 | 23 | 24 | * delCow 方法 25 | 方法描述:删除奶牛 26 | 参数:(string) 27 | 参数描述:奶牛id 28 | 例子:F001000001 29 | 30 | * addCowOperate 方法 31 | 方法描述:添加操作 32 | 参数:(string) 33 | 参数描述:为操作结构体json字符串 34 | 例子:{"cowId":"F001000001","operation":1,"consumptionOrOutput":"b1"} 35 | 36 | | 字段 | 类型 |含义| 37 | |:----:|:----:|:----:| 38 | | cowId | string | 奶牛id | 39 | | operation | int | 操作类型 1为喂养 2为检疫 | 40 | | consumptionOrOutput | string | 消耗或产出即额外数据,建议存json字符串 | 41 | 42 | * addCowMilking 方法 43 | 方法描述:添加操作 44 | 参数:(string) 45 | 参数描述:为操作结构体json字符串 46 | 例子:{"cowId":"F001000001","consumptionOrOutput":"b1"} 47 | 48 | | 字段 | 类型 |含义| 49 | |:----:|:----:|:----:| 50 | | cowId | string | 奶牛id | 51 | | consumptionOrOutput | string | 消耗或产出即额外数据,建议存json字符串| 52 | 53 | * sentProcess 方法 54 | 方法描述:桶发送到工厂 55 | 参数:(string,string) 56 | 参数描述:第一个为桶id,第二个为工厂id 57 | 例子:F001000001000001,M001 58 | 59 | * confirmBucket 方法 60 | 方法描述:工厂确认奶桶是否接受,如果是确认接受会立即向工厂同步奶桶信息 61 | 参数:(string,string,string) 62 | 参数描述:第一个为桶id,第二个为工厂id,第三个为 "1"为确认接受,"2"为拒绝 63 | 例子:F001000001000001,M001,1 64 | 65 | * checkBucketForMachining 方法 66 | 方法描述:查看工厂待收的奶桶 67 | 参数:(string) 68 | 参数描述:工厂id 69 | 例子:M001 70 | 71 | * getOperationHistory 方法 72 | 方法描述:获取操作历史 73 | 参数:(string) 74 | 参数描述:奶牛id或者奶桶id 75 | 例子:F001000001 76 | 77 | * get 方法 78 | 方法描述:获取数据 79 | 参数:(string) 80 | 参数描述:多个key以逗号分割 81 | 例子:F001000001,F001000002 82 | 83 | * set 方法 84 | 方法描述:存入数据 85 | 参数:(string,string) 86 | 参数描述:key,value 87 | 例子:F001000001000001,{"id":"F001000001000001","machiningId":"","time":"2018-10-04 08:40:38","stutas":0} 88 | 89 | #### Machining Chaincode 90 | * addMachining 方法 91 | 方法描述:添加加工厂 92 | 参数:(string,string) 93 | 参数描述:第一个为加工厂id,第二个为加工厂名 94 | 例子:M001,加工厂1 95 | 96 | * addBucket 方法 97 | 方法描述:添加奶桶 98 | 参数:(string) 99 | 参数描述:奶桶结构体json字符串 100 | 例子:{"id":"F001000001000001","machiningId":"M001","time":"2018-10-08 15:26:37","inMachiningTime":"2018-10-08 15:26:37","stutas":0} 101 | 102 | | 字段 | 类型 |含义| 103 | |:----:|:----:|:----:| 104 | | id | string | 桶id | 105 | | machiningId | string | 加工厂id | 106 | | time | string | 装桶时间 | 107 | | inMachiningTime | string |进入加工厂时间 | 108 | | stutas | int | 状态 | 109 | 110 | * addMilkPack 方法 111 | 方法描述:打包牛奶 112 | 参数:(string) 113 | 参数描述:为操作结构体json字符串 114 | 例子:{"bucketId":"F001000001000001","consumptionOrOutput":"打包牛奶"} 115 | 116 | | 字段 | 类型 |含义| 117 | |:----:|:----:|:----:| 118 | | bucketId | string | 桶id | 119 | | consumptionOrOutput | string | 消耗或产出即额外数据,建议存json字符串 | 120 | 121 | * addMilkOperation 方法 122 | 方法描述:添加奶桶操作 123 | 参数:(string) 124 | 参数描述:为操作结构体json字符串 125 | 例子:{"bucketId":"F001000001000001","operation":1,"consumptionOrOutput":"灌装"} 126 | 127 | | 字段 | 类型 |含义| 128 | |:----:|:----:|:----:| 129 | | bucketId | string | 桶id | 130 | | operation | int | 操作类型 0为消毒,1为灌装 | 131 | | consumptionOrOutput | string | 消耗或产出即额外数据,建议存json字符串 | 132 | 133 | 134 | * sentSale 方法 135 | 方法描述:牛奶送到销售 136 | 参数:(string,string) 137 | 参数描述:第一个为牛奶id,第二个为销售id 138 | 例子:F0010000010000010001,S001 139 | 140 | * confirmMilk 方法 141 | 方法描述:销售终端确认牛奶是否接受,如果是确认接受会立即向销售终端同步牛奶信息 142 | 参数:(string,string,string) 143 | 参数描述:第一个为牛奶id,第二个为销售终端id,第三个为 "1"为确认接受,"2"为拒绝 144 | 例子:F0010000010000010001,S001,1 145 | 146 | * checkMilkForSaleterminal 方法 147 | 方法描述:查看销售终端待收的牛奶 148 | 参数:(string) 149 | 参数描述:终端id 150 | 例子:S001 151 | 152 | * getOperationHistory 方法 153 | 方法描述:获取操作历史 154 | 参数:(string) 155 | 参数描述:奶牛id或者奶桶id 156 | 例子:F0010000010000010001 157 | 158 | * get 方法 159 | 方法描述:获取数据 160 | 参数:(string) 161 | 参数描述:多个key以逗号分割 162 | 例子:F0010000010000010001,F0010000010000010002 163 | 164 | * set 方法 165 | 方法描述:存入数据 166 | 参数:(string,string) 167 | 参数描述:key,value 168 | 例子:F001000001000001,{"id":"F001000001000001","machiningId":"","time":"2018-10-04 08:40:38","stutas":0} 169 | 170 | #### Salesterminal Chaincode 171 | * addSalesterminal 方法 172 | 方法描述:添加销售终端 173 | 参数:(string,string) 174 | 参数描述:第一个为销售终端id, 第二个为销售终端名 175 | 例子:S001,销售终端1 176 | 177 | * addMilk 方法 178 | 方法描述:添加奶桶 179 | 参数:(string) 180 | 参数描述:奶桶结构体json字符串 181 | 例子:{"id":"F0010000010000010001","time":"2018-10-08 15:48:27","InSaleTime":"2018-10-08 15:48:27","saleId":"S001","stutas":0} 182 | 183 | | 字段 | 类型 |含义| 184 | |:----:|:----:|:----:| 185 | | id | string | 牛奶id | 186 | | saleId | string | 销售端id | 187 | | time | string | 生产日期 | 188 | | inSaleTime | string | 进入售买日期 || 189 | | stutas | int | 状态 | 190 | 191 | * addOperation 方法 192 | 方法描述:添加操作 193 | 参数:(string) 194 | 参数描述:为操作结构体json字符串 195 | 例子:{"milkId":"F0010000010000010001","operation":1,"consumptionOrOutput":"售出"} 196 | 197 | | 字段 | 类型 |含义| 198 | |:----:|:----:|:----:| 199 | | milkId | string | 牛奶id | 200 | | operation | int | 操作类型 0为上架,1为售出,2为下架 | 201 | | consumptionOrOutput | string | 消耗或产出 | 202 | 203 | * getOperationHistory 方法 204 | 方法描述:获取操作历史 205 | 参数:(string) 206 | 参数描述:奶牛id 207 | 例子:F0010000010000010001 208 | 209 | * getMilkHistory 方法 210 | 方法描述:获取溯源操作历史 211 | 参数:(string) 212 | 参数描述:奶牛id 213 | 例子:F0010000010000010001 214 | 215 | * get 方法 216 | 方法描述:获取数据 217 | 参数:(string) 218 | 参数描述:多个key以逗号分割 219 | 例子:F0010000010000010001,F0010000010000010002 220 | 221 | * set 方法 222 | 方法描述:存入数据 223 | 参数:(string,string) 224 | 参数描述:key,value 225 | 例子:F0010000010000010001,{"id":"F0010000010000010001","time":"2018-10-08 15:48:27","InSaleTime":"2018-10-08 15:48:27","saleId":"S001","stutas":0} 226 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/dairyfarm/cc_dairyfarm.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hyperledger/fabric/core/chaincode/shim" 5 | pb "github.com/hyperledger/fabric/protos/peer" 6 | "fmt" 7 | "time" 8 | "encoding/json" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type DairyfarmChaincode struct { 14 | } 15 | 16 | //奶牛 17 | type Cow struct { 18 | Id string `json:"id"` 19 | FarmId string `json:"farmId"` //奶牛场id 20 | Healthy bool `json:"healthy"` //健康状态 21 | Quarantine bool `json:"quarantine"` //检疫状态 22 | FeedSource string `json:"feedSource"` //食物来源 23 | Stutas int8 `json:"stutas"` //状态 24 | } 25 | 26 | //奶牛场 27 | type DairyFarm struct { 28 | Id string `json:"id"` 29 | Name string `json:"name"` //名称 30 | CreateTime string `json:"createTime"` //创建时间 31 | MaxCowNo uint `json:"maxCowNo"` //最大奶牛编号 32 | MaxBucketNo uint `json:"maxBucketNo"` //最大牛奶桶编号 33 | CowIds []string `json:"cowIds"` //奶牛ids 34 | BucketIds []string `json:"bucketIds"` //桶ids 35 | } 36 | 37 | //奶牛桶 38 | type Bucket struct { 39 | Id string `json:"id"` 40 | MachiningId string `json:"machiningId"` //加工场id 41 | Time string `json:"time"` //装桶时间 42 | InMachiningTime string `json:"inMachiningTime"` //进入加工场时间 43 | Stutas int8 `json:"stutas"` //状态 0待签收,1确认,2拒绝 44 | } 45 | 46 | //奶牛操作 47 | type CowOperation struct { 48 | CowId string `json:"cowId"` //奶牛id 49 | Time string `json:"time"` //时间 50 | Operation int8 `json:"operation"` // 操作类型 0为挤奶 1为喂养 2为检疫 51 | ConsumptionOrOutput string `json:"consumptionOrOutput"` //消耗或产出 52 | } 53 | 54 | const ( 55 | channelName = "mychannel" //通道名 56 | machiningChaincodeName = "machining" // 加工厂chaincode名 57 | operationSuffix = "-opr" //奶牛操作后缀 58 | intPrefix = "%06d" //6位数值,不足前面补0 59 | dateFomat = "2006-01-02 15:04:05" //日期格式 60 | combinedConstruction = "machiningId~buskerId" //组合键 61 | ) 62 | 63 | func (t *DairyfarmChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 64 | 65 | return shim.Success(nil) 66 | } 67 | 68 | func (t *DairyfarmChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 69 | 70 | fn, args := stub.GetFunctionAndParameters() 71 | 72 | fmt.Printf("方法: %s 参数 : %s \n", fn, args) 73 | 74 | if fn == "addDairyFarm" { 75 | return t.addDairyFarm(stub, args) 76 | } else if fn == "addCow" { 77 | return t.addCow(stub, args) 78 | } else if fn == "delCow" { 79 | return t.delCow(stub, args) 80 | } else if fn == "addCowOperate" { 81 | return t.addCowOperate(stub, args) 82 | } else if fn == "addCowMilking" { 83 | return t.addCowMilking(stub, args) 84 | } else if fn == "sentProcess" { 85 | return t.sentProcess(stub, args) 86 | } else if fn == "checkBucketForMachining" { 87 | return t.checkBucketForMachining(stub, args) 88 | } else if fn == "confirmBucket" { 89 | return t.confirmBucket(stub, args) 90 | } else if fn == "getOperationHistory" { 91 | return t.getOperationHistory(stub, args) 92 | } else if fn == "get" { 93 | return t.get(stub, args) 94 | } else if fn == "set" { 95 | return t.set(stub, args) 96 | } 97 | 98 | return shim.Error("Machining No operation:" + fn) 99 | 100 | } 101 | 102 | //添加奶牛场 103 | func (t *DairyfarmChaincode) addDairyFarm(stub shim.ChaincodeStubInterface, args []string) pb.Response { 104 | 105 | if len(args) != 2 { 106 | return shim.Error("参数出错") 107 | } 108 | id := args[0] 109 | name := args[1] 110 | 111 | if isExisted(stub, id) { 112 | return shim.Error("已存在") 113 | } 114 | 115 | dairyFarm := DairyFarm{} 116 | dairyFarm.Id = id 117 | dairyFarm.Name = name 118 | dairyFarm.CreateTime = time.Now().Format(dateFomat) 119 | dairyFarm.MaxCowNo = 0 120 | dairyFarm.MaxBucketNo = 0 121 | dairyFarm.CowIds = []string{} 122 | dairyFarm.BucketIds = []string{} 123 | 124 | jsonString, err := json.Marshal(dairyFarm) 125 | 126 | fmt.Println("json:" + string(jsonString)) 127 | 128 | if err != nil { 129 | return shim.Error("json序列化失败") 130 | } 131 | 132 | err = stub.PutState(id, []byte(jsonString)) 133 | 134 | if err != nil { 135 | return shim.Error(err.Error()) 136 | } 137 | 138 | return shim.Success(jsonString) 139 | 140 | } 141 | 142 | //根据id获取牛奶场 143 | func (t *DairyfarmChaincode) getDairyFarmById(stub shim.ChaincodeStubInterface, id string) (DairyFarm, error) { 144 | 145 | dvalue, err := stub.GetState(id) 146 | var d DairyFarm 147 | err = json.Unmarshal([]byte(dvalue), &d) 148 | return d, err 149 | 150 | } 151 | 152 | //添加奶牛 153 | func (t *DairyfarmChaincode) addCow(stub shim.ChaincodeStubInterface, args []string) pb.Response { 154 | 155 | if len(args) != 1 { 156 | return shim.Error("参数出错") 157 | } 158 | 159 | cowStr := args[0] 160 | 161 | var cow Cow 162 | //这里就是实际的解码和相关的错误检查 163 | if err := json.Unmarshal([]byte(cowStr), &cow); err != nil { 164 | return shim.Error("json反序列化失败") 165 | } 166 | 167 | dairyFarm, err := t.getDairyFarmById(stub, cow.FarmId) 168 | if err != nil { 169 | return shim.Error("奶牛场不存在") 170 | } 171 | 172 | dairyFarm.MaxCowNo += 1 173 | 174 | cowId := cow.FarmId + fmt.Sprintf(intPrefix, dairyFarm.MaxCowNo) 175 | 176 | cowIds := dairyFarm.CowIds 177 | dairyFarm.CowIds = append(cowIds, cowId) 178 | 179 | //跟新牛奶场 180 | jsonString, err := json.Marshal(dairyFarm) 181 | if err != nil { 182 | return shim.Error("json序列化失败") 183 | } 184 | 185 | err = stub.PutState(cow.FarmId, []byte(jsonString)) 186 | if err != nil { 187 | return shim.Error(err.Error()) 188 | } 189 | 190 | fmt.Println("json:" + string(jsonString)) 191 | //添加奶牛 192 | cow.Id = cowId 193 | 194 | jsonString, err = json.Marshal(cow) 195 | if err != nil { 196 | return shim.Error("json序列化失败") 197 | } 198 | 199 | err = stub.PutState(cowId, []byte(jsonString)) 200 | if err != nil { 201 | return shim.Error(err.Error()) 202 | } 203 | 204 | fmt.Println("json:" + string(jsonString)) 205 | return shim.Success(jsonString) 206 | 207 | } 208 | 209 | //删除奶牛 210 | func (t *DairyfarmChaincode) delCow(stub shim.ChaincodeStubInterface, args []string) pb.Response { 211 | 212 | if len(args) != 1 { 213 | return shim.Error("参数出错") 214 | } 215 | 216 | cowId := args[0] 217 | 218 | cowStr, err := stub.GetState(cowId) 219 | if err != nil { 220 | return shim.Error(err.Error()) 221 | } 222 | 223 | var cow Cow 224 | //这里就是实际的解码和相关的错误检查 225 | if err := json.Unmarshal([]byte(cowStr), &cow); err != nil { 226 | return shim.Error("json反序列化失败") 227 | } 228 | //设置死亡 229 | cow.Stutas = 1 230 | 231 | cowStr, err = json.Marshal(cow) 232 | if err != nil { 233 | return shim.Error("json序列化失败") 234 | } 235 | 236 | err = stub.PutState(cowId, cowStr) 237 | if err != nil { 238 | return shim.Error(err.Error()) 239 | } 240 | fmt.Println("json:", string(cowStr)) 241 | return shim.Success(nil) 242 | 243 | } 244 | 245 | //添加奶牛操作 246 | func (t *DairyfarmChaincode) addCowOperate(stub shim.ChaincodeStubInterface, args []string) pb.Response { 247 | 248 | if len(args) != 1 { 249 | return shim.Error("参数出错") 250 | } 251 | 252 | operationStr := args[0] 253 | 254 | var operation CowOperation 255 | //这里就是实际的解码和相关的错误检查 256 | if err := json.Unmarshal([]byte(operationStr), &operation); err != nil { 257 | return shim.Error("json反序列化失败") 258 | } 259 | operation.Time = time.Now().Format(dateFomat) 260 | 261 | cowId := operation.CowId 262 | 263 | operationJson, err := json.Marshal(operation) 264 | if err != nil { 265 | return shim.Error("json序列化失败") 266 | } 267 | fmt.Println("json:" + string(operationJson)) 268 | 269 | err = stub.PutState(cowId+operationSuffix, []byte(operationJson)) 270 | 271 | if err != nil { 272 | return shim.Error(err.Error()) 273 | } 274 | 275 | return shim.Success(operationJson) 276 | 277 | } 278 | 279 | //添加挤奶 280 | func (t *DairyfarmChaincode) addCowMilking(stub shim.ChaincodeStubInterface, args []string) pb.Response { 281 | 282 | if len(args) != 1 { 283 | return shim.Error("参数出错") 284 | } 285 | 286 | operationStr := args[0] 287 | 288 | var operation CowOperation 289 | //这里就是实际的解码和相关的错误检查 290 | if err := json.Unmarshal([]byte(operationStr), &operation); err != nil { 291 | return shim.Error("json反序列化失败") 292 | } 293 | operation.Time = time.Now().Format(dateFomat) 294 | operation.Operation = 0 295 | 296 | cowId := operation.CowId 297 | 298 | operationJson, err := json.Marshal(operation) 299 | if err != nil { 300 | return shim.Error(err.Error()) 301 | } 302 | fmt.Println("json:" + string(operationJson)) 303 | 304 | err = stub.PutState(cowId+operationSuffix, []byte(operationJson)) 305 | if err != nil { 306 | return shim.Error(err.Error()) 307 | } 308 | 309 | ids := cowId[0:4] 310 | dairyFarmId := string(ids) 311 | 312 | dairyFarm, err := t.getDairyFarmById(stub, dairyFarmId) 313 | if err != nil { 314 | return shim.Error("奶牛场不存在") 315 | } 316 | 317 | dairyFarm.MaxBucketNo += 1 318 | 319 | bucketId := cowId + fmt.Sprintf(intPrefix, dairyFarm.MaxBucketNo) 320 | 321 | bucketIds := dairyFarm.BucketIds 322 | dairyFarm.BucketIds = append(bucketIds, bucketId) 323 | 324 | //跟新牛奶场 325 | jsonString, err := json.Marshal(dairyFarm) 326 | if err != nil { 327 | return shim.Error("json序列化失败") 328 | } 329 | err = stub.PutState(dairyFarmId, []byte(jsonString)) 330 | if err != nil { 331 | return shim.Error(err.Error()) 332 | } 333 | fmt.Println("json:" + string(jsonString)) 334 | 335 | //添加奶牛桶 336 | bucket := Bucket{} 337 | bucket.Id = bucketId 338 | bucket.Time = time.Now().Format(dateFomat) 339 | bucket.Stutas = 0 340 | 341 | jsonString, err = json.Marshal(bucket) 342 | if err != nil { 343 | return shim.Error("json序列化失败") 344 | } 345 | err = stub.PutState(bucketId, []byte(jsonString)) 346 | if err != nil { 347 | return shim.Error(err.Error()) 348 | } 349 | 350 | fmt.Println("json:" + string(jsonString)) 351 | 352 | return shim.Success(jsonString) 353 | 354 | } 355 | 356 | //发送到加工厂 357 | func (t *DairyfarmChaincode) sentProcess(stub shim.ChaincodeStubInterface, args []string) pb.Response { 358 | 359 | if len(args) != 2 { 360 | return shim.Error("参数出错") 361 | } 362 | 363 | buckerId := args[0] 364 | machiningId := args[1] 365 | 366 | buckerStr, err := stub.GetState(buckerId) 367 | if err != nil { 368 | return shim.Error(err.Error()) 369 | } 370 | var bucker Bucket 371 | //这里就是实际的解码和相关的错误检查 372 | if err := json.Unmarshal([]byte(buckerStr), &bucker); err != nil { 373 | return shim.Error("json反序列化失败") 374 | } 375 | bucker.MachiningId = machiningId 376 | bucker.InMachiningTime = time.Now().Format(dateFomat) 377 | 378 | buckerStr, err = json.Marshal(bucker) 379 | if err != nil { 380 | return shim.Error("json序列化失败") 381 | } 382 | 383 | err = stub.PutState(buckerId, buckerStr) 384 | 385 | if err != nil { 386 | return shim.Error(err.Error()) 387 | } 388 | 389 | //添加工厂查询奶桶临时组合建 390 | indexKey, err := stub.CreateCompositeKey(combinedConstruction, []string{machiningId, buckerId}) 391 | err = stub.PutState(indexKey, buckerStr) 392 | if err != nil { 393 | return shim.Error(err.Error()) 394 | } 395 | 396 | fmt.Println("json:", string(buckerStr)) 397 | return shim.Success(nil) 398 | 399 | } 400 | 401 | //工厂查询奶桶状态 402 | func (t *DairyfarmChaincode) checkBucketForMachining(stub shim.ChaincodeStubInterface, args []string) pb.Response { 403 | 404 | if len(args) != 1 { 405 | return shim.Error("参数出错") 406 | } 407 | 408 | machiningId := args[0] 409 | 410 | var buckets = make([]Bucket, 0) 411 | 412 | resultIterator, err := stub.GetStateByPartialCompositeKey(combinedConstruction, []string{machiningId}) 413 | if err != nil { 414 | return shim.Error(err.Error()) 415 | } 416 | defer resultIterator.Close() 417 | for resultIterator.HasNext() { 418 | item, err := resultIterator.Next() 419 | if err != nil { 420 | return shim.Error(err.Error()) 421 | } 422 | var bucker Bucket 423 | //这里就是实际的解码和相关的错误检查 424 | if err := json.Unmarshal(item.Value, &bucker); err != nil { 425 | return shim.Error("json反序列化失败") 426 | } 427 | 428 | buckets = append(buckets, bucker) 429 | 430 | } 431 | 432 | jsonStr, err := json.Marshal(buckets) 433 | if err != nil { 434 | return shim.Error("json序列化失败") 435 | } 436 | fmt.Println("json:", string(jsonStr)) 437 | return shim.Success(jsonStr) 438 | 439 | } 440 | 441 | //确认奶桶状态 442 | func (t *DairyfarmChaincode) confirmBucket(stub shim.ChaincodeStubInterface, args []string) pb.Response { 443 | 444 | if len(args) != 3 { 445 | return shim.Error("参数出错") 446 | } 447 | 448 | buckerId := args[0] 449 | machiningId := args[1] 450 | isConfirm := args[2] 451 | 452 | resultIterator, err := stub.GetStateByPartialCompositeKey(combinedConstruction, []string{machiningId, buckerId}) 453 | if err != nil { 454 | return shim.Error(err.Error()) 455 | } 456 | defer resultIterator.Close() 457 | 458 | var bucker Bucket 459 | 460 | var indexKey string 461 | 462 | for resultIterator.HasNext() { 463 | item, err := resultIterator.Next() 464 | if err != nil { 465 | return shim.Error(err.Error()) 466 | } 467 | 468 | indexKey = item.Key 469 | //这里就是实际的解码和相关的错误检查 470 | if err := json.Unmarshal(item.Value, &bucker); err != nil { 471 | return shim.Error("json反序列化失败") 472 | } 473 | 474 | } 475 | 476 | status, err := strconv.Atoi(isConfirm) 477 | if err != nil { 478 | return shim.Error(err.Error()) 479 | } 480 | 481 | bucker.Stutas = int8(status) 482 | 483 | jsonStr, err := json.Marshal(bucker) 484 | if err != nil { 485 | return shim.Error("json序列化失败") 486 | } 487 | fmt.Println("json:", string(jsonStr)) 488 | 489 | //跟新bucket 490 | err = stub.PutState(buckerId, jsonStr) 491 | 492 | if err != nil { 493 | return shim.Error(err.Error()) 494 | } 495 | 496 | //确认签收向工厂添加奶桶 497 | if isConfirm == "1" { 498 | response := stub.InvokeChaincode(machiningChaincodeName, [][]byte{[]byte("addBucket"), []byte(jsonStr)}, channelName) 499 | if response.Status != shim.OK { 500 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 501 | fmt.Printf(errStr) 502 | return shim.Error(errStr) 503 | } 504 | } 505 | 506 | //删除组合键 507 | err = stub.DelState(indexKey) 508 | if err != nil { 509 | return shim.Error(err.Error()) 510 | } 511 | 512 | return shim.Success(jsonStr) 513 | 514 | } 515 | 516 | func (t *DairyfarmChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response { 517 | 518 | 519 | if len(args) != 1 { 520 | return shim.Error("参数出错") 521 | } 522 | 523 | keys := strings.Split(args[0],",") 524 | 525 | var dmap = make(map[string]map[string]interface{}) 526 | 527 | for i := 0; i < len(keys); i++ { 528 | key := keys[i] 529 | //读出 530 | value, err := stub.GetState(key) 531 | if err != nil { 532 | return shim.Error(err.Error()) 533 | } 534 | 535 | if value == nil { 536 | dmap[key] = nil 537 | } else { 538 | var imap map[string]interface{} 539 | //这里就是实际的解码和相关的错误检查 540 | if err := json.Unmarshal([]byte(value), &imap); err != nil { 541 | return shim.Error("json反序列化失败") 542 | } 543 | dmap[key] = imap 544 | } 545 | 546 | } 547 | 548 | jsonStr, err := json.Marshal(dmap) 549 | if err != nil { 550 | return shim.Error("json序列化失败") 551 | } 552 | 553 | fmt.Println("json:", string(jsonStr)) 554 | return shim.Success(jsonStr) 555 | } 556 | 557 | func (t *DairyfarmChaincode) set(stub shim.ChaincodeStubInterface, args []string) pb.Response { 558 | 559 | if len(args) != 2 { 560 | return shim.Error("要输入一个键") 561 | } 562 | 563 | err := stub.PutState(args[0], []byte(args[1])) 564 | if err != nil { 565 | return shim.Error(err.Error()) 566 | } 567 | 568 | return shim.Success(nil) 569 | } 570 | 571 | //获取操作记录 572 | func (t *DairyfarmChaincode) getOperationHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response { 573 | 574 | if len(args) != 1 { 575 | return shim.Error("parm error") 576 | } 577 | id := args[0] 578 | cowId := string(id[0:10]) 579 | keysIter, err := stub.GetHistoryForKey(cowId + operationSuffix) 580 | 581 | if err != nil { 582 | return shim.Error(fmt.Sprintf("GetHistoryForKey failed. Error accessing state: %s", err)) 583 | } 584 | defer keysIter.Close() 585 | 586 | var historys = make([]CowOperation, 0) 587 | 588 | for keysIter.HasNext() { 589 | 590 | response, iterErr := keysIter.Next() 591 | if iterErr != nil { 592 | return shim.Error(fmt.Sprintf("GetHistoryForKey operation failed. Error accessing state: %s", err)) 593 | } 594 | //交易的值 595 | txvalue := response.Value 596 | 597 | var operation CowOperation 598 | //这里就是实际的解码和相关的错误检查 599 | if err := json.Unmarshal(txvalue, &operation); err != nil { 600 | return shim.Error("json反序列化失败") 601 | } 602 | 603 | historys = append(historys, operation) 604 | 605 | } 606 | 607 | jsonKeys, err := json.Marshal(historys) 608 | if err != nil { 609 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 610 | } 611 | fmt.Println("json:", string(jsonKeys)) 612 | return shim.Success(jsonKeys) 613 | } 614 | 615 | func isExisted(stub shim.ChaincodeStubInterface, key string) bool { 616 | val, err := stub.GetState(key) 617 | if err != nil { 618 | fmt.Printf("Error: %s\n", err) 619 | } 620 | 621 | if len(val) == 0 { 622 | return false 623 | } 624 | 625 | return true 626 | } 627 | 628 | func main() { 629 | err := shim.Start(new(DairyfarmChaincode)) 630 | if err != nil { 631 | fmt.Printf("Error starting Simple chaincode: %s", err) 632 | } 633 | } 634 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/dairyfarm/cc_dairyfarm_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "github.com/hyperledger/fabric/core/chaincode/shim" 7 | ) 8 | 9 | var stub *shim.MockStub 10 | 11 | //测试 Init 方法 12 | //func testInit(t *testing.T, args [][]byte) { 13 | // 14 | // res := stub.MockInit("1", args) 15 | // if res.Status != shim.OK { 16 | // fmt.Println("Init failed : ", string(res.Message)) 17 | // t.FailNow() 18 | // } 19 | //} 20 | 21 | func testInvoke(t *testing.T, args [][]byte) { 22 | res := stub.MockInvoke("1", args) 23 | 24 | fmt.Printf("res:" + string(res.Payload) + "\n") 25 | 26 | if res.Status != shim.OK { 27 | fmt.Println("Invoke", "failed", string(res.Message)) 28 | //t.FailNow() 29 | } 30 | 31 | } 32 | 33 | func TestDemo(t *testing.T) { 34 | 35 | scc := new(DairyfarmChaincode) 36 | stub = shim.NewMockStub("dairyfarm", scc) 37 | 38 | testInvoke(t, [][]byte{[]byte("addDairyFarm"), []byte("F001"), []byte("奶牛场1")}) 39 | testInvoke(t, [][]byte{[]byte("addDairyFarm"), []byte("F002"), []byte("奶牛场2")}) 40 | testInvoke(t, [][]byte{[]byte("addDairyFarm"), []byte("F003"), []byte("奶牛场3")}) 41 | 42 | testInvoke(t, [][]byte{[]byte("addCow"), []byte(`{"farmId":"F001","healthy":true,"quarantine":true,"feedSource":"国产","stutas":0}`)}) 43 | testInvoke(t, [][]byte{[]byte("addCow"), []byte(`{"farmId":"F001","healthy":true,"quarantine":true,"feedSource":"国产","stutas":0}`)}) 44 | 45 | testInvoke(t, [][]byte{[]byte("addCow"), []byte(`{"farmId":"F004","healthy":true,"quarantine":true,"feedSource":"国产","stutas":0}`)}) 46 | 47 | testInvoke(t, [][]byte{[]byte("get"), []byte("F001000001")}) 48 | 49 | testInvoke(t, [][]byte{[]byte("addCowOperate"), []byte(`{"cowId":"F001000001","operation":1,"consumptionOrOutput":"食物1"}`)}) 50 | testInvoke(t, [][]byte{[]byte("addCowMilking"), []byte(`{"cowId":"F001000001","consumptionOrOutput":"b1"}`)}) 51 | testInvoke(t, [][]byte{[]byte("addCowMilking"), []byte(`{"cowId":"F001000001","consumptionOrOutput":"b12"}`)}) 52 | 53 | testInvoke(t, [][]byte{[]byte("sentProcess"), []byte("F001000001000001"), []byte("M001")}) 54 | testInvoke(t, [][]byte{[]byte("sentProcess"), []byte("F001000001000002"), []byte("M001")}) 55 | 56 | testInvoke(t, [][]byte{[]byte("checkBucketForMachining"), []byte("M001")}) 57 | 58 | testInvoke(t, [][]byte{[]byte("confirmBucket"), []byte("F001000001000002"), []byte("M001"), []byte("2")}) 59 | 60 | testInvoke(t, [][]byte{[]byte("checkBucketForMachining"), []byte("M001")}) 61 | 62 | testInvoke(t, [][]byte{[]byte("get"), []byte("F001000001000002")}) 63 | 64 | testInvoke(t, [][]byte{[]byte("get"), []byte("F001000001-opr")}) 65 | 66 | testInvoke(t, [][]byte{[]byte("delCow"), []byte("F001000001")}) 67 | 68 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2")}) 69 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2-1")}) 70 | 71 | testInvoke(t, [][]byte{[]byte("get"), []byte("F0010000012222")}) 72 | 73 | } 74 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/machining/cc_machining.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hyperledger/fabric/core/chaincode/shim" 5 | pb "github.com/hyperledger/fabric/protos/peer" 6 | "fmt" 7 | "time" 8 | "encoding/json" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type MachiningChaincode struct { 14 | } 15 | 16 | //牛奶 17 | type Milk struct { 18 | Id string `json:"id"` //批次编号 19 | Time string `json:"time"` //生产日期 20 | InSaleTime string `json:"inSaleTime"` //进入售买日期 21 | SaledTime string `json:"saledTime"` //售出日期 22 | SaleId string `json:"saleId"` // 销售终端id 23 | Stutas int8 `json:"stutas"` //状态 24 | } 25 | 26 | //加工厂 27 | type Machining struct { 28 | Id string `json:"id"` 29 | Name string `json:"name"` //名称 30 | CreateTime string `json:"createTime"` //创建时间 31 | MaxMilkNo uint `json:"maxMilkNo"` //最大牛奶编号 32 | MaxBucketNo int `json:"maxBucketNo"` //最大桶 33 | BucketIds []string `json:"bucketIds"` //状态 34 | MilkIds []string `json:"milkIds"` //牛奶批次 35 | } 36 | 37 | //奶牛桶 38 | type Bucket struct { 39 | Id string `json:"id"` 40 | MachiningId string `json:"machiningId"` //加工场id 41 | Time string `json:"time"` //装桶时间 42 | InMachiningTime string `json:"inMachiningTime"` //进入加工场时间 43 | Stutas int8 `json:"stutas"` //状态 44 | } 45 | 46 | //加工操作 47 | type ProcessOperation struct { 48 | BucketId string `json:"bucketId"` //装满奶牛的桶id 49 | Time string `json:"time"` //时间 50 | Operation int8 `json:"operation"` // 操作类型 0为消毒,1为灌装,2为包装 51 | ConsumptionOrOutput string `json:"consumptionOrOutput"` //消耗或产出 52 | } 53 | 54 | const ( 55 | channelName = "mychannel" //通道名 56 | saletenminalChaincodeName = "salesterminal" // 销售终端chaincode名 57 | operationSuffix = "-opr" //奶牛操作后缀 58 | intPrefix = "%04d" //4位数值,不足前面补0 59 | dateFomat = "2006-01-02 15:04:05" //日期格式 60 | combinedConstruction = "saleId~milkId" //组合键 61 | ) 62 | 63 | func (t *MachiningChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 64 | 65 | return shim.Success(nil) 66 | } 67 | 68 | func (t *MachiningChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 69 | 70 | fn, args := stub.GetFunctionAndParameters() 71 | 72 | fmt.Printf("方法: %s 参数 : %s \n", fn, args) 73 | 74 | if fn == "addMachining" { 75 | return t.addMachining(stub, args) 76 | } else if fn == "addBucket" { 77 | return t.addBucket(stub, args) 78 | } else if fn == "addMilkOperation" { 79 | return t.addMilkOperation(stub, args) 80 | } else if fn == "addMilkPack" { 81 | return t.addMilkPack(stub, args) 82 | } else if fn == "sentSale" { 83 | return t.sentSale(stub, args) 84 | } else if fn == "checkMilkForSaleterminal" { 85 | return t.checkMilkForSaleterminal(stub, args) 86 | } else if fn == "confirmMilk" { 87 | return t.confirmMilk(stub, args) 88 | } else if fn == "getOperationHistory" { 89 | return t.getOperationHistory(stub, args) 90 | } else if fn == "get" { 91 | return t.get(stub, args) 92 | } else if fn == "set" { 93 | return t.set(stub, args) 94 | } 95 | 96 | return shim.Error("Machining No operation:" + fn) 97 | 98 | } 99 | 100 | //添加加工场 101 | func (t *MachiningChaincode) addMachining(stub shim.ChaincodeStubInterface, args []string) pb.Response { 102 | 103 | if len(args) != 2 { 104 | return shim.Error("参数出错") 105 | } 106 | id := args[0] 107 | name := args[1] 108 | 109 | if isExisted(stub, id) { 110 | return shim.Error("已存在") 111 | } 112 | 113 | machining := Machining{} 114 | machining.Id = id 115 | machining.Name = name 116 | machining.CreateTime = time.Now().Format(dateFomat) 117 | machining.MaxMilkNo = 0 118 | machining.MaxBucketNo = 0 119 | machining.MilkIds = []string{} 120 | machining.BucketIds = []string{} 121 | 122 | jsonString, err := json.Marshal(machining) 123 | 124 | fmt.Println("json:" + string(jsonString)) 125 | 126 | if err != nil { 127 | return shim.Error("json序列化失败") 128 | } 129 | 130 | err = stub.PutState(id, []byte(jsonString)) 131 | 132 | if err != nil { 133 | shim.Error(err.Error()) 134 | } 135 | 136 | return shim.Success(jsonString) 137 | 138 | } 139 | 140 | //根据id获取加工厂 141 | func (t *MachiningChaincode) getMachiningById(stub shim.ChaincodeStubInterface, id string) (Machining, error) { 142 | 143 | dvalue, err := stub.GetState(id) 144 | var d Machining 145 | err = json.Unmarshal([]byte(dvalue), &d) 146 | return d, err 147 | 148 | } 149 | 150 | //添加奶桶 151 | func (t *MachiningChaincode) addBucket(stub shim.ChaincodeStubInterface, args []string) pb.Response { 152 | 153 | if len(args) != 1 { 154 | return shim.Error("参数出错") 155 | } 156 | 157 | bucketStr := args[0] 158 | 159 | var bucket Bucket 160 | //这里就是实际的解码和相关的错误检查 161 | if err := json.Unmarshal([]byte(bucketStr), &bucket); err != nil { 162 | return shim.Error("json反序列化失败") 163 | } 164 | 165 | machiningId := bucket.MachiningId 166 | machining, err := t.getMachiningById(stub, machiningId) 167 | if err != nil { 168 | return shim.Error("加工厂不存在") 169 | } 170 | 171 | machining.MaxBucketNo += 1 172 | 173 | bucketIds := machining.BucketIds 174 | machining.BucketIds = append(bucketIds, bucket.Id) 175 | 176 | //跟新牛奶场 177 | jsonString, err := json.Marshal(machining) 178 | if err != nil { 179 | return shim.Error("json序列化失败") 180 | } 181 | err = stub.PutState(machiningId, []byte(jsonString)) 182 | if err != nil { 183 | shim.Error(err.Error()) 184 | } 185 | 186 | fmt.Println("json:" + string(jsonString)) 187 | //添加奶牛 188 | 189 | err = stub.PutState(bucket.Id, []byte(bucketStr)) 190 | if err != nil { 191 | shim.Error(err.Error()) 192 | } 193 | 194 | fmt.Println("json:" + string(bucketStr)) 195 | return shim.Success([]byte(bucketStr)) 196 | 197 | } 198 | 199 | //添加奶桶操作 200 | func (t *MachiningChaincode) addMilkOperation(stub shim.ChaincodeStubInterface, args []string) pb.Response { 201 | 202 | if len(args) != 1 { 203 | return shim.Error("参数出错") 204 | } 205 | 206 | operationStr := args[0] 207 | 208 | var operation ProcessOperation 209 | //这里就是实际的解码和相关的错误检查 210 | if err := json.Unmarshal([]byte(operationStr), &operation); err != nil { 211 | return shim.Error("json反序列化失败") 212 | } 213 | operation.Time = time.Now().Format(dateFomat) 214 | bucketId := operation.BucketId 215 | 216 | operationJson, err := json.Marshal(operation) 217 | if err != nil { 218 | return shim.Error("json序列化失败") 219 | } 220 | 221 | fmt.Println("json:" + string(operationJson)) 222 | err = stub.PutState(bucketId+operationSuffix, []byte(operationJson)) 223 | if err != nil { 224 | return shim.Error(err.Error()) 225 | } 226 | return shim.Success(operationJson) 227 | 228 | } 229 | 230 | //添加打包牛奶 231 | func (t *MachiningChaincode) addMilkPack(stub shim.ChaincodeStubInterface, args []string) pb.Response { 232 | 233 | if len(args) != 1 { 234 | return shim.Error("参数出错") 235 | } 236 | 237 | operationStr := args[0] 238 | 239 | var operation ProcessOperation 240 | //这里就是实际的解码和相关的错误检查 241 | if err := json.Unmarshal([]byte(operationStr), &operation); err != nil { 242 | return shim.Error("json反序列化失败") 243 | } 244 | operation.Time = time.Now().Format(dateFomat) 245 | operation.Operation = 2 246 | 247 | bucketId := operation.BucketId 248 | 249 | operationJson, err := json.Marshal(operation) 250 | if err != nil { 251 | return shim.Error("json序列化失败") 252 | } 253 | 254 | fmt.Println("json:" + string(operationJson)) 255 | err = stub.PutState(bucketId+operationSuffix, []byte(operationJson)) 256 | if err != nil { 257 | return shim.Error(err.Error()) 258 | } 259 | 260 | bucketStr, err := stub.GetState(bucketId) 261 | if err != nil { 262 | return shim.Error(err.Error()) 263 | } 264 | var bucket Bucket 265 | //这里就是实际的解码和相关的错误检查 266 | if err := json.Unmarshal([]byte(bucketStr), &bucket); err != nil { 267 | return shim.Error("json反序列化失败") 268 | } 269 | 270 | machiningId := bucket.MachiningId 271 | machining, err := t.getMachiningById(stub, machiningId) 272 | if err != nil { 273 | return shim.Error("加工厂不存在") 274 | } 275 | 276 | machining.MaxMilkNo += 1 277 | 278 | milkId := bucket.Id + fmt.Sprintf(intPrefix, machining.MaxMilkNo) 279 | 280 | milkIds := machining.MilkIds 281 | machining.MilkIds = append(milkIds, milkId) 282 | 283 | //跟新牛奶场 284 | jsonString, err := json.Marshal(machining) 285 | if err != nil { 286 | return shim.Error("json序列化失败") 287 | } 288 | err = stub.PutState(machiningId, []byte(jsonString)) 289 | if err != nil { 290 | shim.Error(err.Error()) 291 | } 292 | fmt.Println("json:" + string(jsonString)) 293 | 294 | //添加奶牛 295 | milk := Milk{} 296 | milk.Id = milkId 297 | milk.Time = time.Now().Format(dateFomat) 298 | milk.Stutas = 0 299 | 300 | jsonString, err = json.Marshal(milk) 301 | if err != nil { 302 | return shim.Error("json序列化失败") 303 | } 304 | err = stub.PutState(milkId, []byte(jsonString)) 305 | if err != nil { 306 | shim.Error(err.Error()) 307 | } 308 | 309 | fmt.Println("json:" + string(jsonString)) 310 | 311 | return shim.Success(jsonString) 312 | 313 | } 314 | 315 | //发送到销售终端 316 | func (t *MachiningChaincode) sentSale(stub shim.ChaincodeStubInterface, args []string) pb.Response { 317 | 318 | if len(args) != 2 { 319 | return shim.Error("参数出错") 320 | } 321 | 322 | milkId := args[0] 323 | saleId := args[1] 324 | 325 | milkStr, err := stub.GetState(milkId) 326 | if err != nil { 327 | return shim.Error(err.Error()) 328 | } 329 | var milk Milk 330 | //这里就是实际的解码和相关的错误检查 331 | if err := json.Unmarshal([]byte(milkStr), &milk); err != nil { 332 | return shim.Error("json反序列化失败") 333 | } 334 | milk.SaleId = saleId 335 | milk.InSaleTime = time.Now().Format(dateFomat) 336 | 337 | milkStr, err = json.Marshal(milk) 338 | if err != nil { 339 | return shim.Error("json序列化失败") 340 | } 341 | 342 | err = stub.PutState(milkId, milkStr) 343 | 344 | if err != nil { 345 | return shim.Error(err.Error()) 346 | } 347 | 348 | //添加销售终端查询牛奶临时组合建 349 | indexKey, err := stub.CreateCompositeKey(combinedConstruction, []string{saleId, milkId}) 350 | err = stub.PutState(indexKey, milkStr) 351 | if err != nil { 352 | return shim.Error(err.Error()) 353 | } 354 | 355 | fmt.Println("json:", string(milkStr)) 356 | return shim.Success(nil) 357 | 358 | } 359 | 360 | //销售终端查询牛奶批次状态 361 | func (t *MachiningChaincode) checkMilkForSaleterminal(stub shim.ChaincodeStubInterface, args []string) pb.Response { 362 | 363 | if len(args) != 1 { 364 | return shim.Error("参数出错") 365 | } 366 | 367 | saleId := args[0] 368 | 369 | var milks = make([]Milk, 0) 370 | 371 | resultIterator, err := stub.GetStateByPartialCompositeKey(combinedConstruction, []string{saleId}) 372 | if err != nil { 373 | return shim.Error(err.Error()) 374 | } 375 | defer resultIterator.Close() 376 | for resultIterator.HasNext() { 377 | item, err := resultIterator.Next() 378 | if err != nil { 379 | return shim.Error(err.Error()) 380 | } 381 | var milk Milk 382 | //这里就是实际的解码和相关的错误检查 383 | if err := json.Unmarshal(item.Value, &milk); err != nil { 384 | return shim.Error("json反序列化失败") 385 | } 386 | 387 | milks = append(milks, milk) 388 | 389 | } 390 | 391 | jsonStr, err := json.Marshal(milks) 392 | if err != nil { 393 | return shim.Error("json序列化失败") 394 | } 395 | fmt.Println("json:", string(jsonStr)) 396 | return shim.Success(jsonStr) 397 | 398 | } 399 | 400 | //确认奶状态 401 | func (t *MachiningChaincode) confirmMilk(stub shim.ChaincodeStubInterface, args []string) pb.Response { 402 | 403 | if len(args) != 3 { 404 | return shim.Error("参数出错") 405 | } 406 | 407 | milkId := args[0] 408 | saleId := args[1] 409 | isConfirm := args[2] 410 | 411 | resultIterator, err := stub.GetStateByPartialCompositeKey(combinedConstruction, []string{saleId, milkId}) 412 | if err != nil { 413 | return shim.Error(err.Error()) 414 | } 415 | defer resultIterator.Close() 416 | 417 | var milk Milk 418 | 419 | var indexKey string 420 | 421 | for resultIterator.HasNext() { 422 | item, err := resultIterator.Next() 423 | if err != nil { 424 | fmt.Println(err) 425 | return shim.Error(err.Error()) 426 | } 427 | 428 | indexKey = item.Key 429 | //这里就是实际的解码和相关的错误检查 430 | if err := json.Unmarshal(item.Value, &milk); err != nil { 431 | return shim.Error("json反序列化失败") 432 | } 433 | 434 | } 435 | 436 | status, err := strconv.Atoi(isConfirm) 437 | if err != nil { 438 | return shim.Error(err.Error()) 439 | } 440 | 441 | milk.Stutas = int8(status) 442 | 443 | jsonStr, err := json.Marshal(milk) 444 | if err != nil { 445 | return shim.Error("json序列化失败") 446 | } 447 | fmt.Println("json:", string(jsonStr)) 448 | 449 | //跟新bucket 450 | err = stub.PutState(milkId, jsonStr) 451 | 452 | if err != nil { 453 | return shim.Error(err.Error()) 454 | } 455 | 456 | //确认签收向工厂添加奶桶 457 | if isConfirm == "1" { 458 | response := stub.InvokeChaincode(saletenminalChaincodeName, [][]byte{[]byte("addMilk"), []byte(jsonStr)}, channelName) 459 | if response.Status != shim.OK { 460 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 461 | fmt.Printf(errStr) 462 | return shim.Error(errStr) 463 | } 464 | } 465 | 466 | //删除组合键 467 | err = stub.DelState(indexKey) 468 | fmt.Println("删除组合建:" + indexKey) 469 | if err != nil { 470 | return shim.Error(err.Error()) 471 | } 472 | 473 | return shim.Success(jsonStr) 474 | 475 | } 476 | 477 | func (t *MachiningChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response { 478 | 479 | 480 | if len(args) != 1 { 481 | return shim.Error("参数出错") 482 | } 483 | 484 | keys := strings.Split(args[0],",") 485 | 486 | var dmap = make(map[string]map[string]interface{}) 487 | 488 | for i := 0; i < len(keys); i++ { 489 | key := keys[i] 490 | //读出 491 | value, err := stub.GetState(key) 492 | if err != nil { 493 | return shim.Error(err.Error()) 494 | } 495 | 496 | if value == nil { 497 | dmap[key] = nil 498 | } else { 499 | var imap map[string]interface{} 500 | //这里就是实际的解码和相关的错误检查 501 | if err := json.Unmarshal([]byte(value), &imap); err != nil { 502 | return shim.Error("json反序列化失败") 503 | } 504 | dmap[key] = imap 505 | } 506 | 507 | } 508 | 509 | jsonStr, err := json.Marshal(dmap) 510 | if err != nil { 511 | return shim.Error("json序列化失败") 512 | } 513 | 514 | fmt.Println("json:", string(jsonStr)) 515 | return shim.Success(jsonStr) 516 | } 517 | 518 | func (t *MachiningChaincode) set(stub shim.ChaincodeStubInterface, args []string) pb.Response { 519 | 520 | if len(args) != 2 { 521 | return shim.Error("要输入一个键") 522 | } 523 | 524 | err := stub.PutState(args[0], []byte(args[1])) 525 | if err != nil { 526 | return shim.Error(err.Error()) 527 | } 528 | 529 | return shim.Success(nil) 530 | } 531 | 532 | //获取操作记录 533 | func (t *MachiningChaincode) getOperationHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response { 534 | 535 | if len(args) != 1 { 536 | return shim.Error("parm error") 537 | } 538 | id := args[0] 539 | buckerId := string(id[0:16]) 540 | keysIter, err := stub.GetHistoryForKey(buckerId + operationSuffix) 541 | 542 | if err != nil { 543 | return shim.Error(fmt.Sprintf("GetHistoryForKey failed. Error accessing state: %s", err)) 544 | } 545 | defer keysIter.Close() 546 | 547 | var historys = make([]ProcessOperation, 0) 548 | 549 | for keysIter.HasNext() { 550 | 551 | response, iterErr := keysIter.Next() 552 | if iterErr != nil { 553 | return shim.Error(fmt.Sprintf("GetHistoryForKey operation failed. Error accessing state: %s", err)) 554 | } 555 | //交易的值 556 | txvalue := response.Value 557 | 558 | var operation ProcessOperation 559 | //这里就是实际的解码和相关的错误检查 560 | if err := json.Unmarshal(txvalue, &operation); err != nil { 561 | return shim.Error("json反序列化失败") 562 | } 563 | 564 | historys = append(historys, operation) 565 | 566 | } 567 | 568 | jsonKeys, err := json.Marshal(historys) 569 | if err != nil { 570 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 571 | } 572 | 573 | fmt.Println("json:", string(jsonKeys)) 574 | return shim.Success(jsonKeys) 575 | } 576 | 577 | func isExisted(stub shim.ChaincodeStubInterface, key string) bool { 578 | val, err := stub.GetState(key) 579 | if err != nil { 580 | fmt.Printf("Error: %s\n", err) 581 | } 582 | 583 | if len(val) == 0 { 584 | return false 585 | } 586 | 587 | return true 588 | } 589 | 590 | func main() { 591 | err := shim.Start(new(MachiningChaincode)) 592 | if err != nil { 593 | fmt.Printf("Error starting Simple chaincode: %s", err) 594 | } 595 | } 596 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/machining/cc_machining_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "github.com/hyperledger/fabric/core/chaincode/shim" 7 | ) 8 | 9 | var stub *shim.MockStub 10 | 11 | //测试 Init 方法 12 | //func testInit(t *testing.T, args [][]byte) { 13 | // 14 | // res := stub.MockInit("1", args) 15 | // if res.Status != shim.OK { 16 | // fmt.Println("Init failed : ", string(res.Message)) 17 | // t.FailNow() 18 | // } 19 | //} 20 | 21 | func testInvoke(t *testing.T, args [][]byte) { 22 | res := stub.MockInvoke("1", args) 23 | fmt.Printf("res:" + string(res.Payload) + "\n") 24 | 25 | if res.Status != shim.OK { 26 | fmt.Println("Invoke", "failed", string(res.Message)) 27 | //t.FailNow() 28 | } 29 | 30 | } 31 | 32 | func TestDemo(t *testing.T) { 33 | 34 | scc := new(MachiningChaincode) 35 | stub = shim.NewMockStub("machining", scc) 36 | 37 | testInvoke(t, [][]byte{[]byte("addMachining"), []byte("FA001"), []byte("加工厂1")}) 38 | testInvoke(t, [][]byte{[]byte("addMachining"), []byte("FA002"), []byte("加工厂2")}) 39 | 40 | testInvoke(t, [][]byte{[]byte("get"), []byte("M002")}) 41 | 42 | testInvoke(t, [][]byte{[]byte("addBucket"), []byte(`{"id":"F001000001000001","machiningId":"FA001","time":"2018-10-08 15:26:37","inMachiningTime":"2018-10-08 15:26:37","stutas":0}`)}) 43 | testInvoke(t, [][]byte{[]byte("addBucket"), []byte(`{"id":"F001000001000001","machiningId":"FA0012","time":"2018-10-08 15:26:37","inMachiningTime":"2018-10-08 15:26:37","stutas":0}`)}) 44 | 45 | testInvoke(t, [][]byte{[]byte("get"), []byte("F001000001000001")}) 46 | 47 | testInvoke(t, [][]byte{[]byte("addMilkOperation"), []byte(`{"bucketId":"F001000001000001","operation":1,"consumptionOrOutput":"灌装"}`)}) 48 | testInvoke(t, [][]byte{[]byte("addMilkOperation"), []byte(`{"bucketId":"F001000001000001","operation":0,"consumptionOrOutput":"消毒"}`)}) 49 | 50 | testInvoke(t, [][]byte{[]byte("addMilkPack"), []byte(`{"bucketId":"F001000001000001","consumptionOrOutput":"包装"}`)}) 51 | testInvoke(t, [][]byte{[]byte("addMilkPack"), []byte(`{"bucketId":"F001000001000001","consumptionOrOutput":"包装"}`)}) 52 | 53 | testInvoke(t, [][]byte{[]byte("sentSale"), []byte("F0010000010000010001"), []byte("S001")}) 54 | testInvoke(t, [][]byte{[]byte("sentSale"), []byte("F0010000010000010002"), []byte("S001")}) 55 | 56 | testInvoke(t, [][]byte{[]byte("checkMilkForSaleterminal"), []byte("S001")}) 57 | 58 | testInvoke(t, [][]byte{[]byte("confirmMilk"), []byte("F0010000010000010002"), []byte("S001"), []byte("2")}) 59 | 60 | testInvoke(t, [][]byte{[]byte("checkMilkForSaleterminal"), []byte("S001")}) 61 | 62 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2")}) 63 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2-1")}) 64 | 65 | } 66 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/salesterminal/cc_salesterminal.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hyperledger/fabric/core/chaincode/shim" 5 | pb "github.com/hyperledger/fabric/protos/peer" 6 | "fmt" 7 | "time" 8 | "encoding/json" 9 | "strings" 10 | ) 11 | 12 | const ( 13 | channelName = "mychannel" //通道名 14 | machiningChaincodeName = "machining" // 加工厂chaincode名 15 | dairyfarmChaincodeName = "dairyfarm" // 奶牛场chaincode名 16 | operationSuffix = "-opr" //奶牛操作后缀 17 | dateFomat = "2006-01-02 15:04:05" //日期格式 18 | ) 19 | 20 | type MilkHistory struct { 21 | MilkInfo map[string]interface{} `json:"milkInfo"` 22 | BucketInfo map[string]interface{} `json:"bucketInfo"` 23 | CowInfo map[string]interface{} `json:"cowInfo"` 24 | SaleHistory []map[string]interface{} `json:"saleHistory"` 25 | MachHistory []map[string]interface{} `json:"machHistory"` 26 | DairHistory []map[string]interface{} `json:"dairHistory"` 27 | } 28 | 29 | type SalesterminalChaincode struct { 30 | } 31 | 32 | //牛奶 33 | type Milk struct { 34 | Id string `json:"id"` //批次编号 35 | Time string `json:"time"` //生产日期 36 | InSaleTime string `json:"inSaleTime"` //进入售买日期 37 | SaledTime string `json:"saledTime"` //售出日期 38 | SaleId string `json:"saleId"` // 销售终端id 39 | Stutas int8 `json:"stutas"` //状态 40 | } 41 | 42 | //销售终端 43 | type Salesterminal struct { 44 | Id string `json:"id"` 45 | Name string `json:"name"` //名称 46 | CreateTime string `json:"createTime"` //创建时间 47 | MaxMilkNo uint `json:"maxMilkNo"` //最大牛奶编号 48 | MilkIds []string `json:"milkIds"` //牛奶批次 49 | } 50 | 51 | //加工操作 52 | type SaleOperation struct { 53 | MilkId string `json:"milkId"` //装满奶牛的桶id 54 | Time string `json:"time"` //时间 55 | Operation int8 `json:"operation"` // 操作类型 0为上架,1为售出,2为下架 56 | ConsumptionOrOutput string `json:"consumptionOrOutput"` //消耗或产出 57 | } 58 | 59 | 60 | 61 | func (t *SalesterminalChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 62 | return shim.Success(nil) 63 | } 64 | 65 | func (t *SalesterminalChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 66 | 67 | fn, args := stub.GetFunctionAndParameters() 68 | 69 | fmt.Printf("方法: %s 参数 : %s \n", fn, args) 70 | 71 | if fn == "addSalesterminal" { 72 | return t.addSalesterminal(stub, args) 73 | } else if fn == "addMilk" { 74 | return t.addMilk(stub, args) 75 | } else if fn == "addOperation" { 76 | return t.addOperation(stub, args) 77 | } else if fn == "getOperationHistory" { 78 | return t.getOperationHistory(stub, args) 79 | } else if fn == "getMilkHistory" { 80 | return t.getMilkHistory(stub, args) 81 | } else if fn == "get" { 82 | return t.get(stub, args) 83 | } else if fn == "set" { 84 | return t.set(stub, args) 85 | } 86 | 87 | return shim.Error("Salesterminal No operation:" + fn) 88 | 89 | } 90 | 91 | //添加销售终端 92 | func (t *SalesterminalChaincode) addSalesterminal(stub shim.ChaincodeStubInterface, args []string) pb.Response { 93 | 94 | if len(args) != 2 { 95 | return shim.Error("参数出错") 96 | } 97 | 98 | id := args[0] 99 | name := args[1] 100 | 101 | if isExisted(stub, id) { 102 | return shim.Error("已存在") 103 | } 104 | 105 | salesterminal := Salesterminal{} 106 | salesterminal.Id = id 107 | salesterminal.Name = name 108 | salesterminal.CreateTime = time.Now().Format(dateFomat) 109 | salesterminal.MaxMilkNo = 0 110 | salesterminal.MilkIds = []string{} 111 | 112 | jsonString, err := json.Marshal(salesterminal) 113 | 114 | fmt.Println("json:" + string(jsonString)) 115 | 116 | if err != nil { 117 | return shim.Error("json序列化失败") 118 | } 119 | 120 | err = stub.PutState(id, jsonString) 121 | 122 | if err != nil { 123 | shim.Error(err.Error()) 124 | } 125 | 126 | return shim.Success(jsonString) 127 | 128 | } 129 | 130 | //根据id获取加销售终端 131 | func (t *SalesterminalChaincode) getSalesterminalById(stub shim.ChaincodeStubInterface, id string) (Salesterminal,error) { 132 | 133 | dvalue, err := stub.GetState(id) 134 | var d Salesterminal 135 | err = json.Unmarshal([]byte(dvalue), &d) 136 | return d,err 137 | 138 | } 139 | 140 | //添加牛奶 141 | func (t *SalesterminalChaincode) addMilk(stub shim.ChaincodeStubInterface, args []string) pb.Response { 142 | 143 | if len(args) != 1 { 144 | return shim.Error("参数出错") 145 | } 146 | 147 | milkStr := args[0] 148 | 149 | var milk Milk 150 | //这里就是实际的解码和相关的错误检查 151 | if err := json.Unmarshal([]byte(milkStr), &milk); err != nil { 152 | return shim.Error("json反序列化失败") 153 | } 154 | 155 | salesterminalId := milk.SaleId 156 | salesterminal,err:= t.getSalesterminalById(stub, salesterminalId) 157 | if err != nil { 158 | return shim.Error("销售终端不存在") 159 | } 160 | 161 | salesterminal.MaxMilkNo += 1 162 | 163 | milkIds := salesterminal.MilkIds 164 | salesterminal.MilkIds = append(milkIds, milk.Id) 165 | 166 | //跟新销售 167 | jsonString, err := json.Marshal(salesterminal) 168 | if err != nil { 169 | return shim.Error("json序列化失败") 170 | } 171 | err = stub.PutState(salesterminalId, []byte(jsonString)) 172 | if err != nil { 173 | shim.Error(err.Error()) 174 | } 175 | 176 | fmt.Println("json:" + string(jsonString)) 177 | //添加牛奶 178 | err = stub.PutState(milk.Id, []byte(milkStr)) 179 | if err != nil { 180 | shim.Error(err.Error()) 181 | } 182 | 183 | fmt.Println("json:" + string(milkStr)) 184 | return shim.Success([]byte(milkStr)) 185 | 186 | } 187 | 188 | //添加奶桶操作 189 | func (t *SalesterminalChaincode) addOperation(stub shim.ChaincodeStubInterface, args []string) pb.Response { 190 | 191 | if len(args) != 1 { 192 | return shim.Error("参数出错") 193 | } 194 | 195 | operationStr := args[0] 196 | 197 | var operation SaleOperation 198 | //这里就是实际的解码和相关的错误检查 199 | if err := json.Unmarshal([]byte(operationStr), &operation); err != nil { 200 | return shim.Error("json反序列化失败") 201 | } 202 | operation.Time = time.Now().Format(dateFomat) 203 | milkId := operation.MilkId 204 | 205 | operationJson, err := json.Marshal(operation) 206 | if err != nil { 207 | return shim.Error("json序列化失败") 208 | } 209 | 210 | fmt.Println("json:" + string(operationJson)) 211 | err = stub.PutState(milkId+operationSuffix, []byte(operationJson)) 212 | if err != nil { 213 | return shim.Error(err.Error()) 214 | } 215 | //若售出 216 | if operation.Operation == 1 { 217 | 218 | milkStr, err := stub.GetState(milkId) 219 | if err != nil { 220 | return shim.Error(err.Error()) 221 | } 222 | var milk Milk 223 | //这里就是实际的解码和相关的错误检查 224 | if err := json.Unmarshal([]byte(milkStr), &milk); err != nil { 225 | return shim.Error("json反序列化失败") 226 | } 227 | 228 | milk.SaledTime = time.Now().Format(dateFomat) 229 | 230 | //跟新牛奶 231 | jsonString, err := json.Marshal(milk) 232 | if err != nil { 233 | return shim.Error("json序列化失败") 234 | } 235 | err = stub.PutState(milkId, []byte(jsonString)) 236 | if err != nil { 237 | shim.Error(err.Error()) 238 | } 239 | fmt.Println("json:" + string(jsonString)) 240 | 241 | } 242 | 243 | return shim.Success(nil) 244 | 245 | } 246 | 247 | func (t *SalesterminalChaincode) get(stub shim.ChaincodeStubInterface, args []string) pb.Response { 248 | 249 | 250 | if len(args) != 1 { 251 | return shim.Error("参数出错") 252 | } 253 | 254 | keys := strings.Split(args[0],",") 255 | 256 | var dmap = make(map[string]map[string]interface{}) 257 | 258 | for i := 0; i < len(keys); i++ { 259 | key := keys[i] 260 | //读出 261 | value, err := stub.GetState(key) 262 | if err != nil { 263 | return shim.Error(err.Error()) 264 | } 265 | 266 | if value == nil { 267 | dmap[key] = nil 268 | } else { 269 | var imap map[string]interface{} 270 | //这里就是实际的解码和相关的错误检查 271 | if err := json.Unmarshal([]byte(value), &imap); err != nil { 272 | return shim.Error("json反序列化失败") 273 | } 274 | dmap[key] = imap 275 | } 276 | 277 | } 278 | 279 | jsonStr, err := json.Marshal(dmap) 280 | if err != nil { 281 | return shim.Error("json序列化失败") 282 | } 283 | 284 | fmt.Println("json:", string(jsonStr)) 285 | return shim.Success(jsonStr) 286 | } 287 | 288 | func (t *SalesterminalChaincode) set(stub shim.ChaincodeStubInterface, args []string) pb.Response { 289 | 290 | if len(args) != 2 { 291 | return shim.Error("要输入一个键") 292 | } 293 | 294 | err := stub.PutState(args[0], []byte(args[1])) 295 | if err != nil { 296 | return shim.Error(err.Error()) 297 | } 298 | 299 | return shim.Success(nil) 300 | } 301 | 302 | //获取操作记录 303 | func (t *SalesterminalChaincode) getOperationHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response { 304 | 305 | if len(args) != 1 { 306 | return shim.Error("parm error") 307 | } 308 | id := args[0] 309 | keysIter, err := stub.GetHistoryForKey(id + operationSuffix) 310 | 311 | if err != nil { 312 | return shim.Error(fmt.Sprintf("GetHistoryForKey failed. Error accessing state: %s", err)) 313 | } 314 | defer keysIter.Close() 315 | 316 | var historys = make([]SaleOperation, 0) 317 | 318 | for keysIter.HasNext() { 319 | 320 | response, iterErr := keysIter.Next() 321 | if iterErr != nil { 322 | return shim.Error(fmt.Sprintf("GetHistoryForKey operation failed. Error accessing state: %s", err)) 323 | } 324 | //交易的值 325 | txvalue := response.Value 326 | 327 | var operation SaleOperation 328 | //这里就是实际的解码和相关的错误检查 329 | if err := json.Unmarshal(txvalue, &operation); err != nil { 330 | return shim.Error("json反序列化失败") 331 | } 332 | 333 | historys = append(historys, operation) 334 | 335 | } 336 | 337 | jsonKeys, err := json.Marshal(historys) 338 | if err != nil { 339 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 340 | } 341 | 342 | fmt.Println("json:", string(jsonKeys)) 343 | return shim.Success(jsonKeys) 344 | } 345 | 346 | //获取历史记录 347 | func (t *SalesterminalChaincode) getMilkHistory(stub shim.ChaincodeStubInterface, args []string) pb.Response { 348 | if len(args) != 1 { 349 | return shim.Error("parm error") 350 | } 351 | milkId := args[0] 352 | //获取桶编号 353 | bucketId := string(milkId[0:16]) 354 | //获取奶牛编号 355 | cowId := string(milkId[0:10]) 356 | 357 | keysIter, err := stub.GetHistoryForKey(milkId + operationSuffix) 358 | if err != nil { 359 | return shim.Error(fmt.Sprintf("GetHistoryForKey failed. Error accessing state: %s", err)) 360 | } 361 | defer keysIter.Close() 362 | 363 | var milkHisrtoy = MilkHistory{} 364 | 365 | var saleHistory = make([]map[string]interface{}, 0) 366 | 367 | for keysIter.HasNext() { 368 | 369 | response, iterErr := keysIter.Next() 370 | if iterErr != nil { 371 | return shim.Error(fmt.Sprintf("GetHistoryForKey operation failed. Error accessing state: %s", err)) 372 | } 373 | //交易的值 374 | txvalue := response.Value 375 | 376 | var history map[string]interface{} 377 | //这里就是实际的解码和相关的错误检查 378 | if err := json.Unmarshal([]byte(txvalue), &history); err != nil { 379 | return shim.Error("json反序列化失败") 380 | } 381 | 382 | saleHistory = append(saleHistory, history) 383 | 384 | } 385 | 386 | milkHisrtoy.SaleHistory = saleHistory 387 | //获取牛奶信息 388 | milkStr, err := stub.GetState(milkId) 389 | if err != nil { 390 | return shim.Error(err.Error()) 391 | } 392 | var milkInfo map[string]interface{} 393 | if err := json.Unmarshal(milkStr, &milkInfo); err != nil { 394 | return shim.Error("json反序列化失败") 395 | } 396 | milkHisrtoy.MilkInfo = milkInfo 397 | 398 | 399 | //获取加工厂的溯源 400 | response := stub.InvokeChaincode(machiningChaincodeName, [][]byte{[]byte("getOperationHistory"), []byte(bucketId)}, channelName) 401 | 402 | if response.Status != shim.OK { 403 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 404 | fmt.Printf(errStr) 405 | return shim.Error(errStr) 406 | } 407 | 408 | result := string(response.Payload) 409 | fmt.Println("桶操作:",result) 410 | 411 | var machiningHistorys []map[string]interface{} 412 | 413 | if err := json.Unmarshal([]byte(result), &machiningHistorys); err != nil { 414 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 415 | } 416 | 417 | milkHisrtoy.MachHistory = machiningHistorys 418 | 419 | //获取桶信息 420 | response = stub.InvokeChaincode(machiningChaincodeName, [][]byte{[]byte("get"), []byte(bucketId)}, channelName) 421 | 422 | if response.Status != shim.OK { 423 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 424 | fmt.Printf(errStr) 425 | return shim.Error(errStr) 426 | } 427 | 428 | result = string(response.Payload) 429 | 430 | fmt.Println("桶:",result) 431 | 432 | var bucketInfo map[string]interface{} 433 | if err := json.Unmarshal([]byte(result), &bucketInfo); err != nil { 434 | return shim.Error("json反序列化失败") 435 | } 436 | milkHisrtoy.BucketInfo = bucketInfo[bucketId].(map[string]interface{}) 437 | 438 | 439 | //调用奶牛场的溯源信息 440 | response = stub.InvokeChaincode(dairyfarmChaincodeName, [][]byte{[]byte("getOperationHistory"), []byte(cowId)}, channelName) 441 | 442 | if response.Status != shim.OK { 443 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 444 | fmt.Printf(errStr) 445 | return shim.Error(errStr) 446 | } 447 | 448 | result = string(response.Payload) 449 | fmt.Println("奶牛操作:",result) 450 | var dairHistorys []map[string]interface{} 451 | if err := json.Unmarshal([]byte(result), &dairHistorys); err != nil { 452 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 453 | } 454 | 455 | milkHisrtoy.DairHistory = dairHistorys 456 | 457 | //获取奶牛信息 458 | response = stub.InvokeChaincode(dairyfarmChaincodeName, [][]byte{[]byte("get"), []byte(cowId)}, channelName) 459 | 460 | if response.Status != shim.OK { 461 | errStr := fmt.Sprintf("Failed to query chaincode. Got error: %s", response.Payload) 462 | fmt.Printf(errStr) 463 | return shim.Error(errStr) 464 | } 465 | 466 | result = string(response.Payload) 467 | fmt.Println("奶牛:",result) 468 | 469 | var cowInfo map[string]interface{} 470 | if err := json.Unmarshal([]byte(result), &cowInfo); err != nil { 471 | return shim.Error("json反序列化失败") 472 | } 473 | milkHisrtoy.CowInfo = cowInfo[cowId].(map[string]interface{}) 474 | 475 | jsons, err := json.Marshal(milkHisrtoy) 476 | if err != nil { 477 | return shim.Error(fmt.Sprintf("query operation failed. Error marshaling JSON: %s", err)) 478 | } 479 | 480 | return shim.Success(jsons) 481 | } 482 | 483 | func isExisted(stub shim.ChaincodeStubInterface, key string) bool { 484 | val, err := stub.GetState(key) 485 | if err != nil { 486 | fmt.Printf("Error: %s\n", err) 487 | } 488 | 489 | if len(val) == 0 { 490 | return false 491 | } 492 | 493 | return true 494 | } 495 | 496 | func main() { 497 | err := shim.Start(new(SalesterminalChaincode)) 498 | if err != nil { 499 | fmt.Printf("Error starting Simple chaincode: %s", err) 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /food-traceability/chaincode/src/salesterminal/cc_salesterminal_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "github.com/hyperledger/fabric/core/chaincode/shim" 7 | ) 8 | 9 | var stub *shim.MockStub 10 | 11 | //测试 Init 方法 12 | //func testInit(t *testing.T, args [][]byte) { 13 | // 14 | // res := stub.MockInit("1", args) 15 | // if res.Status != shim.OK { 16 | // fmt.Println("Init failed : ", string(res.Message)) 17 | // t.FailNow() 18 | // } 19 | //} 20 | 21 | func testInvoke(t *testing.T, args [][]byte) { 22 | res := stub.MockInvoke("1", args) 23 | fmt.Printf("res:" + string(res.Payload) + "\n") 24 | 25 | if res.Status != shim.OK { 26 | fmt.Println("Invoke", "failed", string(res.Message)) 27 | //t.FailNow() 28 | } 29 | 30 | } 31 | 32 | func TestDemo(t *testing.T) { 33 | 34 | scc := new(SalesterminalChaincode) 35 | stub = shim.NewMockStub("salesterminal", scc) 36 | 37 | testInvoke(t, [][]byte{[]byte("addSalesterminal"), []byte("S001"), []byte("销售终端1")}) 38 | testInvoke(t, [][]byte{[]byte("addSalesterminal"), []byte("S002"), []byte("销售终端2")}) 39 | testInvoke(t, [][]byte{[]byte("get"), []byte("S002")}) 40 | 41 | testInvoke(t, [][]byte{[]byte("addMilk"), []byte(`{"id":"F0010000010000010001","time":"2018-10-08 15:48:27","InSaleTime":"2018-10-08 15:48:27","saleId":"S001","stutas":0}`)}) 42 | 43 | testInvoke(t, [][]byte{[]byte("get"), []byte("F0010000010000010001")}) 44 | 45 | testInvoke(t, [][]byte{[]byte("addOperation"), []byte(`{"milkId":"F0010000010000010001","operation":1,"consumptionOrOutput":"售出"}`)}) 46 | testInvoke(t, [][]byte{[]byte("addOperation"), []byte(`{"milkId":"F0010000010000010001","operation":2,"consumptionOrOutput":"下架"}`)}) 47 | testInvoke(t, [][]byte{[]byte("addOperation"), []byte(`{"milkId":"F0010000010000010001","operation":0,"consumptionOrOutput":"上架"}`)}) 48 | 49 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2")}) 50 | //testInvoke(t,[][]byte{[]byte("getOperationHistory"), []byte("F001_2-1")}) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=net 2 | IMAGE_TAG=1.4.5 3 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/READEME.md: -------------------------------------------------------------------------------- 1 | #### 说明 2 | 3 | 基础系统版本为fabric 1.4.5,单机raft机制 4 | 3个orderer节点,两个组织,每个组织两个节点 5 | 6 | #### 启动 7 | 8 | 生产启动网络所需的证书及创始区块以及通道配置文件。 9 | 10 | ```shell 11 | bash ./updown.sh up 12 | ``` 13 | 14 | 创建channel以及安装实例化链码 15 | 16 | ```shell 17 | bash ./cc.sh 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/base/docker-compose-base.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | version: '2' 7 | 8 | services: 9 | 10 | orderer1.example.com: 11 | container_name: orderer1.example.com 12 | extends: 13 | file: peer-base.yaml 14 | service: orderer-base 15 | volumes: 16 | - ../channel-artifacts/orderer.genesis.block:/var/hyperledger/orderer/orderer.genesis.block 17 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/msp:/var/hyperledger/orderer/msp 18 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/:/var/hyperledger/orderer/tls 19 | ports: 20 | - 7050:7050 21 | 22 | orderer2.example.com: 23 | container_name: orderer2.example.com 24 | extends: 25 | file: peer-base.yaml 26 | service: orderer-base 27 | volumes: 28 | - ../channel-artifacts/orderer.genesis.block:/var/hyperledger/orderer/orderer.genesis.block 29 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/msp:/var/hyperledger/orderer/msp 30 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/:/var/hyperledger/orderer/tls 31 | # - orderer.example.com:/var/hyperledger/production/orderer 32 | ports: 33 | - 8050:7050 34 | 35 | orderer3.example.com: 36 | container_name: orderer3.example.com 37 | extends: 38 | file: peer-base.yaml 39 | service: orderer-base 40 | volumes: 41 | - ../channel-artifacts/orderer.genesis.block:/var/hyperledger/orderer/orderer.genesis.block 42 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/msp:/var/hyperledger/orderer/msp 43 | - ../crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/:/var/hyperledger/orderer/tls 44 | # - orderer.example.com:/var/hyperledger/production/orderer 45 | ports: 46 | - 9050:7050 47 | 48 | peer0.org1.example.com: 49 | container_name: peer0.org1.example.com 50 | extends: 51 | file: peer-base.yaml 52 | service: peer-base 53 | environment: 54 | - CORE_PEER_ID=peer0.org1.example.com 55 | - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 56 | - CORE_PEER_LISTENADDRESS=0.0.0.0:7051 57 | - CORE_PEER_CHAINCODEADDRESS=peer0.org1.example.com:7052 58 | - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:7052 59 | - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 60 | - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 61 | - CORE_PEER_LOCALMSPID=Org1MSP 62 | volumes: 63 | - /var/run/:/host/var/run/ 64 | - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp 65 | - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls 66 | # - peer0.org1.example.com:/var/hyperledger/production 67 | ports: 68 | - 7051:7051 69 | 70 | # peer1.org1.example.com: 71 | # container_name: peer1.org1.example.com 72 | # extends: 73 | # file: peer-base.yaml 74 | # service: peer-base 75 | # environment: 76 | # - CORE_PEER_ID=peer1.org1.example.com 77 | # - CORE_PEER_ADDRESS=peer1.org1.example.com:8051 78 | # - CORE_PEER_LISTENADDRESS=0.0.0.0:8051 79 | # - CORE_PEER_CHAINCODEADDRESS=peer1.org1.example.com:8052 80 | # - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:8052 81 | # - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:8051 82 | # - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 83 | # - CORE_PEER_LOCALMSPID=Org1MSP 84 | # volumes: 85 | # - /var/run/:/host/var/run/ 86 | # - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp 87 | # - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls 88 | # - peer1.org1.example.com:/var/hyperledger/production 89 | 90 | # ports: 91 | # - 8051:8051 92 | 93 | peer0.org2.example.com: 94 | container_name: peer0.org2.example.com 95 | extends: 96 | file: peer-base.yaml 97 | service: peer-base 98 | environment: 99 | - CORE_PEER_ID=peer0.org2.example.com 100 | - CORE_PEER_ADDRESS=peer0.org2.example.com:8051 101 | - CORE_PEER_LISTENADDRESS=0.0.0.0:8051 102 | - CORE_PEER_CHAINCODEADDRESS=peer0.org2.example.com:8052 103 | - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:8052 104 | - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org2.example.com:8051 105 | - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.example.com:8051 106 | - CORE_PEER_LOCALMSPID=Org2MSP 107 | volumes: 108 | - /var/run/:/host/var/run/ 109 | - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/msp:/etc/hyperledger/fabric/msp 110 | - ../crypto-config/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls:/etc/hyperledger/fabric/tls 111 | # - peer0.org2.example.com:/var/hyperledger/production 112 | ports: 113 | - 8051:8051 114 | 115 | # peer1.org2.example.com: 116 | # container_name: peer1.org2.example.com 117 | # extends: 118 | # file: peer-base.yaml 119 | # service: peer-base 120 | # environment: 121 | # - CORE_PEER_ID=peer1.org2.example.com 122 | # - CORE_PEER_ADDRESS=peer1.org2.example.com:10051 123 | # - CORE_PEER_LISTENADDRESS=0.0.0.0:10051 124 | # - CORE_PEER_CHAINCODEADDRESS=peer1.org2.example.com:10052 125 | # - CORE_PEER_CHAINCODELISTENADDRESS=0.0.0.0:10052 126 | # - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org2.example.com:10051 127 | # - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org2.example.com:9051 128 | # - CORE_PEER_LOCALMSPID=Org2MSP 129 | # volumes: 130 | # - /var/run/:/host/var/run/ 131 | # - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/msp:/etc/hyperledger/fabric/msp 132 | # - ../crypto-config/peerOrganizations/org2.example.com/peers/peer1.org2.example.com/tls:/etc/hyperledger/fabric/tls 133 | # - peer1.org2.example.com:/var/hyperledger/production 134 | # ports: 135 | # - 10051:10051 136 | 137 | 138 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/base/peer-base.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | version: '2' 7 | 8 | services: 9 | peer-base: 10 | image: hyperledger/fabric-peer:$IMAGE_TAG 11 | environment: 12 | - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock 13 | # the following setting starts chaincode containers on the same 14 | # bridge network as the peers 15 | # https://docs.docker.com/compose/networking/ 16 | - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=${COMPOSE_PROJECT_NAME}_byfn 17 | - FABRIC_LOGGING_SPEC=INFO 18 | #- FABRIC_LOGGING_SPEC=DEBUG 19 | - CORE_PEER_TLS_ENABLED=true 20 | - CORE_PEER_GOSSIP_USELEADERELECTION=true 21 | - CORE_PEER_GOSSIP_ORGLEADER=false 22 | - CORE_PEER_PROFILE_ENABLED=true 23 | - GODEBUG=netdns=go 24 | - CORE_PEER_TLS_CERT_FILE=/etc/hyperledger/fabric/tls/server.crt 25 | - CORE_PEER_TLS_KEY_FILE=/etc/hyperledger/fabric/tls/server.key 26 | - CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/tls/ca.crt 27 | working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer 28 | command: peer node start 29 | 30 | orderer-base: 31 | image: hyperledger/fabric-orderer:$IMAGE_TAG 32 | environment: 33 | - FABRIC_LOGGING_SPEC=INFO 34 | - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 35 | - ORDERER_GENERAL_GENESISMETHOD=file 36 | - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block 37 | - ORDERER_GENERAL_LOCALMSPID=OrdererMSP 38 | - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp 39 | - GODEBUG=netdns=go 40 | # enabled TLS 41 | - ORDERER_GENERAL_TLS_ENABLED=true 42 | - ORDERER_GENERAL_TLS_PRIVATEKEY=/var/hyperledger/orderer/tls/server.key 43 | - ORDERER_GENERAL_TLS_CERTIFICATE=/var/hyperledger/orderer/tls/server.crt 44 | - ORDERER_GENERAL_TLS_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt] 45 | # - ORDERER_KAFKA_TOPIC_REPLICATIONFACTOR=1 46 | # - ORDERER_KAFKA_VERBOSE=true 47 | - ORDERER_GENERAL_CLUSTER_CLIENTCERTIFICATE=/var/hyperledger/orderer/tls/server.crt 48 | - ORDERER_GENERAL_CLUSTER_CLIENTPRIVATEKEY=/var/hyperledger/orderer/tls/server.key 49 | - ORDERER_GENERAL_CLUSTER_ROOTCAS=[/var/hyperledger/orderer/tls/ca.crt] 50 | working_dir: /opt/gopath/src/github.com/hyperledger/fabric 51 | command: orderer 52 | 53 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/bin/configtxgen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jalins/fabricase/7d9b03c017a294c086a6a3293b8451df45bb0616/food-traceability/fabric_raft/bin/configtxgen -------------------------------------------------------------------------------- /food-traceability/fabric_raft/bin/configtxlator: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jalins/fabricase/7d9b03c017a294c086a6a3293b8451df45bb0616/food-traceability/fabric_raft/bin/configtxlator -------------------------------------------------------------------------------- /food-traceability/fabric_raft/bin/cryptogen: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jalins/fabricase/7d9b03c017a294c086a6a3293b8451df45bb0616/food-traceability/fabric_raft/bin/cryptogen -------------------------------------------------------------------------------- /food-traceability/fabric_raft/cc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | set -e 7 | 8 | docker exec cli bash scripts/official.sh 9 | 10 | 11 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/chaincode/chaincode_example02/go/chaincode_example02.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright IBM Corp. 2016 All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package main 18 | 19 | //WARNING - this chaincode's ID is hard-coded in chaincode_example04 to illustrate one way of 20 | //calling chaincode from a chaincode. If this example is modified, chaincode_example04.go has 21 | //to be modified as well with the new ID of chaincode_example02. 22 | //chaincode_example05 show's how chaincode ID can be passed in as a parameter instead of 23 | //hard-coding. 24 | 25 | import ( 26 | "fmt" 27 | "strconv" 28 | 29 | "github.com/hyperledger/fabric/core/chaincode/shim" 30 | pb "github.com/hyperledger/fabric/protos/peer" 31 | ) 32 | 33 | // SimpleChaincode example simple Chaincode implementation 34 | type SimpleChaincode struct { 35 | } 36 | 37 | func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { 38 | fmt.Println("ex02 Init") 39 | _, args := stub.GetFunctionAndParameters() 40 | var A, B string // Entities 41 | var Aval, Bval int // Asset holdings 42 | var err error 43 | 44 | if len(args) != 4 { 45 | return shim.Error("Incorrect number of arguments. Expecting 4") 46 | } 47 | 48 | // Initialize the chaincode 49 | A = args[0] 50 | Aval, err = strconv.Atoi(args[1]) 51 | if err != nil { 52 | return shim.Error("Expecting integer value for asset holding") 53 | } 54 | B = args[2] 55 | Bval, err = strconv.Atoi(args[3]) 56 | if err != nil { 57 | return shim.Error("Expecting integer value for asset holding") 58 | } 59 | fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) 60 | 61 | // Write the state to the ledger 62 | err = stub.PutState(A, []byte(strconv.Itoa(Aval))) 63 | if err != nil { 64 | return shim.Error(err.Error()) 65 | } 66 | 67 | err = stub.PutState(B, []byte(strconv.Itoa(Bval))) 68 | if err != nil { 69 | return shim.Error(err.Error()) 70 | } 71 | 72 | return shim.Success(nil) 73 | } 74 | 75 | func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { 76 | fmt.Println("ex02 Invoke") 77 | function, args := stub.GetFunctionAndParameters() 78 | if function == "invoke" { 79 | // Make payment of X units from A to B 80 | return t.invoke(stub, args) 81 | } else if function == "delete" { 82 | // Deletes an entity from its state 83 | return t.delete(stub, args) 84 | } else if function == "query" { 85 | // the old "Query" is now implemtned in invoke 86 | return t.query(stub, args) 87 | } 88 | 89 | return shim.Error("Invalid invoke function name. Expecting \"invoke\" \"delete\" \"query\"") 90 | } 91 | 92 | // Transaction makes payment of X units from A to B 93 | func (t *SimpleChaincode) invoke(stub shim.ChaincodeStubInterface, args []string) pb.Response { 94 | var A, B string // Entities 95 | var Aval, Bval int // Asset holdings 96 | var X int // Transaction value 97 | var err error 98 | 99 | if len(args) != 3 { 100 | return shim.Error("Incorrect number of arguments. Expecting 3") 101 | } 102 | 103 | A = args[0] 104 | B = args[1] 105 | 106 | // Get the state from the ledger 107 | // TODO: will be nice to have a GetAllState call to ledger 108 | Avalbytes, err := stub.GetState(A) 109 | if err != nil { 110 | return shim.Error("Failed to get state") 111 | } 112 | if Avalbytes == nil { 113 | return shim.Error("Entity not found") 114 | } 115 | Aval, _ = strconv.Atoi(string(Avalbytes)) 116 | 117 | Bvalbytes, err := stub.GetState(B) 118 | if err != nil { 119 | return shim.Error("Failed to get state") 120 | } 121 | if Bvalbytes == nil { 122 | return shim.Error("Entity not found") 123 | } 124 | Bval, _ = strconv.Atoi(string(Bvalbytes)) 125 | 126 | // Perform the execution 127 | X, err = strconv.Atoi(args[2]) 128 | if err != nil { 129 | return shim.Error("Invalid transaction amount, expecting a integer value") 130 | } 131 | Aval = Aval - X 132 | Bval = Bval + X 133 | fmt.Printf("Aval = %d, Bval = %d\n", Aval, Bval) 134 | 135 | // Write the state back to the ledger 136 | err = stub.PutState(A, []byte(strconv.Itoa(Aval))) 137 | if err != nil { 138 | return shim.Error(err.Error()) 139 | } 140 | 141 | err = stub.PutState(B, []byte(strconv.Itoa(Bval))) 142 | if err != nil { 143 | return shim.Error(err.Error()) 144 | } 145 | 146 | return shim.Success(nil) 147 | } 148 | 149 | // Deletes an entity from state 150 | func (t *SimpleChaincode) delete(stub shim.ChaincodeStubInterface, args []string) pb.Response { 151 | if len(args) != 1 { 152 | return shim.Error("Incorrect number of arguments. Expecting 1") 153 | } 154 | 155 | A := args[0] 156 | 157 | // Delete the key from the state in ledger 158 | err := stub.DelState(A) 159 | if err != nil { 160 | return shim.Error("Failed to delete state") 161 | } 162 | 163 | return shim.Success(nil) 164 | } 165 | 166 | // query callback representing the query of a chaincode 167 | func (t *SimpleChaincode) query(stub shim.ChaincodeStubInterface, args []string) pb.Response { 168 | var A string // Entities 169 | var err error 170 | 171 | if len(args) != 1 { 172 | return shim.Error("Incorrect number of arguments. Expecting name of the person to query") 173 | } 174 | 175 | A = args[0] 176 | 177 | // Get the state from the ledger 178 | Avalbytes, err := stub.GetState(A) 179 | if err != nil { 180 | jsonResp := "{\"Error\":\"Failed to get state for " + A + "\"}" 181 | return shim.Error(jsonResp) 182 | } 183 | 184 | if Avalbytes == nil { 185 | jsonResp := "{\"Error\":\"Nil amount for " + A + "\"}" 186 | return shim.Error(jsonResp) 187 | } 188 | 189 | jsonResp := "{\"Name\":\"" + A + "\",\"Amount\":\"" + string(Avalbytes) + "\"}" 190 | fmt.Printf("Query Response:%s\n", jsonResp) 191 | return shim.Success(Avalbytes) 192 | } 193 | 194 | func main() { 195 | err := shim.Start(new(SimpleChaincode)) 196 | if err != nil { 197 | fmt.Printf("Error starting Simple chaincode: %s", err) 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/chaincode/chaincode_example02/node/chaincode_example02.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Copyright IBM Corp. All Rights Reserved. 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | */ 6 | 7 | const shim = require('fabric-shim'); 8 | const util = require('util'); 9 | 10 | var Chaincode = class { 11 | 12 | // Initialize the chaincode 13 | async Init(stub) { 14 | console.info('========= example02 Init ========='); 15 | let ret = stub.getFunctionAndParameters(); 16 | console.info(ret); 17 | let args = ret.params; 18 | // initialise only if 4 parameters passed. 19 | if (args.length != 4) { 20 | return shim.error('Incorrect number of arguments. Expecting 4'); 21 | } 22 | 23 | let A = args[0]; 24 | let B = args[2]; 25 | let Aval = args[1]; 26 | let Bval = args[3]; 27 | 28 | if (typeof parseInt(Aval) !== 'number' || typeof parseInt(Bval) !== 'number') { 29 | return shim.error('Expecting integer value for asset holding'); 30 | } 31 | 32 | try { 33 | await stub.putState(A, Buffer.from(Aval)); 34 | try { 35 | await stub.putState(B, Buffer.from(Bval)); 36 | return shim.success(); 37 | } catch (err) { 38 | return shim.error(err); 39 | } 40 | } catch (err) { 41 | return shim.error(err); 42 | } 43 | } 44 | 45 | async Invoke(stub) { 46 | let ret = stub.getFunctionAndParameters(); 47 | console.info(ret); 48 | let method = this[ret.fcn]; 49 | if (!method) { 50 | console.log('no method of name:' + ret.fcn + ' found'); 51 | return shim.success(); 52 | } 53 | try { 54 | let payload = await method(stub, ret.params); 55 | return shim.success(payload); 56 | } catch (err) { 57 | console.log(err); 58 | return shim.error(err); 59 | } 60 | } 61 | 62 | async invoke(stub, args) { 63 | if (args.length != 3) { 64 | throw new Error('Incorrect number of arguments. Expecting 3'); 65 | } 66 | 67 | let A = args[0]; 68 | let B = args[1]; 69 | if (!A || !B) { 70 | throw new Error('asset holding must not be empty'); 71 | } 72 | 73 | // Get the state from the ledger 74 | let Avalbytes = await stub.getState(A); 75 | if (!Avalbytes) { 76 | throw new Error('Failed to get state of asset holder A'); 77 | } 78 | let Aval = parseInt(Avalbytes.toString()); 79 | 80 | let Bvalbytes = await stub.getState(B); 81 | if (!Bvalbytes) { 82 | throw new Error('Failed to get state of asset holder B'); 83 | } 84 | 85 | let Bval = parseInt(Bvalbytes.toString()); 86 | // Perform the execution 87 | let amount = parseInt(args[2]); 88 | if (typeof amount !== 'number') { 89 | throw new Error('Expecting integer value for amount to be transaferred'); 90 | } 91 | 92 | Aval = Aval - amount; 93 | Bval = Bval + amount; 94 | console.info(util.format('Aval = %d, Bval = %d\n', Aval, Bval)); 95 | 96 | // Write the states back to the ledger 97 | await stub.putState(A, Buffer.from(Aval.toString())); 98 | await stub.putState(B, Buffer.from(Bval.toString())); 99 | 100 | } 101 | 102 | // Deletes an entity from state 103 | async delete(stub, args) { 104 | if (args.length != 1) { 105 | throw new Error('Incorrect number of arguments. Expecting 1'); 106 | } 107 | 108 | let A = args[0]; 109 | 110 | // Delete the key from the state in ledger 111 | await stub.deleteState(A); 112 | } 113 | 114 | // query callback representing the query of a chaincode 115 | async query(stub, args) { 116 | if (args.length != 1) { 117 | throw new Error('Incorrect number of arguments. Expecting name of the person to query') 118 | } 119 | 120 | let jsonResp = {}; 121 | let A = args[0]; 122 | 123 | // Get the state from the ledger 124 | let Avalbytes = await stub.getState(A); 125 | if (!Avalbytes) { 126 | jsonResp.error = 'Failed to get state for ' + A; 127 | throw new Error(JSON.stringify(jsonResp)); 128 | } 129 | 130 | jsonResp.name = A; 131 | jsonResp.amount = Avalbytes.toString(); 132 | console.info('Query Response:'); 133 | console.info(jsonResp); 134 | return Avalbytes; 135 | } 136 | }; 137 | 138 | shim.start(new Chaincode()); 139 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/chaincode/chaincode_example02/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chaincode_example02", 3 | "version": "1.0.0", 4 | "description": "chaincode_example02 chaincode implemented in node.js", 5 | "engines": { 6 | "node": ">=8.4.0", 7 | "npm": ">=5.3.0" 8 | }, 9 | "scripts": { "start" : "node chaincode_example02.js" }, 10 | "engine-strict": true, 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "fabric-shim": "unstable" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/configtx.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | --- 7 | 8 | Organizations: 9 | 10 | - &OrdererOrg 11 | 12 | Name: OrdererOrg 13 | 14 | ID: OrdererMSP 15 | 16 | MSPDir: crypto-config/ordererOrganizations/example.com/msp 17 | 18 | Policies: 19 | Readers: 20 | Type: Signature 21 | Rule: "OR('OrdererMSP.member')" 22 | Writers: 23 | Type: Signature 24 | Rule: "OR('OrdererMSP.member')" 25 | Admins: 26 | Type: Signature 27 | Rule: "OR('OrdererMSP.admin')" 28 | 29 | - &Org1 30 | 31 | Name: Org1MSP 32 | 33 | ID: Org1MSP 34 | 35 | MSPDir: crypto-config/peerOrganizations/org1.example.com/msp 36 | 37 | Policies: 38 | Readers: 39 | Type: Signature 40 | Rule: "OR('Org1MSP.admin', 'Org1MSP.peer', 'Org1MSP.client')" 41 | Writers: 42 | Type: Signature 43 | Rule: "OR('Org1MSP.admin', 'Org1MSP.client')" 44 | Admins: 45 | Type: Signature 46 | Rule: "OR('Org1MSP.admin')" 47 | 48 | AnchorPeers: 49 | 50 | - Host: peer0.org1.example.com 51 | Port: 7051 52 | 53 | - &Org2 54 | 55 | Name: Org2MSP 56 | 57 | ID: Org2MSP 58 | 59 | MSPDir: crypto-config/peerOrganizations/org2.example.com/msp 60 | 61 | Policies: 62 | Readers: 63 | Type: Signature 64 | Rule: "OR('Org2MSP.admin', 'Org2MSP.peer', 'Org2MSP.client')" 65 | Writers: 66 | Type: Signature 67 | Rule: "OR('Org2MSP.admin', 'Org2MSP.client')" 68 | Admins: 69 | Type: Signature 70 | Rule: "OR('Org2MSP.admin')" 71 | 72 | AnchorPeers: 73 | 74 | - Host: peer0.org2.example.com 75 | Port: 8051 76 | 77 | Capabilities: 78 | Channel: &ChannelCapabilities 79 | 80 | V1_4_3: true 81 | 82 | V1_3: false 83 | 84 | V1_1: false 85 | 86 | Orderer: &OrdererCapabilities 87 | V1_4_2: true 88 | 89 | V1_1: false 90 | 91 | Application: &ApplicationCapabilities 92 | V1_4_2: true 93 | 94 | V1_3: false 95 | 96 | V1_2: false 97 | 98 | V1_1: false 99 | 100 | Application: &ApplicationDefaults 101 | 102 | Organizations: 103 | 104 | Policies: 105 | Readers: 106 | Type: ImplicitMeta 107 | Rule: "ANY Readers" 108 | Writers: 109 | Type: ImplicitMeta 110 | Rule: "ANY Writers" 111 | Admins: 112 | Type: ImplicitMeta 113 | Rule: "MAJORITY Admins" 114 | 115 | Capabilities: 116 | <<: *ApplicationCapabilities 117 | 118 | Orderer: &OrdererDefaults 119 | 120 | #OrdererType: etcdraft 121 | 122 | #Addresses: 123 | # - orderer1.example.com:7050 124 | # - orderer2.example.com:7050 125 | # - orderer3.example.com:7050 126 | 127 | BatchTimeout: 2s 128 | 129 | BatchSize: 130 | 131 | MaxMessageCount: 10 132 | 133 | AbsoluteMaxBytes: 99 MB 134 | 135 | PreferredMaxBytes: 512 KB 136 | 137 | Organizations: 138 | 139 | Policies: 140 | Readers: 141 | Type: ImplicitMeta 142 | Rule: "ANY Readers" 143 | Writers: 144 | Type: ImplicitMeta 145 | Rule: "ANY Writers" 146 | Admins: 147 | Type: ImplicitMeta 148 | Rule: "MAJORITY Admins" 149 | 150 | BlockValidation: 151 | Type: ImplicitMeta 152 | Rule: "ANY Writers" 153 | 154 | Channel: &ChannelDefaults 155 | 156 | Policies: 157 | 158 | Readers: 159 | Type: ImplicitMeta 160 | Rule: "ANY Readers" 161 | 162 | Writers: 163 | Type: ImplicitMeta 164 | Rule: "ANY Writers" 165 | 166 | Admins: 167 | Type: ImplicitMeta 168 | Rule: "MAJORITY Admins" 169 | 170 | Capabilities: 171 | <<: *ChannelCapabilities 172 | 173 | Profiles: 174 | 175 | TwoOrgsOrdererGenesis: 176 | <<: *ChannelDefaults 177 | Orderer: 178 | <<: *OrdererDefaults 179 | Organizations: 180 | - *OrdererOrg 181 | Capabilities: 182 | <<: *OrdererCapabilities 183 | Consortiums: 184 | SampleConsortium: 185 | Organizations: 186 | - *Org1 187 | - *Org2 188 | TwoOrgsChannel: 189 | Consortium: SampleConsortium 190 | <<: *ChannelDefaults 191 | Application: 192 | <<: *ApplicationDefaults 193 | Organizations: 194 | - *Org1 195 | - *Org2 196 | Capabilities: 197 | <<: *ApplicationCapabilities 198 | 199 | SampleDevModeKafka: 200 | <<: *ChannelDefaults 201 | Capabilities: 202 | <<: *ChannelCapabilities 203 | Orderer: 204 | <<: *OrdererDefaults 205 | OrdererType: kafka 206 | Kafka: 207 | Brokers: 208 | - kafka0.example.com:9092 209 | - kafka1.example.com:9092 210 | - kafka2.example.com:9092 211 | - kafka3.example.com:9092 212 | 213 | Organizations: 214 | - *OrdererOrg 215 | Capabilities: 216 | <<: *OrdererCapabilities 217 | Application: 218 | <<: *ApplicationDefaults 219 | Organizations: 220 | - <<: *OrdererOrg 221 | Consortiums: 222 | SampleConsortium: 223 | Organizations: 224 | - *Org1 225 | - *Org2 226 | 227 | SampleMultiNodeEtcdRaft: 228 | <<: *ChannelDefaults 229 | Capabilities: 230 | <<: *ChannelCapabilities 231 | Orderer: 232 | <<: *OrdererDefaults 233 | OrdererType: etcdraft 234 | EtcdRaft: 235 | Consenters: 236 | - Host: orderer1.example.com 237 | Port: 7050 238 | ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt 239 | ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/server.crt 240 | - Host: orderer2.example.com 241 | Port: 7050 242 | ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt 243 | ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer2.example.com/tls/server.crt 244 | - Host: orderer3.example.com 245 | Port: 7050 246 | ClientTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt 247 | ServerTLSCert: crypto-config/ordererOrganizations/example.com/orderers/orderer3.example.com/tls/server.crt 248 | Addresses: 249 | - orderer1.example.com:7050 250 | - orderer2.example.com:7050 251 | - orderer3.example.com:7050 252 | 253 | Organizations: 254 | - *OrdererOrg 255 | Capabilities: 256 | <<: *OrdererCapabilities 257 | Application: 258 | <<: *ApplicationDefaults 259 | Organizations: 260 | - <<: *OrdererOrg 261 | Consortiums: 262 | SampleConsortium: 263 | Organizations: 264 | - *Org1 265 | - *Org2 266 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/crypto-config.yaml: -------------------------------------------------------------------------------- 1 | 2 | OrdererOrgs: 3 | 4 | - Name: Orderer 5 | Domain: example.com 6 | 7 | Specs: 8 | - Hostname: orderer1 9 | - Hostname: orderer2 10 | - Hostname: orderer3 11 | - Hostname: orderer4 12 | - Hostname: orderer5 13 | 14 | PeerOrgs: 15 | 16 | - Name: Org1 17 | Domain: org1.example.com 18 | EnableNodeOUs: true 19 | 20 | Template: 21 | Count: 2 22 | 23 | Users: 24 | Count: 1 25 | 26 | - Name: Org2 27 | Domain: org2.example.com 28 | EnableNodeOUs: true 29 | Template: 30 | Count: 2 31 | Users: 32 | Count: 1 33 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/docker-compose-cli.yaml: -------------------------------------------------------------------------------- 1 | # Copyright IBM Corp. All Rights Reserved. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | 6 | version: '2' 7 | 8 | # 挂载在本地的目录 9 | # volumes: 10 | # orderer.example.com: 11 | # peer0.org1.example.com: 12 | # peer1.org1.example.com: 13 | # peer0.org2.example.com: 14 | # peer1.org2.example.com: 15 | 16 | networks: 17 | byfn: 18 | 19 | services: 20 | 21 | orderer1.example.com: 22 | extends: 23 | file: base/docker-compose-base.yaml 24 | service: orderer1.example.com 25 | container_name: orderer1.example.com 26 | networks: 27 | - byfn 28 | 29 | orderer2.example.com: 30 | extends: 31 | file: base/docker-compose-base.yaml 32 | service: orderer2.example.com 33 | container_name: orderer2.example.com 34 | networks: 35 | - byfn 36 | 37 | orderer3.example.com: 38 | extends: 39 | file: base/docker-compose-base.yaml 40 | service: orderer3.example.com 41 | container_name: orderer3.example.com 42 | networks: 43 | - byfn 44 | 45 | peer0.org1.example.com: 46 | container_name: peer0.org1.example.com 47 | extends: 48 | file: base/docker-compose-base.yaml 49 | service: peer0.org1.example.com 50 | networks: 51 | - byfn 52 | 53 | peer0.org2.example.com: 54 | container_name: peer0.org2.example.com 55 | extends: 56 | file: base/docker-compose-base.yaml 57 | service: peer0.org2.example.com 58 | networks: 59 | - byfn 60 | 61 | 62 | 63 | cli: 64 | container_name: cli 65 | image: hyperledger/fabric-tools:$IMAGE_TAG 66 | tty: true 67 | stdin_open: true 68 | environment: 69 | - GOPATH=/opt/gopath 70 | - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock 71 | #- FABRIC_LOGGING_SPEC=DEBUG 72 | - FABRIC_LOGGING_SPEC=INFO 73 | - CORE_PEER_ID=cli 74 | - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 75 | - CORE_PEER_LOCALMSPID=Org1MSP 76 | - CORE_PEER_TLS_ENABLED=true 77 | - CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt 78 | - CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key 79 | - CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 80 | - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 81 | working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer 82 | command: /bin/bash 83 | volumes: 84 | - /var/run/:/host/var/run/ 85 | - ./chaincode/:/opt/gopath/src/github.com/chaincode 86 | - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ 87 | - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/ 88 | - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts 89 | depends_on: 90 | - orderer1.example.com 91 | - orderer2.example.com 92 | - orderer3.example.com 93 | - peer0.org1.example.com 94 | - peer0.org2.example.com 95 | networks: 96 | - byfn 97 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/genesis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | ININT_PATH=${PWD} 9 | cd "$(dirname "$0")" 10 | SDIR=${PWD} 11 | 12 | if [ -d "crypto-config" ]; then 13 | rm -rf crypto-config 14 | fi 15 | if [ ! -f /usr/local/bin/cryptogen ]; then 16 | cp $SDIR/bin/cryptogen /usr/local/bin 17 | chmod +x /usr/local/bin/cryptogen 18 | fi 19 | cryptogen generate --config=./crypto-config.yaml 20 | 21 | export FABRIC_CFG_PATH=$SDIR 22 | CHANNEL_NAME=mychannel 23 | 24 | 25 | # 判断路径是否存在 26 | function mkdirPath() { 27 | if [ ! -d channel-artifacts ]; then 28 | mkdir channel-artifacts 29 | fi 30 | } 31 | 32 | echo "#############################################################################" 33 | echo "############################## 生成创世块 ####################################" 34 | echo "#############################################################################" 35 | mkdirPath 36 | if [ ! -f /usr/local/bin/configtxgen ]; then 37 | cp $SDIR/bin/configtxgen /usr/local/bin 38 | chmod +x /usr/local/bin/configtxgen 39 | fi 40 | 41 | rm -rf ./channel-artifacts/* 42 | configtxgen -profile SampleMultiNodeEtcdRaft -channelID byfn-sys-channel -outputBlock ./channel-artifacts/orderer.genesis.block 43 | # configtxgen -profile TwoOrgsOrdererGenesis -outputBlock ./channel-artifacts/orderer.genesis.block 44 | configtxgen -profile TwoOrgsChannel -outputCreateChannelTx ./channel-artifacts/mychannel.tx -channelID $CHANNEL_NAME 45 | configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org1MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org1MSP 46 | configtxgen -profile TwoOrgsChannel -outputAnchorPeersUpdate ./channel-artifacts/Org2MSPanchors.tx -channelID $CHANNEL_NAME -asOrg Org2MSP 47 | 48 | echo "################################ 创世块生成 成功###############################" 49 | 50 | # 恢复之前的路径 51 | cd $ININT_PATH 52 | 53 | exit 0 54 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/scripts/official.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | set -e 7 | 8 | CAFILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer1.example.com/tls/ca.crt 9 | CHANNEL_NAME=mychannel 10 | 11 | function operate() { 12 | echo "=================start test offical cc==================" 13 | echo "============ peer channel create:peer channel create -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx --tls true --cafile $CAFILE" 14 | peer channel create -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/mychannel.tx --tls true --cafile $CAFILE 15 | echo "============ peer channel join -b mychannel.block" 16 | peer channel join -b mychannel.block 17 | sleep 2 18 | echo "============ peer channel update -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile $CAFILE" 19 | peer channel update -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile $CAFILE 20 | sleep 2 21 | 22 | echo "============ 切换到Org2MSP" 23 | CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt 24 | CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.key 25 | CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 26 | CORE_PEER_ADDRESS=peer0.org2.example.com:8051 27 | CORE_PEER_LOCALMSPID=Org2MSP 28 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 29 | 30 | echo "============ peer channel join -b mychannel.block" 31 | peer channel join -b mychannel.block 32 | sleep 2 33 | echo "============ peer channel update -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org1MSPanchors.tx --tls --cafile $CAFILE" 34 | peer channel update -o orderer1.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/Org2MSPanchors.tx --tls --cafile $CAFILE 35 | 36 | sleep 2 37 | echo "============ 切换到Org1MSP" 38 | CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.crt 39 | CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/server.key 40 | CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt 41 | CORE_PEER_ADDRESS=peer0.org1.example.com:7051 42 | CORE_PEER_LOCALMSPID=Org1MSP 43 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp 44 | echo "============ peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/" 45 | peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/ 46 | 47 | echo "============ peer chaincode instantiate -o orderer1.example.com:7050 --tls --cafile $CAFILE -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}'" 48 | peer chaincode instantiate -o orderer1.example.com:7050 --tls --cafile $CAFILE -C $CHANNEL_NAME -n mycc -v 1.0 -c '{"Args":["init","a", "100", "b","200"]}' -P "OR ('Org1MSP.peer','Org2MSP.peer')" 49 | sleep 5 50 | 51 | echo "============ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'" 52 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 53 | 54 | echo "============ peer chaincode invoke -o orderer1.example.com:7050 --tls true --cafile $CAFILE -C $CHANNEL_NAME -n mycc" 55 | peer chaincode invoke -o orderer1.example.com:7050 --tls true --cafile $CAFILE -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' 56 | sleep 5 57 | echo "============ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'" 58 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 59 | 60 | 61 | echo "============ 切换到Org2MSP" 62 | CORE_PEER_TLS_CERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.crt 63 | CORE_PEER_TLS_KEY_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/server.key 64 | CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt 65 | CORE_PEER_ADDRESS=peer0.org2.example.com:8051 66 | CORE_PEER_LOCALMSPID=Org2MSP 67 | CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org2.example.com/users/Admin@org2.example.com/msp 68 | 69 | echo "============ peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/" 70 | peer chaincode install -n mycc -v 1.0 -p github.com/chaincode/chaincode_example02/go/ 71 | 72 | echo "============ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'" 73 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 74 | 75 | echo "============ peer chaincode invoke -o orderer1.example.com:7050 --tls true --cafile $CAFILE -C $CHANNEL_NAME -n mycc" 76 | peer chaincode invoke -o orderer1.example.com:7050 --tls true --cafile $CAFILE -C $CHANNEL_NAME -n mycc -c '{"Args":["invoke","a","b","10"]}' 77 | sleep 5 78 | echo "============ peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}'" 79 | peer chaincode query -C $CHANNEL_NAME -n mycc -c '{"Args":["query","a"]}' 80 | 81 | echo "=================test offical cc down==================" 82 | } 83 | operate 84 | exit 0 85 | -------------------------------------------------------------------------------- /food-traceability/fabric_raft/updown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright IBM Corp All Rights Reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | set -e 8 | 9 | function networkUp() { 10 | bash ./genesis.sh 11 | sleep 1 12 | echo "start docker-compose up==================" 13 | docker-compose -f docker-compose-cli.yaml up -d 14 | 15 | sleep 3 16 | docker ps -a 17 | # docker exec cli scripts/poor.sh 18 | } 19 | 20 | function networkDown() { 21 | echo "start docker-compose down==================" 22 | docker-compose -f docker-compose-cli.yaml down --volumes --remove-orphan 23 | 24 | sleep 3 25 | docker ps -a 26 | 27 | } 28 | 29 | function printHelp() { 30 | echo "./updown.sh [command]" 31 | echo "up ---启动网络" 32 | echo "down ---关闭网络" 33 | echo "restart ---重启网络" 34 | echo "-----------------------" 35 | } 36 | 37 | MODE=$1 38 | 39 | if [ "${MODE}" == "up" ]; then 40 | networkUp 41 | elif [ "${MODE}" == "down" ]; then ## Clear the network 42 | networkDown 43 | elif [ "${MODE}" == "restart" ]; then ## Restart the network 44 | networkDown 45 | networkUp 46 | else 47 | printHelp 48 | exit 1 49 | fi 50 | --------------------------------------------------------------------------------