diff --git a/packages/connection-chain/LICENSE b/packages/connection-chain/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/packages/connection-chain/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/connection-chain/README.md b/packages/connection-chain/README.md index 647f8a6ac9..a7b5ee9a30 100644 --- a/packages/connection-chain/README.md +++ b/packages/connection-chain/README.md @@ -1,12 +1,41 @@ -Please commit here your ConnectionChain code as-is as a first step. -If possible, use the commit message -`feat(connection-chain) adding source code in bulk` +# ConnectionChain sample -The commit message structure that we intend to use is the conventional commits: -https://www.conventionalcommits.org/en/v1.0.0/ -This will help with automation of releases and their change logs which we do not -intend to manually write for every release (we intend to release often, at regular intervals) +This distribution allows you to try Fujitsu's security technology to safely connect blockchains, named "ConnectionChain," on your local PC. -Feel free to delete this README file at the time when committing the ConnectionChain code. +Original: FUJITSU ConnectionChain sample (v0.9.0) +- https://github.com/FujitsuLaboratories/ConnectionChain-sample/tree/v0.9.0 -Please ensure that the commit is signed off in git. +## Features + +ConnectionChain enables value exchange of different blockchain ledgers and provides interoperability of tightly related blockchain ledgers as service business logic. +One of the aims of ConnectionChain is transferring digital assets between multiple blockchains. + +ConnectionChain is implemented to automate value exchange transactions between multiple blockchains by using an extended version of smart contract. +Users of ConnectionChain can get the benefits of safely automating exchanges of different value only by implementing a few codes to adapt to other blockchains. + +This distribution contains a functional subset of ConnectionChain and some sample codes for transferring digital assets between multiple blockchains. +This distribution allows you to try ConnectionChain on docker containers for evaluation purposes. + +## Installation + +You should follow the instructions described in [Getting started](/environment/README.md) to configure the runtime environment of demonstration programs. + +## Usage + +After installation above, you can run the sample version of the extended smart contract +which allows you to transfer some digital asset from your wallet on one blockchain (called "end-chain 1") +to the destination wallet on another blockchain (called "end-chain 2"). + +In this sample distribution, the end-chain 1 and the end-chain 2 are composed of two privately managed Ethereum blockchains. + +## References +1.[PRESS RELEASE on November 15, 2017: *"Fujitsu Develops Security Technology to Safely Connect Blockchains"*](https://www.fujitsu.com/global/about/resources/news/press-releases/2017/1115-01.html) + +2.[FUJITSU JOURNAL: *"New Blockchain Technologies to Support Secure Transactions Across Virtual Currencies"*](https://journal.jp.fujitsu.com/en/2018/01/24/01/) + +## Contact + +[ConnectionChain-sample@ml.labs.fujitsu.com ](mailto:ConnectionChain-sample@ml.labs.fujitsu.com) + +## License +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](./LICENSE) file. diff --git a/packages/connection-chain/environment/README.md b/packages/connection-chain/environment/README.md new file mode 100644 index 0000000000..75ec407b45 --- /dev/null +++ b/packages/connection-chain/environment/README.md @@ -0,0 +1,733 @@ +# Getting Started + +This instruction describes the following: +- how to setup the runtime environment of ConnectionChain and end-chains +- how to use the demo system of asset-transfer. + +**NOTE**: In this document, the term "end-chain" describes a blockchain which is connected by ConnectionChain. +In the demo system, ConnectionChain connects two end-chains, called the end-chain 1 and the end-chain 2. + +## Configuration modules + +The runtime environment can be configured with either of the following modules. +- ```base```: + This is the base unit of ConnectionChain which does not include end-chains. + If you would like to use ConnectionChain for other purposes than the demo system in this repository, + you must prepare two end-chains and customize the connecters for the end-chains. + +- ```sample```: + This is a demo system unit which performs as a sample application of asset-transfer from one end-chain to another end-chain via ConnectionChain. + This module must be used after activating the base module. This is composed by a customized version of ConnectionChain for the demo system (on ```sample/cc_env```, whose setting files exist on ```base/cc_env```) and two privately managed Ethereum (on ```sample/ec_env```). + +## Prerequisites + +Before you begin, you need to check that you have all the prerequisites installed as follows +- Host system: CentOS7 (recommended) +- Docker (v17.06.2-ce or greater) +- Docker-compose (v1.14.0 or greater) +- Execution files of Hyperledger Fabric (v1.0.4) + - Download hyperledger-fabric-linux-amd-1.0.4.tar.gz + - https://nexus.hyperledger.org/content/repositories/releases/org/hyperledger/fabric/hyperledger-fabric/linux-amd64-1.0.4/hyperledger-fabric-linux-amd64-1.0.4.tar.gz + - Put the above one in ```base/cc_env``` directory + - Extract archived contents of hyperledger-fabric-linux-amd-1.0.4.tar.gz at base/cc_env directory. + ~~~ + $ cd ConnectionChain-sample/environment/base/cc_env + $ tar -zxvf hyperledger-fabric-linux-amd64-1.0.4.tar.gz + ~~~ + +## Setup steps for the demo system + +Please execute the following three steps for starting the demo system in order: +- [Step 1: Starting two end-chains](#Step-1-Starting-two-end-chains): + - execute the code on ```sample/ec_env```. +- [Step 2: Initial setup of ConnectionChain](#Step-2-Initial-setup-of-ConnectionChain): + - execute the code on ```base/cc_env```. +- [Step 3: Starting ConnectionChain and executing a demo system of asset-transfer](#Step-3-Starting-ConnectionChain-and-executing-a-demo-system-of-asset-transfer): + - execute the code on ```sample/cc_env```. + +### Structure of the demo system + +- The demo system is composed of the following docker containers and networks: + + |Container|Role|Network|Port| + |:---|:---|:---|:---| + |peer0,peer1,peer2,peer3, orderer, ca|The peer nodes of ConnectionChain|ccnet|| + |rest-server|The server of ConnectionChain including the relaying application (called CoreAPI) of operations to end-chains|ccnet|| + |app-server|The server of ConnectionChain including the application for calling CoreAPI|ccnet|3031| + |ec1-adapter, ec1-connector|The servers of ConnectionChain for connecting to the end-chain 1|ccnet(ec1-adapter), ec1net(ec1-connector)|5040(ec1-connector)| + |ec2-adapter, ec2-connector|The servers of ConnectionChain for connecting to the end-chain 2|ccnet(ec2-adapter), ec2net(ec2-connector)|6040(ec2-connector)| + |geth1|The peer node of the end-chain 1|ec1net|| + |geth2|The peer node of the end-chain 2|ec2net|| + +- The docker network topology is as follows: + + +### Remark for the setup +- The sentence of ```/* NOTE: ... */``` means only the comments for the following description. +If you try with this description, please delete the sentence ```/* NOTE: ... */```. +- If you run this demo system behind the firewall, please set the environmental variable $HTTP_PROXY as your proxy. + ~~~ + export HTTP_PROXY=http://id:passwd@your.proxy.com:8080 + ~~~ + +## Step 1: Starting two end-chains +First, please change your working directory: +``` +$ cd ConnectionChain-sample/environment/sample/ec_env +``` + +### Starting the runtime environment +- Account creation: + - Run init-account.sh. + ~~~ + $ ./init-account.sh + ~~~ + - Please keep in your memo the addresses (ec1-accounts[0],[1],..,[4] and ec2-accounts[0],[1],..,[4]), + - These addresses can be examined later, but both ec1's and ec2's accounts[0] are required at least at this time. + - From now on, the Address value should be replaced with your output. + - Sample output + ~~~ + make-account-ec1-accounts[0] + Creating network "ecenv_default" with the default driver + INFO [05-29|08:33:35.658] Maximum peer count ETH=25 LES=0 total=25 + Address: {782646267c64d536983a64af9d9a5ab80e036989} + + make-account-ec1-accounts[1] + INFO [05-29|08:33:42.143] Maximum peer count ETH=25 LES=0 total=25 + Address: {38f6d41b35d1af26865a0c13d41e8aa342e62e61} + + make-account-ec1-accounts[2] + INFO [05-29|08:33:48.534] Maximum peer count ETH=25 LES=0 total=25 + Address: {895b383457a714e051357dfc36bb3b6ddf84f01f} + + make-account-ec1-accounts[3] + INFO [05-29|08:33:54.731] Maximum peer count ETH=25 LES=0 total=25 + Address: {caf99b30857e0d29cd866e27fb39b2e7d2b2dc17} + + make-account-ec1-accounts[4] + INFO [05-29|08:34:01.282] Maximum peer count ETH=25 LES=0 total=25 + Address: {1b75166f65a852216306af320783e4b22986d3e3} + + make-account-ec2-accounts[0] + INFO [05-29|08:34:08.021] Maximum peer count ETH=25 LES=0 total=25 + Address: {8f2244f75a4c53684c5827ec19615dc89c2ad21c} + + make-account-ec2-accounts[1] + INFO [05-29|08:34:14.414] Maximum peer count ETH=25 LES=0 total=25 + Address: {add19019ee1ea604b3fcb55a11b97d0fc81cc221} + + make-account-ec2-accounts[2] + INFO [05-29|08:34:21.292] Maximum peer count ETH=25 LES=0 total=25 + Address: {ab66982e4eb732f0e17c56586e530f94ee9411ce} + + make-account-ec2-accounts[3] + INFO [05-29|08:34:27.947] Maximum peer count ETH=25 LES=0 total=25 + Address: {4809b6329ef15bcd1b5b730e0f148ae751cfd9f6} + + make-account-ec2-accounts[4] + INFO [05-29|08:34:34.360] Maximum peer count ETH=25 LES=0 total=25 + Address: {421c9db39b64575c511f94990acfd4394dd5f1c3} + ~~~ + +- Editing initial block information: + - Change the "ADDRESS" in genesis/genesis-ec1.json to the address for ec1-accounts[0]. + - Sample output: + ~~~ + "alloc" : { + "ADDRESS": + {"balance":"100000000000000000000000000"} + }, + + | + v + + "alloc" : { + "782646267c64d536983a64af9d9a5ab80e036989": /*NOTE: modified */ + {"balance":"100000000000000000000000000"} + }, + ~~~ + Similarly, change the "ADDRESS" in genesis/genesis-ec2.json to the address for ec2-accounts[0]. + +- Initializing end-chains: + - Run init-chain.sh. + ~~~ + $ ./init-chain.sh + ~~~ +- Launching Docker containers: + - Run up.sh. + ~~~ + $ ./up.sh + ~~~ + +If you want to stop, restart, initialize the environment, please refer to [**Appendix A.1**](#A.1:-Stop,-Restart,-Initialize-two-endchains). + + +## Step 2: Initial setup of ConnectionChain + +### 2.1: Configure settings + +- Changing your working directory: + ~~~ + $ cd ConnectionChain-sample/environment/base/cc_env + ~~~ + +- Connection to the interworking node (adapter) setting: +Replace "**xx.xx.xx.xx**" strings in following files with actual IP address of deployed host. + - base/cc_env/servers/cooperation/coreSide/ec1_adapter/config/default.js + - base/cc_env/servers/cooperation/coreSide/ec2_adapter/config/default.js + +- Proxy setting: +When you are behind the firewall, you must do the following before starting the runtime environment. + - Edit following Dockerfiles to enable HTTP connection. + - base/cc_env/servers/restserver/build/Dockerfile + - base/cc_env/servers/appserver/build/Dockerfile + - base/cc_env/servers/cooperation/coreSide/build/Dockerfile + - base/cc_env/servers/cooperation/ecSide/build/Dockerfile + - NOTE: Uncomment the following lines in each Dockerfile. + ~~~ + #ENV http_proxy $HTTP_PROXY + #ENV https_proxy $HTTP_PROXY + #ENV HTTP_PROXY $HTTP_PROXY + #ENV HTTPS_PROXY $HTTP_PROXY + #ENV NO_PROXY "rest-server,ec1-connector,ec2-connector,geth1,geth2" + #RUN npm -g config set proxy $HTTP_PROXY + ~~~ + - Edit following shells to enable HTTP connection. + - base/cc_env/cc_setup_base_only.sh + - sample/cc_env/cc_setup_base+sample.sh + - NOTE: Uncomment the following lines according to your proxy. + ~~~ + PROXY="" + #PROXY=http://your.proxy.com:8080 + #PROXY=http://id:passwd@your.proxy.com:8080 + ~~~ + If your proxy is an no-authentication proxy, please uncomment the second line and comment out other lines. + If your proxy is an authentication proxy, please uncomment the third line and comment out other lines. + Please replace ```your.proxy.com:8080``` and ```id:passwd``` with the parameters of your proxy. + +- Firewall setting (if your OS is CentOS7): + - Execute the following command on your console to open the ports used by the demo system. + ~~~ + $ sudo firewall-cmd --zone=public --add-port=3031/tcp + $ sudo firewall-cmd --zone=public --add-port=5040/tcp + $ sudo firewall-cmd --zone=public --add-port=6040/tcp + ~~~ + - NOTE: The port 3031 is used by the app-server and the ports 5040 and 6040 are used by the ec1-connector and ec2-connector. + +### 2.2: Starting the runtime environment + +- Changing your working directory: + ~~~ + $ cd ConnectionChain-sample/environment/sample/cc_env + ~~~ + +- Starting ConnectionChain: + Run construction shell in sample/cc_env directory with "up" mode. + ~~~ + $ ./cc_setup_base+sample.sh -m up -p $HTTP_PROXY + ~~~ + If you are not in proxy environment, -p option is not needed. + Please wait until the huge text "END" appears in your console for a few minutes. After that, please exit by Ctrl + C. + ~~~ + _____ _ _ ____ + | ____| | \ | | | _ \ + | _| | \| | | | | | + | |___ | |\ | | |_| | + |_____| |_| \_| |____/ + + ~~~ + + +- Starting application servers: + Get the console of running docker containers with docker exec command, and run server manually. + It is recommended to follow the instructions at [**Use of screen command (Appendix A.3)**](#A.3:-Use-of-screen-command) for avoiding termination of the service when the console is closed. + 1. The connector for the end-chain 1 + ~~~ + $ docker exec -it ec1-connector bash + # cd connector + # export NO_PROXY="geth1" + # npm start + ~~~ + 2. The adaptor for the end-chain 1 + ~~~ + $ docker exec -it ec1-adapter bash + # cd adapter + # export NO_PROXY="ec1-connector" + # npm start + ~~~ + 3. The connector for the end-chain 2 + ~~~ + $ docker exec -it ec2-connector bash + # cd connector + # export NO_PROXY="geth2" + # npm start + ~~~ + 4. The adaptor for the end-chain 2 + ~~~ + $ docker exec -it ec2-adapter bash + # cd adapter + # export NO_PROXY="ec2-connector" + # npm start + ~~~ + 5. The REST server + ~~~ + $ docker exec -it rest-server bash + # cd coreapi + # sudo npm start + ~~~ + 6. The APP server + ~~~ + $ docker exec -it app-server bash + ~~~ + At the first run, following command must be issued before executing "npm start" to register administrator account on mongodb. + ~~~ + # mongo + > db.ccusers.insertOne({"userID":"dbadmin", "password":"b7124a2f3abb7eb7d158e1843b134cf46ee6e8aca2db714d700bed8262c1d6b8", "userName":"DBAdmin", "role":"admin"}) + > exit + ~~~ + - Note that the value "b71..." in above example is hashed value of "dbadminpass" by SHA256. + + ~~~ + # cd serviceapi + # export NO_PROXY="rest-server" + # npm start + ~~~ + + If you want to stop, restart, initialize the environment, please refer to [**Appendix A.2**](#A.2:-Stop,-Restart-connectionChain). + + +- Registration of connection information for end-chains: +Execute REST API calls to APP server using REST-client such as [POSTMAN](https://www.getpostman.com/). + 1. Login + Login as administrator user. + ~~~ + - URL: http://:3031/login/ + - Method: POST + - Header: Content-Type "Application/json" + - Body: + { + "username":"dbadmin", + "password":"dbadminpass" + } + - Response body: + { + "userName":"dbadmin", + "role":"admin" + } + ~~~ + + 2. Register connection information for end-chains + Register connection information of the end-chain 1 and the end-chain 2 to ConnectionChain. (You can specify any chainID and chainName.) + ~~~ + - URL: http://:3031/endchains/ + - Method: POST + - Header: Content-Type "Application/json" + - Body: + (1) end chain 1 + { + "chainID":"EndChain1", + "chainName":"EC1", + "adapterUrl":"https://ec1-adapter:5030" + } + (2) end chain 2 + { + "chainID":"EndChain2", + "chainName":"EC2", + "adapterUrl":"https://ec2-adapter:6030" + } + - Response body(example): + { + "chainID": "EndChain1" + } + ~~~ + + +## Step 3: Starting ConnectionChain and executing a demo system of asset-transfer + +### 3.1: Overview of this sample +- Two end-chains should be configured with accounts[1] as an exchanger account, accounts[2] as an escrow account, +and accounts[3] and accounts[4] as user accounts. +- ConnectionChain executes asset-transfer between the accounts on the different end-chains as the following steps: + 1. ConnectionChain executes asset-transfer from the user account to the escrow account on the end-chain 1 (escrow process). + 1. ConnectionChain executes asset-transfer from the exchanger account to the user account on the end-chain 2 (credit process). + 1. ConnectionChain executes asset-transfer from the escrow account to the exchanger account on the end-chain 1 (freezing process). + +- When the balance of the exchanger account is not sufficient, ConnectionChain will refund the asset on the escrow account. + +### 3.2: Register end-chain specific information + +#### Register using registration API +Execute REST API calls to APP server using REST-client such as [POSTMAN](https://www.getpostman.com/). +1. Login: + Login as the administrator. + This step can be omitted if you already logged in as the administrator. + ~~~ + - URL: http://:3031/login/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + { + "username":"dbadmin", + "password":"dbadminpass" + } + ~~~ + +2. Register user account: + Create a user account which does not have any privileges. + You man change any value of parameters except 'role'. + If you change it, please replace the value of it in the following descriptions. + ~~~ + - URL: http://:3031/ccusers/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + (1) user account 1 + { + "userID":"userX", + "password":"userXpass", + "userName":"UserX", + "role":"user" + } + (2) user account 2 + { + "userID":"userY", + "password":"userYpass", + "userName":"UserY", + "role":"user" + } + - Response body (example): + { + "userID":"userX" + } + ~~~ + +3. Register account information on end-chains: + - UserX uses accounts[3] on both the end-chain 1 and the end-chain 2, + UserY uses accounts[4] on both the end-chain 1 and the end-chain 2. + - NOTE: you can check the account address by following instructions for checking balance of accounts found in [Preparation (in 3.3 Test run)](#33-test-run). + ~~~ + - URL: http://:3031/ecaccounts/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + (1) UserX uses ec1-accounts[3] + { + "userID": "userX", + "chainID": "EndChain1", + "accountID": "
", + "alias": "EC1-Account[3]" + } + (2) UserY uses ec1-accounts[4] + { + "userID": "userY", + "chainID": "EndChain1", + "accountID": "
", + "alias": "EC1-Account[4]" + } + (3) UserX uses ec2-accounts[3] + { + "userID": "userX", + "chainID": "EndChain2", + "accountID": "
", + "alias": "EC2-account[3]" + } + (4) UserY uses ec2-accounts[4] + { + "userID": "userY", + "chainID": "EndChain2", + "accountID": "
", + "alias": "EC2-account[4]" + } + - Response body (example): + { + "ECAccountID": "0" + } + ~~~ + +4. Register asset-transfer rule settings: + Register asset-transfer rules between the end-chain 1 and the end-chain 2. + You can set any conversion rate value as you wish (unit percentage). + asset-transfer results two times on from the end-chain 1 to the end-chain 2 on exchange in the following example. + ~~~ + - URL: http://:3031/rules/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + (1) set rate from the end-chain 1 to the end-chain 2 + { + "ruleName":"EC1 => EC2(200%)", + "fromChain": + { + "chainID":"EndChain1", + "settlementAccountID":"
", + "escrowAccountID":"
" + }, + "toChain": + { + "chainID":"EndChain2", + "settlementAccountID":"
" + }, + "rule":"200" + } + (2) set rate from the end-chain 2 to the end-chain 1 + { + "ruleName":"EC2 => EC1(50%)", + "fromChain": + { + "chainID":"EndChain2", + "settlementAccountID":"
", + "escrowAccountID":"
" + }, + "toChain": + { + "chainID":"EndChain1", + "settlementAccountID":"
" + }, + "rule":"50" + } + - Response body(example): + "ruleID" will be sequentially assigned.(0,1,2,...) + { + "ruleID":"0" + } + ~~~ + +### 3.3 Test run + +#### Preparation + +You must execute following operations only on first time. +Get console on connector container, and deposit initial balance. +1. get console ec1-connector: + ~~~ + $ docker exec -it ec1-connector bash + # cd connector/lib/dependent/ + ~~~ +2. check account balance: + ~~~ + # node geth_getAccounts.js + ~~~ + Check if balance of accounts[0] is not zero, and balance of other accounts are zero. +3. transfer asset from accounts[0] to other accounts: + ~~~ + # node geth_sendTransaction.js + ~~~ +4. check balance of accounts again: + ~~~ + # node geth_getAccounts.js + ~~~ + You will see new balance of accounts[1] is 100000, and balance of [3][4] is 1000. +5. exit console on ec1-connector: + ~~~ + # exit + ~~~ +6. execute same operation at console of ec2-connector: + ~~~ + $ docker exec -it ec2-connector bash + # cd connector/lib/dependent/ + # node geth_sendTransaction.js + # node geth_getAccounts.js + # exit + ~~~ + +#### Execute REST API calls +Execute REST-API call at APP server using REST-Client such as [POSTMAN](https://www.getpostman.com/). +1. Login: + Login as any non-privileged user. + ~~~ + - URL: http://:3031/login/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + { + "username":"userX", + "password":"userXpass" + } + - Response body: + { + "userName": "UserX", + "role": "user" + } + ~~~ + +2. Execute the extended smart contract for asset-transfer between the accounts on the different end-chains: + You must specify the required parameters ("ruleID", "chainID", "accountID" of from/to accounts, and "asset"). + ~~~ + - URL: http://:3031/transfers/ + - Method: POST + - Header: Content-Type "application/json" + - Body: + { + "ruleID": "", + "fromChain": { + "chainID": "EndChain1", + "accountID": "
", + "asset":"50" /*NOTE: do not exceed current balance of account */ + }, + "toChain": { + "chainID": "EndChain2", + "accountID": "
" + } + } + - Response body(example): + txID(transaction ID at ConnectionChain) will be returned. + { + "txID":"20190530T072350759_userX" + } + ~~~ + + +#### Check the execution result of the extended smart contract +You can check the execution result of the extended smart contract after some time later. + +1. Obtain transaction record from ConnectionChain: + - Execute REST-API call with REST-Client + ~~~ + - URL: http://:3031/transfers/20190530T072350759_userX /*NOTE:replace with actual transaction ID of ConnectionChain */ + - Method: GET + ~~~ + - Example of the result: + ~~~ + { + "id": "20190530T072350759_userX", + "userID": "userX", + "ruleID": "0", + "fromChain": { + "chainID": "EndChain1", + "accountID": "caf99b30857e0d29cd866e27fb39b2e7d2b2dc17", + "assetType": "number", + "asset": "50", + "escrowAccountID": "895b383457a714e051357dfc36bb3b6ddf84f01f", + "settlementAccountID": "38f6d41b35d1af26865a0c13d41e8aa342e62e61", + "escrowEvID": "0xfe7f98095dbf1ddfd893f55a0265491e0463cf0d3fd32754c5751d94c7fc17de", + "settlementEvID": "0xba81e3547b5a380905aa5f03872d120db4f0d8a408ac0fa612437daf1e028e54" + }, + "toChain": { + "chainID": "EndChain2", + "accountID": "421c9db39b64575c511f94990acfd4394dd5f1c3", + "assetType": "number", + "asset": "100", + "settlementAccountID": "add19019ee1ea604b3fcb55a11b97d0fc81cc221", + "paymentEvID": "0x1864121155c4a0656412d3583e727b7c5f5177671ffee56e9766c14ed59c12fd" + }, + "progress": "complete", + "timestamps": { + "create": "20190530T072350", + "requestMargin": "20190530T072353", + "fixedMargin": "20190530T072358", + "requestCredit": "20190530T072401", + "fixedCredit": "20190530T072406", + "requestFreeze": "20190530T072408", + "fixedFreeze": "20190530T072418" + } + } + ~~~ + + - If transaction ID was omitted, JSON array of all transactions will be returned. + ~~~ + - URL: http://:3031/transfers/ + ~~~ + - note: non-privilege user can refer transactions which is issued by themselves. + The administrator can refer all transactions. + For example, assume UserX issued transaction-1, and UserY issued transaction-2, then UserX will get only transaction-1, and UserY will get only transaction-2. But administrator will get both. + +2. You may check if asset-transfer on the end-chains with following commands. + Execute node.js script for checking balance of accounts. + - Check subtracted balance on the end-chain 1 + You will see subtracted balance on the end-chain 1 (from blockchain). + ~~~ + $ docker exec -it ec1-connector bash + # cd connector/lib/dependent/ + # node geth_getAccounts.js + ~~~ + - Check gained balance on the end-chain 2 + You will see gained balance on the end-chain 2 (to blockchain). + ~~~ + $ docker exec -it ec2-connector bash + # cd connector/lib/dependent/ + # node geth_getAccounts.js + ~~~ + +## Appendix + +### A.1: Stop, Restart, Initialize two-endchains +- First, please change your working directory: + ~~~ + $ cd ConnectionChain-sample/environment/sample/ec_env + ~~~ + +#### A.1.1: Stop the runtime environment +- Stopping Docker Containers: + - Run down.sh. + ~~~ + $ ./down.sh + ~~~ + - Block data and private keys are not deleted. + +#### A.1.2: Restart the runtime environment +- Launching Docker containers: + - Run up.sh. + ~~~ + $ ./up.sh + ~~~ + +#### A.1.3: Initialize the runtime environment +- Stopping docker containers and deleting various data: + - Run delete.sh. + ~~~ + $ ./delete.sh + ~~~ + - Block data, private keys, etc. are also deleted collectively. + + +### A.2: Stop, Restart ConnectionChain +- First, please change your working directory: + ~~~ + $ cd ConnectionChain-sample/environment/sample/cc_env + ~~~ + +#### A.2.1: Stop runtime environment +- Stopping Docker Containers + - Run cc_setup_base+sample.sh with "down" mode. + ~~~ + $ ./cc_setup_base+sample.sh -m down + ~~~ + +#### A.2.2: Restart runtime environment +- Launching Docker containers + - Run cc_setup_base+sample.sh with "restart" mode. + ~~~ + $ ./cc_setup_base+sample.sh -m restart + ~~~ + + +### A-3: Use of screen command +- It is needed to leave console's tty opened when the application server is started from the console. +It might cause problems on checking console logs or stopping service from PID. +You can solve the problem with screen command which allows you to run "npm start" on virtual console. + ~~~ + $ docker exec -it < container name > Bash + # cd + # screen + - It comes off with ENTER though information on the screen command is output + here. + # sudo npm start + ~~~ +- You can exit from the virtual console with key combination ```Ctrl-a d```. +The tty on the console will be maintained after the exit. +Check the status of the virtual console with following command. + ~~~ + # screen -ls + There are screens on: + 82.tty.c04fc474d2a5 (05/17/18 16:13:28) (Detached) + 1 Sockets in /var/run/screen/S-root. + ~~~ +- You may execute following command to return the virtual terminal. + ~~~ + # screen -r + ~~~ +- You must specify targeted PID after "-r" when "screen -ls" showed multiple "Detached" PID. +(PID can be omitted when the result was single "Detached".) +- The status of virtual console can be shown as "dead??" if you restart docker containers. +You can remove such virtual consoles with the command "screen -wipe". + diff --git a/packages/connection-chain/environment/base/cc_env/cc_setup_base_only.sh b/packages/connection-chain/environment/base/cc_env/cc_setup_base_only.sh new file mode 100755 index 0000000000..ef73655a34 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/cc_setup_base_only.sh @@ -0,0 +1,296 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# prepending $PWD/../bin to PATH to ensure we are picking up the correct binaries +# this may be commented out to resolve installed version of tools if desired +export PATH=${PWD}/bin:${PWD}:$PATH +export FABRIC_CFG_PATH=${PWD}/yaml-files + +# Print the usage message +function printHelp () { + echo "Usage: " + echo " cc_setup_base_only.sh -m up|down|restart|generate [-t ] [-d ] [-p ]" + echo " cc_setup_base_only.sh -h|--help (print this message)" + echo " -m - one of 'up', 'down', 'restart' or 'generate'" + echo " - 'up' - bring up the network with docker-compose up" + echo " - 'down' - clear the network with docker-compose down" + echo " - 'restart' - restart the network" + echo " - 'generate' - generate required certificates and genesis block" + echo " -t - CLI timeout duration in microseconds (defaults to 30000)" + echo " -d - delay duration in seconds (defaults to 3)" + echo " -p - specify which proxy use (defaults to blank)" + echo + echo "Taking all defaults:" + echo " cc_setup_base_only.sh -m up" + echo " cc_setup_base_only.sh -m down" +} + +# Ask user for confirmation to proceed +function askProceed () { + read -p "Continue (y/n)? " ans + case "$ans" in + y|Y ) + echo "proceeding ..." + ;; + n|N ) + echo "exiting..." + exit 1 + ;; + * ) + echo "invalid response" + askProceed + ;; + esac +} + +# Generate the needed certificates, the genesis block and start the network. +function networkUp () { + docker network create ccnet + docker network create ec1net + docker network create ec2net + + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY rest-server 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY app-server 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec1-adapter 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec1-connector 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec2-adapter 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec2-connector 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE up -d 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + + # generate artifacts if they don't exist + if [ ! -d "crypto-config" ]; then + generateCerts + replacePrivateKey + generateChannelArtifacts + fi + + chmod 666 $PWD/chaincode/naming_service/naming_service.go + chmod 666 $PWD/chaincode/endchain_information/endchain_information.go + + CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT DELAY=$CLI_DELAY MODE=$MODE docker-compose -f $CC_COMPOSE_FILE up -d 2>&1 + + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to start network" + docker logs -f cli + exit 1 + fi + docker logs -f cli +} + +# Obtain CONTAINER_IDS and remove them +function clearContainers () { + CONTAINER_IDS=$(docker ps -a | grep "endchain_information\|naming_service" | awk '{print $1}') + if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then + echo "---- No containers available for deletion ----" + else + docker rm -f $CONTAINER_IDS + fi +} + +# Delete any images that were generated as a part of this setup +function removeUnwantedImages() { + DOCKER_IMAGE_IDS=$(docker images | grep "endchain_information\|naming_service" | awk '{print $3}') + if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then + echo "---- No images available for deletion ----" + else + docker rmi -f $DOCKER_IMAGE_IDS + fi +} + +# Tear down running network +function networkDown () { + docker-compose -f $SV_COMPOSE_FILE down + docker-compose -f $CC_COMPOSE_FILE down + #Cleanup the chaincode containers + clearContainers + #Cleanup images + removeUnwantedImages + # remove orderer block and other channel configuration transactions and certs + sudo rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config + sudo rm -rf servers/appserver/mongodb/db/* + docker network rm ccnet + docker network rm ec1net + docker network rm ec2net +} + +# Stop running network +function networkStop () { + docker-compose -f $SV_COMPOSE_FILE stop + docker-compose -f $CC_COMPOSE_FILE stop +} + +function replacePrivateKey () { + # sed on MacOSX does not support -i flag with a null extension. We will use + # 't' for our back-up's extension and depete it at the end of the function + ARCH=`uname -s | grep Darwin` + if [ "$ARCH" == "Darwin" ]; then + OPTS="-it" + else + OPTS="-i" + fi + + # Copy the template to the file that will be modified to add the private key + cp ./yaml-files/docker-compose-base-cc-template.yaml $CC_COMPOSE_FILE + + # The next steps will replace the template's contents with the + # actual values of the private key file names for the two CAs. + CURRENT_DIR=$PWD + cd crypto-config/peerOrganizations/org1.example.com/ca/ + PRIV_KEY=$(ls *_sk) + cd $CURRENT_DIR + sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" $CC_COMPOSE_FILE + +} + +# Generates Org certs using cryptogen tool +function generateCerts (){ + which cryptogen + if [ "$?" -ne 0 ]; then + echo "cryptogen tool not found. exiting" + exit 1 + fi + echo + echo "##########################################################" + echo "##### Generate certificates using cryptogen tool #########" + echo "##########################################################" + + cryptogen generate --config=./yaml-files/crypto-config.yaml + if [ "$?" -ne 0 ]; then + echo "Failed to generate certificates..." + exit 1 + fi + echo +} + +# Generate orderer genesis block, channel configuration transaction and +# anchor peer update transactions +function generateChannelArtifacts() { + which configtxgen + if [ "$?" -ne 0 ]; then + echo "configtxgen tool not found. exiting" + exit 1 + fi + + echo "##########################################################" + echo "######### Generating Orderer Genesis block ##############" + echo "##########################################################" + # Note: For some unknown reason (at least for now) the block file can't be + # named orderer.genesis.block or the orderer will fail to launch! + configtxgen -profile OneOrgOrdererGenesis -outputBlock ./channel-artifacts/genesis.block + if [ "$?" -ne 0 ]; then + echo "Failed to generate orderer genesis block..." + exit 1 + fi + echo + echo "#################################################################" + echo "### Generating channel configuration transaction 'channel.tx' ###" + echo "#################################################################" + configtxgen -profile OneOrgChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME + if [ "$?" -ne 0 ]; then + echo "Failed to generate channel configuration transaction..." + exit 1 + fi +} + +# Obtain the OS and Architecture string that will be used to select the correct +# native binaries for your platform +OS_ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}') +# timeout duration - the duration the CLI should wait for a response from +# another container before giving up +CLI_TIMEOUT=30000 +#default for delay +CLI_DELAY=3 +# channel name defaults to "mychannel" +CHANNEL_NAME="mychannel" +# use this as the default docker-compose yaml definition +CC_COMPOSE_FILE=./yaml-files/docker-compose-base-cc.yaml +SV_COMPOSE_FILE=./yaml-files/docker-compose-base-servers.yaml +# use this as the default proxy +PROXY="" +#PROXY=http://your.proxy.com:8080 +#PROXY=http://id:passwd@your.proxy.com:8080 + +# Parse commandline args +while getopts "h?m:t:d:p:" opt; do + case "$opt" in + h|\?) + printHelp + exit 0 + ;; + m) MODE=$OPTARG + ;; + t) CLI_TIMEOUT=$OPTARG + ;; + d) CLI_DELAY=$OPTARG + ;; + p) PROXY=$OPTARG + ;; + esac +done + +# Determine whether starting, stopping, restarting or generating for announce +if [ "$MODE" == "up" ]; then + EXPMODE="Starting" + elif [ "$MODE" == "down" ]; then + EXPMODE="Stopping" + elif [ "$MODE" == "restart" ]; then + EXPMODE="Restarting" + elif [ "$MODE" == "generate" ]; then + EXPMODE="Generating certs and genesis block for" +else + printHelp + exit 1 +fi + +# Announce what was requested + + echo "${EXPMODE} with channel '${CHANNEL_NAME}' and CLI timeout of '${CLI_TIMEOUT}'" + +# ask for confirmation to proceed +askProceed + +#Create the network using docker compose +if [ "${MODE}" == "up" ]; then + networkUp + elif [ "${MODE}" == "down" ]; then ## Clear the network + networkDown + elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts + generateCerts + replacePrivateKey + generateChannelArtifacts + elif [ "${MODE}" == "restart" ]; then ## Restart the network + networkStop + networkUp +else + printHelp + exit 1 +fi diff --git a/packages/connection-chain/environment/base/cc_env/chaincode/deploy_script.sh b/packages/connection-chain/environment/base/cc_env/chaincode/deploy_script.sh new file mode 100755 index 0000000000..7026d881d9 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/chaincode/deploy_script.sh @@ -0,0 +1,186 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo +echo " ____ _____ _ ____ _____ " +echo "/ ___| |_ _| / \ | _ \ |_ _|" +echo "\___ \ | | / _ \ | |_) | | | " +echo " ___) | | | / ___ \ | _ < | | " +echo "|____/ |_| /_/ \_\ |_| \_\ |_| " +echo +echo "Build your first network (BYFN) end-to-end test" +echo +CHANNEL_NAME="$1" +DELAY="$2" +MODE="$3" +: ${CHANNEL_NAME:="mychannel"} +: ${TIMEOUT:="60"} + +COUNTER=1 +MAX_RETRY=5 +ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + +echo "Channel name : "$CHANNEL_NAME +echo "Mode : "$MODE + +# verify the result of the end-to-end test +verifyResult () { + if [ $1 -ne 0 ] ; then + echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo "========= ERROR !!! FAILED to execute End-2-End Scenario ===========" + echo + exit 1 + fi +} + +setGlobals () { + + CORE_PEER_LOCALMSPID="Org1MSP" + 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 + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + CORE_PEER_ADDRESS=peer$1.org1.example.com:7051 + + env |grep CORE +} + +createChannel() { + setGlobals 0 + + peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt + + res=$? + cat log.txt + verifyResult $res "Channel creation failed" + echo "===================== Channel \"$CHANNEL_NAME\" is created successfully ===================== " + echo +} + +## Sometimes Join takes time hence RETRY atleast for 5 times +joinWithRetry () { + peer channel join -b $CHANNEL_NAME.block >&log.txt + res=$? + cat log.txt + if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then + COUNTER=` expr $COUNTER + 1` + echo "PEER$1 failed to join the channel, Retry after 2 seconds" + sleep $DELAY + joinWithRetry $1 + else + COUNTER=1 + fi + verifyResult $res "After $MAX_RETRY attempts, PEER$ch has failed to Join the Channel" +} + +joinChannel () { + for ch in 0 1 2 3; do + setGlobals $ch + joinWithRetry $ch + echo "===================== PEER$ch joined on the channel \"$CHANNEL_NAME\" ===================== " + sleep $DELAY + echo + done +} + +installChaincode () { + CHAINCODE_NAME=$1 + PEER=$2 + setGlobals $PEER + peer chaincode install -n $CHAINCODE_NAME -v 1.0 -p github.com/hyperledger/fabric/work/chaincode/$CHAINCODE_NAME >&log.txt + res=$? + cat log.txt + verifyResult $res "Chaincode installation on remote peer PEER$PEER has Failed" + echo "===================== Chaincode is installed on remote peer PEER$PEER ===================== " + echo +} + +instantiateChaincode () { + CHAINCODE_NAME=$1 + PEER=$2 + setGlobals $PEER + # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), + # lets supply it directly as we know it using the "-o" option + peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n $CHAINCODE_NAME -v 1.0 -c '{"Args":[]}' >&log.txt + + res=$? + cat log.txt + verifyResult $res "Chaincode instantiation on PEER$PEER on channel '$CHANNEL_NAME' failed" + echo "===================== Chaincode Instantiation on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " + echo +} + +chaincodeQuery_ns () { + PEER=$1 + setGlobals $PEER + + peer chaincode query -C $CHANNEL_NAME -n naming_service -c '{"Args":["getECAccountList"]}' + + echo "===================== Query chaincode 'naming_service' on PEER$PEER on channel '$CHANNEL_NAME' is executed ===================== " + echo +} + +chaincodeQuery_ei () { + PEER=$1 + setGlobals $PEER + + peer chaincode query -C $CHANNEL_NAME -n endchain_information -c '{"Args":["getECInfoList"]}' + + echo "===================== Query chaincode 'endchain_information' on PEER$PEER on channel '$CHANNEL_NAME' is executed ===================== " + echo +} + +if [ $MODE = "up" ]; then + ## Create channel + echo "Creating channel..." + createChannel + + ## Join all the peers to the channel + echo "Having all peers join the channel..." + joinChannel + + ## Install chaincode + installChaincode naming_service 0 + installChaincode naming_service 1 + installChaincode naming_service 2 + installChaincode naming_service 3 + + installChaincode endchain_information 0 + installChaincode endchain_information 1 + installChaincode endchain_information 2 + installChaincode endchain_information 3 + + #Instantiate chaincode + echo "Instantiating chaincode" + instantiateChaincode naming_service 0 + instantiateChaincode endchain_information 0 +fi + +#Query chaincode +echo "Querying chaincode" +if [ $MODE = "restart" ]; then + chaincodeQuery_ns 0 +fi +chaincodeQuery_ns 1 +chaincodeQuery_ns 2 +chaincodeQuery_ns 3 + +if [ $MODE = "restart" ]; then + chaincodeQuery_ei 0 +fi +chaincodeQuery_ei 1 +chaincodeQuery_ei 2 +chaincodeQuery_ei 3 + +echo +echo "========= All GOOD, BYFN execution completed =========== " +echo + +echo +echo " _____ _ _ ____ " +echo "| ____| | \ | | | _ \ " +echo "| _| | \| | | | | | " +echo "| |___ | |\ | | |_| | " +echo "|_____| |_| \_| |____/ " +echo + +exit 0 diff --git a/packages/connection-chain/environment/base/cc_env/chaincode/endchain_information/endchain_information.go b/packages/connection-chain/environment/base/cc_env/chaincode/endchain_information/endchain_information.go new file mode 100644 index 0000000000..59bb8a02c4 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/chaincode/endchain_information/endchain_information.go @@ -0,0 +1,300 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * endchain_information.go + */ + +// Package +package main + +import ( + "encoding/json" + "fmt" + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// Object structure definition +// Destination end-chain information +type EndChainInfo struct { + ChainID string `json:"id"` // Chain ID + ChainName string `json:"chainName"` // Display name on the UI + AdapterUrl string `json:"adapterUrl"` // Contact URL for cooperation server(adapter) +} + +// Constant definition +// Connection destination end-chain information storage object +const EC_INFO = "ec_info" + +// Chaincode data structure definition +type EndchainInformation struct { +} + +// Chaincode initialization function +/** + * @param {shim.ChaincodeStubInterface} stub + * @return {pb.Response} Always returns successful response (nil value) +**/ +func (cc *EndchainInformation) Init(stub shim.ChaincodeStubInterface) pb.Response { + // Do nothing + return shim.Success(nil) +} + +// chaincode IF of Invoke, query +/** + * @param {shim.ChaincodeStubInterface} stub + * @return {pb.Response} +**/ +func (cc *EndchainInformation) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + switch function { +// invoke function for setting destination end-chain information + case "addECInfo": + return addECInfo(stub, args) + case "updateECInfo": + return updateECInfo(stub, args) + case "deleteECInfo": + return deleteECInfo(stub, args) +// query function for obtaiing the destination end-chain information + case "getECInfo": + return getECInfo(stub, args) + case "getECInfoList": + return getECInfoList(stub, args) + + default: + return shim.Error("Unkown operation.") + } +} + +/***** Invoke internal functions *****/ + +// Add destination end-chain information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of destination end-chain information to be added + * - args[0] : Chain ID + * - args[1] : Display name on the UI + * - args[2] : URL to contact Adapter server + * @return {pb.Response} +**/ +func addECInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if the size of args[] array is not 3 + if len(args) != 3 { + return shim.Error("[addECInfo] Incorrect number of arguments. Expecting 3") + } + + chainID := args[0] + chainName := args[1] + adapterUrl := args[2] + + // Composite key generation + key, err := stub.CreateCompositeKey(EC_INFO, []string{chainID}) + if err != nil { + return shim.Error("[addECInfo] CreateCompositeKey operation failed. " + err.Error()) + } + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[addECInfo] GetState operation failed. " + err.Error()) + } + if getBytes != nil { + // Return success with null if ID already exists + fmt.Printf("This endchain is already registered. ChainID = %s", chainID) + return shim.Success(nil) + } + + // JSON object creation + ecInfo := &EndChainInfo{chainID, chainName, adapterUrl} + jsonBytes, err := json.Marshal(ecInfo) + if err != nil { + return shim.Error("[addECInfo] Marshal operation failed. " + err.Error()) + } + // Register in World State + err = stub.PutState(key, jsonBytes) + if err != nil { + return shim.Error("[addECInfo] PutState operation failed. " + err.Error()) + } + + // Return ID on successful + return shim.Success([]byte(chainID)) +} + +// Update destination end-chain information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of destination end-chain information to update + * - args[0] : Chain ID + * - args[1] : Display name on the UI + * - args[2] : Adapter URL + * @return {pb.Response} +**/ +func updateECInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if the size of args[] array is not 3 + if len(args) != 3 { + return shim.Error("[updateECInfo] Incorrect number of arguments. Expecting 3") + } + + chainID := args[0] + chainName := args[1] + adapterUrl := args[2] + + // Composite key generation + key, err := stub.CreateCompositeKey(EC_INFO, []string{chainID}) + if err != nil { + return shim.Error("[updateECInfo] CreateCompositeKey operation failed. " + err.Error()) + } + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[updateECInfo] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Return success with null if not registered + fmt.Printf("This endchain is not yet registered. ChainID = %s", chainID) + return shim.Success(nil) + } + + // Overwrite the registered information + var ecInfo EndChainInfo + err = json.Unmarshal(getBytes, &ecInfo) + if err != nil { + return shim.Error("Error unmarshaling JSON: " + err.Error()) + } + // Do not update empty fields + if chainName != "" { + ecInfo.ChainName = chainName + } + if adapterUrl != "" { + ecInfo.AdapterUrl = adapterUrl + } + + jsonBytes, err := json.Marshal(ecInfo) + if err != nil { + return shim.Error("[updateECInfo] Marshal operation failed. " + err.Error()) + } + // Register in World State + err = stub.PutState(key, jsonBytes) + if err != nil { + return shim.Error("[updateECInfo] PutState operation failed. " + err.Error()) + } + + // Return ID on successful update + return shim.Success([]byte(chainID)) +} + +// Delete destination end-chain information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args ID of the destination end-chain information to delete + * - args[0] : Chain ID + * @return {pb.Response} +**/ +func deleteECInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if size of args[] array is not 1 + if len(args) != 1 { + return shim.Error("[deleteECInfo] Incorrect number of arguments. Expecting 1") + } + + chainID := args[0] + + // Composite key generation + key, err := stub.CreateCompositeKey(EC_INFO, []string{chainID}) + if err != nil { + return shim.Error("[deleteECInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // Remove the data from world state + err = stub.DelState(key) + if err != nil { + return shim.Error("[deleteECInfo] DelState operation failed. " + err.Error()) + } + return shim.Success(nil) +} + +/***** Internal query functions *****/ + +// Get single destination end-chain information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args ID of the destination end-chain information to get + * - args[0] : Chain ID + * @return {pb.Response} +**/ +func getECInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if size of args[] array is not 1 + if len(args) != 1 { + return shim.Error("[getECInfo] Incorrect number of arguments. Expecting 1") + } + + chainID := args[0] + + // Composite key generation + key, err := stub.CreateCompositeKey(EC_INFO, []string{chainID}) + if err != nil { + return shim.Error("[getECInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[getECInfo] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Return success with null if not registered + fmt.Printf("This endchain is not yet registered. ChainID = %s", chainID) + return shim.Success(nil) + } + // Return the retrieved content + return shim.Success(getBytes) +} + +// Get destination end-chain information list +/** + * @param {shim.ChaincodeStubInterface} stub + * @return {pb.Response} +**/ +func getECInfoList(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if size of args[] array is non-zero + if len(args) != 0 { + return shim.Error("[getECInfoList] Incorrect number of arguments. Expecting 0") + } + + // Retrieve with partial composite key (Specify object name only) + iter, err := stub.GetStateByPartialCompositeKey(EC_INFO, []string{}) + if err != nil { + return shim.Error("[getECInfoList] GetStateByPartialCompositeKey operation failed. " + err.Error()) + } + defer iter.Close() + + // Obtains sequentially from Iterator and stores in results + var results []EndChainInfo + for iter.HasNext() { + qr, err := iter.Next() + if err != nil { + return shim.Error("[getECInfoList] Iterator.Next operation failed. " + err.Error()) + } + + var info EndChainInfo + err = json.Unmarshal(qr.Value, &info) + if err != nil { + return shim.Error("[getECInfoList] Error unmarshaling JSON: " + err.Error()) + } + results = append(results, info) + } + + // Convert results to JSON format and return + marshalledResults, err := json.Marshal(results) + if err != nil { + return shim.Error("[getECInfoList] Marshal operation failed. " + err.Error()) + } + return shim.Success([]byte(marshalledResults)) +} + + +func main() { + err := shim.Start(new(EndchainInformation)) + if err != nil { + fmt.Printf("Error starting endchaininformation chaincode: %s",err) + } +} diff --git a/packages/connection-chain/environment/base/cc_env/chaincode/naming_service/naming_service.go b/packages/connection-chain/environment/base/cc_env/chaincode/naming_service/naming_service.go new file mode 100644 index 0000000000..53903a871d --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/chaincode/naming_service/naming_service.go @@ -0,0 +1,363 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * naming_service.go + */ + +// Package +package main + +import ( + "encoding/json" + "fmt" + "strconv" + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" +) + +// Object structure definition +// End-chain account information +type ECAccountInfo struct { + ECAccountID string `json:"id"` + UserID string `json:"userID"` + ChainID string `json:"chainID"` + AccountID string `json:"accountID"` + Alias string `json:"alias"` + AuthInfo string `json:"authInfo,omitempty"` +} + +// Constant definition +// End-chain accountID numbering counter +const LAST_INDEX = "last_ec_account_id" + +// Chaincode structure definition +type NamingService struct { +} + +// Chaincode initialization function +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} function + * @param {[]string} args + * @return {pb.Response} +**/ +func (cc *NamingService) Init(stub shim.ChaincodeStubInterface) pb.Response { + // Store initial value 0 only if end-chain accountID numbering counter is not set + getBytes, err := stub.GetState(LAST_INDEX) + if err != nil { + return shim.Error("[Init] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + err := stub.PutState(LAST_INDEX, []byte(strconv.Itoa(0))) + if err != nil { + return shim.Error("[Init] PutState operation failed. " + err.Error()) + } + } + + return shim.Success(nil) +} + +// Invoke, query interface in chaincode +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} function Execution function + * @param {[]string} args Arguments to pass to the function + * @return {pb.Response} +**/ +func (cc *NamingService) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + switch function { +// Invoke functions + // Add end-chain account information + case "addECAccount": + return addECAccount(stub, args) + // Update end-chain account information + case "updateECAccount": + return updateECAccount(stub, args) + // Delete end-chain account information + case "deleteECAccount": + return deleteECAccount(stub, args) +// Query functions + // Get single end-chain account information + case "getECAccount": + return getECAccount(stub, args) + // Get end-chain account information list + case "getECAccountList": + return getECAccountList(stub, args) + + default: + return shim.Error("Unkown operation.") + } +} + +/***** Invoke internal functions *****/ +// Register end-chain account information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of end-chain account information to register + * - args[0] : ID of the ConectionChain user bound to the account + * - args[1] : Chain ID of end-chain + * - args[2] : account ID + * - args[3] : Display name for account + * - args[4] : Authentication information for the asset transfer operation (Optional) + * @return {pb.Response} +**/ +func addECAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if args number is not 4 or 5 + if len(args) != 4 && len(args) != 5 { + return shim.Error("Incorrect number of arguments. Expecting 4 or 5") + } + + userID := args[0] + chainID := args[1] + accountID := args[2] + alias := args[3] + // Register empty string if no authentication parameter is specified + authinfo := "" + if len(args) == 5 { + authinfo = args[4] + } + + // Get end-chain account ID + indexBytes, err := stub.GetState(LAST_INDEX) + if err != nil { + return shim.Error("GetState operation failed. " + err.Error()) + } + indexStr := string(indexBytes) + index, err := strconv.Atoi(indexStr) + if err != nil { + return shim.Error("strconv operation failed. " + err.Error()) + } + + // Update with + 1 counter for end-chain account ID numbering + err = stub.PutState(LAST_INDEX, []byte(strconv.Itoa(index + 1))) + if err != nil { + return shim.Error("PutState operation failed. " + err.Error()) + } + + // JSON style object data creation + accountInfo := &ECAccountInfo{indexStr, userID, chainID, accountID, alias, authinfo} + jsonBytes, err := json.Marshal(accountInfo) + if err != nil { + return shim.Error("Marshal operation failed. " + err.Error()) + } + + // Register in World State + err = stub.PutState(indexStr, jsonBytes) + if err != nil { + return shim.Error("PutState operation failed. " + err.Error()) + } + // Return end-chain account ID + return shim.Success(indexBytes) +} + +// Update end-chain account information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of end-chain account information to update + * - args[0] : End-chain account ID + * - args[1] : ID of the ConnectionChain user tied to the account + * - args[2] : Chain ID of end-chain to which the account belongs + * - args[3] : ID to be specified in the API of the asset management function on end-chain + * - args[4] : Alias to use for the display on the UI + * - args[5] : Parameters to use if authentication is required for the asset transfer operation (Optional) + * @return {pb.Response} +**/ +func updateECAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if size of args[] array is not 5 or 6 + if len(args) != 5 && len(args) != 6 { + return shim.Error("Incorrect number of arguments. Expecting 5 or 6") + } + + ecAccountID := args[0] + userID := args[1] + chainID := args[2] + accountID := args[3] + alias := args[4] + authinfo := "" + if len(args) == 6 { + authinfo = args[5] + } + + // Check if any registered information exists + getBytes, err := stub.GetState(ecAccountID) + if err != nil { + return shim.Error("GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Return success with null if not registered + fmt.Printf("This account is not yet registered. ECAccountID = %s", ecAccountID) + return shim.Success(nil) + } + + // Overwrite the registered information + var accountInfo ECAccountInfo + err = json.Unmarshal(getBytes, &accountInfo) + if err != nil { + return shim.Error("Error unmarshaling JSON: " + err.Error()) + } + // Do not update fields with empty string other than authentication parameter + if userID != "" { + accountInfo.UserID = userID + } + if chainID != "" { + accountInfo.ChainID = chainID + } + if accountID != "" { + accountInfo.AccountID = accountID + } + if alias != "" { + accountInfo.Alias = alias + } + // Update authentication parameters only if specified in argument + if len(args) == 6 { + accountInfo.AuthInfo = authinfo + } + + jsonBytes, err := json.Marshal(accountInfo) + if err != nil { + return shim.Error("Marshal operation failed. " + err.Error()) + } + + // Register in World State + err = stub.PutState(ecAccountID, jsonBytes) + if err != nil { + return shim.Error("PutState operation failed. " + err.Error()) + } + // Return ID on successful update + return shim.Success([]byte(ecAccountID)) +} + +// Delete end-chain account information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args ID of the end-chain account to delete + * - args[0] : End-chain account ID + * @return {pb.Response} +**/ +func deleteECAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if size of args[] array is not 1 + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting 1") + } + + ecAccountID := args[0] + + // Remove the data from world state + err := stub.DelState(ecAccountID) + if err != nil { + return shim.Error("DelState operation failed. " + err.Error()) + } + return shim.Success(nil) +} + +/***** Internal query functions *****/ + +// Get single end-chain account information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args ID of the end-chain account to get + * - args[0] : End-chain account ID + * @return {pb.Response} +**/ +func getECAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 1 + if len(args) != 1 { + return shim.Error("Incorrect number of arguments. Expecting 1") + } + + ecAccountID := args[0] + + // Check if registered information exists + getBytes, err := stub.GetState(ecAccountID) + if err != nil { + return shim.Error("GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Returns success with null if not registered + fmt.Printf("This account is not yet registered. ECAccountID = %s", ecAccountID) + return shim.Success(nil) + } + // Return the retrieved content + return shim.Success(getBytes) +} + +// Get end-chain account information list +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args ID of end-chain account to get. + * - args[0] : ID of the ConnectionChain user associated with the account (Optional) + * - args[1] : Chain ID of end-chain to which the account belongs (Optional) + * @return {pb.Response} +**/ +func getECAccountList(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if the number of args is greater than or equal to 3 + if len(args) > 2 { + return shim.Error("Incorrect number of arguments. Expecting 2 or less") + } + + userID := "" + chainID := "" + if len(args) > 0 { + userID = args[0] + } + if len(args) > 1 { + chainID = args[1] + } + + // Get All information and then filter + iter, err := stub.GetStateByRange("", "~") + if err != nil { + return shim.Error("Unable to start iter") + } + defer iter.Close() + + // Obtains sequentially from Iterator and stores in results + var results []ECAccountInfo + for iter.HasNext() { + qr, err := iter.Next() + if err != nil { + return shim.Error("Iterator.Next operation failed. " + err.Error()) + } + // No need for "End-chain Account ID Numbering Counter" + if qr.Key == LAST_INDEX { + continue + } + + // Analyze registered end-chain account information + var info ECAccountInfo + err = json.Unmarshal(qr.Value, &info) + if err != nil { + return shim.Error("Error unmarshaling JSON: " + err.Error()) + } + + // If the number of args is greater than or equal to 1, filter by user ID + if len(args) > 0 { + if info.UserID != userID { + continue + } + // If the number of args is 2, then filter by chain ID + if len(args) == 2 { + if info.ChainID != chainID { + continue + } + } + } + results = append(results, info) + } + + // Convert results to JSON format and return + marshalledResults, err := json.Marshal(results) + if err != nil { + return shim.Error("Marshal operation failed. " + err.Error()) + } + return shim.Success([]byte(marshalledResults)) +} + +func main() { + err := shim.Start(new(NamingService)) + if err != nil { + fmt.Printf("Error starting namingservice chaincode: %s",err) + } +} diff --git a/packages/connection-chain/environment/base/cc_env/channel-artifacts/.gitkeep b/packages/connection-chain/environment/base/cc_env/channel-artifacts/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/build/Dockerfile b/packages/connection-chain/environment/base/cc_env/servers/appserver/build/Dockerfile new file mode 100644 index 0000000000..a08cfcff3e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/build/Dockerfile @@ -0,0 +1,19 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +FROM hyperledger/fabric-ccenv:x86_64-1.0.4 +#ENV http_proxy $HTTP_PROXY +#ENV https_proxy $HTTP_PROXY +#ENV HTTP_PROXY $HTTP_PROXY +#ENV HTTPS_PROXY $HTTP_PROXY +#ENV NO_PROXY "rest-server,ec1-connector,ec2-connector,geth1,geth2" +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv A15703C6 +RUN echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list +RUN apt update +RUN apt-get install -y --allow-unauthenticated mongodb-org=3.4.7 mongodb-org-server=3.4.7 mongodb-org-shell=3.4.7 mongodb-org-mongos=3.4.7 mongodb-org-tools=3.4.7 +RUN apt-get install -y screen +RUN apt-get install -y npm +#RUN npm -g config set proxy $HTTP_PROXY +RUN npm -g install n +RUN n --version +RUN n 6.9.1 +RUN npm -g install express diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/idle.sh b/packages/connection-chain/environment/base/cc_env/servers/appserver/idle.sh new file mode 100755 index 0000000000..ebc78ff136 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/idle.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo "This is a idle script (infinite loop) to keep container running." +echo "Please replace this script." + +cleanup () +{ + kill -s SIGTERM $! + exit 0 +} + +trap cleanup SIGINT SIGTERM + +while [ 1 ] +do + sleep 60 & + wait $! +done diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/mongodb/db/.gitkeep b/packages/connection-chain/environment/base/cc_env/servers/appserver/mongodb/db/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/mongodb/mongodb.config b/packages/connection-chain/environment/base/cc_env/servers/appserver/mongodb/mongodb.config new file mode 100644 index 0000000000..d06f44d790 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/mongodb/mongodb.config @@ -0,0 +1,3 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +dbpath=/opt/gopath/src/github.com/hyperledger/fabric/work/server/mongodb/db diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/app.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/app.js new file mode 100644 index 0000000000..17a8d90294 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/app.js @@ -0,0 +1,78 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * Service API Server Main +*/ + +var express = require('express'); +var path = require('path'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +var passport = require('passport'); +var session = require('express-session'); + +var login = require('./routes/common/login'); +var ccusers = require('./routes/common/ccusers'); +var ecaccounts = require('./routes/common/ecaccounts'); +var endchains = require('./routes/common/endchains'); +// Write your own additional REST API + +var ServiceAPIError = require('./lib/common/ServiceAPIError.js'); + +var app = express(); + +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(session({resave: false, saveUninitialized: false, secret: 'cookie enc key'})); +app.use(passport.initialize()); +app.use(passport.session()); + +// Login API is not subject to authentication check +app.use('/login', login); + +// Perform authentication checks for APIs other than login +app.use('/ccusers', isAuthenticated, ccusers); +app.use('/ecaccounts', isAuthenticated, ecaccounts); +app.use('/endchains', isAuthenticated, endchains); +// Write your own additional REST API + +function isAuthenticated(req, res, next){ + if (req.isAuthenticated()) { // authenticated + return next(); + } + else { // not authenticated + res.status(401).send(); + } +} + +app.get("/logout", function(req, res){ + // Just unlogin + req.logout(); + res.send('logout.'); +}); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new ServiceAPIError(404, 1000); + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + res.status(err.statusCode || 500); + res.send(err.errorBody); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/config/default.js new file mode 100644 index 0000000000..fdb664821e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/config/default.js @@ -0,0 +1,17 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // DB information to Store ConnectionChain user information + mongodb: { + url: "mongodb://localhost:27017/test" + }, + // Core API connection destination information + coreapi: { + url: "http://rest-server:3030" + } +}; \ No newline at end of file diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/HttpRequestPromise.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/HttpRequestPromise.js new file mode 100644 index 0000000000..f24804eba3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/HttpRequestPromise.js @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * HttpRequestPromise.js + */ + +/* Summary: + * A library for handling HTTP requests in the form of promises + * + * Entry Point: + * None +*/ + +// Base package dependency declaration +var request = require('request'); + +// Send Http Request +function send(options) { + return new Promise((resolve, reject) => { + request(options, function (err, response, body) { + if (err) { + return reject(err); + } + return resolve(response); + }) + }); +} + +exports.send = function(options) { + return send(options); +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIError.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIError.js new file mode 100644 index 0000000000..28fd701b31 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIError.js @@ -0,0 +1,57 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServiceAPIError.js + */ + +/* Summary: + * error response class +*/ + +// user defined error code & message +var userDefinitions = require('../userImple/UserErrorDef.js'); + +// Error code & message definition +const definitions = { +// errorCode : errorMessage + 1000 : 'Resource does not exist.', + 2000 : 'JSON format error.', + 3000 : 'Internal error.', + 4000 : 'Permission error.', + 9000 : 'Unknown error.', +// unregistered information + 1005 : 'Unregistered user ID.', +// Request parameter not specified + 2001 : 'No user ID specified.', + 2027 : 'No password specified.', + 2028 : 'No user display name specified.', + 2029 : 'No user type specified.', +// duplicate registration + 3005 : 'The user ID already registered.' +}; + + +var ServiceAPIError = class { + + constructor(statusCode, errorCode, detail) { + this.statusCode = statusCode; + this.errorCode = errorCode; + var message = userDefinitions[errorCode] || definitions[errorCode] || definitions[9000]; + if (detail != undefined) { + message = message + detail; + } + this.message = message; + var errorBody = + { + "error" : + { + "code" : errorCode, + "message" : message + } + }; + this.errorBody = errorBody; + } +} + +module.exports = ServiceAPIError; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIUtil.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIUtil.js new file mode 100644 index 0000000000..8631e15995 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/ServiceAPIUtil.js @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServiceAPIUtil.js + */ + +/* Summary: + * Utility classes for the service API +*/ + +var crypto = require("crypto"); + +var ServiceAPIUtil = class { + + /** + * Content Type Check + * @param {Obj} req: Request object + * @return{bool} true: enabled/false: disabled + **/ + static isValidContentType(req) { + var cType = req.header('Content-type'); + if (cType != undefined) { + // JSON format Check + if (cType.toLowerCase().indexOf('application/json') != -1) { + return true; + } + } + return false; + } + + /** + * Role Check + * @param {Obj} req: Request object + * @return{bool} true: Administrator/false: General User + **/ + static isAdminUser(req) { + var user = req.user; + if (user != undefined) { + if (user.role == 'admin') { + return true; + } + } + return false; + } + + /** + * hash generation + * @param {string} input: Input character + * @return{string} Hex digest with sha256 + **/ + static getHash(input) { + var sha256 = crypto.createHash('sha256'); + sha256.update(input); + var hash = sha256.digest('hex'); + return hash; + } + +} + +module.exports = ServiceAPIUtil; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/mongo.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/mongo.js new file mode 100644 index 0000000000..900c9fea2e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/common/mongo.js @@ -0,0 +1,32 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * mongo.js + */ + +/* Summary: + * library for MongoDB operations +*/ + +var config = require('config'); + +var db; +var MongoClient = require('mongodb').MongoClient; +var assert = require('assert'); + +// Connection URL +var url = config.mongodb.url; + +// Use connect method to connect to the Server +MongoClient.connect(url, function(err, mongodb) { + assert.equal(null, err); + console.log("Connected correctly to server"); + db = mongodb; +}); + +var collection = function( name ) { + return db.collection( name ); +} + +module.exports = collection; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/userImple/UserErrorDef.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/userImple/UserErrorDef.js new file mode 100644 index 0000000000..cbe8c470fd --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/lib/userImple/UserErrorDef.js @@ -0,0 +1,17 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * UserErrorDef.js + */ + +/* Summary: + * User Defined Error +*/ + +// Error code & message definition +const definitions = { +// Describe in errorCode:"errorMessage" format +}; + +module.exports = definitions; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/package.json b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/package.json new file mode 100644 index 0000000000..722337ee82 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/package.json @@ -0,0 +1,23 @@ +{ + "name": "serviceapi", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.2", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "express-session": "^1.15.5", + "jade": "~1.11.0", + "mongodb": "^2.2.31", + "morgan": "~1.8.1", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "request": "^2.81.0", + "serve-favicon": "~2.4.2" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ccusers.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ccusers.js new file mode 100644 index 0000000000..459c34942a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ccusers.js @@ -0,0 +1,267 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ccusers.js + */ + +/* Summary: + * ConnectionChain User Information Management API +*/ + +var express = require('express'); +var collection = require( '../../lib/common/mongo.js' ); +var ServiceAPIError = require('../../lib/common/ServiceAPIError.js'); +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var router = express.Router(); + +var user_col = 'ccusers'; + +/** + * Generate ConnetionChain user + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "userID": "" , - ID of the ConnectionChain user to register + * "password": "" , - Password to login + * "userName": "", - Alias to use on the UI + * "role": "", -"admin" or "user", etc. Other than "admin", any string can be set. + * } + * Response Body: + * { + * "userID": "" - ID of the registered ConnectionChain user" + * } +**/ +router.post('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + + var userID = req.body.userID; + if (userID == undefined) { + next(new ServiceAPIError(422, 2001)); + return; + } + var password = req.body.password; + if (password == undefined) { + next(new ServiceAPIError(422, 2027)); + return; + } + var userName = req.body.userName; + if (userName == undefined) { + next(new ServiceAPIError(422, 2028)); + return; + } + var role = req.body.role; + if (role == undefined) { + next(new ServiceAPIError(422, 2029)); + return; + } + + // Get user information from DB + collection(user_col).findOne({"userID":{$eq:userID}}, {}, function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (result != undefined) { + // Registered user + next(new ServiceAPIError(422, 3005)); + return; + } + + // Hash password and register in DB + var hash = ServiceAPIUtil.getHash(password); + collection(user_col).insertOne({"userID": userID, + "password": hash, + "userName": userName, + "role": role}, + function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + var resObj = {userID:userID}; + res.status(201).send(resObj); + }); + }); +}); + +/** + * Update ConnectionChain user information + * @name put/:id + * @function + * @inner + * @param {string} path - ConnectionChain user ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * (Elements that should not be changed can be omitted) + * { + * "password": "", + * "userName": "" + * } + * Response Body: + * { + * "userID": "" - ID of the updated ConnectionChain user" + * } +**/ +router.put('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + + var id = req.params.id; + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, you can only change your own information + if (id != req.user.userID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + + // Get user information from DB + collection(user_col).findOne({"userID":{$eq:id}}, {}, function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (result == undefined) { + // Unregistered user + next(new ServiceAPIError(404, 1005)); + return; + } + + + var update_obj = {}; + // Do not update unspecified parameters + var password = req.body.password; + if (password != undefined) { + // Hash the password + var hash = ServiceAPIUtil.getHash(password); + update_obj["password"] = hash; + } + var userName = req.body.userName; + if (userName != undefined) { + update_obj["userName"] = userName; + } + + collection(user_col).updateOne({"userID":{$eq:id}}, {$set: update_obj}, + function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + var resObj = {userID:id}; + res.status(200).send(resObj); + }); + }); +}); + +/** + * Delete ConnectionChain user information + * @name delete/:id + * @function + * @inner + * @param {string} path - ConnectionChain user ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var id = req.params.id; + // Remove user information from DB + collection(user_col).deleteOne({"userID":{$eq:id}}, {}, function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(204).send(); + }); +}); + +/** + * Get one ConnectionChain user information + * @name get/:id + * @function + * @inner + * @param {string} path - ConnectionChain user ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "password": "", + * "userName": "", + * "role": "" + * } +**/ +router.get('/:id', function(req, res, next) { + var id = req.params.id; + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, you can only get your own information + if (id != req.user.userID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + + // Get user information from DB + collection(user_col).findOne({"userID":{$eq:id}}, {}, function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (result == undefined) { + // Unregistered user + next(new ServiceAPIError(404, 1005)); + return; + } + var resObj = result; + res.status(200).send(resObj); + }); +}); + +/** + * Get ConnectionChain user information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Response Body: Array of ConnectionChain user information +**/ +router.get('/', function(req, res, next) { + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + // Get user information from DB + collection(user_col).find().toArray(function(err, result){ + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + var resObj = result; + res.status(200).send(resObj); + }); +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ecaccounts.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ecaccounts.js new file mode 100644 index 0000000000..6634b2093b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/ecaccounts.js @@ -0,0 +1,285 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ecaccounts.js + */ + +/* Summary: + * End-chain Account Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var request = require('request'); +var ServiceAPIError = require('../../lib/common/ServiceAPIError.js'); +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var router = express.Router(); + +/** + * Generate end-chain account information + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "userID": "", - CC user ID tied to the account + * "chainID": "", - ID of end-chain to which the account belongs + * "accountID":"", - ID of end-chain account, used by end-chain API + * "alias": "", - Alias to be used on the UI + * "authInfo": "" - Parameters used when authentication is required for end-chain account operations (Omit if authentication is not required) + * } + * Response Body: + * { + * "ECAccountID": "" - ID of the registered end-chain account information + * } +**/ +router.post('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + // Pass parameters as-is, leave checks to core API + var options = { + url: config.coreapi.url + '/ecaccounts', + method: 'POST', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Update end-chain Account Information + * @name put/:id + * @function + * @inner + * @param {string} path - End-chain account ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * (Elements that should not be changed can be omitted) + * { + * "userID": "", + * "chainID": "", + * "accountID": "", + * "alias": "", + * "authInfo": "" + * } + * Response Body: + * { + * "ECAccountID": "" - ID of the updated End-chain account information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + + var ccUserID = req.user.userID; + var ecAccountID = req.params.id; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, only the information associated with the login user can be modified. + // Get information and then judge if this ConnectionChain user can operate. + var date = new Date(); // dummy body data to not return cache on GET after change + var options = { + url: config.coreapi.url+ '/ecaccounts/' + ecAccountID, + method: 'GET', + headers: headers, + json: true, + body: {ts: date} + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (response.statusCode == 200) { + if (ccUserID != body.userID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + }) + } + + var options = { + url: config.coreapi.url+ '/ecaccounts/' + ecAccountID, + method: 'PUT', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Delete end-chain Account Information + * @name delete/:id + * @function + * @inner + * @param {string} path - End-chain account ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/ecaccounts/' + req.params.id, + method: 'DELETE', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get one end-chain account information + * @name get/:id + * @function + * @inner + * @param {string} path - End-chain account ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "userID": "", + * "chainID": "", + * "chainName": "", + * "accountID": "", + * "alias": "", + * "authInfo": "" + * } +**/ +router.get('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/ecaccounts/' + req.params.id, + method: 'GET', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, only the information associated with the login user can get. + if (response.statusCode == 200) { + if (ccUserID != body.userID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get end-chain account information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Response Body:Array of end-chain account information +**/ +router.get('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + + var ccUserID = req.user.userID; + var qUserID = req.query.userID; + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, only the information associated with the login user can get. + if (qUserID == undefined) { + req.query.userID = ccUserID; + } else if (qUserID != ccUserID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url + '/ecaccounts', + method: 'GET', + headers: headers, + json: true, + qs: req.query + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/endchains.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/endchains.js new file mode 100644 index 0000000000..af6a1ddd65 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/endchains.js @@ -0,0 +1,238 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * endchains.js + */ + +/* Summary: + * Destination End-chain Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var request = require('request'); +var ServiceAPIError = require('../../lib/common/ServiceAPIError.js'); +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var router = express.Router(); + +/** + * Genarate destination end-chain information + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "chainID": "", - ID of the destination end-chain information to register + * "chainName": "", - Alias to be used on the UI + * "adapterUrl": "" - URL of the adapter server connected to end-chain + * } + * Response Body: + * { + * "chainID": "" - ID of registered destination end-chain information + * } +**/ +router.post('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + // Pass parameters as-is, leave checks to core API + var options = { + url: config.coreapi.url + '/endchains', + method: 'POST', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Update destination end-chain information + * @name put/:id + * @function + * @inner + * @param {string} path - Chain ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * (Elements that should not be changed can be omitted) + * { + * "chainName": "", + * "adapterUrl": "" + * } + * Response Body: + * { + * "chainID": "" - ID of the updated destination end-chain information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + + var options = { + url: config.coreapi.url+ '/endchains/' + req.params.id, + method: 'PUT', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Delete destination end-chain information + * @name delete/:id + * @function + * @inner + * @param {string} path - Chain ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/endchains/' + req.params.id, + method: 'DELETE', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get one destination end-chain information + * @name get/:id + * @function + * @inner + * @param {string} path - Chain ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "chainName": "", + * "adapterUrl": "" + * } +**/ +router.get('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/endchains/' + req.params.id, + method: 'GET', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get destination end-chain information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Response Body: Array of destination end-chain information +**/ +router.get('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url + '/endchains', + method: 'GET', + headers: headers, + json: true, + qs: req.query + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/login.js b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/login.js new file mode 100644 index 0000000000..f9f567fab3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/serviceapi/routes/common/login.js @@ -0,0 +1,67 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * login.js + */ + +var express = require('express'); +var router = express.Router(); + +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var collection = require( '../../lib/common/mongo.js' ); +var passport = require('passport') + , LocalStrategy = require('passport-local').Strategy; + +var user_col = 'ccusers'; + +passport.use(new LocalStrategy( + function(username, password, done) { + + // Get user information from DB + collection(user_col).findOne({"userID":{$eq:username}}, {}, function(err, result){ + if (result != undefined) { + // password check + var hash = ServiceAPIUtil.getHash(password); + if (result.password == hash) { + return done(null, result); + } + } + return done(null, false); + }); + } +)); + +/** + * Login + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {function} middleware - Passport middleware + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "username": "", + * "password": "" + * } +**/ +router.post('/', passport.authenticate('local', {session: true }), function(req, res, next){ + // If "failureRedirect" is not specified in the second argument of authenticate, + // directly return error with code 401 on authentication failure + + // Return users and roles as login success only (Do not make screen transitions) + var resObj = {userName:req.user.userName, role:req.user.role}; + res.send(resObj); + } +); + +passport.serializeUser(function(user, done) { + done(null, user); +}); + +passport.deserializeUser(function(user, done) { + done(null, user); +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/appserver/setup.sh b/packages/connection-chain/environment/base/cc_env/servers/appserver/setup.sh new file mode 100755 index 0000000000..d85721d8a7 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/appserver/setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/serviceapi +npm install +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/ +mongod --config /opt/gopath/src/github.com/hyperledger/fabric/work/server/mongodb/mongodb.config diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/build/Dockerfile b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/build/Dockerfile new file mode 100644 index 0000000000..07ab9bc367 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/build/Dockerfile @@ -0,0 +1,16 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +FROM hyperledger/fabric-ccenv:x86_64-1.0.4 +#ENV http_proxy $HTTP_PROXY +#ENV https_proxy $HTTP_PROXY +#ENV HTTP_PROXY $HTTP_PROXY +#ENV HTTPS_PROXY $HTTP_PROXY +#ENV NO_PROXY "rest-server,ec1-connector,ec2-connector,geth1,geth2" +RUN apt update +RUN apt-get install -y screen +RUN apt-get install -y npm +#RUN npm -g config set proxy $HTTP_PROXY +RUN npm -g install n +RUN n --version +RUN n 8.9.0 +RUN npm -g install express diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.crt b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.crt new file mode 100644 index 0000000000..34773b65c3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBdTCCARoCCQC/F+Mh551QzDAKBggqhkjOPQQDAjBCMQswCQYDVQQGEwJKUDEQ +MA4GA1UECAwHZXNqbXMxMjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg +THRkMB4XDTE4MDYyNzA3MjIzNVoXDTI4MDYyNDA3MjIzNVowQjELMAkGA1UEBhMC +SlAxEDAOBgNVBAgMB2Vzam1zMTIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDPpSD2w0zrqJKraGD1b +5Jq2sDuacThSUqi7fvz8oyrWtuKDjZ15zIaSOtak6XRxFh9V9Gokdg5GNbW/pTZc +TuowCgYIKoZIzj0EAwIDSQAwRgIhAKH6ERsyd5bpEMIkY4clPqguwDWoTLk2VKq6 +ONEhUqotAiEA4yJxGmZpFdRScG2gDUIF2VDeX+XfHdJI2J41hyW9/zI= +-----END CERTIFICATE----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.csr b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.csr new file mode 100644 index 0000000000..e828a1414b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.csr @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIH9MIGkAgEAMEIxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdlc2ptczEyMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQz6Ug9sNM66iSq2hg9W+SatrA7mnE4UlKou378/KMq1rbig42decyG +kjrWpOl0cRYfVfRqJHYORjW1v6U2XE7qoAAwCgYIKoZIzj0EAwIDSAAwRQIgCUA1 +B5mZK7Hx79J1xBb0MGwuoUkt4bGPXbHqWEMZXQMCIQCRgadPkrNw56+pT5MVxA5K +vV6xTgmxUYrYnpkR4tptqQ== +-----END CERTIFICATE REQUEST----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.priv b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.priv new file mode 100644 index 0000000000..6faf76a31e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/CA/adapter.priv @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIlCfK3zMTFzUgdaj01LAHjJmHlbg6Xql9+i70iPz5EoAoGCCqGSM49 +AwEHoUQDQgAEM+lIPbDTOuokqtoYPVvkmrawO5pxOFJSqLt+/PyjKta24oONnXnM +hpI61qTpdHEWH1X0aiR2DkY1tb+lNlxO6g== +-----END EC PRIVATE KEY----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/app.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/app.js new file mode 100644 index 0000000000..b390816010 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/app.js @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * + */ + +var express = require('express'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var app = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + console.log(err); + res.send(err); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/config/default.js new file mode 100644 index 0000000000..0fc9d54c27 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/config/default.js @@ -0,0 +1,20 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // definition value for a connector-independent part + // Destination dependent definition values should be in lib/PluginConfig.js. + "sslParam" : { + "port" : 5030, + "key" : './CA/adapter.priv', + "cert" : './CA/adapter.crt' + }, + // URL of the connector server + "connecter_url" : 'https://xx.xx.xx.xx:5040', + // Log level (trace/debug/info/warn/error/fatal) + "logLevel" : "debug" +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/connector_if.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/connector_if.js new file mode 100644 index 0000000000..026816d0d0 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/connector_if.js @@ -0,0 +1,165 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * connector_if.js + */ + +/* + * Summary: + * Adapter: a part independent on end-chains (connector side) + * Communication portion with the connector server + */ + +var io = require('socket.io-client'); +var HttpsProxyAgent = require('https-proxy-agent'); +var proxy = process.env.http_proxy; +var agent = null; +if (proxy) { + agent = new HttpsProxyAgent(proxy); +} +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('connector_if[' + process.pid + ']'); +logger.level = config.logLevel; + +// socket correspondence table between core to adapter and adapter to connector +var sockets = {}; + +exports.request = function(client, func, args) { + return new Promise((resolve, reject) => { + + var socket = sockets[client.id]; + if (socket && socket.connected) { + var requestData = { + func : func, + args : args + }; + socket.emit('request', requestData); + } else { + // return as failure if socket with connector is missing or not connected + if (socket) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + var errorResponse = { + 'status' : 503, + 'errorDetail' : 'Connecter server not connected.' + }; + return reject(errorResponse); + } + + socket.on('response', function (res) { + logger.info('on:response'); + logger.info(res); + return resolve(res); + }); + + socket.on('connector_error', function (e) { + logger.error('on:connector_error'); + return reject(e); + }); + + socket.on('disconnect', function (reason) { + logger.info('on:disconnect'); + logger.info(reason); + if (sockets[client.id]) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + // socket disconnect from connector also disconnect socket between corresponding core and adapter + client.disconnect(); + }); + + }); +} + +exports.startMonitor = function(client, cb) { + var socket = sockets[client.id]; + if (socket && socket.connected) { + socket.emit('startMonitor'); + } else { + // Disconnecting the socket on the core side if there is no socket or connection with the connector side + if (socket) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + client.disconnect(); + } + + socket.on('eventReceived', function (res) { + logger.info('on:eventReceived'); + cb(res); + }); + socket.on('monitor_error', function (res) { + logger.error('on:monitor_error'); + // Return on error with same callback as event received + // (In the end, it will be judged by res.status later, so it might be good to collect all the emits from the cooperating server into one.) + cb(res); + }); + socket.on('disconnect', function (reason) { + logger.info('on:disconnect/monitor'); + logger.info(reason); + if (sockets[client.id]) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + // socket disconnect from connector also disconnect socket between corresponding core and adapter + client.disconnect(); + }); +} + +exports.stopMonitor = function(clientid) { + var socket = sockets[clientid]; + if (socket) { + socket.emit('stopMonitor'); + } +} + + +// socket connection between an adapter and a connector when socket connection from core side +exports.connect = function(clientid, connecter_url) { + return new Promise((resolve, reject) => { + if (!sockets[clientid]) { + var options = { + rejectUnauthorized: false, // temporary avoidance since self-signed certificates are used + reconnection : false, + timeout : 20000 + }; + if (agent) { + options.agent = agent; + } + var socket = io(connecter_url, options); + + socket.on('connect', function() { + logger.info('on:connect/connecter server') + sockets[clientid] = socket; + logger.info(Object.keys(sockets)); + return resolve(); + }); + + socket.on('connect_error', function() { + logger.error('on:connect_error/connecter server') + return reject('connect_error'); + }); + + socket.on('connect_timeout', function() { + logger.error('on:connect_timeout/connecter server') + return reject('connect_timeout'); + }); + } + }); +} + +// socket disconnect between an adapter and a connector when socket is disconnected from core side +exports.disconnect = function(clientid) { + if (sockets[clientid]) { + if (!sockets[clientid].disconnected) { + sockets[clientid].disconnect(); + } + delete sockets[clientid]; + logger.info(Object.keys(sockets)); + } +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/ClientPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/ClientPlugin.js new file mode 100644 index 0000000000..8fb6bb6d22 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/ClientPlugin.js @@ -0,0 +1,117 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ClientPlugin.js + */ + +/* + * Summary: + * Adapter: a part dependent on end-chains + */ + +// configuration file +var CplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ClientPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Libraries of a part independent of end-chains (cooperation server side) +var connector_if = require('../connector_if.js'); + +/* + * ClientPlugin + * Class definitions for client plugins + */ +var ClientPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /** + * execConnecterApi: Connector API execution request + * @param {Object} client : The core-side client from which the run was requested + * @param {JSON} params : Information to determine the distribution of operations to be performed on the EC side + **/ + execConnecterApi(client, params) { + var functionName = ""; + var args = {}; + // Write the processing according to the API definition of the connector side + // Example) + /* + // Determine the function of the connector to be executed based on the contents of params + // build arguments from params, configuration file, etc. + var apiType = params.type; + switch(apiType) { + case "test": + functionName = "connecterFunc1"; + args = { + "arg1": params.info, + "arg2": CplugConfig.adapterDefData + }; + break; + case "dummy": + functionName = "connecterFunc2"; + args = { + "argX": "dummy_data" + }; + break; + default: + } + */ + + return new Promise((resolve, reject) => { + logger.info(' function : ' + functionName); + logger.info(' args : ' + JSON.stringify(args)); + + connector_if.request(client, functionName, args) + .then((response) => { + return resolve(response); + }) + .catch((err) => { + return reject(err); + }); + }); + } + + startMonitor(client, cb) { + var plugCb = function(res) { + var events = []; + if (res.status == 200) { + // res into a core-readable format (Array of identification IDs and data bodies) + // Example) + /* + for (var i = 0; i < res.data.length; i++) { + var eventInfo = { + "id" : res.data[i].id, + "data": res.data[i].detail + }; + events[i] = eventInfo; + } + */ + + var ret_obj = { + "status" : 200, + "events" : events + }; + cb(ret_obj); + } else { + // Callback in response to error + cb(res); + } + } + connector_if.startMonitor(client, plugCb); + } + + stopMonitor(clientid) { + connector_if.stopMonitor(clientid); + } + +} /* class */ + +module.exports = ClientPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/PluginConfig.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/PluginConfig.js new file mode 100644 index 0000000000..47c6b690b3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/lib/dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Configuration file of adapter dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/package.json new file mode 100644 index 0000000000..d037854e2a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec1_adapter/package.json @@ -0,0 +1,22 @@ +{ + "name": "adapter", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.1", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "https-proxy-agent": "^2.2.1", + "log4js": "^3.0.6", + "morgan": "~1.8.1", + "request": "^2.81.0", + "serve-favicon": "~2.4.2", + "socket.io": "^2.0.4", + "socket.io-client": "^2.0.4" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.crt b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.crt new file mode 100644 index 0000000000..34773b65c3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBdTCCARoCCQC/F+Mh551QzDAKBggqhkjOPQQDAjBCMQswCQYDVQQGEwJKUDEQ +MA4GA1UECAwHZXNqbXMxMjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg +THRkMB4XDTE4MDYyNzA3MjIzNVoXDTI4MDYyNDA3MjIzNVowQjELMAkGA1UEBhMC +SlAxEDAOBgNVBAgMB2Vzam1zMTIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDPpSD2w0zrqJKraGD1b +5Jq2sDuacThSUqi7fvz8oyrWtuKDjZ15zIaSOtak6XRxFh9V9Gokdg5GNbW/pTZc +TuowCgYIKoZIzj0EAwIDSQAwRgIhAKH6ERsyd5bpEMIkY4clPqguwDWoTLk2VKq6 +ONEhUqotAiEA4yJxGmZpFdRScG2gDUIF2VDeX+XfHdJI2J41hyW9/zI= +-----END CERTIFICATE----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.csr b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.csr new file mode 100644 index 0000000000..e828a1414b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.csr @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIH9MIGkAgEAMEIxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdlc2ptczEyMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQz6Ug9sNM66iSq2hg9W+SatrA7mnE4UlKou378/KMq1rbig42decyG +kjrWpOl0cRYfVfRqJHYORjW1v6U2XE7qoAAwCgYIKoZIzj0EAwIDSAAwRQIgCUA1 +B5mZK7Hx79J1xBb0MGwuoUkt4bGPXbHqWEMZXQMCIQCRgadPkrNw56+pT5MVxA5K +vV6xTgmxUYrYnpkR4tptqQ== +-----END CERTIFICATE REQUEST----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.priv b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.priv new file mode 100644 index 0000000000..6faf76a31e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/CA/adapter.priv @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIlCfK3zMTFzUgdaj01LAHjJmHlbg6Xql9+i70iPz5EoAoGCCqGSM49 +AwEHoUQDQgAEM+lIPbDTOuokqtoYPVvkmrawO5pxOFJSqLt+/PyjKta24oONnXnM +hpI61qTpdHEWH1X0aiR2DkY1tb+lNlxO6g== +-----END EC PRIVATE KEY----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/app.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/app.js new file mode 100644 index 0000000000..b390816010 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/app.js @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * + */ + +var express = require('express'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var app = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + console.log(err); + res.send(err); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/config/default.js new file mode 100644 index 0000000000..25456ceb88 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/config/default.js @@ -0,0 +1,20 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // definition value for a connector-independent part + // Destination dependent definition values should be in lib/PluginConfig.js. + "sslParam" : { + "port" : 6030, + "key" : './CA/adapter.priv', + "cert" : './CA/adapter.crt' + }, + // URL of the connector server + "connecter_url" : 'https://xx.xx.xx.xx:6040', + // Log level (trace/debug/info/warn/error/fatal) + "logLevel" : "debug" +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/connector_if.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/connector_if.js new file mode 100644 index 0000000000..026816d0d0 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/connector_if.js @@ -0,0 +1,165 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * connector_if.js + */ + +/* + * Summary: + * Adapter: a part independent on end-chains (connector side) + * Communication portion with the connector server + */ + +var io = require('socket.io-client'); +var HttpsProxyAgent = require('https-proxy-agent'); +var proxy = process.env.http_proxy; +var agent = null; +if (proxy) { + agent = new HttpsProxyAgent(proxy); +} +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('connector_if[' + process.pid + ']'); +logger.level = config.logLevel; + +// socket correspondence table between core to adapter and adapter to connector +var sockets = {}; + +exports.request = function(client, func, args) { + return new Promise((resolve, reject) => { + + var socket = sockets[client.id]; + if (socket && socket.connected) { + var requestData = { + func : func, + args : args + }; + socket.emit('request', requestData); + } else { + // return as failure if socket with connector is missing or not connected + if (socket) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + var errorResponse = { + 'status' : 503, + 'errorDetail' : 'Connecter server not connected.' + }; + return reject(errorResponse); + } + + socket.on('response', function (res) { + logger.info('on:response'); + logger.info(res); + return resolve(res); + }); + + socket.on('connector_error', function (e) { + logger.error('on:connector_error'); + return reject(e); + }); + + socket.on('disconnect', function (reason) { + logger.info('on:disconnect'); + logger.info(reason); + if (sockets[client.id]) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + // socket disconnect from connector also disconnect socket between corresponding core and adapter + client.disconnect(); + }); + + }); +} + +exports.startMonitor = function(client, cb) { + var socket = sockets[client.id]; + if (socket && socket.connected) { + socket.emit('startMonitor'); + } else { + // Disconnecting the socket on the core side if there is no socket or connection with the connector side + if (socket) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + client.disconnect(); + } + + socket.on('eventReceived', function (res) { + logger.info('on:eventReceived'); + cb(res); + }); + socket.on('monitor_error', function (res) { + logger.error('on:monitor_error'); + // Return on error with same callback as event received + // (In the end, it will be judged by res.status later, so it might be good to collect all the emits from the cooperating server into one.) + cb(res); + }); + socket.on('disconnect', function (reason) { + logger.info('on:disconnect/monitor'); + logger.info(reason); + if (sockets[client.id]) { + delete sockets[client.id]; + logger.info(Object.keys(sockets)); + } + // socket disconnect from connector also disconnect socket between corresponding core and adapter + client.disconnect(); + }); +} + +exports.stopMonitor = function(clientid) { + var socket = sockets[clientid]; + if (socket) { + socket.emit('stopMonitor'); + } +} + + +// socket connection between an adapter and a connector when socket connection from core side +exports.connect = function(clientid, connecter_url) { + return new Promise((resolve, reject) => { + if (!sockets[clientid]) { + var options = { + rejectUnauthorized: false, // temporary avoidance since self-signed certificates are used + reconnection : false, + timeout : 20000 + }; + if (agent) { + options.agent = agent; + } + var socket = io(connecter_url, options); + + socket.on('connect', function() { + logger.info('on:connect/connecter server') + sockets[clientid] = socket; + logger.info(Object.keys(sockets)); + return resolve(); + }); + + socket.on('connect_error', function() { + logger.error('on:connect_error/connecter server') + return reject('connect_error'); + }); + + socket.on('connect_timeout', function() { + logger.error('on:connect_timeout/connecter server') + return reject('connect_timeout'); + }); + } + }); +} + +// socket disconnect between an adapter and a connector when socket is disconnected from core side +exports.disconnect = function(clientid) { + if (sockets[clientid]) { + if (!sockets[clientid].disconnected) { + sockets[clientid].disconnect(); + } + delete sockets[clientid]; + logger.info(Object.keys(sockets)); + } +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/ClientPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/ClientPlugin.js new file mode 100644 index 0000000000..8fb6bb6d22 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/ClientPlugin.js @@ -0,0 +1,117 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ClientPlugin.js + */ + +/* + * Summary: + * Adapter: a part dependent on end-chains + */ + +// configuration file +var CplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ClientPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Libraries of a part independent of end-chains (cooperation server side) +var connector_if = require('../connector_if.js'); + +/* + * ClientPlugin + * Class definitions for client plugins + */ +var ClientPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /** + * execConnecterApi: Connector API execution request + * @param {Object} client : The core-side client from which the run was requested + * @param {JSON} params : Information to determine the distribution of operations to be performed on the EC side + **/ + execConnecterApi(client, params) { + var functionName = ""; + var args = {}; + // Write the processing according to the API definition of the connector side + // Example) + /* + // Determine the function of the connector to be executed based on the contents of params + // build arguments from params, configuration file, etc. + var apiType = params.type; + switch(apiType) { + case "test": + functionName = "connecterFunc1"; + args = { + "arg1": params.info, + "arg2": CplugConfig.adapterDefData + }; + break; + case "dummy": + functionName = "connecterFunc2"; + args = { + "argX": "dummy_data" + }; + break; + default: + } + */ + + return new Promise((resolve, reject) => { + logger.info(' function : ' + functionName); + logger.info(' args : ' + JSON.stringify(args)); + + connector_if.request(client, functionName, args) + .then((response) => { + return resolve(response); + }) + .catch((err) => { + return reject(err); + }); + }); + } + + startMonitor(client, cb) { + var plugCb = function(res) { + var events = []; + if (res.status == 200) { + // res into a core-readable format (Array of identification IDs and data bodies) + // Example) + /* + for (var i = 0; i < res.data.length; i++) { + var eventInfo = { + "id" : res.data[i].id, + "data": res.data[i].detail + }; + events[i] = eventInfo; + } + */ + + var ret_obj = { + "status" : 200, + "events" : events + }; + cb(ret_obj); + } else { + // Callback in response to error + cb(res); + } + } + connector_if.startMonitor(client, plugCb); + } + + stopMonitor(clientid) { + connector_if.stopMonitor(clientid); + } + +} /* class */ + +module.exports = ClientPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/PluginConfig.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/PluginConfig.js new file mode 100644 index 0000000000..47c6b690b3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/lib/dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Configuration file of adapter dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/package.json new file mode 100644 index 0000000000..d037854e2a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/ec2_adapter/package.json @@ -0,0 +1,22 @@ +{ + "name": "adapter", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.1", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "https-proxy-agent": "^2.2.1", + "log4js": "^3.0.6", + "morgan": "~1.8.1", + "request": "^2.81.0", + "serve-favicon": "~2.4.2", + "socket.io": "^2.0.4", + "socket.io-client": "^2.0.4" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/idle.sh b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/idle.sh new file mode 100755 index 0000000000..ebc78ff136 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/idle.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo "This is a idle script (infinite loop) to keep container running." +echo "Please replace this script." + +cleanup () +{ + kill -s SIGTERM $! + exit 0 +} + +trap cleanup SIGINT SIGTERM + +while [ 1 ] +do + sleep 60 & + wait $! +done diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/setup.sh b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/setup.sh new file mode 100755 index 0000000000..30e2f374b5 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/coreSide/setup.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter +npm install diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/build/Dockerfile b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/build/Dockerfile new file mode 100644 index 0000000000..07ab9bc367 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/build/Dockerfile @@ -0,0 +1,16 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +FROM hyperledger/fabric-ccenv:x86_64-1.0.4 +#ENV http_proxy $HTTP_PROXY +#ENV https_proxy $HTTP_PROXY +#ENV HTTP_PROXY $HTTP_PROXY +#ENV HTTPS_PROXY $HTTP_PROXY +#ENV NO_PROXY "rest-server,ec1-connector,ec2-connector,geth1,geth2" +RUN apt update +RUN apt-get install -y screen +RUN apt-get install -y npm +#RUN npm -g config set proxy $HTTP_PROXY +RUN npm -g install n +RUN n --version +RUN n 8.9.0 +RUN npm -g install express diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.crt b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.crt new file mode 100644 index 0000000000..34773b65c3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBdTCCARoCCQC/F+Mh551QzDAKBggqhkjOPQQDAjBCMQswCQYDVQQGEwJKUDEQ +MA4GA1UECAwHZXNqbXMxMjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg +THRkMB4XDTE4MDYyNzA3MjIzNVoXDTI4MDYyNDA3MjIzNVowQjELMAkGA1UEBhMC +SlAxEDAOBgNVBAgMB2Vzam1zMTIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDPpSD2w0zrqJKraGD1b +5Jq2sDuacThSUqi7fvz8oyrWtuKDjZ15zIaSOtak6XRxFh9V9Gokdg5GNbW/pTZc +TuowCgYIKoZIzj0EAwIDSQAwRgIhAKH6ERsyd5bpEMIkY4clPqguwDWoTLk2VKq6 +ONEhUqotAiEA4yJxGmZpFdRScG2gDUIF2VDeX+XfHdJI2J41hyW9/zI= +-----END CERTIFICATE----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.csr b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.csr new file mode 100644 index 0000000000..e828a1414b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.csr @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIH9MIGkAgEAMEIxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdlc2ptczEyMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQz6Ug9sNM66iSq2hg9W+SatrA7mnE4UlKou378/KMq1rbig42decyG +kjrWpOl0cRYfVfRqJHYORjW1v6U2XE7qoAAwCgYIKoZIzj0EAwIDSAAwRQIgCUA1 +B5mZK7Hx79J1xBb0MGwuoUkt4bGPXbHqWEMZXQMCIQCRgadPkrNw56+pT5MVxA5K +vV6xTgmxUYrYnpkR4tptqQ== +-----END CERTIFICATE REQUEST----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.priv b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.priv new file mode 100644 index 0000000000..6faf76a31e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/CA/connector.priv @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIlCfK3zMTFzUgdaj01LAHjJmHlbg6Xql9+i70iPz5EoAoGCCqGSM49 +AwEHoUQDQgAEM+lIPbDTOuokqtoYPVvkmrawO5pxOFJSqLt+/PyjKta24oONnXnM +hpI61qTpdHEWH1X0aiR2DkY1tb+lNlxO6g== +-----END EC PRIVATE KEY----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/app.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/app.js new file mode 100644 index 0000000000..b390816010 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/app.js @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * + */ + +var express = require('express'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var app = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + console.log(err); + res.send(err); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/config/default.js new file mode 100644 index 0000000000..f690f99548 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/config/default.js @@ -0,0 +1,18 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // Defined value for the destination independent part. I don't think I can use it only at www, so I think I can write directly there. + // Destination dependent definition values should be in lib/PluginConfig.js. + "sslParam" : { + "port" : 5040, + "key" : './CA/connector.priv', + "cert" : './CA/connector.crt' + }, + // Log level (trace/debug/info/warn/error/fatal) + "logLevel" : "debug" +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginConfig.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginConfig.js new file mode 100644 index 0000000000..b2ab1aa978 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Cooperation server: configuration file of a part dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginUtil.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginUtil.js new file mode 100644 index 0000000000..4216996906 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/PluginUtil.js @@ -0,0 +1,14 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginUtil.js + */ + +/* + * Summary: + * Cooperation server: utility libraries of a part dependent on end-chains + * For example, implementing internal functions that should not be exposed as functions of ServerPlugin. + */ + +// Nothing special diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerMonitorPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerMonitorPlugin.js new file mode 100644 index 0000000000..383563804b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerMonitorPlugin.js @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerMonitorPlugin.js + */ + +/* + * Summary: + * Connector: monitoring class of a part dependent on end-chains + * Used in the case of monitoring continuously. + * Processing that ends with the acceptance of a single event is handled by a custom function implementation in server plugins. + * Unlike server plugins, it basically does not handle its own functions. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerMonitorPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Load libraries, SDKs, etc. according to specifications of endchains as needed + +/* + * ServerMonitorPlugin + * Class definitions of server monitoring + */ +var ServerMonitorPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * startMonitor + * Start Monitoring + * @param {string} clientId: Client ID from which monitoring start request was made + * @param {function} cb: A callback function that receives monitoring results at any time. + */ + startMonitor(clientId, cb) { + logger.info('*** START MONITOR ***'); + logger.info('Client ID :' + clientId); + // Implement handling to receive events from an endchain and return them in a callback function + + } + + /* + * stopMonitor + * monitoring stop + * @param {string} clientId: Client ID from which monitoring stop request was made + */ + stopMonitor(clientId) { + // Implement a process to end EC monitoring + } + +} /* class */ + +module.exports = ServerMonitorPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerPlugin.js new file mode 100644 index 0000000000..bb3d70a5b9 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/ServerPlugin.js @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerPlugin.js + */ + +/* + * Summary: + * Connector: a part dependent on endchains + * Define and implement the function according to the connection destination dependent part (ADAPTER) on the core side. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// utility +var SplugUtil = require('./PluginUtil.js'); +// Load libraries, SDKs, etc. according to specifications of endchains as needed + +/* + * ServerPlugin + * Class definition for server plugins + */ +var ServerPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * isExistFunction + * + * @param {String} funcName: The function name to check. + * + * @return {Boolean} true: Yes./false: does not exist. + * + * @desc Return if end-chain specific funtion is implemented + * Scope of this function is in this class + * Functions that should not be called directly should be implemented outside this class like utilities. + */ + isExistFunction(funcName) { + if(this[funcName]!=undefined) { + return true; + } else { + return false; + } + } + + // Define an arbitrary function and implement it according to specifications of end-chains + + +} /* class */ + +module.exports = ServerPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/package.json new file mode 100644 index 0000000000..289b20b1f1 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/lib/dependent/package.json @@ -0,0 +1,7 @@ +{ + "name": "dependent", + "version": "0.0.0", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/package.json new file mode 100644 index 0000000000..4cde14a5a5 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec1_connector/package.json @@ -0,0 +1,19 @@ +{ + "name": "connector", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.1", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "log4js": "^3.0.6", + "morgan": "~1.8.1", + "serve-favicon": "~2.4.2", + "socket.io": "^2.0.4" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.crt b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.crt new file mode 100644 index 0000000000..34773b65c3 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.crt @@ -0,0 +1,10 @@ +-----BEGIN CERTIFICATE----- +MIIBdTCCARoCCQC/F+Mh551QzDAKBggqhkjOPQQDAjBCMQswCQYDVQQGEwJKUDEQ +MA4GA1UECAwHZXNqbXMxMjEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkg +THRkMB4XDTE4MDYyNzA3MjIzNVoXDTI4MDYyNDA3MjIzNVowQjELMAkGA1UEBhMC +SlAxEDAOBgNVBAgMB2Vzam1zMTIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDPpSD2w0zrqJKraGD1b +5Jq2sDuacThSUqi7fvz8oyrWtuKDjZ15zIaSOtak6XRxFh9V9Gokdg5GNbW/pTZc +TuowCgYIKoZIzj0EAwIDSQAwRgIhAKH6ERsyd5bpEMIkY4clPqguwDWoTLk2VKq6 +ONEhUqotAiEA4yJxGmZpFdRScG2gDUIF2VDeX+XfHdJI2J41hyW9/zI= +-----END CERTIFICATE----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.csr b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.csr new file mode 100644 index 0000000000..e828a1414b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.csr @@ -0,0 +1,8 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIH9MIGkAgEAMEIxCzAJBgNVBAYTAkpQMRAwDgYDVQQIDAdlc2ptczEyMSEwHwYD +VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAAQz6Ug9sNM66iSq2hg9W+SatrA7mnE4UlKou378/KMq1rbig42decyG +kjrWpOl0cRYfVfRqJHYORjW1v6U2XE7qoAAwCgYIKoZIzj0EAwIDSAAwRQIgCUA1 +B5mZK7Hx79J1xBb0MGwuoUkt4bGPXbHqWEMZXQMCIQCRgadPkrNw56+pT5MVxA5K +vV6xTgmxUYrYnpkR4tptqQ== +-----END CERTIFICATE REQUEST----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.priv b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.priv new file mode 100644 index 0000000000..6faf76a31e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/CA/connector.priv @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEICIlCfK3zMTFzUgdaj01LAHjJmHlbg6Xql9+i70iPz5EoAoGCCqGSM49 +AwEHoUQDQgAEM+lIPbDTOuokqtoYPVvkmrawO5pxOFJSqLt+/PyjKta24oONnXnM +hpI61qTpdHEWH1X0aiR2DkY1tb+lNlxO6g== +-----END EC PRIVATE KEY----- diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/app.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/app.js new file mode 100644 index 0000000000..b390816010 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/app.js @@ -0,0 +1,41 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * + */ + +var express = require('express'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var app = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + console.log(err); + res.send(err); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/config/default.js new file mode 100644 index 0000000000..1d4a51ed6e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/config/default.js @@ -0,0 +1,18 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // Defined value for the destination independent part. I don't think I can use it only at www, so I think I can write directly there. + // Destination dependent definition values should be in lib/PluginConfig.js. + "sslParam" : { + "port" : 6040, + "key" : './CA/connector.priv', + "cert" : './CA/connector.crt' + }, + // Log level (trace/debug/info/warn/error/fatal) + "logLevel" : "debug" +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginConfig.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginConfig.js new file mode 100644 index 0000000000..b2ab1aa978 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Cooperation server: configuration file of a part dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginUtil.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginUtil.js new file mode 100644 index 0000000000..4216996906 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/PluginUtil.js @@ -0,0 +1,14 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginUtil.js + */ + +/* + * Summary: + * Cooperation server: utility libraries of a part dependent on end-chains + * For example, implementing internal functions that should not be exposed as functions of ServerPlugin. + */ + +// Nothing special diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerMonitorPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerMonitorPlugin.js new file mode 100644 index 0000000000..383563804b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerMonitorPlugin.js @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerMonitorPlugin.js + */ + +/* + * Summary: + * Connector: monitoring class of a part dependent on end-chains + * Used in the case of monitoring continuously. + * Processing that ends with the acceptance of a single event is handled by a custom function implementation in server plugins. + * Unlike server plugins, it basically does not handle its own functions. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerMonitorPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Load libraries, SDKs, etc. according to specifications of endchains as needed + +/* + * ServerMonitorPlugin + * Class definitions of server monitoring + */ +var ServerMonitorPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * startMonitor + * Start Monitoring + * @param {string} clientId: Client ID from which monitoring start request was made + * @param {function} cb: A callback function that receives monitoring results at any time. + */ + startMonitor(clientId, cb) { + logger.info('*** START MONITOR ***'); + logger.info('Client ID :' + clientId); + // Implement handling to receive events from an endchain and return them in a callback function + + } + + /* + * stopMonitor + * monitoring stop + * @param {string} clientId: Client ID from which monitoring stop request was made + */ + stopMonitor(clientId) { + // Implement a process to end EC monitoring + } + +} /* class */ + +module.exports = ServerMonitorPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerPlugin.js b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerPlugin.js new file mode 100644 index 0000000000..bb3d70a5b9 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/ServerPlugin.js @@ -0,0 +1,62 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerPlugin.js + */ + +/* + * Summary: + * Connector: a part dependent on endchains + * Define and implement the function according to the connection destination dependent part (ADAPTER) on the core side. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// utility +var SplugUtil = require('./PluginUtil.js'); +// Load libraries, SDKs, etc. according to specifications of endchains as needed + +/* + * ServerPlugin + * Class definition for server plugins + */ +var ServerPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * isExistFunction + * + * @param {String} funcName: The function name to check. + * + * @return {Boolean} true: Yes./false: does not exist. + * + * @desc Return if end-chain specific funtion is implemented + * Scope of this function is in this class + * Functions that should not be called directly should be implemented outside this class like utilities. + */ + isExistFunction(funcName) { + if(this[funcName]!=undefined) { + return true; + } else { + return false; + } + } + + // Define an arbitrary function and implement it according to specifications of end-chains + + +} /* class */ + +module.exports = ServerPlugin; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/package.json new file mode 100644 index 0000000000..289b20b1f1 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/lib/dependent/package.json @@ -0,0 +1,7 @@ +{ + "name": "dependent", + "version": "0.0.0", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/package.json b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/package.json new file mode 100644 index 0000000000..4cde14a5a5 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/ec2_connector/package.json @@ -0,0 +1,19 @@ +{ + "name": "connector", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.1", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "log4js": "^3.0.6", + "morgan": "~1.8.1", + "serve-favicon": "~2.4.2", + "socket.io": "^2.0.4" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/idle.sh b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/idle.sh new file mode 100755 index 0000000000..ebc78ff136 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/idle.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo "This is a idle script (infinite loop) to keep container running." +echo "Please replace this script." + +cleanup () +{ + kill -s SIGTERM $! + exit 0 +} + +trap cleanup SIGINT SIGTERM + +while [ 1 ] +do + sleep 60 & + wait $! +done diff --git a/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/setup.sh b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/setup.sh new file mode 100755 index 0000000000..1e56b49900 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/cooperation/ecSide/setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/connector +npm install +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/connector/lib/dependent +npm install diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/build/Dockerfile b/packages/connection-chain/environment/base/cc_env/servers/restserver/build/Dockerfile new file mode 100644 index 0000000000..1cd081eec0 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/build/Dockerfile @@ -0,0 +1,16 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +FROM hyperledger/fabric-ccenv:x86_64-1.0.4 +#ENV http_proxy $HTTP_PROXY +#ENV https_proxy $HTTP_PROXY +#ENV HTTP_PROXY $HTTP_PROXY +#ENV HTTPS_PROXY $HTTP_PROXY +#ENV NO_PROXY "rest-server,ec1-connector,ec2-connector,geth1,geth2" +RUN apt update +RUN apt-get install -y screen +RUN apt-get install -y npm +#RUN npm -g config set proxy $HTTP_PROXY +RUN npm -g install n +RUN n --version +RUN n 6.9.1 +RUN npm -g install express diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/app.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/app.js new file mode 100644 index 0000000000..53ba3f088b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/app.js @@ -0,0 +1,61 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * Core API Server Main +*/ + +var express = require('express'); +var path = require('path'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var ecaccounts = require('./routes/common/ecaccounts'); +var endchains = require('./routes/common/endchains'); +// Write your own additional REST API if you have one + +var CoreAPIError = require('./lib/common/CoreAPIError.js'); +var MonitorManager = require('./lib/common/MonitorManager.js'); +var EventMediator = require('./lib/userImple/EventMediator.js'); +var config = require('config'); + +var app = express(); + +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +app.use('/ecaccounts', ecaccounts); +app.use('/endchains', endchains); +// Write your own additional REST API if you have one + + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new CoreAPIError(404, 1000); + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + res.status(err.statusCode || 500); + res.send(err.errorBody); +}); + +// Execute server startup processing on user created chaincode of CC or EC +EventMediator.initAction(); + +// Start Event Monitoring (CC & EC) +MonitorManager.startAllMonitor(); + +module.exports = app; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/config/default.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/config/default.js new file mode 100644 index 0000000000..bd0ea6afd2 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/config/default.js @@ -0,0 +1,50 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // CC network information + network: { + orderer: "grpc://orderer.example.com:7050", + ca: { + url: "http://ca.example.com:7054", + name: "ca.example.com" + }, + mspid: "Org1MSP", + peers:[ + { + requests: "grpc://peer0.org1.example.com:7051", + events: "grpc://peer0.org1.example.com:7053" + }, + { + requests: "grpc://peer1.org1.example.com:7051", + events: "grpc://peer1.org1.example.com:7053" + }, + { + requests: "grpc://peer2.org1.example.com:7051", + events: "grpc://peer2.org1.example.com:7053" + }, + { + requests: "grpc://peer3.org1.example.com:7051", + events: "grpc://peer3.org1.example.com:7053" + } + ], + submitter: { + name: "admin", + secret: "adminpw" + } + }, + // Log level (trace/debug/info/warn/error/fatal) + logLevel: "debug", + // Chain ID of CC + chainId: "ConnectionChain", + // CC chaincode information + channelName: "mychannel", + chaincodeID_ec: "endchain_information", + chaincodeID_ns: "naming_service", + // Custom addition of chaincode for CC + chaincodeIDs: [] +}; \ No newline at end of file diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIError.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIError.js new file mode 100644 index 0000000000..82d4b27e0f --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIError.js @@ -0,0 +1,60 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * CoreAPIError.js + */ + +/* Summary: + * Class for error response +*/ + +// user defined error code & message +var userDefinitions = require('../userImple/UserErrorDef.js'); + +// Error code & message definition +const definitions = { +// errorCode : errorMessage + 1000 : 'Resource does not exist. ', + 2000 : 'The request is not in JSON format. ', + 3000 : 'Internal error. ', + 9000 : 'Unknown error. ', +// unregistered information + 1001 : 'Account ID not registered. ', + 1002 : 'Unregistered chain ID of the destination. ', +// Request parameter not specified + 2001 : 'No user ID specified. ', + 2002 : 'No chain id specified. ', + 2003 : 'No account ID specified', + 2004 : 'No account display name specified', + 2005 : 'No display name specified for the chain. ', + 2007 : 'The adapter URL is not specified. ', +// duplicate registration + 3001 : 'This is an already registered account ID. ',// unused + 3002 : 'This is the chain ID of the already registered destination. ' +}; + + +var CoreAPIError = class { + + constructor(statusCode, errorCode, detail) { + this.statusCode = statusCode; + this.errorCode = errorCode; + var message = userDefinitions[errorCode] || definitions[errorCode] || definitions[9000]; + if (detail != undefined) { + message = message + detail; + } + this.message = message; + var errorBody = + { + "error" : + { + "code" : errorCode, + "message" : message + } + }; + this.errorBody = errorBody; + } +} + +module.exports = CoreAPIError; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIUtil.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIUtil.js new file mode 100644 index 0000000000..2ed4211b2c --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/CoreAPIUtil.js @@ -0,0 +1,31 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * CoreAPIUtil.js + */ + +/* Summary: + * Utility classes for the core API +*/ + +var CoreAPIUtil = class { + + /** + * Content type check + * @param {Obj} req: request + * @return{bool} true: enabled/false: disabled + **/ + static isValidContentType(req) { + var cType = req.header('Content-type'); + if (cType != undefined) { + // whether it is JSON format or not + if (cType.toLowerCase().indexOf('application/json') != -1) { + return true; + } + } + return false; + } +} + +module.exports = CoreAPIUtil; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EcInfoAction.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EcInfoAction.js new file mode 100644 index 0000000000..60d87037e2 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EcInfoAction.js @@ -0,0 +1,140 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * EcInfoAction.js + */ + +/* Summary: + * Event processing related destination EC information and cache management library +*/ + +var fabricv10Post = require("./fabric_v1.0/sdk_if.js"); +var MonitorManager = require('./MonitorManager.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('EcInfoAction[' + process.pid + ']'); +logger.level = config.logLevel; + +// Hash table for cache of destination EC information +var ECTable = {}; + +/** + * Get the destination EC information + * Returns cached information, if any. If not, it is retrieved from the chaincode and added back to the cache. + * @param {string} chainID :Chain ID + * @return{object} Cache of the destination EC information +**/ +function getECInfoCache(chainID) { + return new Promise((resolve, reject) => { + var ecCache = ECTable[chainID]; + if (ecCache) { + logger.info('Hit EC cache. id: ' + chainID); + return resolve(ecCache); + } else { + logger.info('Get EC info request. id: ' + chainID); + var queryArgs = [chainID]; + fabricv10Post.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'getECInfo', + queryArgs) + .then((response) => { + if (response != ''){ + var ecInfo = JSON.parse(response); + var chainName = ecInfo.chainName; + var adapterUrl = ecInfo.adapterUrl; + // Cache update of the destination EC information + ecCache = new ECInfo(chainName, adapterUrl); + ECTable[chainID] = ecCache; + return resolve(ecCache); + } else { + throw new Error('Chain information does not exist. id: ' + chainID); + } + }) + .catch((err) => { + return reject(err); + }); + } + }); +} + +exports.getECInfoCache = function(chainID) { + return getECInfoCache(chainID); +} + +/** + * processing after detecting the event of operation for the destination EC information + * @param {string} func : invoke execute function + * @param {[]string} args :invoke run-time arguments +**/ +exports.receivedECInfoEvent = function(func, args) { + // add/modify/delete destination EC information + if (func == 'addECInfo') { + operation_addECInfo(args); + } else if (func == 'updateECInfo') { + operation_updateECInfo(args); + } else if (func == 'deleteECInfo') { + operation_deleteECInfo(args); + } +} + + +// Processing after adding destination EC information +function operation_addECInfo(args) { + var chainID = args[0]; + var chainName = args[1]; + var adapterUrl = args[2]; + // Generate cache of destination EC information + var ecCache = new ECInfo(chainName, adapterUrl); + ECTable[chainID] = ecCache; + // Add ECMonitor + MonitorManager.addECMonitor(chainID, adapterUrl); +} + +// Processing after updating destination EC information +function operation_updateECInfo(args) { + var chainID = args[0]; + // Delete cached destination EC information for a moment + delete ECTable[chainID]; + // Get the updated destination end-chain information from the chain code and store it in the cache + getECInfoCache(chainID) + .then((ecCache) => { + // Removed & re-added ECmonitor + MonitorManager.removeECMonitor(chainID); + MonitorManager.addECMonitor(chainID, ecCache._adapterUrl); + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + +// Processing after deleting the destination EC information +function operation_deleteECInfo(args) { + var chainID = args[0]; + // Delete cache of destination EC information + delete ECTable[chainID]; + // Delete ECmonitor + MonitorManager.removeECMonitor(chainID); +} + +// Storage class for destination EC information +var ECInfo = class { + + constructor(chainName, adapterUrl) { + this._chainName = chainName; + this._adapterUrl = adapterUrl; + } + + // Return as JSON object + getJsonObj() { + var returnObj = { + chainName : this._chainName, + adapterUrl : this._adapterUrl + }; + return returnObj; + } +}; + diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EventEntrance.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EventEntrance.js new file mode 100644 index 0000000000..496b192a5a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/EventEntrance.js @@ -0,0 +1,49 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * EventEntrance.js + */ + +/* Summary: + * Allocate the events detected by the CCMonitor/ECMonitor to the library for each subsequent process. +*/ +var EcInfoAction = require('./EcInfoAction.js'); +var EventMediator = require('../userImple/EventMediator.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('EventEntrance[' + process.pid + ']'); +logger.level = config.logLevel; + +/** + * Subsequent processing calls for discovery events from ConnectionChain + * @param {string} cainId :Chain ID ("ConnectionChain") + * @param {string} txid :Transaction id + * @param {string} ccid :Chaincode ID + * @param {string} func :Invoke execute function + * @param {[]string} args :Invoke run-time arguments +**/ +exports.action_fromCC = function(chainId, txid, ccid, func, args) { + if (ccid == config.chaincodeID_ec) { + // events from the chaincode for destination information management + EcInfoAction.receivedECInfoEvent(func, args); + } else if (ccid == config.chaincodeID_ns) { + // events from the chaincode for naming service + // No special handling + } else { + // events from chaincodes which is user-written (or out of the functions of ConnectionChain) + EventMediator.receivedUserChaincodeEvent(ccid, func, args); + } +} + +/** + * Subsequent processing calls for discovery events from the EC (via the pass of connector -> adapter) + * @param {string} cainId :Chain ID + * @param {string} eventId :Event ID + * @param {JSON} eventInfo :Event Information +**/ +exports.action_fromEC= function(chainId, eventId, eventInfo) { + EventMediator.receivedEndchainEvent(chainId, eventId, eventInfo); +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/HttpRequestPromise.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/HttpRequestPromise.js new file mode 100644 index 0000000000..4262a4956d --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/HttpRequestPromise.js @@ -0,0 +1,42 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * HttpRequestPromise.js + */ + +/* Summary: + * A library for handling HTTP requests in the form of promises + * + * Entry Point: + * None (For lib) +*/ + +// Dependency declaration of base package +var request = require('request'); +var HttpsProxyAgent = require('https-proxy-agent'); +var proxy = process.env.http_proxy; +var agent = null; +if (proxy) { + agent = new HttpsProxyAgent(proxy); +} + +// Send Http Request +function send(options) { + return new Promise((resolve, reject) => { + if (agent) { + options.agent = agent; + } + request(options, function (err, response, body) { + if (err) { + return reject(err); + } + return resolve(response); + }) + }); +} + +exports.send = function(options) { + return send(options); +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/MonitorManager.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/MonitorManager.js new file mode 100644 index 0000000000..0ebcd2b97b --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/MonitorManager.js @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * MonitorManager.js + */ + +/* Summary: + * Library for managing event monitoring classes +*/ + +var CCMonitor = require('./fabric_v1.0/CCMonitor.js'); +var ExpMonitor = require('./exp/ECMonitor.js'); +var fabricSdkPost = require("./fabric_v1.0/sdk_if.js"); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('MonitorManager[' + process.pid + ']'); +logger.level = config.logLevel; + +var ECMonitorTable = {}; +var allStarted = false; + +/** + * startAllMonitor: Starts monitoring all (CC, EC) events +**/ +exports.startAllMonitor = function() { + // Run once at server startup + if (allStarted) return; + + // Start CCMonitor + var ccm = new CCMonitor(config.chainId, config.network); + ccm.start(); + + // Get registered connection EC information list + var queryArgs = []; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'getECInfoList', + queryArgs) + .then((response) => { + var ecList = JSON.parse(response); + if (ecList) { + // ECMonitor started for each EC + for (var i = 0; i < ecList.length; i++) { + var chainId = ecList[i].id; + var adapterUrl = ecList[i].adapterUrl; + addECMonitor(chainId, adapterUrl); + } + } + }) + .catch((err) => { + logger.error(err); + }); + + allStarted = true; +} + +/** + * addECMonitor: add & start new EC event monitoring + * @param {string} chainId: Chain ID + * @param {string} adapterUrl: Adapter server URL +**/ +function addECMonitor(chainId, adapterUrl) { + var ecm = ECMonitorTable[chainId]; + if (ecm) return; // Already added + + ecm = new ExpMonitor(chainId, adapterUrl); + ECMonitorTable[chainId] = ecm; + ecm.start(); + + logger.info('ECMonitorTable:'); + logger.info(Object.keys(ECMonitorTable)); +} + +exports.addECMonitor = function(chainId, adapterUrl) { + return addECMonitor(chainId, adapterUrl); +} + +/** + * removeECMonitor: stops and removes EC event monitoring + * @param {string} chainId: Chain ID +**/ +exports.removeECMonitor = function(chainId) { + var ecm = ECMonitorTable[chainId]; + if (!ecm) return; // no longer exists + + ecm.end(); + delete ECMonitorTable[chainId]; + + logger.info('ECMonitorTable:'); + logger.info(Object.keys(ECMonitorTable)); +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/ECMonitor.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/ECMonitor.js new file mode 100644 index 0000000000..a55693a1ad --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/ECMonitor.js @@ -0,0 +1,155 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ECMonitor.js + */ + +/* Summary: + * Event motinoring of end-chain +*/ + +var EventEntrance = require('../EventEntrance.js'); + +// Dependency declaration of base package +var process = require('process'); +var io = require('socket.io-client'); +var HttpsProxyAgent = require('https-proxy-agent'); +var proxy = process.env.http_proxy; +var agent = null; +if (proxy) { + agent = new HttpsProxyAgent(proxy); +} +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ECMonitor_exp[' + process.pid + ']'); +logger.level = config.logLevel; + +var ECMonitor = class { + + constructor(chainId, adapterUrl) { + this._id = chainId; // Using chainId, EventMediator gets the destination EC information + this._asUrl = adapterUrl; + this._socket = null; + } + + start() { + try { + logger.info('Connecting adapter server.'); + var options = { + rejectUnauthorized: false, // temporary avoidance since self-signed certificates are used + reconnection : true, // connection with adapter fails but repeats + reconnectionDelay : 5000, // Default 1000 + reconnectionDelayMax : 10000 // Default 5000 + }; + if (agent) { + options.agent = agent; + } + this._socket = io(this._asUrl, options); // Connect to the adapter + + // Successful connection from core to adapter + this._socket.on('connect', () => { + logger.info('on:connect/adapter'); + // Do not start monitoring yet at this time + }); + + var reconnect = function(socket) { + logger.info('Reconnecting adapter server.'); + if (socket) { + socket.connect(); + } + } + + // Disconnecting from adapter + this._socket.on('disconnect', (reason) => { + logger.info('on:disconnect/adapter'); + logger.info(reason); + // Attempt to reconnect if explicit disconnect (Disconnecting by the adapter without connecting to the connector server side) + if (reason == 'io server disconnect') { + // Wait for the same amount of time as defined reconnectionDelayMax options + var delay = this._socket.io._reconnectionDelayMax; + setTimeout(reconnect, delay, this._socket); + } + }); + + // Successful connection (core => adapter => connector server) + this._socket.on('connect_connecter_server', () => { + logger.info('on:connect/connecter'); + logger.info('Start monitor.'); + // Now finally the adapter (via connector servers) starts monitoring + this._socket.emit('startMonitor'); + }); + + // Connection failure (adapter => connector server) + this._socket.on('error_connecter_server', (err_obj) => { + logger.error('on:error/connecter'); + logger.error(JSON.stringify(err_obj)); + // Disconnected from adapter + }); + + this._socket.on('connect_error', (err) => { + logger.error('on:connect_error/adapter'); + var err_obj = { + "status" : 404, + "errorDetail" : err + }; + logger.error(JSON.stringify(err_obj)); + if(!this._socket.disconnected) { + this._socket.disconnect(); + } + }); + + this._socket.on('connect_timeout', (err) => { + logger.error('on:connect_timeout/adapter'); + var err_obj = { + "status" : 404, + "errorDetail" : err + }; + logger.error(JSON.stringify(err_obj)); + if(!this._socket.disconnected) { + this._socket.disconnect(); + } + }); + + // Receive event notification from adapter (via connector servers) + this._socket.on('eventReceived', (res_obj) => { + logger.info('on:eventReceived'); + logger.info('*** Receive Event ***'); + logger.info('chain id :' + this._id); + var events = res_obj.events; + var len = events.length; + logger.info('events.length :' + len); + for (var i = 0; i < len; i++) { + var eventData = events[i].data; + var eventId = events[i].id; + logger.info('event id :' + eventId); + logger.info('event data :'); + logger.info(JSON.stringify(eventData)); + // Request to perform subsequent processing + EventEntrance.action_fromEC(this._id, eventId, eventData); + } + }); + + this._socket.on('monitor_error', (err_obj) => { + logger.error('on:monitor_error'); + logger.error(JSON.stringify(err_obj)); + }); + + } catch (e) { + logger.error(JSON.stringify(e)); + } + } + + end() { + logger.info('Stop monitor.'); + if (this._socket) { + //this._socket.emit('stopMonitor'); + // The monitor will connect a new socket each time it starts, so you have to disconnect it when it stops. + // We have created a connector server side to stop monitoring when the connection is broken, so this is all you need. + this._socket.disconnect(); + } + } +}; + +module.exports = ECMonitor; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/adapter_if.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/adapter_if.js new file mode 100644 index 0000000000..eb5554619d --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/adapter_if.js @@ -0,0 +1,131 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * adapter_if.js + */ + +/* Summary: + * Libraries for EC operation requests via federated servers + * + * Entry point: + * None (For lib) +*/ + +var CoreAPIUtil = require('../CoreAPIUtil.js'); + +// Base package dependency declaration +var util = require('util'); +var process = require('process'); +var io = require('socket.io-client'); +var HttpsProxyAgent = require('https-proxy-agent'); +var proxy = process.env.http_proxy; +var agent = null; +if (proxy) { + agent = new HttpsProxyAgent(proxy); +} +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('adapter_if[' + process.pid + ']'); +logger.level = config.logLevel; + +/** + * send: send request to adapter + * @param {string} url: Adapter URL (Example: "https:// 12.34.56.78: 4030") + * @param {JSON} params: Information to determine the distribution of the operation to be performed +**/ +function send(url, params) { + return new Promise((resolve, reject) => { + // https://socket.io/docs/client-api/ + // set retry with false to avoid infinite loop when connection failure. + // set timeout with default value, to make it easy to edit. + var options = { + rejectUnauthorized: false, // temporary avoidance since self-signed certificates are used + reconnection : false, + timeout : 20000 + }; + if (agent) { + options.agent = agent; + } + var socket = io(url, options); + + // Successful connection from core to adapter + socket.on('connect', () => { + logger.info('on:connect/adapter'); + }); + + // Disconnecting from adapter + socket.on('disconnect', (reason) => { + logger.info('on:disconnect/adapter'); + logger.info(reason); + // Adapter or my self, do not return an explicit disconnect as a failure + if (reason != 'io server disconnect' && reason != 'io client disconnect') { + var ret_obj = { + "status" : 503, + "errorDetail" : reason + }; + return reject(ret_obj); + } + }); + + // Successful connection from core to adapter to connector + socket.on('connect_connecter_server', () => { + logger.info('on:connect/connecter'); + socket.emit('requestEcOperation', params); + }); + + // Connection failure from adapter to connector + socket.on('error_connecter_server', (err_obj) => { + logger.error('on:error/connecter'); + logger.error(JSON.stringify(err_obj)); + return reject(err_obj); + // Disconnect between core and adapter is executed by adapter + }); + + function tmout(err) { + // Connection failed or TIMEOUT. + logger.error(err.toString()); + var emsg = "Cooperation node down?"; + var ret_obj = { + "status" : 404, + "errorDetail" : emsg + }; + if(!socket.disconnected) { + socket.disconnect(); + } + return reject(ret_obj); + } + + // Both are returned as the same error. + socket.on('connect_error', function(err){ + logger.error('on:connect_error/adapter'); + return tmout(err); + }); + socket.on('connect_timeout', function(err){ + logger.error('on:connect_timeout/adapter'); + return tmout(err); + }); + + socket.on('responseEcOperation', function (res) { + logger.info('on:responseEcOperation/adapter'); + if(!socket.disconnected) { + socket.disconnect(); + } + return resolve(res); + }); + + socket.on('errorEcOperation', function (e) { + logger.error('on:errorEcOperation/adapter'); + if(!socket.disconnected) { + socket.disconnect(); + } + return reject(e); + }); + + }); +} + +exports.send = function(url, params) { + return send(url, params); +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/package.json b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/package.json new file mode 100644 index 0000000000..ccd95e4eca --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/exp/package.json @@ -0,0 +1,11 @@ +{ + "name": "exp", + "version": "1.0.0", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "https-proxy-agent": "^2.2.1", + "socket.io-client": "^2.0.4" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/CCMonitor.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/CCMonitor.js new file mode 100644 index 0000000000..2247a6073a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/CCMonitor.js @@ -0,0 +1,127 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * CCMonitor.js + */ + +/* Summary: + * Monitoring events of generating blocks in fabric v 1.0 (For CC) +*/ + +var EventEntrance = require('../EventEntrance.js'); + +// Dependency declaration of base package +var process = require('process'); +var path = require('path'); +var grpc = require('grpc'); +var config = require('config'); + +// Dependency declaration of fabric-client +var sdk = require('./sdk_if.js'); +var utils = require('fabric-client/lib/utils.js'); +var EventHub = require('fabric-client/lib/EventHub.js'); +// Read protobuf definition +var _ccProto = grpc.load(path.join(__dirname, '/node_modules/fabric-client/lib/protos/peer/chaincode.proto')).protos; + +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('CCMonitor[' + process.pid + ']'); +logger.level = config.logLevel; + +var CCMonitor = class { + + constructor(chainId, network) { + this._id = chainId; + this._network = network; + this._eh = null; + } + + start() { + var network = this._network; + var cli = null; + sdk.getClient(network) + .then((client) => { + cli = client; + return sdk.getSubmitter(network.submitter, client, network.mspid, network.ca) + }) + .then((submitter) => { + this._eh = cli.newEventHub(); + this._eh.setPeerAddr(network.peers[0].events); + logger.info('Connecting the event hub'); + this._eh.connect(); + + this._eh.registerBlockEvent((block) => { + logger.info('*** Block Event ***'); + logger.info('chain id :' + this._id); + var len = block.data.data.length; + logger.info('data.data.length :' + len); + for (var i = 0; i < len; i++) { + var payload = block.data.data[i].payload; + var channel_header = payload.header.channel_header; + if (channel_header.type == 'ENDORSER_TRANSACTION') { + var txid = channel_header.tx_id; + logger.info('transaction id :' + txid); + var transaction = payload.data; + var actionPayload = transaction.actions[0].payload; + var proposalPayload = actionPayload.chaincode_proposal_payload; + // Unlike v0.6, invoke/deploy cannot be differentiated by this stage + // * Decoding a deployment transaction in InvocationSpec does not cause a prima facie error + // DeploymentSpec also has a ChaincodeSpec at the beginning, just like InvocationSpec. + // At deployment time, ccid is "lccc" (system chain code) so decision can be made after decode + var invocationSpec = _ccProto.ChaincodeInvocationSpec.decode(proposalPayload.input); + // Chaincode name and argument list (function name at the beginning) can be obtained from invocationSpec + var ccid = invocationSpec.chaincode_spec.chaincode_id.name; + logger.info('chaincode id :' + ccid); + // Refine before performing action. + // Transactions are excluded if they are not from chaincode related ConnectionChain + var isRelated = false; + if (ccid == config.chaincodeID_ec || ccid == config.chaincodeID_ns) { + isRelated = true; + } else { + for (var k = 0; k < config.chaincodeIDs.length; k++) { + if (ccid == config.chaincodeIDs[k]) { + isRelated = true; + break; + } + } + } + if (isRelated) { + var args = invocationSpec.chaincode_spec.input.args; + logger.info('args.length :' + args.length); + for (var j = 0; j < args.length; j++) { + // toString must be coded + args[j] = args[j].toString('utf8'); + logger.info('args[' + j + '] :' + args[j]); + } + // Request to perform subsequent processing + var func = args[0]; + args.shift(); + EventEntrance.action_fromCC(this._id, txid, ccid, func, args); + } else { + logger.info('not related to connection chain.'); + } + } + } + }); + + }) + .catch((err) => { + logger.error(err); + }); + } + + end() { + if (this._eh == null) { + logger.error('EventHub does not exist'); + return; + } + if (this._eh.isconnected()) { + logger.info('Disconnecting the event hub'); + this._eh.disconnect(); + } + } + +}; + +module.exports = CCMonitor; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/package.json b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/package.json new file mode 100644 index 0000000000..20e5ab1830 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/package.json @@ -0,0 +1,12 @@ +{ + "name": "fabric_v1.0", + "version": "1.0.0", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "fabric-client": "1.0.2", + "fabric-ca-client": "1.0.2", + "grpc": "1.4.1" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/sdk_if.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/sdk_if.js new file mode 100644 index 0000000000..4067c99f38 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/common/fabric_v1.0/sdk_if.js @@ -0,0 +1,490 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * sdk_if.js + */ + +/* Summary: + * Library for fabricv 1.0 invoke, query requests + * + * Entry point: + * None (For lib) +*/ + +var CoreAPIUtil = require('../CoreAPIUtil.js'); + +// Dependency declaration of base package +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var process = require('process'); +var config = require('config'); + +// Dependency declaration of fabric-client +var fclient = require('fabric-client'); +var utils = require('fabric-client/lib/utils.js'); +var Peer = require('fabric-client/lib/Peer.js'); +var Orderer = require('fabric-client/lib/Orderer.js'); +var User = require('fabric-client/lib/User.js'); +var EventHub = require('fabric-client/lib/EventHub.js'); + +// Dependency declaration of fabric-ca-client +var copService = require('fabric-ca-client/lib/FabricCAClientImpl.js'); + +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('sdk_if[' + process.pid + ']'); +logger.level = config.logLevel; + +// List of fabric-client objects +var clients = {} + +/** + * invokeRequest: invoke instruction in fabric v 1.0 chained code + * @param {string} chainId: Chain ID (Example: "ConnectionChain") * No longer needed + * @param {JSON} network: Chain network information (following format) + * JSON: { + * "orderer": (string) URL of order. e.g. "grpc://localhost:7050" + * "ca" { + * "url": (string) URL of ca,// e.g. "http://localhost:7054", + * "name": (string) Name of ca (Value specified by FABRIC_CA_SERVER_CA_NAME in the yaml file) + * }, + * "mspid": (string) MSPID of the organization to which peers belongs ,// For example , "Org1 MSP" + * "peers": []{ + * "requests": (string) URL for request ,// ex : "grpc://localhost:7051" + * "events": (string) event standby URL ,// ex : "grpc://localhost:7053" + * }, + * "submitter": { + * "name": (string) operation user name ,// ex : "admin" + * "secret": (string) Operation user password// For example : "adminpw" + * } + * } + * @param {string} channelName: Channel name (Example: "mychannel") + * @param {string} chaincodeID: Chain code name (Example: "endchain_information") + * @param {string} func: invoke function name (Example: "deleteECInfo") + * @param {[]string} args: invoke argument (Example: "['Chain1']") + * @param {bool} isWait: flag enable/disable wait for block generation +**/ +function invokeRequest(chainId, network, channelName, chaincodeID, func, args, isWait) { + var tx_id = null; + var nonce = null; + var the_user = null; + var eventhubs = []; + var invokeResponse = {uuid:null, data:null}; + + return new Promise((resolve, reject) => { + var channel = null; + var client = null; + getClientAndChannel(channelName, network) + .then((retObj) => { + channel = retObj.channel; + client = retObj.client; + + return getSubmitter(network.submitter, client, network.mspid, network.ca); + }) + .then((admin) => { + // Set a successful Enroll User object + the_user = admin; + + // Put an EventHub connection to the EP + for (var i = 0; i < network.peers.length; i++) { + var eh = client.newEventHub(); + eh.setPeerAddr(network.peers[i].events); + eh.connect(); + eventhubs.push(eh); + } + + return channel.initialize(); + }).then((nothing) => { + + // Generate a request object for Invoke execution + tx_id = client.newTransactionID(); + invokeResponse.uuid = tx_id.getTransactionID(); + var request = { + chaincodeId : chaincodeID, + fcn: func, + args: args, + txId: tx_id + }; + // Proposal Execution + logger.info('sendTransactionProposal start, txid=' + tx_id.getTransactionID()); + return channel.sendTransactionProposal(request); + }) + .then((results) => { + logger.info('sendTransactionProposal end'); + // Check that the Proposal response returned by EP is successful + var proposalResponses = results[0]; + var proposal = results[1]; + var header = results[2]; + var all_good = true; + var resStr = ''; // invoke function with return value for storing results + for(var i in proposalResponses) { + let one_good = false; + if (proposalResponses && proposalResponses[i].response && proposalResponses[i].response.status === 200) { + if (proposalResponses[i].response.payload) { + resStr = new String(proposalResponses[i].response.payload); + invokeResponse.data = resStr; + } + one_good = true; + logger.info('transaction proposal was good'); + } else { + logger.error('transaction proposal was bad'); + } + all_good = all_good & one_good; + } + if (all_good) { + // Waiting for transaction events from EventHub + var invokeId = tx_id.getTransactionID(); + var cnt = 0; + eventhubs.forEach((eh) => { + eh.registerTxEvent(invokeId.toString(), (tx, code) => { + eh.unregisterTxEvent(invokeId); + if (code !== 'VALID') { + logger.error('Transaction Commited Failure, code = ' + code + ', ' + eh.getPeerAddr() + ', tx_id=' + invokeId); + } else { + logger.info('Transaction Commited, ' + eh.getPeerAddr() + ', tx_id=' + invokeId); + } + // disconnect from the EventHub server + if (eh.isconnected()) { + logger.info('Disconnecting the event hub'); + eh.disconnect(); + } + // When waiting for block generation, resolve when all transaction events from EventHub are complete + cnt++; + if (cnt == eventhubs.length && isWait) { + return resolve(invokeResponse); + } + }); + }); + // sendTransaction to Ordering Service + var request = { + proposalResponses: proposalResponses, + proposal: proposal, + header: header + }; + logger.info('sendTransaction(to Orderer) start'); + return channel.sendTransaction(request) + .then((response) => { + if (response.status === 'SUCCESS') { + logger.info('sendTransaction(to Orderer) end'); + if (!isWait) { + // resolve now if you do not want to wait for block generation + return resolve(invokeResponse); + } + } else { + throw new Error('Failed to order the transaction. Error code: ' + response.status); + } + }); + } else { + // come here if the chaincode itself returns an error + // Unlike query, error messages from chain codes cannot be retrieved. Please refer to the log. + throw new Error('Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'); + } + }) + .catch((err) => { + eventhubs.forEach((eh) => { + // disconnect from the EventHub server + if (eh.isconnected()) { + logger.info('Disconnecting the event hub'); + eh.disconnect(); + } + }); + return reject(err); + }); + }); +} + +/** + * queryRequest: query request to chaincode of fabric v1.0 + * @param {string} chainId: Chain ID (Example: "ConnectionChain") * No longer needed + * @param {JSON} network: Chain network information (following format) + * JSON: { + * "orderer": (string) URL of order. e.g. "grpc://localhost:7050" + * "ca" { + * "url": (string) URL of ca,// e.g. "http://localhost:7054", + * "name": (string) Name of ca (Value specified by FABRIC_CA_SERVER_CA_NAME in the yaml file) + * }, + * "mspid": (string) MSPID of the organization to which peers belongs ,// For example , "Org1 MSP" + * "peers": []{ + * "requests": (string) URL for request ,// ex : "grpc://localhost:7051" + * "events": (string) event standby URL ,// ex : "grpc://localhost:7053" * not used for query + * }, + * "submitter": { + * "name": (string) operation user name ,// ex : "admin" + * "secret": (string) Operation user password// For example : "adminpw" + * } + * } + * @param {string} channelName: Channel name (Example: "mychannel") + * @param {string} chaincodeID: Chain code name (Example: "endchain_information") + * @param {string} func: query function name (Example: "getRuleInfoList") + * @param {[]string} args: query argument (Example: "['', '']") +**/ +function queryRequest(chainId, network, channelName, chaincodeID, func, args) { + var tx_id = null; + var nonce = null; + var the_user = null; + + return new Promise((resolve, reject) => { + var channel = null; + var client = null; + getClientAndChannel(channelName, network) + .then((retObj) => { + channel = retObj.channel; + client = retObj.client + return getSubmitter(network.submitter, client, network.mspid, network.ca); + }) + .then((admin) => { + // Set a successful Enroll User object + the_user = admin; + + return channel.initialize(); + }).then((nothing) => { + + // Generate a request object for Query execution + tx_id = client.newTransactionID(); + var request = { + chaincodeId : chaincodeID, + fcn: func, + args: args, + txId: tx_id + }; + // Run Query + logger.info('queryByChaincode start, txid=' + tx_id.getTransactionID()); + return channel.queryByChaincode(request); + }) + .then((response_payloads) => { + logger.info('queryByChaincode end'); + if (response_payloads) { + var resStr = ''; + var errMsg = ''; + var valid = false; + for(let i = 0; i < response_payloads.length; i++) { + resStr = new String(response_payloads[i]); + // contains an error message if the chaincode itself returns an error + // Error message from SDK such as connection failure (Error: Connect Failed) + if (resStr.indexOf('Error:') == 0){ + errMsg = resStr; + } else { + valid = true; + break; + } + } + if (valid) { + // return one normal result if any + return resolve(resStr); + } else { + // Response from all EPs is error + throw new Error(errMsg); + } + } else { + throw new Error('response_payloads is null'); + } + }) + .catch((err) => { + return reject(err); + }); + }); +} + +// export the above function (external library that can be referenced by other functions) +exports.invokeRequest = function(chainId, network, channelName, chaincodeID, func, args) { + // CC always does not wait for block generation + return invokeRequest(chainId, network, channelName, chaincodeID, func, args, false); +} +exports.queryRequest = function(chainId, network, channelName, chaincodeID, func, args) { + return queryRequest(chainId, network, channelName, chaincodeID, func, args); +} + +// fabric-client and Channel object generation +function getClientAndChannel(channelName, network) { + var retObj = {client:null, channel:null}; + // Because only one KVS can be set for client, it is managed on a per-CA basis, similar to a KVS path. + var isNewClient = false; + var client = clients[network.ca.name]; + if (!client) { + logger.info('create new fabric-client'); + client = new fclient(); + clients[network.ca.name] = client; + isNewClient = true; + } + + var channel = null; + + // * If there is no getChannel in the v 1.0 SDK, so try .. catch + // This will always log an error from Client.js the first time, but there is no harm. + try { + channel = client.getChannel(channelName); + // Reflect the difference if the current peer and orderer set in channel are different from the destination specified this time + // TODO: I think this will work, but I don't know how it works when I change it. + var oldPeers = channel.getPeers(); + var oldPeerUrls = []; + for (var i = 0; i < oldPeers.length; i++) { + oldPeerUrls[i] = oldPeers[i].getUrl(); + } + var newPeerUrls = []; + for (var i = 0; i < network.peers.length; i++) { + newPeerUrls[i] = network.peers[i].requests; + } + // old, then new array generated + var allPeerUrls = [...oldPeerUrls, ...newPeerUrls]; + for (var [i, e] of allPeerUrls.entries()) { + if(!oldPeerUrls.includes(e)) { + // added because it is not in the current peers + var newPeer = client.newPeer(e); + channel.addPeer(newPeer); + } + if(!newPeerUrls.includes(e)) { + // deleted because it is not in the current peers + channel.removePeer(oldPeers[i]); + } + } + // Currently orderer is specified as the only one, so replace it if it is different + var oldOrderer = channel.getOrderers(); + var newOrderer = network.orderer; + if (oldOrderer[0].getUrl() != newOrderer) { + channel.removeOrderer(oldOrderer[0]); + channel.addOrderer(client.newOrderer(newOrderer)); + } + + }catch(e) { + if(channel == null) { + logger.info('create new channel, name=' + channelName); + channel = client.newChannel(channelName); + // orderConfiguration + channel.addOrderer(client.newOrderer(network.orderer)); + // EP Settings + for (var i = 0; i < network.peers.length; i++) { + var peer = client.newPeer(network.peers[i].requests); + channel.addPeer(peer); + } + } else { + // Exception when reflecting of difference of the destination information + logger.error(e); + } + } + retObj.channel = channel; + + return new Promise((resolve, reject) => { + if (isNewClient) { + var storePath = '/tmp/kvs-' + network.ca.name; + var cryptoSuite = fclient.newCryptoSuite(); + cryptoSuite.setCryptoKeyStore( + fclient.newCryptoKeyStore({ + path: storePath + }) + ); + client.setCryptoSuite(cryptoSuite); + + // Generate a storage location of Enrollment information + return fclient.newDefaultKeyValueStore({ + path: storePath + }) + .then((store) => { + // Set KeyValue store + client.setStateStore(store); + retObj.client = client; + resolve(retObj); + }) + } else { + retObj.client = client; + resolve(retObj); + } + }); +} + +// fabric-client object generation +function getClient(network) { + // Because only one KVS can be set for client, it is managed on a per-CA basis, similar to a KVS path. + var isNewClient = false; + var client = clients[network.ca.name]; + if (!client) { + logger.info('create new fabric-client'); + client = new fclient(); + clients[network.ca.name] = client; + isNewClient = true; + } + + return new Promise((resolve, reject) => { + if (isNewClient) { + var storePath = '/tmp/kvs-' + network.ca.name; + var cryptoSuite = fclient.newCryptoSuite(); + cryptoSuite.setCryptoKeyStore( + fclient.newCryptoKeyStore({ + path: storePath + }) + ); + client.setCryptoSuite(cryptoSuite); + + // Generate a storage location of Enrollment information + return fclient.newDefaultKeyValueStore({ + path: storePath + }) + .then((store) => { + // Set KeyValue store + client.setStateStore(store); + resolve(client); + }) + } else { + resolve(client); + } + }); +} + + +// Get the user object to submit a proposal to EP +// * This is because it is set by setUserContext and used inside client. +// The returned user object itself is no longer used. +function getSubmitter(submitter, cli, mspID, ca) { + var caUrl = ca.url; + var caName = ca.name; + return cli.getUserContext(submitter.name, true) + .then((user) => { + return new Promise((resolve, reject) => { + if (user && user.isEnrolled()) { + return resolve(user); + } + + var member = new User(submitter.name); + var cryptoSuite = cli.getCryptoSuite(); + if (!cryptoSuite) { + var storePath = '/tmp/kvs-' + caName; + cryptoSuite = fclient.newCryptoSuite(); + cryptoSuite.setCryptoKeyStore( + fclient.newCryptoKeyStore({ + path: storePath + }) + ); + cli.setCryptoSuite(cryptoSuite); + } + member.setCryptoSuite(cryptoSuite); + + var tlsOptions = { + trustedRoots: [], + verify: false + }; + + var cop = new copService(caUrl,tlsOptions, caName, cryptoSuite); + return cop.enroll({ + enrollmentID: submitter.name, + enrollmentSecret: submitter.secret + }).then((enrollment) => { + return member.setEnrollment(enrollment.key, enrollment.certificate, mspID); + }).then(() => { + return cli.setUserContext(member, false); + }).then(() => { + return resolve(member); + }).catch((err) => { + return reject(err); + }); + }); + }); +} + +// exports for use by the Monitor class for 1.0 +exports.getClient = function(network) { + return getClient(network); +} +exports.getSubmitter = function(submitter, cli, mspID, ca) { + return getSubmitter(submitter, cli, mspID, ca); +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js new file mode 100644 index 0000000000..e014c31b88 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js @@ -0,0 +1,95 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * EventMediator.js + */ + +/* Summary: + * Allocate subsequent processing for chaincodes added to CC by user and events from EC +*/ + +var fabricv10Post = require("../common/fabric_v1.0/sdk_if.js"); +var expPost = require("../common/exp/adapter_if.js"); +var EcInfoAction = require("../common/EcInfoAction.js"); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('EventMediator[' + process.pid + ']'); +logger.level = config.logLevel; + + +/** + * Perform subsequent processing of detected events from your own additional chaincode + * @param {string} ccid : Chain Code ID + * @param {string} func : invoke execute function + * @param {[]string} args : invoke run-time arguments +**/ +exports.receivedUserChaincodeEvent = function(ccid, func, args) { + if (ccid == config.chaincodeIDs[0]) { + // describe the processing for each chaincode listed in chaincodeIDs + // Example) + /* + // Get the ID of the end-chain to operate on, for example, from the value of args + var chainID = args[0]; + // Get adapter URL from end-chain info + EcInfoAction.getECInfoCache(chainID) + .then((ecCache) => { + var adapterUrl = ecCache._adapterUrl; + // Build parameters to pass to the adapter from args values, etc. + var params = { + type: "test", + info: args[2] + }; + // API execution of the adapter + return expPost.send(adapterUrl, params) + }) + .then((response) => { + }) + .catch((err) => { + }) + */ + } +} + +/** + * Successive processing of detected events from EC (via connector -> adapter) + * @param {string} cainId : Chain ID + * @param {string} eventId : Event ID + * @param {JSON} eventInfo : Event information +**/ +exports.receivedEndchainEvent = function(chainId, eventId, eventInfo) { + // Write an event description or other context sensitive action + // Example) + /* + var args = []; + var func = ""; + if (eventInfo.status == 0) { + args = [chainId, eventId, eventInfo.dataX, "dummy"]; + func = "function1"; + } else { + args = [chainId, eventId, eventInfo.dataY]; + func = "function2"; + } + // Request execution to your own additional chaincode + fabricv10Post.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + func, + args) + .then((response) => { + }) + .catch((err) => { + }); + */ +} + + +/** + * Initial processing at server startup +**/ +exports.initAction = function() { + // Write what to do at server startup +} + diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js new file mode 100644 index 0000000000..cbe8c470fd --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js @@ -0,0 +1,17 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * UserErrorDef.js + */ + +/* Summary: + * User Defined Error +*/ + +// Error code & message definition +const definitions = { +// Describe in errorCode:"errorMessage" format +}; + +module.exports = definitions; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/package.json b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/package.json new file mode 100644 index 0000000000..1507b3db43 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/package.json @@ -0,0 +1,21 @@ +{ + "name": "coreapi", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.17.1", + "config": "^1.26.1", + "cookie-parser": "~1.4.3", + "debug": "~4.1.1", + "express": "~4.15.2", + "https-proxy-agent": "^2.2.1", + "jade": "~1.11.0", + "log4js": "^3.0.6", + "morgan": "~1.8.1", + "request": "^2.81.0", + "serve-favicon": "~2.4.2" + } +} diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/ecaccounts.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/ecaccounts.js new file mode 100644 index 0000000000..98cacaca0e --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/ecaccounts.js @@ -0,0 +1,329 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ecaccounts.js + */ + +/* Summary: + * EC Account Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var fabricSdkPost = require("../../lib/common/fabric_v1.0/sdk_if.js"); +var EcInfoAction = require('../../lib/common/EcInfoAction.js'); +var CoreAPIError = require('../../lib/common/CoreAPIError.js'); +var CoreAPIUtil = require('../../lib/common/CoreAPIUtil.js'); +var router = express.Router(); + + +/** + * EC account information generation + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "userID": "", - CC user ID tied to the account + * "chainID": "", - ID of the EC to which the account belongs + * "accountID": "", - ID of the EC-side account, such as when using the EC API + * "alias": "", - display name, for example, on the UI + * "authInfo": "" - Parameters used when authentication is required for EC-side account operations (Omit if authentication is not required.) + * } + * Response Body: + * { + * "ECAccountID": "" - ID of the registered EC account information + * } +**/ +router.post('/', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var userID = req.body.userID; + if (userID == undefined) { + next(new CoreAPIError(422, 2001)); + return; + } + var chainID = req.body.chainID; + if (chainID == undefined) { + next(new CoreAPIError(422, 2002)); + return; + } + var accountID = req.body.accountID; + if (accountID == undefined) { + next(new CoreAPIError(422, 2003)); + return; + } + var alias = req.body.alias; + if (alias == undefined) { + next(new CoreAPIError(422, 2004)); + return; + } + + var invokeArgs = [userID, chainID, accountID, alias]; + + // Only in the credential, argument itself omitted if unspecified + var authInfo = req.body.authInfo; + if (authInfo != undefined) { + invokeArgs.push(authInfo); + } + + fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeID_ns, + 'addECAccount', + invokeArgs) + .then((response) => { + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {ECAccountID:response.data}; + res.status(201).send(resObj); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Update EC account information + * @name put/:id + * @function + * @inner + * @param {string} path - EC account ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * * Elements that should not be changed can be omitted. + * { + * "userID": "", + * "chainID": "", + * "accountID": "", + * "alias": "", + * "authInfo": "" + * } + * Response Body: + * { + * "ECAccountID": "" - ID of the updated EC account information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var id = req.params.id; + + // Unspecified parameter specifies an empty string (Never Update) + var userID = req.body.userID; + if (userID == undefined) { + userID = ''; + } + var chainID = req.body.chainID; + if (chainID == undefined) { + chainID = ''; + } + var accountID = req.body.accountID; + if (accountID == undefined) { + accountID = ''; + } + var alias = req.body.alias; + if (alias == undefined) { + alias = ''; + } + + var invokeArgs = [id, userID, chainID, accountID, alias]; + + // Only in the credential, argument itself omitted if unspecified + var authInfo = req.body.authInfo; + if (authInfo != undefined) { + invokeArgs.push(authInfo); + } + + fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeID_ns, + 'updateECAccount', + invokeArgs) + .then((response) => { + if (response.data != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {ECAccountID:response.data}; + res.send(resObj); + }else{ + next(new CoreAPIError(404, 1001)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Delete EC account information + * @name delete/:id + * @function + * @inner + * @param {string} path - EC account ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + var invokeArgs = [req.params.id]; + fabricSdkPost.invokeRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ns, + 'deleteECAccount', + invokeArgs) + .then((response) => { + res.status(204).send(); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Acquire one of EC account information + * @name get/:id + * @function + * @inner + * @param {string} path - EC account ID to retrieve + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "userID": "", + * "chainID": "", + * "chainName": "", + * "accountID": "", + * "alias": "", + * "authInfo": "" + * } +**/ +router.get('/:id', function(req, res, next) { + var queryArgs = [req.params.id]; + var resInfo = {}; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ns, + 'getECAccount', + queryArgs) + .then((response) => { + if (response != ''){ + resInfo = JSON.parse(response); + // Get destination EC information by specifying chain ID + return EcInfoAction.getECInfoCache(resInfo.chainID); + }else{ + next(new CoreAPIError(404, 1001)); + } + }) + .then((ecCache) => { + resInfo.chainName = ecCache._chainName; // give a chain name + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(resInfo); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Acquisition of EC account information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Response Body:Array of EC account information +**/ +router.get('/', function(req, res, next) { + var queryArgs = []; + var userID = req.query.userID; + var chainID = req.query.chainID; + + if (userID != undefined) { + queryArgs.push(userID); + if (chainID != undefined) { + queryArgs.push(chainID); + } + } else { + // Error if the specified arguments is only the chain ID + if (chainID != undefined) { + next(new CoreAPIError(422, 2001)); + return; + } + } + var resArray = []; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ns, + 'getECAccountList', + queryArgs) + .then((response) => { + resArray = JSON.parse(response); + // Provide a chain name for each EC account information in the array + return setChainName(resArray, 0) + }) + .then((accountList) => { + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(accountList); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +function setChainName(accountList, index) { + var listLength = 0; + if (accountList != null) { + listLength = accountList.length; + } + return new Promise((resolve, reject) => { + if (index < listLength) { + EcInfoAction.getECInfoCache(accountList[index].chainID) + .then((ecCache) => { + accountList[index].chainName = ecCache._chainName; // give a chain name + return setChainName(accountList, index+1) + }) + .then((returnList) => { + // resolve below (accountList); Returns the return value from to the nested caller in order + /* + Chain name given to accountList [0] + Chain name given to accountList [1] + Chain name given to accountList [2] + resolve(accountList); + resolve(returnList); + resolve(returnList); + resolve(returnList); <- To the original call Acquisition of EC account information list of "then" + */ + return resolve(returnList); + }) + .catch((err) => { + return reject(err); + }); + } else { + // When all the EC account information in the array has been processed, return + return resolve(accountList); + } + }); +} + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/endchains.js b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/endchains.js new file mode 100644 index 0000000000..ded0463dfc --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/coreapi/routes/common/endchains.js @@ -0,0 +1,236 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * endchains.js + */ + +/* Summary: + * Destination EC information management API +*/ + +var express = require('express'); +var config = require('config'); +var fabricSdkPost = require("../../lib/common/fabric_v1.0/sdk_if.js"); +var CoreAPIError = require('../../lib/common/CoreAPIError.js'); +var CoreAPIUtil = require('../../lib/common/CoreAPIUtil.js'); +var router = express.Router(); + + +/** + * Destination EC information generation + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "chainID": "", - ID of the destination EC information to register + * "chainName": "", - display name, for example, on the UI + * "adapterUrl": "" - URL of the adapter server to the appropriate EC + * } + * Response Body: + * { + * "chainID": "" - ID of registered destination EC information + * } +**/ +router.post('/', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var chainID = req.body.chainID; + if (chainID == undefined) { + next(new CoreAPIError(422, 2002)); + return; + } + var chainName = req.body.chainName; + if (chainName == undefined) { + next(new CoreAPIError(422, 2005)); + return; + } + var adapterUrl = req.body.adapterUrl; + if (adapterUrl == undefined) { + next(new CoreAPIError(422, 2007)); + return; + } + + var invokeArgs = [chainID, chainName, adapterUrl]; + + fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'addECInfo', + invokeArgs) + .then((response) => { + if (response.data != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {chainID:response.data}; + res.status(201).send(resObj); + }else{ + next(new CoreAPIError(500, 3002)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Update destination EC information + * @name put/:id + * @function + * @inner + * @param {string} path - Chain ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * * Elements that should not be changed can be omitted. + * { + * "chainName": "", + * "adapterUrl": "" + * } + * Response Body: + * { + * "chainID": "" - ID of the updated destination EC information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var id = req.params.id; + + // Unspecified parameter specifies an empty string (Never Update) + var chainName = req.body.chainName; + if (chainName == undefined) { + chainName = ''; + } + var adapterUrl = req.body.adapterUrl; + if (adapterUrl == undefined) { + adapterUrl = ''; + } + + var invokeArgs = [id, chainName, adapterUrl]; + + fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'updateECInfo', + invokeArgs) + .then((response) => { + if (response.data != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {chainID:response.data}; + res.send(resObj); + }else{ + next(new CoreAPIError(404, 1002)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Delete destination EC information + * @name delete/:id + * @function + * @inner + * @param {string} path - Chain ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + var invokeArgs = [req.params.id]; + fabricSdkPost.invokeRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'deleteECInfo', + invokeArgs) + .then((response) => { + res.status(204).send(); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Obtain one destination EC information + * @name get/:id + * @function + * @inner + * @param {string} path - Chain ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "chainName": "", + * "adapterUrl": "" + * } +**/ +router.get('/:id', function(req, res, next) { + var queryArgs = [req.params.id]; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'getECInfo', + queryArgs) + .then((response) => { + if (response != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(JSON.parse(response)); + }else{ + next(new CoreAPIError(404, 1002)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Get the list of destination EC information + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Response Body:Array of destination EC information +**/ +router.get('/', function(req, res, next) { + var queryArgs = []; + + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeID_ec, + 'getECInfoList', + queryArgs) + .then((response) => { + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(JSON.parse(response)); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/idle.sh b/packages/connection-chain/environment/base/cc_env/servers/restserver/idle.sh new file mode 100755 index 0000000000..ebc78ff136 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/idle.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo "This is a idle script (infinite loop) to keep container running." +echo "Please replace this script." + +cleanup () +{ + kill -s SIGTERM $! + exit 0 +} + +trap cleanup SIGINT SIGTERM + +while [ 1 ] +do + sleep 60 & + wait $! +done diff --git a/packages/connection-chain/environment/base/cc_env/servers/restserver/setup.sh b/packages/connection-chain/environment/base/cc_env/servers/restserver/setup.sh new file mode 100755 index 0000000000..39bc2bc4fc --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/servers/restserver/setup.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi +npm install +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/lib/common/exp +npm install +cd /opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/lib/common/fabric_v1.0 +npm install diff --git a/packages/connection-chain/environment/base/cc_env/yaml-files/configtx.yaml b/packages/connection-chain/environment/base/cc_env/yaml-files/configtx.yaml new file mode 100644 index 0000000000..ba226fe5a2 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/yaml-files/configtx.yaml @@ -0,0 +1,46 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +--- +Profiles: + OneOrgOrdererGenesis: + Orderer: + <<: *OrdererDefaults + Organizations: + - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org1 + OneOrgChannel: + Consortium: SampleConsortium + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 +Organizations: + - &OrdererOrg + Name: OrdererOrg + ID: OrdererMSP + MSPDir: ../crypto-config/ordererOrganizations/example.com/msp + - &Org1 + Name: Org1MSP + ID: Org1MSP + MSPDir: ../crypto-config/peerOrganizations/org1.example.com/msp + AnchorPeers: + - Host: peer0.org1.example.com + Port: 7051 +Orderer: &OrdererDefaults + OrdererType: solo + Addresses: + - orderer.example.com:7050 + BatchTimeout: 2s + BatchSize: + MaxMessageCount: 10 + AbsoluteMaxBytes: 99 MB + PreferredMaxBytes: 512 KB + Kafka: + Brokers: + - 127.0.0.1:9092 + Organizations: +Application: &ApplicationDefaults + Organizations: diff --git a/packages/connection-chain/environment/base/cc_env/yaml-files/crypto-config.yaml b/packages/connection-chain/environment/base/cc_env/yaml-files/crypto-config.yaml new file mode 100644 index 0000000000..bc4abd424a --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/yaml-files/crypto-config.yaml @@ -0,0 +1,14 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +OrdererOrgs: + - Name: Orderer + Domain: example.com + Specs: + - Hostname: orderer +PeerOrgs: + - Name: Org1 + Domain: org1.example.com + Template: + Count: 4 + Users: + Count: 1 diff --git a/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-cc-template.yaml b/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-cc-template.yaml new file mode 100644 index 0000000000..43585e46fa --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-cc-template.yaml @@ -0,0 +1,193 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '2' +networks: + ccnet: + external: true +services: + ca.example.com: + image: hyperledger/fabric-ca:x86_64-1.0.4 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.example.com +# ports: +# - "7054:7054" + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/CA1_PRIVATE_KEY -b admin:adminpw -d' + volumes: + - ../crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + container_name: ca.example.com + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + orderer.example.com: + container_name: orderer.example.com + image: hyperledger/fabric-orderer:x86_64-1.0.4 + environment: + - ORDERER_GENERAL_LOGLEVEL=debug + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISMETHOD=file + - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block + - ORDERER_GENERAL_LOCALMSPID=OrdererMSP + - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: orderer + volumes: + - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls +# ports: +# - 7050:7050 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "3" + peer0.org1.example.com: + container_name: peer0.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer0.org1.example.com + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 7051:7051 +# - 7053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + peer1.org1.example.com: + container_name: peer1.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer1.org1.example.com + - CORE_PEER_ADDRESS=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 8051:7051 +# - 8053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + peer2.org1.example.com: + container_name: peer2.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer2.org1.example.com + - CORE_PEER_ADDRESS=peer2.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer2.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 9051:7051 +# - 9053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + peer3.org1.example.com: + container_name: peer3.org1.example.com + extends: + file: peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer3.org1.example.com + - CORE_PEER_ADDRESS=peer3.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer3.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer3.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer3.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer3.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 10051:7051 +# - 10053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + - peer2.org1.example.com + cli: + container_name: cli + image: hyperledger/fabric-tools:x86_64-1.0.4 + tty: true + environment: + - GOPATH=/opt/gopath + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_LOGGING_LEVEL=DEBUG + - CORE_PEER_ID=cli + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + - 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 + - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer + command: /bin/bash -c './scripts/deploy_script.sh ${CHANNEL_NAME} ${DELAY} ${MODE}; sleep $TIMEOUT' + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ + - ../channel-artifacts/:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/ + - ../chaincode/naming_service/:/opt/gopath/src/github.com/hyperledger/fabric/work/chaincode/naming_service/ + - ../chaincode/endchain_information/:/opt/gopath/src/github.com/hyperledger/fabric/work/chaincode/endchain_information/ + - ../chaincode/deploy_script.sh:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/deploy_script.sh + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + - peer2.org1.example.com + - peer3.org1.example.com + networks: + - ccnet diff --git a/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-servers.yaml b/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-servers.yaml new file mode 100644 index 0000000000..47e63d64aa --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/yaml-files/docker-compose-base-servers.yaml @@ -0,0 +1,123 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '2' +networks: + ccnet: + external: true + ec1net: + external: true + ec2net: + external: true +services: + rest-server: + build: ../servers/restserver/build + image: hyperledger/ccenv-node6-rest-server + container_name: rest-server + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/restserver:/opt/gopath/src/github.com/hyperledger/fabric/work/server + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '3030:3030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + app-server: + build: ../servers/appserver/build + image: hyperledger/ccenv-node6-app-server + container_name: app-server + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/appserver:/opt/gopath/src/github.com/hyperledger/fabric/work/server + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '3031:3031' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec1-adapter: + build: ../servers/cooperation/coreSide/build + image: hyperledger/ccenv-node8-adapter-server + container_name: ec1-adapter + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/cooperation/coreSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../servers/cooperation/coreSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../servers/cooperation/coreSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../servers/cooperation/coreSide/ec1_adapter:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '5030:5030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec1-connector: + build: ../servers/cooperation/ecSide/build + image: hyperledger/ccenv-node8-connector-server + container_name: ec1-connector + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/cooperation/ecSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../servers/cooperation/ecSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../servers/cooperation/ecSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../servers/cooperation/ecSide/ec1_connector:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector + networks: + - ec1net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '5040:5040' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec2-adapter: + image: hyperledger/ccenv-node8-adapter-server + container_name: ec2-adapter + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/cooperation/coreSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../servers/cooperation/coreSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../servers/cooperation/coreSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../servers/cooperation/coreSide/ec2_adapter:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '6030:6030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec2-connector: + image: hyperledger/ccenv-node8-connector-server + container_name: ec2-connector + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../servers/cooperation/ecSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../servers/cooperation/ecSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../servers/cooperation/ecSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../servers/cooperation/ecSide/ec2_connector:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector + networks: + - ec2net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '6040:6040' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" diff --git a/packages/connection-chain/environment/base/cc_env/yaml-files/peer-base.yaml b/packages/connection-chain/environment/base/cc_env/yaml-files/peer-base.yaml new file mode 100644 index 0000000000..539e381364 --- /dev/null +++ b/packages/connection-chain/environment/base/cc_env/yaml-files/peer-base.yaml @@ -0,0 +1,16 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '2' +services: + peer-base: + image: hyperledger/fabric-peer:x86_64-1.0.4 + environment: + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=ccnet + - CORE_LOGGING_LEVEL=ERROR + - CORE_PEER_TLS_ENABLED=false + - CORE_PEER_GOSSIP_USELEADERELECTION=true + - CORE_PEER_GOSSIP_ORGLEADER=false + - CORE_PEER_PROFILE_ENABLED=true + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer + command: peer node start diff --git a/packages/connection-chain/environment/network-topology.png b/packages/connection-chain/environment/network-topology.png new file mode 100644 index 0000000000..cb5d88fc4d Binary files /dev/null and b/packages/connection-chain/environment/network-topology.png differ diff --git a/packages/connection-chain/environment/sample/cc_env/cc_setup_base+sample.sh b/packages/connection-chain/environment/sample/cc_env/cc_setup_base+sample.sh new file mode 100755 index 0000000000..689b416c0d --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/cc_setup_base+sample.sh @@ -0,0 +1,295 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +# prepending $PWD/../bin to PATH to ensure we are picking up the correct binaries +# this may be commented out to resolve installed version of tools if desired +export PATH=${PWD}/../../base/cc_env/bin:${PWD}:$PATH +export FABRIC_CFG_PATH=${PWD}/yaml-files + +# Print the usage message +function printHelp () { + echo "Usage: " + echo " cc_setup_base+sample.sh -m up|down|restart|generate [-t ] [-d ] [-p ]" + echo " cc_setup_base+sample.sh -h|--help (print this message)" + echo " -m - one of 'up', 'down', 'restart' or 'generate'" + echo " - 'up' - bring up the network with docker-compose up" + echo " - 'down' - clear the network with docker-compose down" + echo " - 'restart' - restart the network" + echo " - 'generate' - generate required certificates and genesis block" + echo " -t - CLI timeout duration in microseconds (defaults to 30000)" + echo " -d - delay duration in seconds (defaults to 3)" + echo " -p - specify which proxy use (defaults to blank)" + echo + echo "Taking all defaults:" + echo " cc_setup_base+sample.sh -m up" + echo " cc_setup_base+sample.sh -m down" +} + +# Ask user for confirmation to proceed +function askProceed () { + read -p "Continue (y/n)? " ans + case "$ans" in + y|Y ) + echo "proceeding ..." + ;; + n|N ) + echo "exiting..." + exit 1 + ;; + * ) + echo "invalid response" + askProceed + ;; + esac +} + +# Generate the needed certificates, the genesis block and start the network. +function networkUp () { + docker network create ccnet + + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY rest-server 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY app-server 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec1-adapter 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec1-connector 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec2-adapter 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE build --build-arg HTTP_PROXY=$PROXY ec2-connector 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + docker-compose -f $SV_COMPOSE_FILE up -d 2>&1 + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to pull the images " + exit 1 + fi + + # generate artifacts if they don't exist + if [ ! -d "crypto-config" ]; then + generateCerts + replacePrivateKey + generateChannelArtifacts + fi + + chmod 666 $PWD/../../base/cc_env/chaincode/naming_service/naming_service.go + chmod 666 $PWD/../../base/cc_env/chaincode/endchain_information/endchain_information.go + chmod 666 $PWD/chaincode/transfer_information/* + + CHANNEL_NAME=$CHANNEL_NAME TIMEOUT=$CLI_TIMEOUT DELAY=$CLI_DELAY MODE=$MODE docker-compose -f $CC_COMPOSE_FILE up -d 2>&1 + + if [ $? -ne 0 ]; then + echo "ERROR !!!! Unable to start network" + docker logs -f cli + exit 1 + fi + docker logs -f cli +} + +# Obtain CONTAINER_IDS and remove them +function clearContainers () { + CONTAINER_IDS=$(docker ps -a | grep "endchain_information\|naming_service\|transfer_information" | awk '{print $1}') + if [ -z "$CONTAINER_IDS" -o "$CONTAINER_IDS" == " " ]; then + echo "---- No containers available for deletion ----" + else + docker rm -f $CONTAINER_IDS + fi +} + +# Delete any images that were generated as a part of this setup +function removeUnwantedImages() { + DOCKER_IMAGE_IDS=$(docker images | grep "endchain_information\|naming_service\|transfer_information" | awk '{print $3}') + if [ -z "$DOCKER_IMAGE_IDS" -o "$DOCKER_IMAGE_IDS" == " " ]; then + echo "---- No images available for deletion ----" + else + docker rmi -f $DOCKER_IMAGE_IDS + fi +} + +# Tear down running network +function networkDown () { + docker-compose -f $SV_COMPOSE_FILE down + docker-compose -f $CC_COMPOSE_FILE down + #Cleanup the chaincode containers + clearContainers + #Cleanup images + removeUnwantedImages + # remove orderer block and other channel configuration transactions and certs + sudo rm -rf channel-artifacts/*.block channel-artifacts/*.tx crypto-config + sudo rm -rf ../../base/cc_env/servers/appserver/mongodb/db/* + docker network rm ccnet + docker network rm ec1net + docker network rm ec2net +} + +# Stop running network +function networkStop () { + docker-compose -f $SV_COMPOSE_FILE stop + docker-compose -f $CC_COMPOSE_FILE stop +} + +function replacePrivateKey () { + # sed on MacOSX does not support -i flag with a null extension. We will use + # 't' for our back-up's extension and depete it at the end of the function + ARCH=`uname -s | grep Darwin` + if [ "$ARCH" == "Darwin" ]; then + OPTS="-it" + else + OPTS="-i" + fi + + # Copy the template to the file that will be modified to add the private key + cp ./yaml-files/docker-compose-sample-cc-template.yaml $CC_COMPOSE_FILE + + # The next steps will replace the template's contents with the + # actual values of the private key file names for the two CAs. + CURRENT_DIR=$PWD + cd crypto-config/peerOrganizations/org1.example.com/ca/ + PRIV_KEY=$(ls *_sk) + cd $CURRENT_DIR + sed $OPTS "s/CA1_PRIVATE_KEY/${PRIV_KEY}/g" $CC_COMPOSE_FILE + +} + +# Generates Org certs using cryptogen tool +function generateCerts (){ + which cryptogen + if [ "$?" -ne 0 ]; then + echo "cryptogen tool not found. exiting" + exit 1 + fi + echo + echo "##########################################################" + echo "##### Generate certificates using cryptogen tool #########" + echo "##########################################################" + + cryptogen generate --config=../../base/cc_env/yaml-files/crypto-config.yaml + if [ "$?" -ne 0 ]; then + echo "Failed to generate certificates..." + exit 1 + fi + echo +} + +# Generate orderer genesis block, channel configuration transaction and +# anchor peer update transactions +function generateChannelArtifacts() { + which configtxgen + if [ "$?" -ne 0 ]; then + echo "configtxgen tool not found. exiting" + exit 1 + fi + + echo "##########################################################" + echo "######### Generating Orderer Genesis block ##############" + echo "##########################################################" + # Note: For some unknown reason (at least for now) the block file can't be + # named orderer.genesis.block or the orderer will fail to launch! + configtxgen -profile OneOrgOrdererGenesis -outputBlock ./channel-artifacts/genesis.block + if [ "$?" -ne 0 ]; then + echo "Failed to generate orderer genesis block..." + exit 1 + fi + echo + echo "#################################################################" + echo "### Generating channel configuration transaction 'channel.tx' ###" + echo "#################################################################" + configtxgen -profile OneOrgChannel -outputCreateChannelTx ./channel-artifacts/channel.tx -channelID $CHANNEL_NAME + if [ "$?" -ne 0 ]; then + echo "Failed to generate channel configuration transaction..." + exit 1 + fi +} + +# Obtain the OS and Architecture string that will be used to select the correct +# native binaries for your platform +OS_ARCH=$(echo "$(uname -s|tr '[:upper:]' '[:lower:]'|sed 's/mingw64_nt.*/windows/')-$(uname -m | sed 's/x86_64/amd64/g')" | awk '{print tolower($0)}') +# timeout duration - the duration the CLI should wait for a response from +# another container before giving up +CLI_TIMEOUT=30000 +#default for delay +CLI_DELAY=3 +# channel name defaults to "mychannel" +CHANNEL_NAME="mychannel" +# use this as the default docker-compose yaml definition +CC_COMPOSE_FILE=./yaml-files/docker-compose-sample-cc.yaml +SV_COMPOSE_FILE=./yaml-files/docker-compose-sample-servers.yaml +# use this as the default proxy +PROXY="" +#PROXY=http://your.proxy.com:8080 +#PROXY=http://id:passwd@your.proxy.com:8080 + +# Parse commandline args +while getopts "h?m:t:d:p:" opt; do + case "$opt" in + h|\?) + printHelp + exit 0 + ;; + m) MODE=$OPTARG + ;; + t) CLI_TIMEOUT=$OPTARG + ;; + d) CLI_DELAY=$OPTARG + ;; + p) PROXY=$OPTARG + ;; + esac +done + +# Determine whether starting, stopping, restarting or generating for announce +if [ "$MODE" == "up" ]; then + EXPMODE="Starting" + elif [ "$MODE" == "down" ]; then + EXPMODE="Stopping" + elif [ "$MODE" == "restart" ]; then + EXPMODE="Restarting" + elif [ "$MODE" == "generate" ]; then + EXPMODE="Generating certs and genesis block for" +else + printHelp + exit 1 +fi + +# Announce what was requested + + echo "${EXPMODE} with channel '${CHANNEL_NAME}' and CLI timeout of '${CLI_TIMEOUT}'" + +# ask for confirmation to proceed +askProceed + +#Create the network using docker compose +if [ "${MODE}" == "up" ]; then + networkUp + elif [ "${MODE}" == "down" ]; then ## Clear the network + networkDown + elif [ "${MODE}" == "generate" ]; then ## Generate Artifacts + generateCerts + replacePrivateKey + generateChannelArtifacts + elif [ "${MODE}" == "restart" ]; then ## Restart the network + networkStop + networkUp +else + printHelp + exit 1 +fi diff --git a/packages/connection-chain/environment/sample/cc_env/chaincode/deploy_script.sh b/packages/connection-chain/environment/sample/cc_env/chaincode/deploy_script.sh new file mode 100755 index 0000000000..d302776ade --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/chaincode/deploy_script.sh @@ -0,0 +1,209 @@ +#!/bin/bash +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 + +echo +echo " ____ _____ _ ____ _____ " +echo "/ ___| |_ _| / \ | _ \ |_ _|" +echo "\___ \ | | / _ \ | |_) | | | " +echo " ___) | | | / ___ \ | _ < | | " +echo "|____/ |_| /_/ \_\ |_| \_\ |_| " +echo +echo "Build your first network (BYFN) end-to-end test" +echo +CHANNEL_NAME="$1" +DELAY="$2" +MODE="$3" +: ${CHANNEL_NAME:="mychannel"} +: ${TIMEOUT:="60"} + +COUNTER=1 +MAX_RETRY=5 +ORDERER_CA=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem + +echo "Channel name : "$CHANNEL_NAME +echo "Mode : "$MODE + +# verify the result of the end-to-end test +verifyResult () { + if [ $1 -ne 0 ] ; then + echo "!!!!!!!!!!!!!!! "$2" !!!!!!!!!!!!!!!!" + echo "========= ERROR !!! FAILED to execute End-2-End Scenario ===========" + echo + exit 1 + fi +} + +setGlobals () { + + CORE_PEER_LOCALMSPID="Org1MSP" + 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 + CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + CORE_PEER_ADDRESS=peer$1.org1.example.com:7051 + + env |grep CORE +} + +createChannel() { + setGlobals 0 + + peer channel create -o orderer.example.com:7050 -c $CHANNEL_NAME -f ./channel-artifacts/channel.tx >&log.txt + + res=$? + cat log.txt + verifyResult $res "Channel creation failed" + echo "===================== Channel \"$CHANNEL_NAME\" is created successfully ===================== " + echo +} + +## Sometimes Join takes time hence RETRY atleast for 5 times +joinWithRetry () { + peer channel join -b $CHANNEL_NAME.block >&log.txt + res=$? + cat log.txt + if [ $res -ne 0 -a $COUNTER -lt $MAX_RETRY ]; then + COUNTER=` expr $COUNTER + 1` + echo "PEER$1 failed to join the channel, Retry after 2 seconds" + sleep $DELAY + joinWithRetry $1 + else + COUNTER=1 + fi + verifyResult $res "After $MAX_RETRY attempts, PEER$ch has failed to Join the Channel" +} + +joinChannel () { + for ch in 0 1 2 3; do + setGlobals $ch + joinWithRetry $ch + echo "===================== PEER$ch joined on the channel \"$CHANNEL_NAME\" ===================== " + sleep $DELAY + echo + done +} + +installChaincode () { + CHAINCODE_NAME=$1 + PEER=$2 + setGlobals $PEER + peer chaincode install -n $CHAINCODE_NAME -v 1.0 -p github.com/hyperledger/fabric/work/chaincode/$CHAINCODE_NAME >&log.txt + res=$? + cat log.txt + verifyResult $res "Chaincode installation on remote peer PEER$PEER has Failed" + echo "===================== Chaincode is installed on remote peer PEER$PEER ===================== " + echo +} + +instantiateChaincode () { + CHAINCODE_NAME=$1 + PEER=$2 + setGlobals $PEER + # while 'peer chaincode' command can get the orderer endpoint from the peer (if join was successful), + # lets supply it directly as we know it using the "-o" option + peer chaincode instantiate -o orderer.example.com:7050 -C $CHANNEL_NAME -n $CHAINCODE_NAME -v 1.0 -c '{"Args":[]}' >&log.txt + + res=$? + cat log.txt + verifyResult $res "Chaincode instantiation on PEER$PEER on channel '$CHANNEL_NAME' failed" + echo "===================== Chaincode Instantiation on PEER$PEER on channel '$CHANNEL_NAME' is successful ===================== " + echo +} + +chaincodeQuery_ns () { + PEER=$1 + setGlobals $PEER + + peer chaincode query -C $CHANNEL_NAME -n naming_service -c '{"Args":["getECAccountList"]}' + + echo "===================== Query chaincode 'naming_service' on PEER$PEER on channel '$CHANNEL_NAME' is executed ===================== " + echo +} + +chaincodeQuery_ei () { + PEER=$1 + setGlobals $PEER + + peer chaincode query -C $CHANNEL_NAME -n endchain_information -c '{"Args":["getECInfoList"]}' + + echo "===================== Query chaincode 'endchain_information' on PEER$PEER on channel '$CHANNEL_NAME' is executed ===================== " + echo +} + +chaincodeQuery_ti () { + PEER=$1 + setGlobals $PEER + + peer chaincode query -C $CHANNEL_NAME -n transfer_information -c '{"Args":["getRuleInfoList", "", ""]}' + + echo "===================== Query chaincode 'transfer_information' on PEER$PEER on channel '$CHANNEL_NAME' is executed ===================== " + echo +} + +if [ $MODE = "up" ]; then + ## Create channel + echo "Creating channel..." + createChannel + + ## Join all the peers to the channel + echo "Having all peers join the channel..." + joinChannel + + ## Install chaincode + installChaincode naming_service 0 + installChaincode naming_service 1 + installChaincode naming_service 2 + installChaincode naming_service 3 + + installChaincode endchain_information 0 + installChaincode endchain_information 1 + installChaincode endchain_information 2 + installChaincode endchain_information 3 + + installChaincode transfer_information 0 + installChaincode transfer_information 1 + installChaincode transfer_information 2 + installChaincode transfer_information 3 + + #Instantiate chaincode + echo "Instantiating chaincode" + instantiateChaincode naming_service 0 + instantiateChaincode endchain_information 0 + instantiateChaincode transfer_information 0 +fi + +#Query chaincode +echo "Querying chaincode" +if [ $MODE = "restart" ]; then + chaincodeQuery_ns 0 +fi +chaincodeQuery_ns 1 +chaincodeQuery_ns 2 +chaincodeQuery_ns 3 + +if [ $MODE = "restart" ]; then + chaincodeQuery_ei 0 +fi +chaincodeQuery_ei 1 +chaincodeQuery_ei 2 +chaincodeQuery_ei 3 + +if [ $MODE = "restart" ]; then + chaincodeQuery_ti 0 +fi +chaincodeQuery_ti 1 +chaincodeQuery_ti 2 +chaincodeQuery_ti 3 + +echo +echo "========= All GOOD, BYFN execution completed =========== " +echo + +echo +echo " _____ _ _ ____ " +echo "| ____| | \ | | | _ \ " +echo "| _| | \| | | | | | " +echo "| |___ | |\ | | |_| | " +echo "|_____| |_| \_| |____/ " +echo + +exit 0 diff --git a/packages/connection-chain/environment/sample/cc_env/chaincode/transfer_information/transfer_information.go b/packages/connection-chain/environment/sample/cc_env/chaincode/transfer_information/transfer_information.go new file mode 100644 index 0000000000..4fc5996a9e --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/chaincode/transfer_information/transfer_information.go @@ -0,0 +1,1186 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * transfer_information.go + */ + +// Package +package main + +import ( + "encoding/json" + "errors" + "fmt" + "strconv" + "github.com/hyperledger/fabric/core/chaincode/shim" + pb "github.com/hyperledger/fabric/protos/peer" + "time" +) + +// Object structure definition + +// Conversion rule information +type RuleInfo struct { + RuleID string `json:"id"` // RuleID + RuleName string `json:"ruleName"` // Rule Display Name + From RuleChainInfo `json:"fromChain"` // Source Chain Information + To RuleChainInfo `json:"toChain"` // Destination Chain Information + Rule string `json:"rule"` // Conversion Rule + Commission string `json:"commission"` +} + +// Information of each chain in conversion rule +type RuleChainInfo struct { + ChainID string `json:"chainID"` + AssetType string `json:"assetType"` + EscrowAccountID string `json:"escrowAccountID,omitempty"` // Used in "fromChain" Only + SettlementAccountID string `json:"settlementAccountID"` // ID of the representative account of end-chain +} + +// Asset transfer transaction information +type TxInfo struct { + TxID string `json:"id"` // transaction ID of ConnectionChain + UserID string `json:"userID"` // ID of the ConnectionChain user who requested the asset transfer + RuleID string `json:"ruleID"` // The ID of the conversion rule to apply + From TxChainInfo `json:"fromChain"` // Source Chain Information + To TxChainInfo `json:"toChain"` // Destination chain Information + Progress string `json:"progress"` // transfer status + Timestamps TxTimestamps `json:"timestamps"` +} + +// chain-specific information in asset transfer transactions +type TxChainInfo struct { + ChainID string `json:"chainID"` + AccountID string `json:"accountID"` + AssetType string `json:"assetType"` + Asset string `json:"asset"` // The amount of asset transfer or ID in the end-chain (depending on the type) + EscrowAccountID string `json:"escrowAccountID,omitempty"` // Used in "fromChain" Only + SettlementAccountID string `json:"settlementAccountID"` // ID of the representative account of end-chain + EscrowEvID string `json:"escrowEvID,omitempty"` // Escrow Request Event ID (Used in "fromChain" Only) + SettlementEvID string `json:"settlementEvID,omitempty"` // Event ID of the freeze or direct freeze request (Used in "fromChain" Only) + RestoreEvID string `json:"restoreEvID,omitempty"` // Recover Request event ID (Used in "fromChain" Only) + PaymentEvID string `json:"paymentEvID,omitempty"` // payment Request Event ID (Used in "toChain" Only) +} + +// Timestamp information in the asset transfer transaction +type TxTimestamps struct { + Create string `json:"create"` // date of asset transfer transaction created + RequestMargin string `json:"requestMargin,omitempty"` // date of "EscrowEvID" recorded + FixedMargin string `json:"fixedMargin,omitempty"` // date of Escrow Commitment status recorded + FailedMargin string `json:"failedMargin,omitempty"` // date of Escrow Failure status recorded + RequestDirectFreeze string `json:"requestDirectFreeze,omitempty"` // date of "SettlementEvID" recorded (direct freeze) + FixedDirectFreeze string `json:"fixedDirectFreeze,omitempty"` // date of Direct Freeze Commitment status recorded + FailedDirectFreeze string `json:"failedDirectFreeze,omitempty"` // date of Direct Freeze Failure status recorded + RequestCredit string `json:"requestCredit,omitempty"` // date of "PaymentEvID" recorded + FixedCredit string `json:"fixedCredit,omitempty"` // date of Payment Commitment status recorded + FailedCredit string `json:"failedCredit,omitempty"` // date of Payment Failure status recorded + RequestRecovery string `json:"requestRecovery,omitempty"` // date of "RestoreEvID" recorded + FixedRecovery string `json:"fixedRecovery,omitempty"` // date of Recovery Commitment status recorded + RequestFreeze string `json:"requestFreeze,omitempty"` // date of "SettlementEvID" recorded(freeze) + FixedFreeze string `json:"fixedFreeze,omitempty"` // date of Freeze Commitment status recorded +} + +// Asset transfer transaction information (Used Internal) +// Basic information +type TxBaseInfo struct { + TxID string // transaction ID of ConnectionChain + UserID string // ID of the ConnectionChain user who requested the asset transfer + RuleID string // ID of the conversion rule to apply + From TxChainInfo // Source chain information + To TxChainInfo // Destination chain information + CreateTime string // date of asset transfer transaction created +} + + +// constant definition + +// transformation rule information storage object +const RULE_INFO = "rule_info" + +// asset transfer transaction information storage object +const TX_INFO = "tx_info" + +// asset transfer transaction ID storage object +const TX_ID = "tx_id" + +// Counter for conversion rule ID numbering +const LAST_INDEX = "last_rule_id" + +// progress of the asset transfer transaction +// other than INIT and COMPLETE, this is also name of timestamp storage field of asset transfer transaction information +const INIT = "initial" // initial state +const MARGIN_REQ = "requestMargin" // Escrow request on Source End-chain +const MARGIN_FIX = "fixedMargin" // Escrow commitment on Source End-chain +const MARGIN_ERR = "failedMargin" // Escrow failure on Source End-chain (Used in Escrow transaction: Transfer process ends) +const DIRECT_REQ = "requestDirectFreeze" // Direct freeze request on Source End-chain +const DIRECT_FIX = "fixedDirectFreeze" // Direct freeze commitment on Source End-chain +const DIRECT_ERR = "failedDirectFreeze" // Direct freeze failure on Source End-chain (Used in NonEscrow transaction: Transfer process ends) +const CREDIT_REQ = "requestCredit" // Payment request on Destination End-chain +const CREDIT_FIX = "fixedCredit" // Payment commitment on Destination End-chain (Used in NonEscrow transaction: Transfer process ends) +const CREDIT_ERR = "failedCredit" // Payment failure on Destination End-chain +const RECOVERY_REQ = "requestRecovery" // Recover request on Source End-chain +const RECOVERY_FIX = "fixedRecovery" // Recovery commitment on Source End-chain (transfer process ends) +const FREEZE_REQ = "requestFreeze" // Freeze request on Source End-chain +const FREEZE_FIX = "fixedFreeze" // Freeze commitment on Source End-chain (Used in Escrow transaction: Transfer process ends) +const COMPLETE = "complete" // complete state + +// Basic information storage field name of asset transfer transaction information (Internal) +const BASE_INFO = "base_info" + +// End-chain side event ID storage field name of asset transfer transaction information (Internal) +const ESCROW_ID = "escrowEvID" // event ID of escrow request +const PAYMENT_ID = "paymentEvID" // event ID of payment request +const RESTORE_ID = "restoreEvID" // event ID of recover request +const SETTLEMENT_ID = "settlementEvID" // event ID of freeze request +// asset transfer transaction information name of the field to be stored when updating the asset information +const FROM_ASSET = "fromAsset" // post-update asset information for the source End-chain +const TO_ASSET = "toAsset" // post-update asset information for the destination End-chain + +// Timestamp format +const TIME_FORMAT = "20060102T150405" + +// Chaincode structure definition +type TransferInformation struct { +} + +// Chaincode initialization function +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} function + * @param {[]string} args + * @return {pb.Response} +**/ +func (cc *TransferInformation) Init(stub shim.ChaincodeStubInterface) pb.Response { + // Store initial value 0 only if conversion rule ID numbering counter is not set + getBytes, err := stub.GetState(LAST_INDEX) + if err != nil { + return shim.Error("[Init] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + err = stub.PutState(LAST_INDEX, []byte(strconv.Itoa(0))) + if err != nil { + return shim.Error("[Init] PutState operation failed. " + err.Error()) + } + } + + return shim.Success(nil) +} + +// Invoke query interface in chaincode +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} function Execution function + * @param {[]string} args Arguments to pass to the function + * @return {pb.Response} +**/ +func (cc *TransferInformation) Invoke(stub shim.ChaincodeStubInterface) pb.Response { + function, args := stub.GetFunctionAndParameters() + switch function { +// Invoke function for conversion rule information + case "addRuleInfo": + return addRuleInfo(stub, args) + case "updateRuleInfo": + return updateRuleInfo(stub, args) + case "deleteRuleInfo": + return deleteRuleInfo(stub, args) +// Query function for conversion rule information + case "getRuleInfo": + return getRuleInfo(stub, args) + case "getRuleInfoList": + return getRuleInfoList(stub, args) + +// Invoke function for asset transfer transaction information + case "createTransferTransaction": + return createTransferTransaction(stub, args) + case "updateTransferTransaction": + return updateTransferTransaction(stub, args) +// Query function for asset transfer transaction information + case "getTransferTransaction": + return getTransferTransaction(stub, args) + case "getTransferTransactionList": + return getTransferTransactionList(stub, args) + + default: + return shim.Error("Unkown operation.") + } +} + +/***** Invoke internal functions *****/ + +// Add conversion rule information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of conversion rule information to add + * - args[0] : Display name of the rule + * - args[1] : Chain ID of Source End-chain + * - args[2] : Chain ID of Destination End-chain + * - args[3] : Type of asset on Source End-chain + * - args[4] : Type of asset on Destination End-chain + * - args[5] : ID of Escrow account to be used in Source End-chain. Specify an empty character in Non-Escrow transaction. + * - args[6] : ID of Representative account to be used in Source End-chain. + * - args[7] : ID of Representative account to be used in Destination End-chain. + * - args[8] : conversion rule + * - args[9] : Charge + * @return {pb.Response} +**/ +func addRuleInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 10 + if len(args) != 10 { + return shim.Error("[addRuleInfo] Incorrect number of arguments. Expecting 10") + } + + ruleName := args[0] + fromChainID := args[1] + toChainID := args[2] + fromAssetType := args[3] + toAssetType := args[4] + fromEscrowAccountID := args[5] + fromSettlementAccountID := args[6] + toSettlementAccountID := args[7] + rule := args[8] + commission := args[9] + + // Get conversion rule ID + indexBytes, err := stub.GetState(LAST_INDEX) + if err != nil { + return shim.Error("[addRuleInfo#ID] GetState operation failed. " + err.Error()) + } + indexStr := string(indexBytes) + index, err := strconv.Atoi(indexStr) + if err != nil { + return shim.Error("[addRuleInfo#ID] strconv operation failed. " + err.Error()) + } + + // Composite key generation + key, err := stub.CreateCompositeKey(RULE_INFO, []string{indexStr}) + if err != nil { + return shim.Error("[addRuleInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // Update conversion rule ID numbering counter by + 1 + err = stub.PutState(LAST_INDEX, []byte(strconv.Itoa(index + 1))) + if err != nil { + return shim.Error("[addRuleInfo#ID] PutState operation failed. " + err.Error()) + } + + // JSON style object data creation + fromInfo := RuleChainInfo{fromChainID, fromAssetType, fromEscrowAccountID, fromSettlementAccountID} + toInfo := RuleChainInfo{toChainID, toAssetType, "", toSettlementAccountID} + ruleInfo := &RuleInfo{indexStr, ruleName, fromInfo, toInfo, rule, commission} + jsonBytes, err := json.Marshal(ruleInfo) + if err != nil { + return shim.Error("[addRuleInfo] Marshal operation failed. " + err.Error()) + } + + // Register in World State + err = stub.PutState(key, jsonBytes) + if err != nil { + return shim.Error("[addRuleInfo] PutState operation failed. " + err.Error()) + } + // Return conversion rule ID + return shim.Success(indexBytes) +} + +// Update conversion rule information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Content of conversion rule information to update. + * - args[0] : conversion rule ID + * - args[1] : Display name of the rule + * - args[2] : Chain ID of Source End-chain + * - args[3] : Chain ID of Destination End-chain + * - args[4] : Type of asset in Source End-chain + * - args[5] : Type of asset in Destination End-chain + * - args[6] : ID of Escrow account used in Source End-chain + * - args[7] : ID of representative account used in Source End-chain + * - args[8] : ID of representative account used in Destination End-chain + * - args[9] : conversion rule + * - args[10] : Charge + * - args[11] : Optional value. Delete Escrow account ID when non-empty string is specified. + * @return {pb.Response} +**/ +func updateRuleInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 12 + if len(args) != 12 { + return shim.Error("[updateRuleInfo] Incorrect number of arguments. Expecting 12") + } + + ruleID := args[0] + ruleName := args[1] + fromChainID := args[2] + toChainID := args[3] + fromAssetType := args[4] + toAssetType := args[5] + fromEscrowAccountID := args[6] + fromSettlementAccountID := args[7] + toSettlementAccountID := args[8] + rule := args[9] + commission := args[10] + removeEscrow := args[11] + + // Composite key generation + key, err := stub.CreateCompositeKey(RULE_INFO, []string{ruleID}) + if err != nil { + return shim.Error("[updateRuleInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[updateRuleInfo] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Returns success with null if not registered + fmt.Printf("This rule is not yet registered. RuleID = %s", ruleID) + return shim.Success(nil) + } + + // Overwrite the registered information + var ruleInfo RuleInfo + err = json.Unmarshal(getBytes, &ruleInfo) + if err != nil { + return shim.Error("Error unmarshaling JSON: " + err.Error()) + } + + // Do not update empty fields + if ruleName != "" { + ruleInfo.RuleName = ruleName + } + if fromChainID != "" { + ruleInfo.From.ChainID = fromChainID + } + if toChainID != "" { + ruleInfo.To.ChainID = toChainID + } + if fromAssetType != "" { + ruleInfo.From.AssetType = fromAssetType + } + if toAssetType != "" { + ruleInfo.To.AssetType = toAssetType + } + if fromEscrowAccountID != "" { + ruleInfo.From.EscrowAccountID = fromEscrowAccountID + } + if fromSettlementAccountID != "" { + ruleInfo.From.SettlementAccountID = fromSettlementAccountID + } + if toSettlementAccountID != "" { + ruleInfo.To.SettlementAccountID = toSettlementAccountID + } + if rule != "" { + ruleInfo.Rule = rule + } + if commission != "" { + ruleInfo.Commission = commission + } + if removeEscrow != "" { + // Update the escrow account to an empty string to stop the escrow process + ruleInfo.From.EscrowAccountID = "" + } + + jsonBytes, err := json.Marshal(ruleInfo) + if err != nil { + return shim.Error("[updateRuleInfo] Marshal operation failed. " + err.Error()) + } + + // Register in World State + err = stub.PutState(key, jsonBytes) + if err != nil { + return shim.Error("[updateRuleInfo] PutState operation failed. " + err.Error()) + } + // Return ID on update success + return shim.Success([]byte(ruleID)) +} + +// Register asset transfer transaction information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Contents of asset transfer transaction information to add + * - args[0] : Transaction ID of ConnectionChain + * - args[1] : ID of ConnectionChain user who requested asset transfer + * - args[2] : ID of conversion rule to apply + * - args[3] : Chain ID of Source End-chain + * - args[4] : ID of source account + * - args[5] : Asset type of source account + * - args[6] : Amount or ID of asset to be transferred in source account + * - args[7] : ID of Escrow account used in Source End-chain + * - args[8] : ID of Representative account used in Source End-chain + * - args[9] : Chain ID of Destination End-chain + * - args[10] : ID of destination account + * - args[11] : Asset type of destination account + * - args[12] : Amount or ID of asset to be transferred in destination account + * - args[13] : ID of Representative account used in Destination End-chain + * - args[14] : Transaction creation time (YYYYMMDDDThhmmss) + * @return {pb.Response} +**/ +func createTransferTransaction(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 15 + if len(args) != 15 { + return shim.Error("[createTransferTransaction] Incorrect number of arguments. Expecting 15") + } + + txID := args[0] + userID := args[1] + ruleID := args[2] + fromChainID := args[3] + fromAccountID := args[4] + fromAssetType := args[5] + fromAsset := args[6] + fromEscrowAccountID := args[7] + fromSettlementAccountID := args[8] + toChainID := args[9] + toAccountID := args[10] + toAssetType := args[11] + toAsset := args[12] + toSettlementAccountID := args[13] + createTime := args[14] + + + // Composite key generation (basic information about asset transfer transactions) + key, err := stub.CreateCompositeKey(TX_INFO, []string{txID, BASE_INFO}) + if err != nil { + return shim.Error("[createTransferTransaction] CreateCompositeKey operation failed. " + err.Error()) + } + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[createTransferTransaction] GetState operation failed. " + err.Error()) + } + if getBytes != nil { + //Return success with null if ID already exists + fmt.Printf("This transaction is already registered. TransactionID = %s", txID) + return shim.Success(nil) + } + + // Error if date/time format is incorrect + _, err = time.Parse(TIME_FORMAT, createTime) + if err != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", createTime) + return shim.Error(msg) + } + + // JSON style object data creation + fromInfo := TxChainInfo{fromChainID, fromAccountID, fromAssetType, fromAsset, fromEscrowAccountID, fromSettlementAccountID, "", "", "", ""} + toInfo := TxChainInfo{toChainID, toAccountID, toAssetType, toAsset, "", toSettlementAccountID, "", "", "", ""} + txInfo := &TxBaseInfo{txID, userID, ruleID, fromInfo, toInfo, createTime} + jsonBytes, err := json.Marshal(txInfo) + if err != nil { + return shim.Error("[createTransferTransaction] Marshal operation failed. " + err.Error()) + } + + // Register in World State + // Composite key generation (For TxID list) + idKey, err := stub.CreateCompositeKey(TX_ID, []string{txID}) + if err != nil { + return shim.Error("[createTransferTransaction#ID] CreateCompositeKey operation failed. " + err.Error()) + } + err = stub.PutState(idKey, []byte(txID)) + if err != nil { + return shim.Error("[createTransferTransaction#ID] PutState operation failed. " + err.Error()) + } + err = stub.PutState(key, jsonBytes) + if err != nil { + return shim.Error("[createTransferTransaction] PutState operation failed. " + err.Error()) + } + + // Return ID on successful registration + return shim.Success([]byte(txID)) +} + +// Update asset transfer transaction information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {[]string} args Contents of asset transfer transaction information to add + * - args[0] : Transaction ID of ConnectionChain + * - args[1] : Progress of the asset transfer transaction information + * - args[2] : ID of event requested to perform on End-chain + * Specify an empty string for anything other than status "fixedMargin", "requestDirectFreeze", "requestMargin", "fixedDirectFreeze", + * "requestCredit", "requestRecovery", or "requestFreeze". + * (Ignores non-empty characters) + * In "requestMargin" and "fixedMargin" and "requestDirectFreeze" and "fixedDirectFreeze" + * they must only be specified as either request or fixed. + * - args[3] : Asset transfer amount or transferred Asset ID to be updated + * - args[4] : Updated time (YYYYMMDDDThhmmss) + * @return {pb.Response} +**/ +func updateTransferTransaction(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 5 + if len(args) != 5 { + return shim.Error("[updateTransferTransaction] Incorrect number of arguments. Expecting 5") + } + + txID := args[0] + status := args[1] + evID := args[2] + updateAsset := args[3] + updateTime := args[4] + + // Error if date/time format is incorrect + _, err := time.Parse(TIME_FORMAT, updateTime) + if err != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", updateTime) + return shim.Error(msg) + } + + // Get one asset transfer transaction information + getBytes, err := getTxFieldInfo(stub, txID, BASE_INFO) + if err != nil { + return shim.Error(err.Error()) + } + if getBytes == nil { + // Return success with null if not registered + return shim.Success(nil) + } + + // set the value in the field according to the specified state + tsField := status + idField := "" + assetField := FROM_ASSET + + switch status { + case MARGIN_REQ: + idField = ESCROW_ID + case MARGIN_FIX: + idField = ESCROW_ID + case MARGIN_ERR: + case DIRECT_REQ: + idField = SETTLEMENT_ID + case DIRECT_FIX: + idField = SETTLEMENT_ID + case DIRECT_ERR: + case CREDIT_REQ: + idField = PAYMENT_ID + assetField = TO_ASSET + case CREDIT_FIX: + assetField = TO_ASSET + case CREDIT_ERR: + assetField = TO_ASSET + case RECOVERY_REQ: + idField = RESTORE_ID + case RECOVERY_FIX: + case FREEZE_REQ: + idField = SETTLEMENT_ID + case FREEZE_FIX: + default: + return shim.Error("Unkown progress.") + } + + // ID registration of events performed in End-chain + if idField != "" && evID != "" { + // Composite key generation + key, err := stub.CreateCompositeKey(TX_INFO, []string{txID, idField}) + if err != nil { + return shim.Error("[updateTransferTransaction#evID] CreateCompositeKey operation failed. " + err.Error()) + } + // Register in World State + err = stub.PutState(key, []byte(evID)) + if err != nil { + return shim.Error("[updateTransferTransaction#evID] PutState operation failed. " + err.Error()) + } + } + + // Set asset transfer amount or ID update information (only if it is not set in the registration of asset transfer transaction information) + if updateAsset != "" { + var getInfo TxBaseInfo + err = json.Unmarshal(getBytes, &getInfo) + if err != nil { + return shim.Error("[updateTransferTransaction#Asset] Error unmarshaling JSON: " + err.Error()) + } + baseAsset := getInfo.From.Asset + if assetField != FROM_ASSET { + baseAsset = getInfo.To.Asset + } + if baseAsset == "" { + // Composite key generation + key, err := stub.CreateCompositeKey(TX_INFO, []string{txID, assetField}) + if err != nil { + return shim.Error("[updateTransferTransaction#Asset] CreateCompositeKey operation failed. " + err.Error()) + } + // Register in World State + err = stub.PutState(key, []byte(updateAsset)) + if err != nil { + return shim.Error("[updateTransferTransaction#Asset] PutState operation failed. " + err.Error()) + } + } + } + + // Register timestamp + // Composite key generation + key, err := stub.CreateCompositeKey(TX_INFO, []string{txID, tsField}) + if err != nil { + return shim.Error("[updateTransferTransaction#Timestamp] CreateCompositeKey operation failed. " + err.Error()) + } + // Register in World State + err = stub.PutState(key, []byte(updateTime)) + if err != nil { + return shim.Error("[updateTransferTransaction#Timestamp] PutState operation failed. " + err.Error()) + } + // Return ID on update success + return shim.Success([]byte(txID)) +} + +// Remove conversion rule information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args ID of conversion rule information to delete + * - args[0] : ID + * @return {pb.Response} +**/ +func deleteRuleInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 1 + if len(args) != 1 { + return shim.Error("[deleteRuleInfo] Incorrect number of arguments. Expecting 1") + } + + ruleID := args[0] + + // Composite key generation + key, err := stub.CreateCompositeKey(RULE_INFO, []string{ruleID}) + if err != nil { + return shim.Error("[deleteRuleInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // remove the data from world state + err = stub.DelState(key) + if err != nil { + return shim.Error("[deleteRuleInfo] DelState operation failed. " + err.Error()) + } + return shim.Success(nil) +} + +/***** Internal query functions *****/ + +// Get single conversion rule info +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args ID of conversion rule information to get + * - args[0] : ID + * @return {pb.Response} +**/ +func getRuleInfo(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 1 + if len(args) != 1 { + return shim.Error("[getRuleInfo] Incorrect number of arguments. Expecting 1") + } + + ruleID := args[0] + + // Composite key generation + key, err := stub.CreateCompositeKey(RULE_INFO, []string{ruleID}) + if err != nil { + return shim.Error("[getRuleInfo] CreateCompositeKey operation failed. " + err.Error()) + } + + // Check if registered information exists + getBytes, err := stub.GetState(key) + if err != nil { + return shim.Error("[getRuleInfo] GetState operation failed. " + err.Error()) + } + if getBytes == nil { + // Return success with null if not registered + fmt.Printf("This rule is not yet registered. RuleID = %s", ruleID) + return shim.Success(nil) + } + // Return the retrieved content as is + return shim.Success(getBytes) +} + +// Get single asset transfer transaction information +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args ID of asset transfer transaction information to get + * - args[0] : ID + * @return {pb.Response} +**/ +func getTransferTransaction(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 1 + if len(args) != 1 { + return shim.Error("[getTransferTransaction] Incorrect number of arguments. Expecting 1") + } + + txID := args[0] + txInfo, _, err := getTxInfo(stub, txID) + if err != nil { + return shim.Error(err.Error()) + } + if txInfo == nil { + // Return success with null if not registered + return shim.Success(nil) + } + + // convert to json format and return + marshalledInfo, err := json.Marshal(txInfo) + if err != nil { + return shim.Error("[getTransferTransaction] Marshal operation failed. " + err.Error()) + } + return shim.Success([]byte(marshalledInfo)) +} + +// Get conversion rule info list +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args Filtering condition of conversion rule information + * If an empty character is specified, no filtering is performed under that condition. + * - args[0] : Chain ID of Source End-chain + * - args[1] : Chain ID of Destination End-chain + * @return {pb.Response} +**/ +func getRuleInfoList(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 2 + if len(args) != 2 { + return shim.Error("[getRuleInfoList] Incorrect number of arguments. Expecting 2") + } + + fromChainID := args[0] + toChainID := args[1] + + // Retrieve with partial composite key (Specify object name only) + iter, err := stub.GetStateByPartialCompositeKey(RULE_INFO, []string{}) + if err != nil { + return shim.Error("[getRuleInfoList] GetStateByPartialCompositeKey operation failed. " + err.Error()) + } + defer iter.Close() + + // Obtains sequentially from Iterator and stores in results + var results []RuleInfo + for iter.HasNext() { + qr, err := iter.Next() + if err != nil { + return shim.Error("[getRuleInfoList] Iterator.Next operation failed. " + err.Error()) + } + + // Parse the registered transformation rule information + var info RuleInfo + err = json.Unmarshal(qr.Value, &info) + if err != nil { + return shim.Error("[getRuleInfoList] Error unmarshaling JSON: " + err.Error()) + } + + // Filter by chain ID of source End-chain + if fromChainID != "" && fromChainID != info.From.ChainID { + continue + } + + // Filter by chain ID of destination End-chain + if toChainID != "" && toChainID != info.To.ChainID { + continue + } + + results = append(results, info) + } + + // convert results to json format and return + marshalledResults, err := json.Marshal(results) + if err != nil { + return shim.Error("[getRuleInfoList] Marshal operation failed. " + err.Error()) + } + return shim.Success([]byte(marshalledResults)) +} + +// Get Asset Transfer Transaction Information List +/** + * @param {shim.ChaincodeStubInterface} stub + * @param {string} args Filtering condition of Asset Transfer Transaction Information to get + * If an empty character is specified, no filtering is performed under that condition. + * - args[0] : ID of the ConnectionChain user who requested the asset transfer + * - args[1] : Progress of Asset transfer + * - args[2] : Creation Date (Before the specified date) + * - args[3] : Creation Date (After the specified date) + * - args[4] : Update Date (Before the specified date) + * - args[5] : Update Date (After the specified date) + * @return {pb.Response} +**/ +func getTransferTransactionList(stub shim.ChaincodeStubInterface, args []string) pb.Response { + // Error if number of args is not 6 + if len(args) != 6 { + return shim.Error("[getTransferTransactionList] Incorrect number of arguments. Expecting 6") + } + + userID := args[0] + progress := args[1] + createdBefore := time.Now() + createdAfter := time.Now() + updatedBefore := time.Now() + updatedAfter := time.Now() + cbStr := args[2] + caStr := args[3] + ubStr := args[4] + uaStr := args[5] + var parseErr error + + if cbStr != "" { + createdBefore, parseErr = time.Parse(TIME_FORMAT, cbStr) + if parseErr != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", cbStr) + return shim.Error(msg) + } + } + + if caStr != "" { + createdAfter, parseErr = time.Parse(TIME_FORMAT, caStr) + if parseErr != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", caStr) + return shim.Error(msg) + } + } + + if ubStr != "" { + updatedBefore, parseErr = time.Parse(TIME_FORMAT, ubStr) + if parseErr != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", ubStr) + return shim.Error(msg) + } + } + + if uaStr != "" { + updatedAfter, parseErr = time.Parse(TIME_FORMAT, uaStr) + if parseErr != nil { + msg := fmt.Sprintf("Invalid timestamp format. [%s]", uaStr) + return shim.Error(msg) + } + } + + // Get Asset Transfer Transaction ID List + // Retrieve with partial composite key (Specify object name only) + iter, err := stub.GetStateByPartialCompositeKey(TX_ID, []string{}) + if err != nil { + return shim.Error("[getTransferTransactionList] GetStateByPartialCompositeKey operation failed. " + err.Error()) + } + defer iter.Close() + + // Get IDs sequentially from iterator + // Retrieve asset transfer transaction information with acquired ID and store it in results + var results []TxInfo + for iter.HasNext() { + qr, err := iter.Next() + if err != nil { + return shim.Error("[getTransferTransactionList] Iterator.Next operation failed. " + err.Error()) + } + txID := string(qr.Value) + + // Get one asset transfer transaction information + info, lastUpdate, err := getTxInfo(stub, txID) + if err != nil { + return shim.Error(err.Error()) + } + + // Filter by User ID + if userID != "" && userID != info.UserID { + continue + } + + // Filter by Progress + if progress != "" && progress != info.Progress { + continue + } + + // Filter by Creation Date + // (Parse check has ended at the time of registration, so there is no error here) + createTime, _ := time.Parse(TIME_FORMAT, info.Timestamps.Create) + + if cbStr != "" && createTime.After(createdBefore) { + continue + } + if caStr != "" && createTime.Before(createdAfter) { + continue + } + + // Filter by Update Date + updateTime, _ := time.Parse(TIME_FORMAT, lastUpdate) + if ubStr != "" && updateTime.After(updatedBefore) { + continue + } + if uaStr != "" && updateTime.Before(updatedAfter) { + continue + } + + results = append(results, *info) + } + + // convert results to json format and return + marshalledResults, err := json.Marshal(results) + if err != nil { + return shim.Error("[getTransferTransactionList] Marshal operation failed. " + err.Error()) + } + return shim.Success([]byte(marshalledResults)) +} + +// Get one asset transfer transaction information +func getTxInfo(stub shim.ChaincodeStubInterface, txID string) (*TxInfo, string, error) { + // Prepare object for return + fromInfo := TxChainInfo{"", "", "", "", "", "", "", "", "", ""} + toInfo := TxChainInfo{"", "", "", "", "", "", "", "", "", ""} + timestamps := TxTimestamps{"", "", "", "", "", "", "", "", "", "", "", "", "", ""} + txInfo := TxInfo{txID, "", "", fromInfo, toInfo, INIT, timestamps} + + // for return of last updated date + lastUpdate := "" + + // Get the individual information belonging to txID and stuff it into return object + // Basic information + getBytes, err := getTxFieldInfo(stub, txID, BASE_INFO) + if err != nil { + return nil, "", err + } + if getBytes != nil { + var getInfo TxBaseInfo + err = json.Unmarshal(getBytes, &getInfo) + if err != nil { + msg := fmt.Sprintf("[getTxInfo] Error unmarshaling JSON: " + err.Error()) + return nil, "", errors.New(msg) + } + txInfo.UserID = getInfo.UserID + txInfo.RuleID = getInfo.RuleID + txInfo.From = getInfo.From + txInfo.To = getInfo.To + txInfo.Timestamps.Create = getInfo.CreateTime + lastUpdate = getInfo.CreateTime + } else { + // If basic information is not registered, both transaction information and error are returned in null state. + fmt.Printf("This transaction is not yet registered. TransactionID = %s", txID) + return nil, "", nil + } + + // If there is an update of the asset, set it to the return contents + getBytes, err = getTxFieldInfo(stub, txID, FROM_ASSET) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.From.Asset = string(getBytes) + } + getBytes, err = getTxFieldInfo(stub, txID, TO_ASSET) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.To.Asset = string(getBytes) + } + + + // state transition changes with or without escrow + // * Escrow: Escrow -> Payment -> Freeze (or Recovery) + // * Non-Escrow: Freeze -> Payment (-> Recovery) + isEscrow := true + if txInfo.From.EscrowAccountID == "" { + isEscrow = false + } + + if isEscrow { + // Recorded time of Ecrow request EvID + getBytes, err = getTxFieldInfo(stub, txID, MARGIN_REQ) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.RequestMargin = lastUpdate + // Recorded Escrow request EvID + getBytes, err = getTxFieldInfo(stub, txID, ESCROW_ID) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.From.EscrowEvID = string(getBytes) + } + txInfo.Progress = MARGIN_REQ + } + + // Record time of Escrow commitment status + getBytes, err = getTxFieldInfo(stub, txID, MARGIN_FIX) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FixedMargin = lastUpdate + txInfo.Progress = MARGIN_FIX + } + + // Record time of Escrow failure status + getBytes, err = getTxFieldInfo(stub, txID, MARGIN_ERR) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FailedMargin = lastUpdate + txInfo.Progress = MARGIN_ERR + } + + } else { + + // Recorded time of Direct freeze request EvID + getBytes, err = getTxFieldInfo(stub, txID, DIRECT_REQ) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.RequestDirectFreeze = lastUpdate + txInfo.Progress = DIRECT_REQ + // Recorded freeze request EvID + getBytes, err = getTxFieldInfo(stub, txID, SETTLEMENT_ID) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.From.SettlementEvID = string(getBytes) + } + } + + // Recorded time of Freeze commitment status + getBytes, err = getTxFieldInfo(stub, txID, DIRECT_FIX) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FixedDirectFreeze = lastUpdate + txInfo.Progress = DIRECT_FIX + } + + // Recorded time of Freeze failure status + getBytes, err = getTxFieldInfo(stub, txID, DIRECT_ERR) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FailedDirectFreeze = lastUpdate + txInfo.Progress = DIRECT_ERR + } + + } + + // Recorded time of Payment request EvID + getBytes, err = getTxFieldInfo(stub, txID, CREDIT_REQ) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.RequestCredit = lastUpdate + txInfo.Progress = CREDIT_REQ + // Recorded Payment Request EvID + getBytes, err = getTxFieldInfo(stub, txID, PAYMENT_ID) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.To.PaymentEvID = string(getBytes) + } + } + + // Recorded time of Payment commitment status + getBytes, err = getTxFieldInfo(stub, txID, CREDIT_FIX) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FixedCredit = lastUpdate + if isEscrow { + txInfo.Progress = CREDIT_FIX + } else { + // Complete here if non-escrow transaction + txInfo.Progress = COMPLETE + } + } + + // Recorded time of Payment failure status + getBytes, err = getTxFieldInfo(stub, txID, CREDIT_ERR) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FailedCredit = lastUpdate + txInfo.Progress = CREDIT_ERR + } + + // Recorded time of Recover request EvID + getBytes, err = getTxFieldInfo(stub, txID, RECOVERY_REQ) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.RequestRecovery = lastUpdate + txInfo.Progress = RECOVERY_REQ + // Recorded recover request EvID + getBytes, err = getTxFieldInfo(stub, txID, RESTORE_ID) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.From.RestoreEvID = string(getBytes) + } + } + + // Recorded time of Recovery commitment status + getBytes, err = getTxFieldInfo(stub, txID, RECOVERY_FIX) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FixedRecovery = lastUpdate + txInfo.Progress = RECOVERY_FIX + } + + if isEscrow { + // Recorded time of Freeze request EvID + getBytes, err = getTxFieldInfo(stub, txID, FREEZE_REQ) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.RequestFreeze = lastUpdate + txInfo.Progress = FREEZE_REQ + // Recorded freeze request EvID + getBytes, err = getTxFieldInfo(stub, txID, SETTLEMENT_ID) + if err != nil { + return nil, "", err + } + if getBytes != nil { + txInfo.From.SettlementEvID = string(getBytes) + } + } + + // Recorded time of Freeze commitment status + getBytes, err = getTxFieldInfo(stub, txID, FREEZE_FIX) + if err != nil { + return nil, "", err + } + if getBytes != nil { + lastUpdate = string(getBytes) + txInfo.Timestamps.FixedFreeze = lastUpdate + // Complete here if escrow transaction + txInfo.Progress = COMPLETE + } + } + + return &txInfo, lastUpdate, nil +} + +/***** invoke, query common internal functions *****/ +// Get Individual Field Values for Asset Transfer Transaction Information +func getTxFieldInfo(stub shim.ChaincodeStubInterface, txID string, field string) ([]byte, error) { + // Composite key generation + key, err := stub.CreateCompositeKey(TX_INFO, []string{txID, field}) + if err != nil { + return nil, err + } + return stub.GetState(key) +} + + +func main() { + err := shim.Start(new(TransferInformation)) + if err != nil { + fmt.Printf("Error starting transferinformation chaincode: %s",err) + } +} diff --git a/packages/connection-chain/environment/sample/cc_env/channel-artifacts/.gitkeep b/packages/connection-chain/environment/sample/cc_env/channel-artifacts/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/app.js b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/app.js new file mode 100644 index 0000000000..261d775469 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/app.js @@ -0,0 +1,80 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * Service API Server Main +*/ + +var express = require('express'); +var path = require('path'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +var passport = require('passport'); +var session = require('express-session'); + +var login = require('./routes/common/login'); +var ccusers = require('./routes/common/ccusers'); +var ecaccounts = require('./routes/common/ecaccounts'); +var endchains = require('./routes/common/endchains'); +var rules = require('./routes/userImple/rules'); +var transfers = require('./routes/userImple/transfers'); + +var ServiceAPIError = require('./lib/common/ServiceAPIError.js'); + +var app = express(); + +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(session({resave: false, saveUninitialized: false, secret: 'cookie enc key'})); +app.use(passport.initialize()); +app.use(passport.session()); + +// Login API is not subject to authentication check +app.use('/login', login); + +// Perform authentication checks for APIs other than login +app.use('/ccusers', isAuthenticated, ccusers); +app.use('/ecaccounts', isAuthenticated, ecaccounts); +app.use('/endchains', isAuthenticated, endchains); +app.use('/rules', isAuthenticated, rules); +app.use('/transfers', isAuthenticated, transfers); + +function isAuthenticated(req, res, next){ + if (req.isAuthenticated()) { // authenticated + return next(); + } + else { // not authenticated + res.status(401).send(); + } +} + +app.get("/logout", function(req, res){ + // just unlogin + req.logout(); + res.send('logout.'); +}); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new ServiceAPIError(404, 1000); + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + res.status(err.statusCode || 500); + res.send(err.errorBody); +}); + +module.exports = app; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/rules.js b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/rules.js new file mode 100644 index 0000000000..829e0ba85b --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/rules.js @@ -0,0 +1,266 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * rules.js + */ + +/* Summary: + * Conversion Rules Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var request = require('request'); +var ServiceAPIError = require('../../lib/common/ServiceAPIError.js'); +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var router = express.Router(); + +/** + * Generate Conversion Rule Information + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "ruleName": "", - Alias to be used on the UI + * "fromChain":{ - Source information + * "chainID": "", - ID of Source End-chain + * "settlementAccountID": "", - ID of the representative account of Source End-chain + * "escrowAccountID": "" - ID of the escrow account of Source End-chain. If omitted, no escrow occurs in transaction. + * }, + * "toChain":{ - Destination information + * "chainID": "", - ID of Destination End-chain + * "settlementAccountID": "" - ID of the representative account of Destination End-chain + * }, + * "rule": "" - Conversion ratio (%) from the source asset to the destination asset. The default is 100. + * "commission": "" - Amount to deduct from the source asset before conversion. The default is 0. + * } + * Response Body: + * { + * "ruleID": "" - ID of the registered conversion rule information + * } +**/ +router.post('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + // Pass parameters as-is, leave checks to core API + var options = { + url: config.coreapi.url + '/rules', + method: 'POST', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Update conversion rule information + * @name put/:id + * @function + * @inner + * @param {string} path - Rule ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * (Elements that you do not change can be omitted.) + * { + * "ruleName": "", + * "fromChain":{ + * "chainID": "", + "isEscrow" , - True when non-escrow -> escrow, False when escrow -> non-escrow + * "settlementAccountID": "", + * "escrowAccountID": "" + * }, + * "toChain":{ + * "chainID": "", + * "settlementAccountID": "" + * }, + * "rule": "", + * "commission": "" + * } + * Response Body: + * { + * "ruleID": "" - ID of the updated conversion rule information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + + var options = { + url: config.coreapi.url+ '/rules/' + req.params.id, + method: 'PUT', + headers: headers, + json: true, + body: req.body + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Delete Conversion Rule Information + * @name delete/:id + * @function + * @inner + * @param {string} path - Rule ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isAdminUser(req)) { + next(new ServiceAPIError(403, 4000)); + return; + } + + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/rules/' + req.params.id, + method: 'DELETE', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get one conversion rule information + * @name get/:id + * @function + * @inner + * @param {string} path - Rule ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "ruleName": "", + * "fromChain":{ + * "chainID": "", + * "settlementAccountID": "", + * "escrowAccountID": "" + * }, + * "toChain":{ + * "chainID": "", + * "settlementAccountID": "" + * }, + * "rule": "", + * "commission": "" + * } +**/ +router.get('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/rules/' + req.params.id, + method: 'GET', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get conversion rule information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * Filter condition. You can omit elements that you do not want to filter. + * { + * "fromChainID": "", + * "toChainID": "" + * } + * Response Body:Response body: Array of conversion rule information +**/ +router.get('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url + '/rules', + method: 'GET', + headers: headers, + json: true, + qs: req.query + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/transfers.js b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/transfers.js new file mode 100644 index 0000000000..264fe284d0 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/appserver/serviceapi/routes/userImple/transfers.js @@ -0,0 +1,222 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * transfers.js + */ + +/* Summary: + * Asset Transfer Transaction Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var request = require('request'); +var ServiceAPIError = require('../../lib/common/ServiceAPIError.js'); +var ServiceAPIUtil = require('../../lib/common/ServiceAPIUtil.js'); +var router = express.Router(); + +/** + * Generate asset transfer transaction information + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "txID" "", - ID of the asset transfer transaction information. The default is set automatically. + * "ruleID": "", - ID of the conversion rule to apply" + * "fromChain":{ - Source information + * "chainID": "", - ID of Source End-chain + * "accountID": "", - ID of Source account + * "asset": "" - Amount of assets in Source End-chain.(This can be omitted when "asset" of "toChain" is specified.) + * }, + * "toChain":{ - Destination information + * "chainID": "", - ID of Destination End-chain + * "accountID": "", - ID of Destination account + * "asset": "" - Amount of assets in Destination End-chain.(This can be omitted when "asset" of "fromChain" is specified.) + * } + * } + * Response Body: + * { + * "txID": "" - ID of the generated asset transfer transaction information + * } +**/ +router.post('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + // Pass parameters as-is, leave checks to core API + var options = { + url: config.coreapi.url + '/transfers', + method: 'POST', + headers: headers, + json: true, + body: req.body + } + // console.log(options); + // console.log(options.body); + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + // console.log(body); + res.status(response.statusCode).send(body); + }) +}); + + +/** + * Get one asset transfer transaction information + * @name get/:id + * @function + * @inner + * @param {string} path - Operation ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * Each event ID and timestamp is displayed only when the corresponding operation is performed. + * { + * "id": "", + * "userID": "", - ID of the user who generated the asset transfer transaction information + * "ruleID": "", + * "fromChain":{ + * "chainID": "", + * "accountID": "", + * "asset": "", + * "settlementAccountID": "", + * "escrowAccountID": "", + * "escrowEvID":" ", - Event ID of asset transfer request to escrow account + * "settlementEvID": "", - Event ID of asset transfer request to representative account + * "restoreEvID": "" - Event ID of asset transfer request from escrow account to source account + * }, + * "toChain":{ + * "chainID": "", + * "accountID": "", + * "asset": "", + * "settlementAccountID": "", + * "paymentEvID": "" - Event ID of asset transfer request from representative account to destination account + * }, + * "progress": "", - Current asset transfer transaction progress + * "timestamps" :{ - Timestamp at each progress of the asset transfer transaction + * "create": "", + * "requestMargin": "", + * "fixedMargin": "", + * "failedMargin": "", + * "requestDirectFreeze": "", + * "fixedDirectFreeze": "", + * "failedDirectFreeze": "", + * "requestCredit": "", + * "fixedCredit": "", + * "failedCredit": "", + * "requestRecovery": "", + * "fixedRecovery": "", + * "requestFreeze": "", + * "fixedFreeze": "" + * } + * } +**/ +router.get('/:id', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + var ccUserID = req.user.userID; + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url+ '/transfers/' + req.params.id, + method: 'GET', + headers: headers, + json: true + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, then only the information associated with the logged-in user is visible. + if (response.statusCode == 200) { + if (ccUserID != body.userID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + } + res.status(response.statusCode).send(body); + }) +}); + +/** + * Get Asset transfer transaction information list + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * Filter condition. You can omit elements that you do not want to filter. + * (You can only specify your own userID while you are logged in as a non-administrator) + * { + * "userID": "", + * "progress": "", + * "createdBefore": "", + * "createdAfter": "", + * "updatedBefore": "", + * "updatedAfter": "" + * } + * Response Body: Array of asset transfer transaction information +**/ +router.get('/', function(req, res, next) { + if (!ServiceAPIUtil.isValidContentType(req)) { + next(new ServiceAPIError(400, 2000)); + return; + } + + var ccUserID = req.user.userID; + var qUserID = req.query.userID; + if (!ServiceAPIUtil.isAdminUser(req)) { + // If not admin, then only the information associated with the logged-in user is visible. + if (qUserID == undefined) { + req.query.userID = ccUserID; + } else if (qUserID != ccUserID) { + next(new ServiceAPIError(403, 4000)); + return; + } + } + + var headers = { + 'Content-Type':'application/json', + 'X-CCUser':ccUserID + } + var options = { + url: config.coreapi.url + '/transfers', + method: 'GET', + headers: headers, + json: true, + qs: req.query + } + request(options, function (err, response, body) { + if (err) { + var detail = err.stack ? err.stack : err; + next(new ServiceAPIError(500, 3000, detail)); + return; + } + res.status(response.statusCode).send(body); + }) +}); + +module.exports = router; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/ClientPlugin.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/ClientPlugin.js new file mode 100644 index 0000000000..205d6e93d1 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/ClientPlugin.js @@ -0,0 +1,212 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ClientPlugin.js + */ + +/* + * Summary: + * Adapter: a part dependent on end-chains + */ + +// configuration file +var CplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ClientPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Library of a part independent of end-chains (cooperation server side) +var connector_if = require('../connector_if.js'); + +/* + * ClientPlugin + * Class definitions for client plugins + */ +var ClientPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /** + * execConnecterApi: Connector API execution request + * @param {Object} client : The core-side client from which the run was requested + * @param {JSON} params : Information to determine the distribution of operations to be performed on the EC side + * { + * "apiType":, - one of "reference" "," "transfer" "," "payment" + * "progress":, - one of "MarginOrDfreeze" "," "Credit" "," "Freeze" "," "Recovery" "," "Other" + * "txInfo": , object for inter-EC asset transfer transaction information" + * "otherData": - as is, where to find in the pre-supply review" "targetAccountID" "only" + * } + **/ + execConnecterApi(client, params) { + var functionName = ""; + var args = {}; + // Write the processing according to the API definition of the connector side + var apiType = params.apiType; + var progress = params.progress; + var txInfo = params.txInfo; + var otherData = params.otherData; + var fromAddress = ""; + var toAddress = ""; + var referedAddress = ""; + var amount = 0; + + switch(apiType) { + case "reference": + var referedAddress = ''; + if (progress == 'MarginOrDfreeze') { + // Confirmation of fromAccount and balance before escrow or directly freezing + functionName = 'getNumericBalance'; + referedAddress = txInfo._fromAccountID; + } else if (progress == 'Credit') { + functionName = 'getNumericBalance'; + referedAddress = otherData.targetAccountID; + } else if (progress == 'Other') { + functionName = 'getNumericBalance'; + referedAddress = otherData.targetAccountID; + } else { + // Suppress sending to server plugins by making functionName empty. + functionName = ""; + } + // Create the argument as a JSON object. + args = { + "referedAddress" : referedAddress + }; + break; + case "transfer": + functionName = 'transferNumericAsset'; + if (progress == 'MarginOrDfreeze') { + // Escrow or directly freezing request + // segregate destination with/without escrow + if (txInfo._fromEscrowID != '') { + toAddress = txInfo._fromEscrowID; + } else { + toAddress = txInfo._fromSettlementID; + } + fromAddress = txInfo._fromAccountID; + } else if (progress == 'Freeze') { + // Freezing request + fromAddress = txInfo._fromEscrowID; + toAddress = txInfo._fromSettlementID; + } else if (progress == 'Recovery') { + // Recovery request + // segregate assets with/without escrow + if (txInfo._fromEscrowID != '') { + fromAddress = txInfo._fromEscrowID; + } else { + fromAddress = txInfo._fromSettlementID; + } + toAddress = txInfo._fromAccountID; + } else { + // Bad pattern, empty function name leads to error + functionName = ""; + } + amount = txInfo._fromAsset; + // Create the argument as a JSON object. + args = { + "fromAddress" : fromAddress, + "toAddress" : toAddress, + "amount" : amount + }; + break; + case "payment": + if (progress == 'Credit') { + amount = txInfo._toAsset; + // Credit request + // Trasnfering from the settlement account of the fromChain + functionName = 'transferNumericAsset'; + fromAddress = txInfo._toSettlementID; + toAddress = txInfo._toAccountID; + args = { + "fromAddress" : fromAddress, + "toAddress" : toAddress, + "amount" : amount + }; + } else { + // Bad pattern, empty function name leads to error + functionName = ""; + } + break; + default: + return new Promise((resolve, reject) => { + var emsg = "apiType \"" + apiType + "\" not found!"; + logger.error(emsg); + var ret_obj = { + "status" : 404, + "errorCode" : 3000, + "errorDetail" : emsg + }; + return reject(ret_obj); + }); + } // switch(apiType) + + if (functionName=="") { + return new Promise((resolve, reject) => { + var emsg = "Function not found!"; + logger.error(emsg); + var ret_obj = { + "status" : 404, + "errorCode" : 3000, + "errorDetail" : emsg + }; + return reject(ret_obj); + }); + } + + return new Promise((resolve, reject) => { + logger.info(' function : ' + functionName); + logger.info(' args : ' + JSON.stringify(args)); + + connector_if.request(client, functionName, args) + .then((response) => { + return resolve(response); + }) + .catch((err) => { + return reject(err); + }); + }); + } + + startMonitor(client, cb) { + var plugCb = function(res) { + var events = []; + if (res.status == 200) { + // res into a core-readable format (Array of identification IDs and data bodies) + var block = res.blockData; + var len = block.transactions.length; + for (var i = 0; i < len; i++) { + var txData = block.transactions[i]; + var txid = txData.hash; // Ethereum has id in "hash" + var eventInfo = { + 'id' : txid, + 'data' : txData + }; + events[i] = eventInfo; + } + + var ret_obj = { + "status" : 200, + "events" : events + }; + cb(ret_obj); + } else { + // Callback in response to error + cb(res); + } + } + connector_if.startMonitor(client, plugCb); + } + + stopMonitor(clientid) { + connector_if.stopMonitor(clientid); + } + +} /* class */ + +module.exports = ClientPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/PluginConfig.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/PluginConfig.js new file mode 100644 index 0000000000..57595f28c4 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec1_dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Configuration file of adapter: a part dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/ClientPlugin.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/ClientPlugin.js new file mode 100644 index 0000000000..205d6e93d1 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/ClientPlugin.js @@ -0,0 +1,212 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ClientPlugin.js + */ + +/* + * Summary: + * Adapter: a part dependent on end-chains + */ + +// configuration file +var CplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ClientPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Library of a part independent of end-chains (cooperation server side) +var connector_if = require('../connector_if.js'); + +/* + * ClientPlugin + * Class definitions for client plugins + */ +var ClientPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /** + * execConnecterApi: Connector API execution request + * @param {Object} client : The core-side client from which the run was requested + * @param {JSON} params : Information to determine the distribution of operations to be performed on the EC side + * { + * "apiType":, - one of "reference" "," "transfer" "," "payment" + * "progress":, - one of "MarginOrDfreeze" "," "Credit" "," "Freeze" "," "Recovery" "," "Other" + * "txInfo": , object for inter-EC asset transfer transaction information" + * "otherData": - as is, where to find in the pre-supply review" "targetAccountID" "only" + * } + **/ + execConnecterApi(client, params) { + var functionName = ""; + var args = {}; + // Write the processing according to the API definition of the connector side + var apiType = params.apiType; + var progress = params.progress; + var txInfo = params.txInfo; + var otherData = params.otherData; + var fromAddress = ""; + var toAddress = ""; + var referedAddress = ""; + var amount = 0; + + switch(apiType) { + case "reference": + var referedAddress = ''; + if (progress == 'MarginOrDfreeze') { + // Confirmation of fromAccount and balance before escrow or directly freezing + functionName = 'getNumericBalance'; + referedAddress = txInfo._fromAccountID; + } else if (progress == 'Credit') { + functionName = 'getNumericBalance'; + referedAddress = otherData.targetAccountID; + } else if (progress == 'Other') { + functionName = 'getNumericBalance'; + referedAddress = otherData.targetAccountID; + } else { + // Suppress sending to server plugins by making functionName empty. + functionName = ""; + } + // Create the argument as a JSON object. + args = { + "referedAddress" : referedAddress + }; + break; + case "transfer": + functionName = 'transferNumericAsset'; + if (progress == 'MarginOrDfreeze') { + // Escrow or directly freezing request + // segregate destination with/without escrow + if (txInfo._fromEscrowID != '') { + toAddress = txInfo._fromEscrowID; + } else { + toAddress = txInfo._fromSettlementID; + } + fromAddress = txInfo._fromAccountID; + } else if (progress == 'Freeze') { + // Freezing request + fromAddress = txInfo._fromEscrowID; + toAddress = txInfo._fromSettlementID; + } else if (progress == 'Recovery') { + // Recovery request + // segregate assets with/without escrow + if (txInfo._fromEscrowID != '') { + fromAddress = txInfo._fromEscrowID; + } else { + fromAddress = txInfo._fromSettlementID; + } + toAddress = txInfo._fromAccountID; + } else { + // Bad pattern, empty function name leads to error + functionName = ""; + } + amount = txInfo._fromAsset; + // Create the argument as a JSON object. + args = { + "fromAddress" : fromAddress, + "toAddress" : toAddress, + "amount" : amount + }; + break; + case "payment": + if (progress == 'Credit') { + amount = txInfo._toAsset; + // Credit request + // Trasnfering from the settlement account of the fromChain + functionName = 'transferNumericAsset'; + fromAddress = txInfo._toSettlementID; + toAddress = txInfo._toAccountID; + args = { + "fromAddress" : fromAddress, + "toAddress" : toAddress, + "amount" : amount + }; + } else { + // Bad pattern, empty function name leads to error + functionName = ""; + } + break; + default: + return new Promise((resolve, reject) => { + var emsg = "apiType \"" + apiType + "\" not found!"; + logger.error(emsg); + var ret_obj = { + "status" : 404, + "errorCode" : 3000, + "errorDetail" : emsg + }; + return reject(ret_obj); + }); + } // switch(apiType) + + if (functionName=="") { + return new Promise((resolve, reject) => { + var emsg = "Function not found!"; + logger.error(emsg); + var ret_obj = { + "status" : 404, + "errorCode" : 3000, + "errorDetail" : emsg + }; + return reject(ret_obj); + }); + } + + return new Promise((resolve, reject) => { + logger.info(' function : ' + functionName); + logger.info(' args : ' + JSON.stringify(args)); + + connector_if.request(client, functionName, args) + .then((response) => { + return resolve(response); + }) + .catch((err) => { + return reject(err); + }); + }); + } + + startMonitor(client, cb) { + var plugCb = function(res) { + var events = []; + if (res.status == 200) { + // res into a core-readable format (Array of identification IDs and data bodies) + var block = res.blockData; + var len = block.transactions.length; + for (var i = 0; i < len; i++) { + var txData = block.transactions[i]; + var txid = txData.hash; // Ethereum has id in "hash" + var eventInfo = { + 'id' : txid, + 'data' : txData + }; + events[i] = eventInfo; + } + + var ret_obj = { + "status" : 200, + "events" : events + }; + cb(ret_obj); + } else { + // Callback in response to error + cb(res); + } + } + connector_if.startMonitor(client, plugCb); + } + + stopMonitor(clientid) { + connector_if.stopMonitor(clientid); + } + +} /* class */ + +module.exports = ClientPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/PluginConfig.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/PluginConfig.js new file mode 100644 index 0000000000..57595f28c4 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/coreSide/adapter/lib/ec2_dependent/PluginConfig.js @@ -0,0 +1,16 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Configuration file of adapter: a part dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // Nothing special +}; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginConfig.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginConfig.js new file mode 100644 index 0000000000..508e286eee --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginConfig.js @@ -0,0 +1,17 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginConfig.js + */ + +/* + * Summary: + * Configuration file of cooperation server: a part dependent on end-chains + * Definition values specific to the connection dependent part, etc. + */ + +module.exports = { + // URL to operate and monitor + "provider" : 'http://geth1:8545' +}; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginUtil.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginUtil.js new file mode 100644 index 0000000000..fb2f81637f --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/PluginUtil.js @@ -0,0 +1,68 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * PluginUtil.js + */ + +/* + * Summary: + * Cooperation server: utility library dependent on endchains + * For example, implementing internal functions that should not be exposed as functions of server plugins. + */ + +/* + * convNum + * + * @param {String/Number} value: The scientific string or numeric value to be converted. For numbers, it is returned as is. + * @param {String/Number} default_value: The value to use if conversion was not possible. Text or number in scientific notation. Optional. + * + * @return {Number} The value converted to a number, or the default_value if the conversion failed. + * + * @desc exponentiation is also supported. The following formats are supported:. (value, default_value) + * 3.78*10^14 + * 3.78e14 + */ +exports.convNum = function(value, default_value) { + var retvalue = 0; + var def_value = 0; + + switch(typeof(default_value)) { + case "number": + def_value = default_value; + break; + case "string": + var def_value_str = default_value.replace(/\*10\^/g, "e"); + def_value = parseFloat(def_value_str); + break; + default: // undefined should also be included here. + // Fixed value because of cumbersome handling. + def_value = 0; + break; + } // switch(typeof(default_value)) + + if(def_value==NaN) { // number is guaranteed. + def_value = 0; + } + + switch(typeof(value)) { + case "number": + retvalue = value; + break; + case "string": + var value_str = value.replace(/\*10\^/g, "e"); + retvalue = parseFloat(value_str); + break; + default: + // Set default value. + retvalue = def_value; + break; + } // switch(typeof(value)) + + if(retvalue==NaN) { // number is guaranteed. + retvalue = def_value; + } + + return retvalue; +} + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerMonitorPlugin.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerMonitorPlugin.js new file mode 100644 index 0000000000..e6555b2f50 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerMonitorPlugin.js @@ -0,0 +1,115 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerMonitorPlugin.js + */ + +/* + * Summary: + * Connector: monitoring class of a part dependent on end-chains + * Used in the case of monitoring continuously. + * Processing that ends with the acceptance of a single event is handled by a custom function implementation in server plugins. + * Unlike server plugins, it basically does not handle its own functions. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerMonitorPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// Load libraries, SDKs, etc. according to specifications of endchains as needed +var Web3 = require('web3'); + +/* + * ServerMonitorPlugin + * Class definitions of server monitoring + */ +var ServerMonitorPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + // Initialize monitored filter + this._filterTable = {}; + } + + /* + * startMonitor + * Start Monitoring + * @param {string} clientId: Client ID from which monitoring start request was made + * @param {function} cb: A callback function that receives monitoring results at any time. + */ + startMonitor(clientId, cb) { + logger.info('*** START MONITOR ***'); + logger.info('Client ID :' + clientId); + // Implement handling to receive events from an end-chain and return them in a callback function + var filter = this._filterTable[clientId]; + if (!filter) { + logger.info('create new web3 filter and start watching.'); + try { + var web3 = new Web3(); + var provider = new web3.providers.HttpProvider(SplugConfig.provider); + web3.setProvider(provider); + filter = web3.eth.filter("latest"); + // filter should be managed by client ID + // (You should never watch multiple urls from the same client) + this._filterTable[clientId] = filter; + filter.watch(function (error, blockHash) { + if (!error) { + var blockData = web3.eth.getBlock(blockHash, true); + var trLength = blockData.transactions.length; + logger.info(trLength + " transactions in block " + blockData.number); + if (trLength > 0) { + logger.info('*** SEND BLOCK DATA ***'); + // Notify only if transaction exists + var ret_obj = { + "status" : 200, + "blockData" : blockData + }; + cb(ret_obj); + } + } else { + var err_obj = { + "status" : 504, + "errorDetail" : error + }; + cb(err_obj); + } + }); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + var err_obj = { + "status" : 504, + "errorDetail" : emsg + }; + cb(err_obj); + } + } else { + logger.info('target filter has already start watching.'); + } + } + + /* + * stopMonitor + * monitoring stop + * @param {string} clientId: Client ID from which monitoring stop request was made + */ + stopMonitor(clientId) { + // Implement a process to end EC monitoring + var filter = this._filterTable[clientId]; + if (filter) { + // Stop the filter & Remove it from table + logger.info('stop watching and remove filter.'); + filter.stopWatching(); + delete this._filterTable[clientId]; + } + } + +} /* class */ + +module.exports = ServerMonitorPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerPlugin.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerPlugin.js new file mode 100644 index 0000000000..fa9c3aa710 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/ServerPlugin.js @@ -0,0 +1,177 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerPlugin.js + */ + +/* + * Summary: + * Connector: a part dependent on endchains + * Define and implement the function according to the connection destination dependent part (ADAPTER) on the core side. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// utility +var SplugUtil = require('./PluginUtil.js'); +// Load libraries, SDKs, etc. according to specifications of endchains as needed +var Web3 = require('web3'); + +/* + * ServerPlugin + * Class definition for server plugins + */ +var ServerPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * isExistFunction + * + * @param {String} funcName: The function name to check. + * + * @return {Boolean} true: Yes./false: does not exist. + * + * @desc Return if end-chain specific funtion is implemented + * Scope of this function is in this class + * Functions that should not be called directly should be implemented outside this class like utilities. + */ + isExistFunction(funcName) { + if(this[funcName]!=undefined) { + return true; + } else { + return false; + } + } + + // Define an arbitrary function and implement it according to specifications of end-chains + /* + * getNumericBalance + * Get numerical balance + * + * @param {Object} args JSON Object + * { + * "referedAddress": + * } + * @return {Object} JSON object + */ + getNumericBalance(args) { + // * The Web3 API can be used synchronously, but each function is always an asynchronous specification because of the use of other APIs such as REST, + return new Promise((resolve, reject) => { + logger.info("getNumericBalance start"); + var ret_obj = {}; + if(args["referedAddress"]==undefined) { + var emsg = "JSON parse error!"; + logger.info(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + var ethargs = args["referedAddress"]; + // Handling exceptions to absorb the difference of interest. + try { + var web3 = new Web3(); + web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); + var balance = web3.eth.getBalance(ethargs); + var amount_val = balance.toNumber(); + + ret_obj = { + "status" : 200, + "amount" : amount_val + }; + return resolve(ret_obj); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + }); + } + + /* + * transferNumericAsset + * Transfer numerical asset + * + * @param {Object} args JSON Object + * { + * "fromAddress":, + * "toAddress":, + * "amount": + * } + * @return {Object} JSON object + */ + transferNumericAsset(args) { + return new Promise((resolve, reject) => { + logger.info("transferNumericAsset start"); + + var ret_obj = {}; + var send_args = {}; + var send_function = 'sendTransaction'; + + if((args["fromAddress"]==undefined)||(args["toAddress"]==undefined)|| + (args["amount"]==undefined)) { + var emsg = "JSON parse error!"; + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + // Dealing with exponentiation strings. + var amount = SplugUtil.convNum(args["amount"], 0); + + // Create an argument object for sendTransaction. + send_args = { + "from" : args["fromAddress"], + "to" : args["toAddress"], + "value" : amount + }; + + + logger.info('send_func :' + send_function); + logger.info('send_args :' + JSON.stringify(send_args)); + + // Handle the exception once to absorb the difference of interest. + try { + var web3 = new Web3(); + web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); + var res = web3.eth[send_function](send_args); + + ret_obj = { + "status" : 200, + "txid" : res + }; + return resolve(ret_obj); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + }); + } + +} /* class */ + +module.exports = ServerPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/geth_getAccounts.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/geth_getAccounts.js new file mode 100644 index 0000000000..82341c5629 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec1_dependent/geth_getAccounts.js @@ -0,0 +1,20 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * geth_getAccounts.js + */ + +// File for direct operation of geth +// Account & balance checking +var SplugConfig = require('./PluginConfig.js'); +var Web3 = require('web3'); +var web3 = new Web3(); +web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); +var accounts = web3.eth.accounts; +for (var i=0; i 0) { + logger.info('*** SEND BLOCK DATA ***'); + // Notify only if transaction exists + var ret_obj = { + "status" : 200, + "blockData" : blockData + }; + cb(ret_obj); + } + } else { + var err_obj = { + "status" : 504, + "errorDetail" : error + }; + cb(err_obj); + } + }); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + var err_obj = { + "status" : 504, + "errorDetail" : emsg + }; + cb(err_obj); + } + } else { + logger.info('target filter has already start watching.'); + } + } + + /* + * stopMonitor + * monitoring stop + * @param {string} clientId: Client ID from which monitoring stop request was made + */ + stopMonitor(clientId) { + // Implement a process to end EC monitoring + var filter = this._filterTable[clientId]; + if (filter) { + // Stop the filter & Remove it from table + logger.info('stop watching and remove filter.'); + filter.stopWatching(); + delete this._filterTable[clientId]; + } + } + +} /* class */ + +module.exports = ServerMonitorPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/ServerPlugin.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/ServerPlugin.js new file mode 100644 index 0000000000..fa9c3aa710 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/ServerPlugin.js @@ -0,0 +1,177 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * ServerPlugin.js + */ + +/* + * Summary: + * Connector: a part dependent on endchains + * Define and implement the function according to the connection destination dependent part (ADAPTER) on the core side. + */ + +// configuration file +var SplugConfig = require('./PluginConfig.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('ServerPlugin[' + process.pid + ']'); +logger.level = config.logLevel; +// utility +var SplugUtil = require('./PluginUtil.js'); +// Load libraries, SDKs, etc. according to specifications of endchains as needed +var Web3 = require('web3'); + +/* + * ServerPlugin + * Class definition for server plugins + */ +var ServerPlugin = class { + /* + * constructors + */ + constructor(){ + // Define dependent specific settings + } + + /* + * isExistFunction + * + * @param {String} funcName: The function name to check. + * + * @return {Boolean} true: Yes./false: does not exist. + * + * @desc Return if end-chain specific funtion is implemented + * Scope of this function is in this class + * Functions that should not be called directly should be implemented outside this class like utilities. + */ + isExistFunction(funcName) { + if(this[funcName]!=undefined) { + return true; + } else { + return false; + } + } + + // Define an arbitrary function and implement it according to specifications of end-chains + /* + * getNumericBalance + * Get numerical balance + * + * @param {Object} args JSON Object + * { + * "referedAddress": + * } + * @return {Object} JSON object + */ + getNumericBalance(args) { + // * The Web3 API can be used synchronously, but each function is always an asynchronous specification because of the use of other APIs such as REST, + return new Promise((resolve, reject) => { + logger.info("getNumericBalance start"); + var ret_obj = {}; + if(args["referedAddress"]==undefined) { + var emsg = "JSON parse error!"; + logger.info(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + var ethargs = args["referedAddress"]; + // Handling exceptions to absorb the difference of interest. + try { + var web3 = new Web3(); + web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); + var balance = web3.eth.getBalance(ethargs); + var amount_val = balance.toNumber(); + + ret_obj = { + "status" : 200, + "amount" : amount_val + }; + return resolve(ret_obj); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + }); + } + + /* + * transferNumericAsset + * Transfer numerical asset + * + * @param {Object} args JSON Object + * { + * "fromAddress":, + * "toAddress":, + * "amount": + * } + * @return {Object} JSON object + */ + transferNumericAsset(args) { + return new Promise((resolve, reject) => { + logger.info("transferNumericAsset start"); + + var ret_obj = {}; + var send_args = {}; + var send_function = 'sendTransaction'; + + if((args["fromAddress"]==undefined)||(args["toAddress"]==undefined)|| + (args["amount"]==undefined)) { + var emsg = "JSON parse error!"; + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + // Dealing with exponentiation strings. + var amount = SplugUtil.convNum(args["amount"], 0); + + // Create an argument object for sendTransaction. + send_args = { + "from" : args["fromAddress"], + "to" : args["toAddress"], + "value" : amount + }; + + + logger.info('send_func :' + send_function); + logger.info('send_args :' + JSON.stringify(send_args)); + + // Handle the exception once to absorb the difference of interest. + try { + var web3 = new Web3(); + web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); + var res = web3.eth[send_function](send_args); + + ret_obj = { + "status" : 200, + "txid" : res + }; + return resolve(ret_obj); + } catch (e) { + var emsg = e.toString().replace(/Error: /g , ""); + logger.error(emsg); + ret_obj = { + "status" : 504, + "errorDetail" : emsg + }; + return reject(ret_obj); + } + }); + } + +} /* class */ + +module.exports = ServerPlugin; + diff --git a/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/geth_getAccounts.js b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/geth_getAccounts.js new file mode 100644 index 0000000000..82341c5629 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/cooperation/ecSide/connector/lib/ec2_dependent/geth_getAccounts.js @@ -0,0 +1,20 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * geth_getAccounts.js + */ + +// File for direct operation of geth +// Account & balance checking +var SplugConfig = require('./PluginConfig.js'); +var Web3 = require('web3'); +var web3 = new Web3(); +web3.setProvider(new web3.providers.HttpProvider(SplugConfig.provider)); +var accounts = web3.eth.accounts; +for (var i=0; i { + + if (fromType == 'number' && toType == 'number') { + // only one of the assets must be specified + if (!isSpecified(fromAsset) && !isSpecified(toAsset)) { + return reject(new CoreAPIError(422, 2023)); + } + if (isSpecified(fromAsset) && isSpecified(toAsset)) { + return reject(new CoreAPIError(422, 2022)); + } + } else { + // Not supported when the assets are not numeric + return reject(new CoreAPIError(422, 3104)); + } + + // At this moment, only the source/destination asset information is specified or both are not specified. + var isFrom = true; + var input = fromAsset; + if (isSpecified(toAsset)) { + isFrom = false; + input = toAsset; + } + + // Apply conversion rules based on the asset types of source and destination asset + // For numeric to numeric, apply conversion ratio and commission + if (fromType == 'number') { + var ratio = parseFloat(rule.rule); + var commission = rule.commission; + var com_cons = 0; // commission constant value + var com_rate = 0; // commission ratio + var percentIndex = commission.indexOf('%'); + if (percentIndex == -1) { + // Commission is represented as a constant + com_cons = parseInt(commission, 10); + } else if (percentIndex > 0) { + // Commission is represented as a ratio + com_rate = parseFloat(commission); // parseFloat can remove the percent notation + }// * If the first character is "%" (percentIndex = = 0), it is treated as zero commission. + + if (!isFinite(ratio) || !isFinite(com_cons) || !isFinite(com_rate) || com_rate >= 100) { + logger.error('Rule or commission values are inappropriate.'); + return reject(new CoreAPIError(500, 3101)); + } + + // number -> number + if (toType == 'number') { + var inputAsset = parseInt(input, 10); + var retAsset = 0; + if (isFrom) { + retAsset = (inputAsset * (100 - com_rate) / 100 - com_cons) * ratio / 100; + // TODO: what if the result is a decimal? + // Truncate as is + retAsset = Math.floor(retAsset); + retObj.toAsset = retAsset; + } else { + retAsset = (inputAsset * 100 / ratio) * 100 / (100 - com_rate) + com_cons; + // Round up in the calculation (to => from) + retAsset = Math.ceil(retAsset); + retObj.fromAsset = retAsset; + } + return resolve(retObj); + } + } + }); + } +} + +function isSpecified(src) { + if (src != undefined && src != '') { + return true; + } + return false; +} + +module.exports = AssetConverter; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js new file mode 100644 index 0000000000..302d4cf480 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/EventMediator.js @@ -0,0 +1,744 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * EventMediator.js + */ + +/* Summary: + * Allocate subsequent processing for chaincodes added to CC by user and events from EC +*/ + +var fabricv10Post = require("../common/fabric_v1.0/sdk_if.js"); +var expPost = require("../common/exp/adapter_if.js"); +var EcInfoAction = require("../common/EcInfoAction.js"); +var UserImpleUtil = require('./UserImpleUtil.js'); +var config = require('config'); +// Log settings +var log4js = require('log4js'); +var logger = log4js.getLogger('EventMediator[' + process.pid + ']'); +logger.level = config.logLevel; + +// hash table for caching asset transfer transaction information +var TxTable = {}; + +/** + * Perform subsequent processing of detected events from your own additional chaincode + * @param {string} ccid :Chaincode ID + * @param {string} func :invoke execute function + * @param {[]string} args :invoke run-time arguments +**/ +exports.receivedUserChaincodeEvent = function(ccid, func, args) { + if (ccid == config.chaincodeIDs[0]) { + // Events from chaincodes for asset transfer information management + // add/update asset transfer transaction information + if (func == 'createTransferTransaction') { + operation_createTransferTransaction(args); + } else if (func == 'updateTransferTransaction') { + operation_updateTransferTransaction(args); + } + } else { + // Events from chaincodes outside the ConnectionChain function in the blockchain in the CC side + // No special handling + } +} + + +// Perform asset transfer transaction cache update and state update requests +function updateTransactionInfo(txID, progress, eventID) { + var txCache = TxTable[txID]; + var argEcEvID = eventID; + // Update cache contents + var updateTime = UserImpleUtil.getTimestampString(); + txCache._updateTime = updateTime; + txCache._progress = progress; + if (progress == 'requestMargin' || progress == 'fixedMargin') { + if (txCache._escrowEvID == '') { + txCache._escrowEvID = eventID; + } else { + argEcEvID = ''; // Do not update if already set + } + } else if (progress == 'requestDirectFreeze' || progress == 'fixedDirectFreeze') { + if (txCache._settlementEvID == '') { + txCache._settlementEvID = eventID; + } else { + argEcEvID = ''; // Do not update if already set + } + } else if (progress == 'requestCredit') { + txCache._paymentEvID = eventID; + } else if (progress == 'requestFreeze') { + txCache._settlementEvID = eventID; + } else if (progress == 'requestRecovery') { + txCache._restoreEvID = eventID; + } + + // You can update the asset information, but it is not necessary for the current sample. + var assetInfo = ''; + + // Update asset transfer transaction information + var invokeArgs = [txID, progress, argEcEvID, assetInfo, updateTime]; + fabricv10Post.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'updateTransferTransaction', + invokeArgs) + .then((response) => { + if (response.data != '') { + logger.info('Update Transaction information requested. id: ' + txID + ', progress: ' + progress); + } else { + throw new Error('Transaction information does not exist. id: ' + txID); + } + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + +// processing after generating asset transfer transaction information +function operation_createTransferTransaction(args) { + var txID = args[0]; + var ruleID = args[2]; + var fromChainID = args[3]; + var fromAccountID = args[4]; + var fromAssetType = args[5]; + var fromAsset = args[6]; + var fromEscrowID = args[7]; + var fromSettlementID = args[8]; + var toChainID = args[9]; + var toAccountID = args[10]; + var toAssetType = args[11]; + var toAsset = args[12]; + var toSettlementID = args[13]; + var createTime = args[14]; + + logger.info(args); // for call timing and parameter verification. + // add asset transfer transaction information to cache + var txCache = new TxInfo(ruleID, fromChainID, fromAccountID, fromAssetType, fromAsset, + fromEscrowID, fromSettlementID, + toChainID, toAccountID, toAssetType, toAsset, + toSettlementID, createTime); + TxTable[txID] = txCache; + + var ecInfoJson = {}; + // run flag for the escrow (or direct freezing) failure action + var isTransferFailed = false; + + // Get destination EC information by specifying destination end-chain ID + EcInfoAction.getECInfoCache(fromChainID) + .then((ecCache) => { + ecInfoJson = ecCache.getJsonObj(); + var otherData = {}; + // Get the asset information of the sender user + return execEndChainApi(ecInfoJson.adapterUrl, fromChainID, 'reference', 'MarginOrDfreeze', txCache, otherData) + }) + .then((response) => { + // Confirm the asset information of the sender user (Balance or ID Exists) + checkExistAsset(response, fromAssetType, fromAsset); + + var otherData = {}; + // Transfer assets from the sender user to the escrow account (or representative account) in the sender end-chain + return execEndChainApi(ecInfoJson.adapterUrl, fromChainID, 'transfer', 'MarginOrDfreeze', txCache, otherData) + }) + .catch((err) => { + // Failure of escrow / direct freeze if error occurs anywhere in the above flow + isTransferFailed = true; + throw err; + }) + .then((response) => { + // Get and return the event ID (Or substituted value) from the execution result of the transfer and credit API + var parsedResponse = parseEndChainApiResponse(response); + var eventID = parsedResponse.eventID; + + if (txCache.isEscrow()) { + // Escrow + logger.info('escrow id: ' + eventID); + // Update asset transfer transaction information + updateTransactionInfo(txID, 'requestMargin', eventID); + } else { + // Direct freezing + logger.info('settlement id: ' + eventID); + // Update asset transfer transaction information + updateTransactionInfo(txID, 'requestDirectFreeze', eventID); + } + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + if (isTransferFailed){ + if (txCache.isEscrow()) { + // Update asset transfer transaction information to failure state of escrow + updateTransactionInfo(txID, 'failedMargin', ''); + } else { + // Update asset transfer transaction information to failure state of direct freezing + updateTransactionInfo(txID, 'failedDirectFreeze', ''); + } + } + }); +} + + +// Processing after updating asset transfer transaction information +function operation_updateTransferTransaction(args) { + var txID = args[0]; + var progress = args[1]; + + if (progress == 'fixedMargin') { + // Completion of update to fixedMargin (escrow complete) state + // Initiate credit requests in the receiver end-chain + operation_updatedFixedMargin(txID); + } else if (progress == 'failedMargin') { + // Completion of update to failedMargin (escrow failure) state + operation_updatedFailedMargin(txID); + } else if (progress == 'fixedDirectFreeze') { + // Completion of update to fixedDirectFreeze (direct freezing complete) state + // Start credit request in the receiver end-chain (Same process as after completion of escrow) + operation_updatedFixedMargin(txID); + } else if (progress == 'failedDirectFreeze') { + // Completion of update to failedDirectFreeze (direct freeze failure) state + operation_updatedFailedDirectFreeze(txID); + } else if (progress == 'fixedCredit') { + // Completion of update to fixedCredit (credit complete) state + // Start freezing request on the sender end-chain + // or completion of the transfer process itself (in the case of no escrow) + operation_updatedFixedCredit(txID); + } else if (progress == 'failedCredit') { + // Completion of update to failedCredit (credit failure) state + // Start recovery request in the sender end-chain + operation_updatedFailedCredit(txID); + } else if (progress == 'fixedRecovery') { + // Completion of update to fixedRecovery (recovery complete) state + operation_updatedFixedRecovery(txID); + } else if (progress == 'fixedFreeze') { + // Completion of update to fixedFreeze (freezing complete) state + // = completion of the transfer process itself + operation_updatedComplete(txID); + } + // Do nothing when notifying completion of update to various request states +} + +// Processing after update to escrow complete state +function operation_updatedFixedMargin(txID) { + var txCache = TxTable[txID]; + var toChainID = txCache._toChainID; + var toAccountID = txCache._toAccountID; + var toAsset = txCache._toAsset; + var toAssetType = txCache._toAssetType; + var toSettlementID = txCache._toSettlementID; + + var ecInfoJson = {}; + // whether the credit process is transferred from the representative account + var isTransfer = false; + // Execution flag of credit failure processing + var isCreditFailed = false; + + // Get the receiver end-chain information + EcInfoAction.getECInfoCache(toChainID) + .then((ecCache) => { + ecInfoJson = ecCache.getJsonObj(); + // Before a credit request, the balance checking API checks for the presence of the receiver account + // Specify the receiver account as the target + var otherData = {targetAccountID:toAccountID}; + // Get the receiver user's asset information (Do not check the contents) + return execEndChainApi(ecInfoJson.adapterUrl, toChainID, 'reference', 'Credit', txCache, otherData) + }) + .then((response) => { + // Check balance of credit source + // Specify the representative account of the receiver end-chain as the target + var otherData = {targetAccountID:toSettlementID}; + // Get the asset information of the representative account of the receiver end-chain + return execEndChainApi(ecInfoJson.adapterUrl, toChainID, 'reference', 'Credit', txCache, otherData) + }) + .then((response) => { + // Check balance of credit source + checkExistAsset(response, toAssetType, toAsset); + + var otherData = {}; + // Asset Credit to the receiver user (move from the representative account) + return execEndChainApi(ecInfoJson.adapterUrl, toChainID, 'payment', 'Credit', txCache, otherData) + }) + .catch((err) => { + // Credit failure if there is an error somewhere in the above flow + isCreditFailed = true; + throw err; + }) + .then((response) => { + // Get and return the event ID (or substituted value) from the execution result of the transfer and credit API + var parsedResponse = parseEndChainApiResponse(response); + var eventID = parsedResponse.eventID; + logger.info('payment id: ' + eventID); + // Update of asset transfer transaction information + updateTransactionInfo(txID, 'requestCredit', eventID); + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + if (isCreditFailed){ + // Update of asset transfer transaction information to credit failure state + updateTransactionInfo(txID, 'failedCredit', ''); + } + }); +} + +// Processing after updating to credit complete state +function operation_updatedFixedCredit(txID) { + var txCache = TxTable[txID]; + + // In the case of no escrow, the transfer process itself is now complete + if (!txCache.isEscrow()) { + operation_updatedComplete(txID); + return; + } + + var fromChainID = txCache._fromChainID; + var fromAsset = txCache._fromAsset; + var ecInfoJson = {}; + + // Get the sender end-chain information + EcInfoAction.getECInfoCache(fromChainID) + .then((ecCache) => { + ecInfoJson = ecCache.getJsonObj(); + var otherData = {}; + + // Asset transfer from the escrow account to the representative account in the sender end-chain + return execEndChainApi(ecInfoJson.adapterUrl, fromChainID, 'transfer', 'Freeze', txCache, otherData) + }) + .then((response) => { + // Get and return the event ID (or substituted value) from the execution result of the transfer and credit API + var parsedResponse = parseEndChainApiResponse(response); + var eventID = parsedResponse.eventID; + logger.info('settlement id: ' + eventID); + // Update of asset transfer transaction information + updateTransactionInfo(txID, 'requestFreeze', eventID); + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + +// Processing after updating to credit failure state +function operation_updatedFailedCredit(txID) { + var txCache = TxTable[txID]; + var fromChainID = txCache._fromChainID; + var fromAccountID = txCache._fromAccountID; + var fromAsset = txCache._fromAsset; + + var ecInfoJson = {}; + + // Get the sender end-chain information + EcInfoAction.getECInfoCache(fromChainID) + .then((ecCache) => { + ecInfoJson = ecCache.getJsonObj(); + var otherData = {}; + + // Asset transfer from the escrow account to the sender account in the sender end-chain + return execEndChainApi(ecInfoJson.adapterUrl, fromChainID, 'transfer', 'Recovery', txCache, otherData) + }) + .then((response) => { + // Get and return the event ID (or substituted value) from the execution result of the transfer and credit API + var parsedResponse = parseEndChainApiResponse(response); + var eventID = parsedResponse.eventID; + logger.info('restore id: ' + eventID); + // Update of asset transfer transaction information + updateTransactionInfo(txID, 'requestRecovery', eventID); + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + +// Processing after updating to escrow failure state +function operation_updatedFailedMargin(txID) { + // Cache deleting of asset transfer transaction information + delete TxTable[txID]; +} + +// Processing after updating to directly freezing failure state +function operation_updatedFailedDirectFreeze(txID) { + // Cache deleting of asset transfer transaction information + delete TxTable[txID]; +} + +// Processing after updating to recovery complete state +function operation_updatedFixedRecovery(txID) { + // Cache deleting of asset transfer transaction information + delete TxTable[txID]; +} + +// Processing after updating to freezing complete state or credit complete (no escrow) state +function operation_updatedComplete(txID) { + // Cache deleting of asset transfer transaction information + delete TxTable[txID]; +} + + +/** + * Execute of processing after detected events from EC (via the pass from connector to adapter) + * @param {string} cainId : Chain ID + * @param {string} eventId : Event ID + * @param {JSON} eventInfo : Event Information +**/ +exports.receivedEndchainEvent = function(chainId, eventId, eventInfo) { + // Get destination end-chain information + EcInfoAction.getECInfoCache(chainId) + .then((ecCache) => { + var ecInfoJson = ecCache.getJsonObj(); + var transferEvid = eventId; + var paymentEvid = eventId; + + // cache lookup of asset transfer transaction information + var isFind = false; + for (txID in TxTable) { + var txCache = TxTable[txID]; + if (chainId == txCache._fromChainID) { + // One of the events in the sender end-chain (Escrow/Directly Freezing/Freezing/Recovery). + // Find transaction information matching each condition. + // Usually, it is enough to determine with only progress and eventID. + // In some cases, the contents of eventInfo are also used to determine, but this example does not require it. + + if (txCache._progress == 'requestMargin') { + // If it is a transfer from the sender account to the escrow account? + if (transferEvid == txCache._escrowEvID) { + // If found, update the asset transfer transaction to escrow complete state. + isFind = true; + updateTransactionInfo(txID, 'fixedMargin', transferEvid); + break; + } + } else if (txCache._progress == 'requestDirectFreeze') { + // If it is a transfer from the sender account to the representative account? + if (transferEvid == txCache._settlementEvID) { + // If found, update the asset transfer transaction to directly freezing complete state. + isFind = true; + updateTransactionInfo(txID, 'fixedDirectFreeze', transferEvid); + break; + } + } else if (txCache._progress == 'requestFreeze') { + // If it is a transfer from the escrow account to the representative account? + if (transferEvid == txCache._settlementEvID) { + // If found, update the asset transfer transaction to recovery complete state. + isFind = true; + updateTransactionInfo(txID, 'fixedFreeze', ''); + break; + } + } else if (txCache._progress == 'requestRecovery') { + // If it is a transfer from the escrow account to the sender account or a transfer from the representative account to the sender account? + if (transferEvid == txCache._restoreEvID) { + // If found, update the asset transfer transaction to recovery complete state. + isFind = true; + updateTransactionInfo(txID, 'fixedRecovery', ''); + break; + } + } + } + if (chainId == txCache._toChainID) { + // events in the receiver chain (credit) + + if (txCache._progress == 'requestCredit') { + // If it is a moving or adding to the receiver account? + if (paymentEvid == txCache._paymentEvID) { + // If found, update the asset transfer transaction to credit complete state. + isFind = true; + updateTransactionInfo(txID, 'fixedCredit', ''); + break; + } + } + } + } + + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + + +/* + * end-chain API Execution Functions + * @param {string} adapterUrl :Adapter URL + * @param {string} chainID :Chain ID of the end-chain to execute. Processically unused, for review logs + * @param {string} apiType :API type + * Reference ("reference") | Transfer ("transfer") | Credit ("payment") + * @param {string} progress :Transfer Status + * Escrow/Directly Frozen ("MarginOrDfreeze") | Credit ("Credit") | Freezing ("Freeze") | Recovery ("Recovery") | Other ("Other") + * @param {JSON} txInfo :Information about the running transaction + * @param {JSON} otherData :Other information to give to the API. + */ +function execEndChainApi(adapterUrl, chainID, apiType, progress, txInfo, otherData) { + logger.info('exec EndChain Api.'); + logger.info(' chain id : ' + chainID); + logger.info(' api type : ' + apiType); + logger.info(' progress : ' + progress); + var params = { + apiType : apiType, + progress : progress, + txInfo : txInfo, + otherData : otherData + }; + + logger.info(' adapter : ' + adapterUrl); + logger.info(' params : ' + JSON.stringify(params)); + + return new Promise((resolve, reject) => { + // Just call it as it is (Branch processing is performed in the adapter.) + expPost.send(adapterUrl, params) + .then((response) => { + // Return in JSON format if parse is possible. Use as is if it cannot be converted (Just a string, already a JSON Object... etc.) + var resData = null; + logger.info('api response:'); + try { + resData = JSON.parse(response); + } catch (e) { + resData = response; + } + logger.info(JSON.stringify(resData)); + return resolve(resData); + }) + .catch((err) => { + return reject(err); + }); + }); +} + +// Check asset existence (Balance) +function checkExistAsset(srcData, assetType, assetInfo) { + logger.info('check asset.'); + // Get balance value from execEndChainApi (reference) response content + + var fieldName = 'amount'; // The name of the balance storage area set in the Connector + var referenceData = UserImpleUtil.getFieldData(srcData, fieldName); + if (referenceData == undefined) { + // No area with the specified name exists + throw new Error('The specified data field does not exist:' + fieldName); + } + logger.info(' asset type : ' + assetType); + logger.info(' reference data : ' + referenceData); + logger.info(' necessary asset: ' + assetInfo); + if (assetType == 'number') { + // For numerical values, compare the balance with the current transfer amount + if (Number(referenceData) < Number(assetInfo)) { + // insufficient balance + throw new Error('Transfer source quantity is insufficient. remaining: ' + referenceData + ' , necessary: ' + assetInfo); + } + } else { + // Currently, only numerical values ('number') are available for asset types. + // Excluding numbers, eliminated by AssetConverter when generating asset transfer transaction + } +} + +// Result to analize execution of execEndChainApi (Transfer/Credit) +function parseEndChainApiResponse(response) { + // Get and return the event ID (Or substituted values) + var retObj = {eventID:''}; + var resultField = 'txid'; // EC-side event ID storage area name set by the adapter + // If it is a number, it will be treated as a string. + retObj.eventID = '' + UserImpleUtil.getFieldData(response, resultField); + return retObj; +} + +function setTxInfoCache() { + // Store in cache the asset transfer transaction information in an incomplete state + logger.info('Cache Transaction info start.'); + getTxListByProgress(0); +} + +function getTxListByProgress(index) { + // Escrow Failure (failedMargin), Directly Freezing Failure (failedDirectFreeze), Recovery Complete (fixedRecovery), Completed (complete) + // Get list in each cases different from the above states. + var targetProgress = ['initial', 'requestMargin', 'fixedMargin', + 'requestDirectFreeze', 'fixedDirectFreeze', + 'requestCredit', 'fixedCredit', + 'requestRecovery', 'requestFreeze']; + logger.info('Step :' + index); + var queryArgs = ['', targetProgress[index], '', '', '', '']; + fabricv10Post.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getTransferTransactionList', + queryArgs) + .then((response) => { + var txList = JSON.parse(response); + if (txList) { + for (var i = 0; i < txList.length; i++) { + var txInfo = txList[i]; + var txID = txInfo.id; + var ruleID = txInfo.ruleID; + var fromChainID = txInfo.fromChain.chainID; + var fromAccountID = txInfo.fromChain.accountID; + var fromAssetType = txInfo.fromChain.assetType; + var fromAsset = txInfo.fromChain.asset; + var fromEscrowID = txInfo.fromChain.escrowAccountID; + if (fromEscrowID == undefined) { + fromEscrowID = ''; + } + var fromSettlementID = txInfo.fromChain.settlementAccountID; + var toChainID = txInfo.toChain.chainID; + var toAccountID = txInfo.toChain.accountID; + var toAssetType = txInfo.toChain.assetType; + var toAsset = txInfo.toChain.asset; + var toSettlementID = txInfo.toChain.settlementAccountID; + var createTime = txInfo.timestamps.create; + + // caching transfer transaction information. + var txCache = new TxInfo(ruleID, fromChainID, fromAccountID, fromAssetType, fromAsset, + fromEscrowID, fromSettlementID, + toChainID, toAccountID, toAssetType, toAsset, + toSettlementID, createTime); + + txCache._progress = txInfo.progress; + // Set in cache if there is escrow state information. + var escrowEvID = txInfo.fromChain.escrowEvID; + if (escrowEvID != undefined) { + txCache._escrowEvID = escrowEvID; + var fixedMarginTime = txInfo.timestamps.fixedMargin; + var failedMarginTime = txInfo.timestamps.failedMargin; + if (fixedMarginTime != undefined) { + txCache._updateTime = fixedMarginTime; + } else if (failedMarginTime != undefined) { + // Impossible route because escrow failure status has not been obtained. + txCache._updateTime = failedMarginTime; + } else { + txCache._updateTime = txInfo.timestamps.requestMargin; + } + } + // Set in cache if there is directly freezing state information. + var settlementEvID = txInfo.fromChain.settlementEvID; + if (settlementEvID != undefined && !txCache.isEscrow()) { + txCache._settlementEvID = settlementEvID; + var fixedDirectFreezeTime = txInfo.timestamps.fixedDirectFreeze; + var failedDirectFreezeTime = txInfo.timestamps.failedDirectFreeze; + if (fixedDirectFreezeTime != undefined) { + txCache._updateTime = fixedDirectFreezeTime; + } else if (failedDirectFreezeTime != undefined) { + // Impossible route because directly freezing failure status is not obtained. + txCache._updateTime = failedDirectFreezeTime; + } else { + txCache._updateTime = txInfo.timestamps.requestDirectFreeze; + } + } + // Set in cache if there is credit status information. + var paymentEvID = txInfo.toChain.paymentEvID; + if (paymentEvID != undefined) { + txCache._paymentEvID = paymentEvID; + var fixedCreditTime = txInfo.timestamps.fixedCredit; + var failedCreditTime = txInfo.timestamps.failedCredit; + if (fixedCreditTime != undefined) { + txCache._updateTime = fixedCreditTime; + } else if (failedCreditTime != undefined) { + txCache._updateTime = failedCreditTime; + } else { + txCache._updateTime = txInfo.timestamps.requestCredit; + } + } + // Set in cache if there is recovery state information. + var restoreEvID = txInfo.fromChain.restoreEvID; + if (restoreEvID != undefined) { + txCache._restoreEvID = restoreEvID; + var fixedRecoveryTime = txInfo.timestamps.fixedRecovery; + if (fixedRecoveryTime != undefined) { + // Impossible route because we have not obtained the fixed state. + txCache._updateTime = fixedRecoveryTime; + } else { + txCache._updateTime = txInfo.timestamps.requestRecovery; + } + } + // Set in cache if there is freezing state information. + if (settlementEvID != undefined && txCache.isEscrow()) { + txCache._settlementEvID = settlementEvID; + var fixedFreezeTime = txInfo.timestamps.fixedFreeze; + if (fixedFreezeTime != undefined) { + // Impossible route because the completion state has not been obtained. + txCache._updateTime = fixedFreezeTime; + } else { + txCache._updateTime = txInfo.timestamps.requestFreeze; + } + } + // Append to cache table. + TxTable[txID] = txCache; + } + } + logger.info('TxTable:'); + logger.info(Object.keys(TxTable)); + var nextIndex = index + 1; + if (nextIndex < targetProgress.length) { + // get list for next state. + getTxListByProgress(nextIndex); + } else { + logger.info('Cache Transaction info end.'); + } + }) + .catch((err) => { + logger.error(JSON.stringify(err)); + }); +} + + +/** + * Initial processing at server startup +**/ +exports.initAction = function() { + return setTxInfoCache(); +} + + +// Asset transfer transaction information storage class +var TxInfo = class { + + constructor(ruleID, fromChainID, fromAccountID, fromAssetType, fromAsset, + fromEscrowID, fromSettlementID, + toChainID, toAccountID, toAssetType, toAsset, + toSettlementID, createTime) { + this._ruleID = ruleID; + this._fromChainID = fromChainID; + this._fromAccountID = fromAccountID; + this._fromAssetType = fromAssetType; + this._fromAsset = fromAsset; + this._fromEscrowID = fromEscrowID; + this._fromSettlementID = fromSettlementID; + this._toChainID = toChainID; + this._toAccountID = toAccountID; + this._toAssetType = toAssetType; + this._toAsset = toAsset; + this._toSettlementID = toSettlementID; + this._createTime = createTime; + this._updateTime = createTime; + this._progress = 'initial'; + this._escrowEvID = ''; + this._paymentEvID = ''; + this._restoreEvID = ''; + this._settlementEvID = ''; + } + + isEscrow() { + if (this._fromEscrowID == '') { + return false; + } else { + return true; + } + } + + getJsonObj() { + var returnObj = { + ruleID : this._ruleID, + fromChain : { + chainID : this._fromChainID, + accountID : this._fromAccountID, + assetType : this._fromAssetType, + asset : this._fromAsset, + escrowAccountID : this._fromEscrowID, + settlementAccountID : this._fromSettlementID, + escrowEvID : this._escrowEvID, + settlementEvID : this._settlementEvID, + restoreEvID : this._restoreEvID + }, + toChain : { + chainID : this._toChainID, + accountID : this._toAccountID, + assetType : this._toAssetType, + asset : this._toAsset, + settlementAccountID : this._toSettlementID, + paymentEvID : this._paymentEvID + }, + progress : this._progress + }; + return returnObj; + } +}; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js new file mode 100644 index 0000000000..52e4c228ff --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserErrorDef.js @@ -0,0 +1,49 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * UserErrorDef.js + */ + +/* Summary: + * User Defined Error +*/ + +// Error code & message definition +const definitions = { +// Describe in errorCode:"errorMessage" format +// unregistered information + 1003 : 'Unregistered conversion rule ID.', + 1004 : 'Unregistered transaction ID.', +// Request parameter not specified + 2010 : 'No escrow account ID specified.', + 2011 : 'No representative account ID specified.', + 2012 : 'No display name for the transformation rule specified.', + 2013 : 'No sender end-chain ID specified.', + 2014 : 'No asset type of the sender specified.', // unused + 2015 : 'No receiver end-chain ID specified.', + 2016 : 'No asset type of the receiver specified.', // unused + 2017 : 'No conversion rule specified', // unused + 2018 : 'No commission specified.', // unused + 2019 : 'No transformation rule ID specified.', + 2020 : 'No sender end-chain account ID has been specified.', + 2021 : 'No receiver end-chain account ID has been specified.', + 2022 : 'Both the assets of the sender and the receiver are specified.', + 2023 : 'Either sender or reveiver asset information is required.', + 2024 : 'No sender end-chain information specified.', + 2025 : 'No receiver end-chain information specified.', + 2027 : 'Escrow or no escrow specified.', // unused + 2028 : 'The escrow account and the representative account are duplicated.', + 2029 : 'The sender account and the receiver account are duplicated.', + 2030 : 'The sender account and the representative account of the sender end-chain are duplicated.', + 2031 : 'The receiver account and the representative account of the receiver end-chain are duplicated.', + 2032 : 'The specified end-chain ID and the end-chain ID defined in the transformation rule do not match.', +// duplicated registration + 3003 : 'The conversion rule ID is already registered.', // unused + 3004 : 'This is the already registered transaction ID.', // unused +// Other (related in asset conversion) + 3101 : 'Incorrect conversion ratio or commission value.', + 3104 : 'This is not a asset type which can be converted.' +}; + +module.exports = definitions; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserImpleUtil.js b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserImpleUtil.js new file mode 100644 index 0000000000..59224f3851 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/lib/userImple/UserImpleUtil.js @@ -0,0 +1,102 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * UserImpleUtil.js + */ + +/* Summary: + * Utility classes for user implementations +*/ + +var UserImpleUtil = class { + + /** + * time stamp string creation + * @return{string} Current time string (YYYYMMDDDThhmmss format) + **/ + static getTimestampString() { + var dateObj = new Date(); + var y = dateObj.getFullYear(); + var m = ('0' + (dateObj.getMonth() + 1)).slice(-2); + var d = ('0' + dateObj.getDate()).slice(-2); + var h = ('0' + dateObj.getHours()).slice(-2); + var min = ('0' + dateObj.getMinutes()).slice(-2); + var s = ('0' + dateObj.getSeconds()).slice(-2); + return ''+ y + m + d + 'T' + h + min + s; + } + + /** + * Create Date object from timestamp string + * @param {string} tsString: Timestamp string (YYYYMMDDDThhmmss format) + * @return{object} Date object + **/ + static getDateObject(tsString) { + var y = parseInt(tsString.substr(0,4), 10); + var m = parseInt(tsString.substr(4,2), 10) -1; + var d = parseInt(tsString.substr(6,2), 10); + var h = parseInt(tsString.substr(9,2), 10); + var min = parseInt(tsString.substr(11,2), 10); + var s = parseInt(tsString.substr(13,2), 10); + return new Date(y, m, d, h, min, s); + } + + /** + * Retrieves data from a JsonObject at the named location + * @param {JSON} input: JsonObject (Example: {testData:{response:{result:100}}}) + * @param {string} fieldName: Field names in the hierarchy separated by periods (Example: "testdata.response.result") + * @return{} data at that location (Example: 100) + * If the specified field contains an array, return a single array of values stored in those fields + * (Example: + * input:[{user:A, asset:10}, {user:B, asset:11}] + * fieldName:".asset" * If each element in the array is not named, connect the empty string with a period + * -> Return [10, 11] + * ) + **/ + static getFieldData(input, fieldName) { + var fieldData = input; + var fieldNames = []; + if (fieldName != null && fieldName != undefined && fieldName != '') { + fieldNames = fieldName.split('.'); + } + for (var i = 0; i < fieldNames.length; i++) { + if (fieldData != null && fieldNames[i] != '') { + fieldData = fieldData[fieldNames[i]]; + } + if (fieldData == null || fieldData == undefined) { + break; + } + // If fieldData is an array and fieldNames still follows + if ((fieldData instanceof Array) && (i < fieldNames.length -1)) { + var fieldDataArray = []; + // Reconnect subsequent field names with a period + var subFieldName = fieldNames[i + 1]; + for (var j = i + 2; j < fieldNames.length; j++) { + subFieldName = subFieldName + '.' + fieldNames[j]; + } + for (var j = 0; j < fieldData.length; j++) { + // Reapply this function to each element of the array + fieldDataArray[j] = this.getFieldData(fieldData[j], subFieldName); + } + // If each element is an array, combine them into one + var newArray = []; + var isConcat = false; + for (var j = 0; j < fieldDataArray.length; j++) { + if (fieldDataArray[j] instanceof Array) { + newArray = newArray.concat(fieldDataArray[j]); + isConcat = true; + } + } + if (isConcat) { + fieldData = newArray; + } else { + fieldData = fieldDataArray; + } + break; + } + } + return fieldData; + } +} + +module.exports = UserImpleUtil; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/rules.js b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/rules.js new file mode 100644 index 0000000000..e73461f012 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/rules.js @@ -0,0 +1,444 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * rules.js + */ + +/* Summary: + * Transformation Rules Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var fabricSdkPost = require("../../lib/common/fabric_v1.0/sdk_if.js"); +var CoreAPIError = require('../../lib/common/CoreAPIError.js'); +var CoreAPIUtil = require('../../lib/common/CoreAPIUtil.js'); +var router = express.Router(); + + +/** + * Transformation Rule Information Generation + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "ruleName": "", - display name on the UI + * "fromChain":{ - The sender EC information + * "chainID": "", - The ID of the sender EC + * "settlementAccountID": "", - The ID of the representative account of the sender EC + * "escrowAccountID": "" - The ID of the escrow account of the sender EC. If omitted, no escrow occurs. + * }, + * "toChain":{ - The receiver EC information + * "chainID": "", - The ID of the receiver EC + * "settlementAccountID": "" - The ID of the representative account of the receiver EC + * }, + * "rule": "", - The conversion ratio (%) for the conversion of value from the sender asset to the receiver asset. The default is 100. + * "commission": "" - Amount to deduct from the receiver asset before conversion. The default is 0. + * } + * Response Body: + * { + * "ruleID": "" - ID of the registered transformation rule information + * } +**/ +router.post('/', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var ruleName = req.body.ruleName; + if (ruleName == undefined) { + next(new CoreAPIError(422, 2012)); + return; + } + if (req.body.fromChain == undefined) { + next(new CoreAPIError(422, 2024)); + return; + } + var fromChainID = req.body.fromChain.chainID; + if (fromChainID == undefined) { + next(new CoreAPIError(422, 2013)); + return; + } + var fromAssetType = req.body.fromChain.assetType; + if (fromAssetType == undefined) { + // Treat as a number if omitted + fromAssetType = 'number'; + } + if (req.body.toChain == undefined) { + next(new CoreAPIError(422, 2025)); + return; + } + var toChainID = req.body.toChain.chainID; + if (toChainID == undefined) { + next(new CoreAPIError(422, 2015)); + return; + } + var toAssetType = req.body.toChain.assetType; + if (toAssetType == undefined) { + // Treat as a number if omitted + toAssetType = 'number'; + } + var rule = req.body.rule; + if (rule == undefined) { + // Treat as 100% by default + rule = '100'; + } + var commission = req.body.commission; + if (commission == undefined) { + // Treat as 0 by default + commission = '0'; + } + var fromEscrowID = req.body.fromChain.escrowAccountID; + if (fromEscrowID == undefined){ + // Set empty if omitted (treatment as no escrow) + fromEscrowID = ''; + } + var fromSettlementID = req.body.fromChain.settlementAccountID; + var toSettlementID = req.body.toChain.settlementAccountID; + // Representative account must be specified + if (fromSettlementID == undefined || toSettlementID == undefined) { + next(new CoreAPIError(422, 2011)); + return; + } + // Error if the same account ID is used for the escrow account and the representative account + if (fromEscrowID == fromSettlementID) { + next(new CoreAPIError(422, 2028)); + return; + } + + // Register transformation rule information + var invokeArgs = [ruleName, fromChainID, toChainID, + fromAssetType, toAssetType, + fromEscrowID, fromSettlementID, toSettlementID, + rule, commission]; + + fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'addRuleInfo', + invokeArgs) + .then((response) => { + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {ruleID:response.data}; + res.status(201).send(resObj); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Update transformation rule information + * @name put/:id + * @function + * @inner + * @param {string} path - The rule ID to update + * @param {callback} middleware - Express middleware + * Request Body: + * * Elements that is not changed can be omitted. + * { + * "ruleName": "", + * "fromChain":{ + * "chainID": "", + "isEscrow" , - true if the change (no escrow -> escrow), false if the change (escrow -> no escrow) + * "settlementAccountID": "", + * "escrowAccountID": "" + * }, + * "toChain":{ + * "chainID": "", + * "settlementAccountID": "" + * }, + * "rule": "", + * "commission": "" + * } + * Response Body: + * { + * "ruleID": "" - ID of the updated transformation rule information + * } +**/ +router.put('/:id', function(req, res, next) { + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + var id = req.params.id; + var ruleName = ''; + var fromChainID = ''; + var fromAssetType = ''; + var isEscrow = null; + var fromEscrowID = ''; + var fromSettlementID = ''; + var toSettlementID = ''; + var toChainID = ''; + var toAssetType = ''; + var rule = ''; + var commission = ''; + var removeEscrow = ''; + // Changed or not of the end-chain itself used in the rule + var fromChainChange = false; + var toChainChange = false; + + // Get current transformation rule information + var ruleInfo = null; + var queryArgs = [id]; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getRuleInfo', + queryArgs) + .then((response) => { + if (response != ''){ + ruleInfo = JSON.parse(response); + }else{ + throw new CoreAPIError(404, 1003); + } + + // Unspecified parameter specifies an empty string (Never Update) + // * If the chain itself is changed, there is no guarantee that there will be a deposit/representative account with the same ID as the chain before the change. + // Specifying is required even if same ID exists + if (req.body.ruleName != undefined) { + ruleName = req.body.ruleName; + } + if (req.body.fromChain != undefined) { + if (req.body.fromChain.chainID != undefined) { + fromChainID = req.body.fromChain.chainID; + if (fromChainID != ruleInfo.fromChain.chainID) { + fromChainChange = true; + } + } + if (req.body.fromChain.assetType != undefined) { + fromAssetType = req.body.fromChain.assetType; + } + isEscrow = req.body.fromChain.isEscrow; + var escrowID = req.body.fromChain.escrowAccountID; + if (isEscrow != undefined) { + // Escrow or not is specified + if (isEscrow) { + // Change to "escrow" + if (escrowID == undefined) { + // Error if unspecified + throw new CoreAPIError(404, 2010); + } else { + // Update to specified value if specified + fromEscrowID = escrowID; + } + } else { + // Some value for removeEscrow if changing to "no escrow" + removeEscrow = '-'; + } + } else { + // If escrow or not specified, + if (ruleInfo.fromChain.escrowAccountID == undefined) { + // If there is no current value, no matter what else you specify "No change (Blank)" + fromEscrowID = ''; + } else if (escrowID == undefined) { + // If there is a current value and the escrow account specification is also omitted + if (fromChainChange) { + // Error if not specified in the context of changing the chain itself + throw new CoreAPIError(404, 2010); + } else { + // If the chain itself has not changed "No change (Blank)" + fromEscrowID = ''; + } + } else { + // Current value exists, update to specified value if specified + fromEscrowID = escrowID; + } + } + if (req.body.fromChain.settlementAccountID != undefined) { + fromSettlementID = req.body.fromChain.settlementAccountID; + } else if (fromChainChange) { + // Error if not specified in the context of changing the chain itself + throw new CoreAPIError(404, 2011); + } + } + if (req.body.toChain != undefined) { + if (req.body.toChain.chainID != undefined) { + toChainID = req.body.toChain.chainID; + if (toChainID != ruleInfo.toChain.chainID) { + toChainChange = true; + } + } + if (req.body.toChain.assetType != undefined) { + toAssetType = req.body.toChain.assetType; + } + if (req.body.toChain.settlementAccountID != undefined) { + toSettlementID = req.body.toChain.settlementAccountID; + } else if (toChainChange) { + // Error if not specified in the context of changing the chain itself + throw new CoreAPIError(404, 2011); + } + } + if (req.body.rule != undefined) { + rule = req.body.rule; + } + if (req.body.commission != undefined) { + commission = req.body.commission; + } + + // Error if the same account ID is used for the escrow account and the representative account + if ((fromEscrowID != '') && (fromEscrowID == fromSettlementID)) { + throw new CoreAPIError(422, 2028); + } + + var invokeArgs = [id, ruleName, fromChainID, toChainID, + fromAssetType, toAssetType, + fromEscrowID, fromSettlementID, toSettlementID, + rule, commission, removeEscrow]; + + return fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'updateRuleInfo', + invokeArgs) + }) + .then((response) => { + if (response.data != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {ruleID:response.data}; + res.send(resObj); + }else{ + next(new CoreAPIError(404, 1003)); + } + }) + .catch((err) => { + if (err instanceof CoreAPIError) { + next(err); + } else { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + } + }); +}); + +/** + * Delete conversion rule information + * @name delete/:id + * @function + * @inner + * @param {string} path - Rule ID to delete + * @param {callback} middleware - Express middleware +**/ +router.delete('/:id', function(req, res, next) { + var invokeArgs = [req.params.id]; + fabricSdkPost.invokeRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'deleteRuleInfo', + invokeArgs) + .then((response) => { + res.status(204).send(); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Get one conversion rule information + * @name get/:id + * @function + * @inner + * @param {string} path - Rule ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * { + * "id": "", + * "ruleName": "", + * "fromChain":{ + * "chainID": "", + * "settlementAccountID": "", + * "escrowAccountID": "" + * }, + * "toChain":{ + * "chainID": "", + * "settlementAccountID": "" + * }, + * "rule": "", + * "commission": "" + * } +**/ +router.get('/:id', function(req, res, next) { + var queryArgs = [req.params.id]; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getRuleInfo', + queryArgs) + .then((response) => { + if (response != ''){ + var resInfo = JSON.parse(response); + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(resInfo); + }else{ + next(new CoreAPIError(404, 1003)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Get the list of conversion rule information + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * Squeezing condition. You can omit elements that you do not want to narrow down. + * { + * "fromChainID": "", + * "toChainID": "" + * } + * Response Body: Array of transformation rule information +**/ +router.get('/', function(req, res, next) { + var fromChainID = req.query.fromChainID; + if (fromChainID == undefined) { + fromChainID = ''; + } + var toChainID = req.query.toChainID; + if (toChainID == undefined) { + toChainID = ''; + } + + var queryArgs = [fromChainID, toChainID]; + + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getRuleInfoList', + queryArgs) + .then((response) => { + var resInfo = JSON.parse(response); + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(resInfo); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/transfers.js b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/transfers.js new file mode 100644 index 0000000000..34350c85da --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/servers/restserver/coreapi/routes/userImple/transfers.js @@ -0,0 +1,328 @@ +/* + * Copyright 2019 Fujitsu Laboratories Ltd. + * SPDX-License-Identifier: Apache-2.0 + * + * transfers.js + */ + +/* Summary: + * Asset Transfer Transaction Information Management API +*/ + +var express = require('express'); +var config = require('config'); +var fabricSdkPost = require("../../lib/common/fabric_v1.0/sdk_if.js"); +var CoreAPIError = require('../../lib/common/CoreAPIError.js'); +var CoreAPIUtil = require('../../lib/common/CoreAPIUtil.js'); +var UserImpleUtil = require('../../lib/userImple/UserImpleUtil.js'); +var AssetConverter = require('../../lib/userImple/AssetConverter.js'); +var router = express.Router(); + + +/** + * Asset transfer transaction information generation + * @name post + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * { + * "txID" "<", - ID of the asset transfer transaction information. The default is set automatically." + * "ruleID": "", - ID of the conversion rule to apply + * "fromChain":{ - the sender information + * "chainID": "", - ID of the sender EC + * "accountID": "", - ID of the sender account + * "asset": "" - The amount of assets in the sender end-chain. You can specify only one of it or that in the receiver EC. + * }, + * "toChain":{ - the receiver information + * "chainID": "", - The ID of the receiver EC + * "accountID": "", - The ID of the receiver account + * "asset": "" - The amount of assets in the receiver end-chain. You can specify only one of it or that in the sender EC + * } + * } + * Response Body: + * { + * "txID": "" - ID of the generated asset transfer transaction information + * } +**/ +router.post('/', function(req, res, next) { + var userID = req.header('X-CCUser'); + if (userID == undefined) { + next(new CoreAPIError(422, 2001)); + return; + } + if (!CoreAPIUtil.isValidContentType(req)) { + next(new CoreAPIError(400, 2000)); + return; + } + + // TxID can also be specified as a parameter. Generated here if not specified. + var txID = req.body.txID; + var timestamp = UserImpleUtil.getTimestampString(); + if (txID == undefined) { + var date = new Date(); + var ms = ('0' + date.getMilliseconds()).slice(-3); + txID = timestamp + ms + '_'+ userID; + } + + var ruleID = req.body.ruleID; + if (ruleID == undefined) { + next(new CoreAPIError(422, 2019)); + return; + } + + if (req.body.fromChain == undefined) { + next(new CoreAPIError(422, 2024)); + return; + } + var fromChain = req.body.fromChain.chainID; + if (fromChain == undefined) { + next(new CoreAPIError(422, 2013)); + return; + } + var fromAccount = req.body.fromChain.accountID; + if (fromAccount == undefined) { + next(new CoreAPIError(422, 2020)); + return; + } + if (req.body.toChain == undefined) { + next(new CoreAPIError(422, 2025)); + return; + } + var toChain = req.body.toChain.chainID; + if (toChain == undefined) { + next(new CoreAPIError(422, 2015)); + return; + } + var toAccount = req.body.toChain.accountID; + if (toAccount == undefined) { + next(new CoreAPIError(422, 2021)); + return; + } + var fromAsset = req.body.fromChain.asset; + var toAsset = req.body.toChain.asset; + + var fromEscrowID = ''; + var fromSettlementID = ''; + var toSettlementID = ''; + + // Get conversion Rule + var queryArgs = [ruleID]; + var rule = {}; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getRuleInfo', + queryArgs) + .then((response) => { + // Result of getting conversion the rule + if (response != ''){ + rule = JSON.parse(response); + if ((rule.fromChain.chainID != fromChain) || (rule.toChain.chainID != toChain)) { + // The chain ID specified does not match the chain ID defined in the rule + throw new CoreAPIError(422, 2032); + } + // Convert assets according to the rule + return AssetConverter.convert(rule, fromAsset, toAsset) + }else{ + // If no conversion rule exists for the given ID + throw new CoreAPIError(404, 1003); + } + }) + .then((output) => { + // Reflect asset conversion result + fromAsset = output.fromAsset; + toAsset = output.toAsset; + + // Reflect parameters from the rule (Transfer Source Esccrow/Representative Account, Transfer Receiver Representative Account) in the Tx information + fromEscrowID = rule.fromChain.escrowAccountID; + if (fromEscrowID == undefined) { + fromEscrowID = ''; + } + fromSettlementID = rule.fromChain.settlementAccountID; + toSettlementID = rule.toChain.settlementAccountID; + + // Check duplicated accounts + // The sender account and the escrow account are the same + if (fromAccount == fromEscrowID) { + throw new CoreAPIError(422, 2029); + } + // The sender account and the representative account of the sender EC are the same + if (fromAccount == fromSettlementID) { + throw new CoreAPIError(422, 2030); + } + // The receiver account and the representative account of the receiver EC are the same + if (toAccount == fromSettlementID) { + throw new CoreAPIError(422, 2031); + } + + // request to create asset transfer transaction + var invokeArgs = [txID, userID, ruleID, + fromChain, fromAccount, rule.fromChain.assetType, ''+fromAsset, fromEscrowID, fromSettlementID, + toChain, toAccount, rule.toChain.assetType, ''+toAsset, toSettlementID, timestamp]; + + return fabricSdkPost.invokeRequest(config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'createTransferTransaction', + invokeArgs) + }) + .then((response) => { + // result of request to create asset transfer transaction + res.header('Content-Type', 'application/json; charset=UTF-8'); + var resObj = {txID:txID}; + res.send(resObj); + }) + .catch((err) => { + console.log(err); + if (err instanceof CoreAPIError) { + // No conversion rules or error on conversion failure + next(err); + } else { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + } + }); +}); + +/** + * Get one asset transfer transaction + * @name get/:id + * @function + * @inner + * @param {string} path - The operation ID to get + * @param {callback} middleware - Express middleware + * Response Body: + * Each event ID and time stamp is displayed only when the corresponding operation is performed. + * { + * "id": "", + * "userID": "", - ID of the user who generated the asset transfer transaction information + * "ruleID": "", + * "fromChain":{ + * "chainID": "", + * "accountID": "", + * "asset": "", + * "settlementAccountID": "", + * "escrowAccountID": "", + * "escrowEvID":" ", - Event ID of the asset transfer request to the escrow account + * "settlementEvID": "",- Event ID of the asset transfer request to the representative account + * "restoreEvID": "" - Event ID of the transfer request from the escrow account to the sender account + * }, + * "toChain":{ + * "chainID": "", + * "accountID": "", + * "asset": "", + * "settlementAccountID": "", + * "paymentEvID": "" - Event ID of the transfer request from the representative account to the receiver account + * }, + * "progress": "", - Current asset transfer transaction progress + * "timestamps" :{ - Timestamp at each progress of the asset transfer transaction + * "create": "", + * "requestMargin": "", + * "fixedMargin": "", + * "failedMargin": "", + * "requestDirectFreeze": "", + * "fixedDirectFreeze": "", + * "failedDirectFreeze": "", + * "requestCredit": "", + * "fixedCredit": "", + * "failedCredit": "", + * "requestRecovery": "", + * "fixedRecovery": "", + * "requestFreeze": "", + * "fixedFreeze": "" + * } + * } +**/ +router.get('/:id', function(req, res, next) { + var queryArgs = [req.params.id]; + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getTransferTransaction', + queryArgs) + .then((response) => { + if (response != ''){ + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(JSON.parse(response)); + }else{ + next(new CoreAPIError(404, 1004)); + } + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +/** + * Geth the list of asset transfer transaction information + * @name get + * @function + * @inner + * @param {string} path - Express path + * @param {callback} middleware - Express middleware + * Request Body: + * Squeezing condition. You can omit elements that you do not want to narrow down. + * { + * "userID": "", + * "progress": "", + * "createdBefore": "", + * "createdAfter": "", + * "updatedBefore": "", + * "updatedAfter": "" + * } + * Response Body:Array of asset transfer transaction information +**/ +router.get('/', function(req, res, next) { + var userID = req.query.userID; + if (userID == undefined) { + userID = ''; + } + var progress = req.query.progress; + if (progress == undefined) { + progress = ''; + } + var createdBefore = req.query.createdBefore; + if (createdBefore == undefined) { + createdBefore = ''; + } + var createdAfter = req.query.createdAfter; + if (createdAfter == undefined) { + createdAfter = ''; + } + var updatedBefore = req.query.updatedBefore; + if (updatedBefore == undefined) { + updatedBefore = ''; + } + var updatedAfter = req.query.updatedAfter; + if (updatedAfter == undefined) { + updatedAfter = ''; + } + + var queryArgs = [userID, progress, createdBefore, createdAfter, updatedBefore, updatedAfter]; + + fabricSdkPost.queryRequest( config.chainId, + config.network, + config.channelName, + config.chaincodeIDs[0], + 'getTransferTransactionList', + queryArgs) + + .then((response) => { + res.header('Content-Type', 'application/json; charset=UTF-8'); + res.send(JSON.parse(response)); + }) + .catch((err) => { + var detail = err.stack ? err.stack : err; + next(new CoreAPIError(500, 3000, detail)); + return; + }); +}); + +module.exports = router; diff --git a/packages/connection-chain/environment/sample/cc_env/yaml-files/configtx.yaml b/packages/connection-chain/environment/sample/cc_env/yaml-files/configtx.yaml new file mode 100644 index 0000000000..ba226fe5a2 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/yaml-files/configtx.yaml @@ -0,0 +1,46 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +--- +Profiles: + OneOrgOrdererGenesis: + Orderer: + <<: *OrdererDefaults + Organizations: + - *OrdererOrg + Consortiums: + SampleConsortium: + Organizations: + - *Org1 + OneOrgChannel: + Consortium: SampleConsortium + Application: + <<: *ApplicationDefaults + Organizations: + - *Org1 +Organizations: + - &OrdererOrg + Name: OrdererOrg + ID: OrdererMSP + MSPDir: ../crypto-config/ordererOrganizations/example.com/msp + - &Org1 + Name: Org1MSP + ID: Org1MSP + MSPDir: ../crypto-config/peerOrganizations/org1.example.com/msp + AnchorPeers: + - Host: peer0.org1.example.com + Port: 7051 +Orderer: &OrdererDefaults + OrdererType: solo + Addresses: + - orderer.example.com:7050 + BatchTimeout: 2s + BatchSize: + MaxMessageCount: 10 + AbsoluteMaxBytes: 99 MB + PreferredMaxBytes: 512 KB + Kafka: + Brokers: + - 127.0.0.1:9092 + Organizations: +Application: &ApplicationDefaults + Organizations: diff --git a/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-cc-template.yaml b/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-cc-template.yaml new file mode 100644 index 0000000000..09eb318667 --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-cc-template.yaml @@ -0,0 +1,194 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '2' +networks: + ccnet: + external: true +services: + ca.example.com: + image: hyperledger/fabric-ca:x86_64-1.0.4 + environment: + - FABRIC_CA_HOME=/etc/hyperledger/fabric-ca-server + - FABRIC_CA_SERVER_CA_NAME=ca.example.com +# ports: +# - "7054:7054" + command: sh -c 'fabric-ca-server start --ca.certfile /etc/hyperledger/fabric-ca-server-config/ca.org1.example.com-cert.pem --ca.keyfile /etc/hyperledger/fabric-ca-server-config/CA1_PRIVATE_KEY -b admin:adminpw -d' + volumes: + - ../crypto-config/peerOrganizations/org1.example.com/ca/:/etc/hyperledger/fabric-ca-server-config + container_name: ca.example.com + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + orderer.example.com: + container_name: orderer.example.com + image: hyperledger/fabric-orderer:x86_64-1.0.4 + environment: + - ORDERER_GENERAL_LOGLEVEL=debug + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISMETHOD=file + - ORDERER_GENERAL_GENESISFILE=/var/hyperledger/orderer/orderer.genesis.block + - ORDERER_GENERAL_LOCALMSPID=OrdererMSP + - ORDERER_GENERAL_LOCALMSPDIR=/var/hyperledger/orderer/msp + working_dir: /opt/gopath/src/github.com/hyperledger/fabric + command: orderer + volumes: + - ../channel-artifacts/genesis.block:/var/hyperledger/orderer/orderer.genesis.block + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp + - ../crypto-config/ordererOrganizations/example.com/orderers/orderer.example.com/tls/:/var/hyperledger/orderer/tls +# ports: +# - 7050:7050 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "3" + peer0.org1.example.com: + container_name: peer0.org1.example.com + extends: + file: ../../../base/cc_env/yaml-files/peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer0.org1.example.com + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 7051:7051 +# - 7053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + peer1.org1.example.com: + container_name: peer1.org1.example.com + extends: + file: ../../../base/cc_env/yaml-files/peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer1.org1.example.com + - CORE_PEER_ADDRESS=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer1.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer1.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 8051:7051 +# - 8053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + peer2.org1.example.com: + container_name: peer2.org1.example.com + extends: + file: ../../../base/cc_env/yaml-files/peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer2.org1.example.com + - CORE_PEER_ADDRESS=peer2.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer2.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer2.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer2.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 9051:7051 +# - 9053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + peer3.org1.example.com: + container_name: peer3.org1.example.com + extends: + file: ../../../base/cc_env/yaml-files/peer-base.yaml + service: peer-base + environment: + - CORE_PEER_ID=peer3.org1.example.com + - CORE_PEER_ADDRESS=peer3.org1.example.com:7051 + - CORE_PEER_GOSSIP_EXTERNALENDPOINT=peer3.org1.example.com:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer3.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer3.org1.example.com/msp:/etc/hyperledger/fabric/msp + - ../crypto-config/peerOrganizations/org1.example.com/peers/peer3.org1.example.com/tls:/etc/hyperledger/fabric/tls +# ports: +# - 10051:7051 +# - 10053:7053 + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + - peer2.org1.example.com + cli: + container_name: cli + image: hyperledger/fabric-tools:x86_64-1.0.4 + tty: true + environment: + - GOPATH=/opt/gopath + - CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock + - CORE_LOGGING_LEVEL=DEBUG + - CORE_PEER_ID=cli + - CORE_PEER_ADDRESS=peer0.org1.example.com:7051 + - CORE_PEER_LOCALMSPID=Org1MSP + - 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 + - CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/peer + command: /bin/bash -c './scripts/deploy_script.sh ${CHANNEL_NAME} ${DELAY} ${MODE}; sleep $TIMEOUT' + volumes: + - /var/run/:/host/var/run/ + - ../crypto-config/:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ + - ../channel-artifacts/:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts/ + - ../../../base/cc_env/chaincode/naming_service/:/opt/gopath/src/github.com/hyperledger/fabric/work/chaincode/naming_service/ + - ../../../base/cc_env/chaincode/endchain_information/:/opt/gopath/src/github.com/hyperledger/fabric/work/chaincode/endchain_information/ + - ../chaincode/transfer_information/:/opt/gopath/src/github.com/hyperledger/fabric/work/chaincode/transfer_information/ + - ../chaincode/deploy_script.sh:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/deploy_script.sh + depends_on: + - orderer.example.com + - peer0.org1.example.com + - peer1.org1.example.com + - peer2.org1.example.com + - peer3.org1.example.com + networks: + - ccnet diff --git a/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-servers.yaml b/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-servers.yaml new file mode 100644 index 0000000000..53996051dd --- /dev/null +++ b/packages/connection-chain/environment/sample/cc_env/yaml-files/docker-compose-sample-servers.yaml @@ -0,0 +1,133 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '2' +networks: + ccnet: + external: true + ec1net: + external: true + ec2net: + external: true +services: + rest-server: + build: ../../../base/cc_env/servers/restserver/build + image: hyperledger/ccenv-node6-rest-server + container_name: rest-server + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/restserver:/opt/gopath/src/github.com/hyperledger/fabric/work/server + - ../servers/restserver/coreapi/config:/opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/config + - ../servers/restserver/coreapi/lib/userImple:/opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/lib/userImple + - ../servers/restserver/coreapi/routes/userImple:/opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/routes/userImple + - ../servers/restserver/coreapi/app.js:/opt/gopath/src/github.com/hyperledger/fabric/work/server/coreapi/app.js + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '3030:3030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + app-server: + build: ../../../base/cc_env/servers/appserver/build + image: hyperledger/ccenv-node6-app-server + container_name: app-server + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/appserver:/opt/gopath/src/github.com/hyperledger/fabric/work/server + - ../servers/appserver/serviceapi/routes/userImple:/opt/gopath/src/github.com/hyperledger/fabric/work/server/serviceapi/routes/userImple + - ../servers/appserver/serviceapi/app.js:/opt/gopath/src/github.com/hyperledger/fabric/work/server/serviceapi/app.js + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '3031:3031' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec1-adapter: + build: ../../../base/cc_env/servers/cooperation/coreSide/build + image: hyperledger/ccenv-node8-adapter-server + container_name: ec1-adapter + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/cooperation/coreSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../../../base/cc_env/servers/cooperation/coreSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../../../base/cc_env/servers/cooperation/coreSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../../../base/cc_env/servers/cooperation/coreSide/ec1_adapter:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter + - ../servers/cooperation/coreSide/adapter/lib/ec1_dependent:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter/lib/dependent + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '5030:5030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec1-connector: + build: ../../../base/cc_env/servers/cooperation/ecSide/build + image: hyperledger/ccenv-node8-connector-server + container_name: ec1-connector + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/cooperation/ecSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../../../base/cc_env/servers/cooperation/ecSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../../../base/cc_env/servers/cooperation/ecSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../../../base/cc_env/servers/cooperation/ecSide/ec1_connector:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector + - ../servers/cooperation/ecSide/connector/lib/ec1_dependent:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector/lib/dependent + networks: + - ec1net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '5040:5040' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec2-adapter: + image: hyperledger/ccenv-node8-adapter-server + container_name: ec2-adapter + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/cooperation/coreSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../../../base/cc_env/servers/cooperation/coreSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../../../base/cc_env/servers/cooperation/coreSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../../../base/cc_env/servers/cooperation/coreSide/ec2_adapter:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter + - ../servers/cooperation/coreSide/adapter/lib/ec2_dependent:/opt/gopath/src/github.com/hyperledger/fabric/work/server/adapter/lib/dependent + networks: + - ccnet + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" +# ports: +# - '6030:6030' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" + ec2-connector: + image: hyperledger/ccenv-node8-connector-server + container_name: ec2-connector + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/work/server + volumes: + - ../../../base/cc_env/servers/cooperation/ecSide/build:/opt/gopath/src/github.com/hyperledger/fabric/work/server/build + - ../../../base/cc_env/servers/cooperation/ecSide/idle.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh + - ../../../base/cc_env/servers/cooperation/ecSide/setup.sh:/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh + - ../../../base/cc_env/servers/cooperation/ecSide/ec2_connector:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector + - ../servers/cooperation/ecSide/connector/lib/ec2_dependent:/opt/gopath/src/github.com/hyperledger/fabric/work/server/connector/lib/dependent + networks: + - ec2net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + ports: + - '6040:6040' + command: /bin/bash -c "/opt/gopath/src/github.com/hyperledger/fabric/work/server/setup.sh && /opt/gopath/src/github.com/hyperledger/fabric/work/server/idle.sh" diff --git a/packages/connection-chain/environment/sample/ec_env/delete.sh b/packages/connection-chain/environment/sample/ec_env/delete.sh new file mode 100755 index 0000000000..897e3e4ec1 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/delete.sh @@ -0,0 +1,7 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +docker-compose -f docker-init.yml down +docker-compose down +sudo rm -r ./data* +docker network rm ec1net +docker network rm ec2net diff --git a/packages/connection-chain/environment/sample/ec_env/docker-compose.yml b/packages/connection-chain/environment/sample/ec_env/docker-compose.yml new file mode 100644 index 0000000000..bb62f0836b --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/docker-compose.yml @@ -0,0 +1,77 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '3' +services: + geth1: + container_name: geth1 + image: ethereum/client-go:v1.8.27 + volumes: + - "$PWD/data-ec1:/root/data" + - "$PWD/genesis/genesis-ec1.json:/root/data/genesis.json" # mount genesis.js from host + working_dir: /root + #ports: + # - "8545:8545" #uncomment this line to enable JSON-RPC access from outside of network + # - "30303:30303" + expose: + - "8545" # enable JSON-RPC access fron only inside of network + networks: + - ec1net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + # following parameters are given to geth + command: > + --rpc + --networkid 10 + --nodiscover + --datadir=/root/data + --rpcaddr "0.0.0.0" + --rpcport "8545" + --rpccorsdomain "*" + --rpcvhosts "*" + --rpcapi "eth,web3" + --unlock 0,1,2,3,4 + --password "/dev/null" + --gasprice 0 + --mine --minerthreads=1 + geth2: + container_name: geth2 + image: ethereum/client-go:v1.8.27 + volumes: + - "$PWD/data-ec2:/root/data" + - "$PWD/genesis/genesis-ec2.json:/root/data/genesis.json" # mount genesis.js from host + working_dir: /root + #ports: + # - "8546:8545" #uncomment this line to enable JSON-RPC access from outside of network + # - "30303:30303" + expose: + - "8545" # enable JSON-RPC access fron only inside of network + networks: + - ec2net + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + # following parameters are given to geth + command: > + --rpc + --networkid 10 + --nodiscover + --datadir=/root/data + --rpcaddr "0.0.0.0" + --rpcport "8545" + --rpccorsdomain "*" + --rpcvhosts "*" + --rpcapi "eth,web3" + --unlock 0,1,2,3,4 + --password "/dev/null" + --gasprice 0 + --mine --minerthreads=1 +networks: + ec1net: + external: true + ec2net: + external: true diff --git a/packages/connection-chain/environment/sample/ec_env/docker-init.yml b/packages/connection-chain/environment/sample/ec_env/docker-init.yml new file mode 100644 index 0000000000..7dd3db86f0 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/docker-init.yml @@ -0,0 +1,44 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +version: '3' +services: + # ec1 + make-account-ec1: + container_name: make-account-ec1 + image: ethereum/client-go:v1.8.27 + tty: true + volumes: + - "$PWD/data-ec1:/root/data" + command: > + --datadir "/root/data" + account new --password /dev/null > /root/data/log.txt + init-chain-ec1: + container_name: init-chain-ec1 + image: ethereum/client-go:v1.8.27 + tty: true + volumes: + - "$PWD/data-ec1:/root/data" + - "$PWD/genesis/genesis-ec1.json:/root/data/genesis.json" + command: > + --datadir "/root/data" + init /root/data/genesis.json + # ec2 + make-account-ec2: + container_name: make-account-ec2 + image: ethereum/client-go:v1.8.27 + tty: true + volumes: + - "$PWD/data-ec2:/root/data" + command: > + --datadir "/root/data" + account new --password /dev/null > /root/data/log.txt + init-chain-ec2: + container_name: init-chain-ec2 + image: ethereum/client-go:v1.8.27 + tty: true + volumes: + - "$PWD/data-ec2:/root/data" + - "$PWD/genesis/genesis-ec2.json:/root/data/genesis.json" + command: > + --datadir "/root/data" + init /root/data/genesis.json diff --git a/packages/connection-chain/environment/sample/ec_env/down.sh b/packages/connection-chain/environment/sample/ec_env/down.sh new file mode 100755 index 0000000000..cb983db697 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/down.sh @@ -0,0 +1,4 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +docker-compose -f docker-init.yml down +docker-compose down diff --git a/packages/connection-chain/environment/sample/ec_env/genesis/template/genesis-template.json b/packages/connection-chain/environment/sample/ec_env/genesis/template/genesis-template.json new file mode 100644 index 0000000000..1d3401b722 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/genesis/template/genesis-template.json @@ -0,0 +1,20 @@ +{ + "config": { + "chainId": 10, + "homesteadBlock": 0, + "eip155Block": 0, + "eip158Block": 0 + }, + "alloc" : { + "ADDRESS": + {"balance":"100000000000000000000000000"} + }, + "coinbase" : "0x3333333333333333333333333333333333333333", + "difficulty" : "0x4000", + "extraData" : "", + "gasLimit" : "0x8000000", + "nonce" : "0x0000000000000042", + "mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp" : "0x00" +} diff --git a/packages/connection-chain/environment/sample/ec_env/init-account.sh b/packages/connection-chain/environment/sample/ec_env/init-account.sh new file mode 100755 index 0000000000..609653c85c --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/init-account.sh @@ -0,0 +1,34 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +echo "make-account-ec1-accounts[0]" +docker-compose -f docker-init.yml run make-account-ec1 +cp ./genesis/template/genesis-template.json ./genesis/genesis-ec1.json +echo " " +echo "make-account-ec1-accounts[1]" +docker-compose -f docker-init.yml run make-account-ec1 +echo " " +echo "make-account-ec1-accounts[2]" +docker-compose -f docker-init.yml run make-account-ec1 +echo " " +echo "make-account-ec1-accounts[3]" +docker-compose -f docker-init.yml run make-account-ec1 +echo " " +echo "make-account-ec1-accounts[4]" +docker-compose -f docker-init.yml run make-account-ec1 +echo " " +echo "make-account-ec2-accounts[0]" +docker-compose -f docker-init.yml run make-account-ec2 +cp ./genesis/template/genesis-template.json ./genesis/genesis-ec2.json +echo " " +echo "make-account-ec2-accounts[1]" +docker-compose -f docker-init.yml run make-account-ec2 +echo " " +echo "make-account-ec2-accounts[2]" +docker-compose -f docker-init.yml run make-account-ec2 +echo " " +echo "make-account-ec2-accounts[3]" +docker-compose -f docker-init.yml run make-account-ec2 +echo " " +echo "make-account-ec2-accounts[4]" +docker-compose -f docker-init.yml run make-account-ec2 +echo " " diff --git a/packages/connection-chain/environment/sample/ec_env/init-chain.sh b/packages/connection-chain/environment/sample/ec_env/init-chain.sh new file mode 100755 index 0000000000..a8c3a55fe6 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/init-chain.sh @@ -0,0 +1,4 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +docker-compose -f docker-init.yml run init-chain-ec1 +docker-compose -f docker-init.yml run init-chain-ec2 diff --git a/packages/connection-chain/environment/sample/ec_env/logview.sh b/packages/connection-chain/environment/sample/ec_env/logview.sh new file mode 100755 index 0000000000..78054f6bda --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/logview.sh @@ -0,0 +1,3 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +docker-compose logs -f diff --git a/packages/connection-chain/environment/sample/ec_env/up.sh b/packages/connection-chain/environment/sample/ec_env/up.sh new file mode 100755 index 0000000000..0ffd049bb2 --- /dev/null +++ b/packages/connection-chain/environment/sample/ec_env/up.sh @@ -0,0 +1,5 @@ +# Copyright 2019 Fujitsu Laboratories Ltd. +# SPDX-License-Identifier: Apache-2.0 +docker network create ec1net +docker network create ec2net +docker-compose up -d