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": "