├── .gitignore
├── README.md
├── images
├── 创建应用.png
├── 合约部署.png
├── 应用详情.png
└── 编译部署流程.png
├── pom.xml
├── src
└── main
│ ├── java
│ └── cn
│ │ └── hyperchain
│ │ └── application
│ │ ├── Application.java
│ │ ├── QsnarkTest.java
│ │ ├── ServletInitializer.java
│ │ ├── common
│ │ ├── constant
│ │ │ └── Code.java
│ │ ├── listener
│ │ │ └── StartupListener.java
│ │ ├── response
│ │ │ ├── BaseResult.java
│ │ │ └── BaseResultFactory.java
│ │ └── utils
│ │ │ ├── ContractUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── Logger.java
│ │ │ └── PropertiesUtils.java
│ │ ├── config
│ │ └── Swagger2.java
│ │ ├── contract
│ │ ├── business
│ │ │ └── Actor.java
│ │ └── invoke
│ │ │ └── ContractInvoke.java
│ │ └── controller
│ │ └── ContractController.java
│ └── resources
│ ├── contractSourceCode
│ └── supplychain.sol
│ ├── hyperchain.properties
│ ├── lib
│ └── qsnarksdk-1.0-SNAPSHOT-jar-with-dependencies.jar
│ └── 参考合约
│ ├── SafeMath.sol
│ ├── supplychainNoSafeMath.sol
│ ├── supplychainWithSafeMath.sol
│ ├── testOverflow.sol
│ └── useSafeMath.sol
└── web
└── WEB-INF
└── web.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | *.iml
3 | .idea
4 | bin
5 | .project
6 | .classpath
7 | .factorypath
8 | log
9 | .settings
10 | .vscode
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # minata
2 | 基于开发者平台的供应链溯源案例(网易云课程)
3 | ## 环境准备
4 | ### 注册与授权
5 | 1. 使用手机号进行注册后
6 | 2. 进入控制台
7 | 3. 创建应用
8 | 
9 | 4. 获取AppKey和AppSecret
10 | 
11 | 1. 获取token
12 | 使用我们的注册的账户和密码包括刚刚的appkey和appsecret来获取token
13 | 在这里我们使用的是开发者平台提供的sdk
14 | ```java
15 | GetTokenReturn getTokenReturn = api.getAccess_Token(
16 | "AppKey",
17 | "AppSecret",
18 | "phone",
19 | "password");
20 | logger.info(getTokenReturn.getCode(),
21 | getTokenReturn.getAccess_token(),
22 | getTokenReturn.getRefresh_token());
23 | ```
24 |
25 | ### 合约的编写
26 |
27 | #### 业务分析
28 | 当前的业务是溯源案例,即供应链溯源。所谓的供应链溯源实则是将商品从原料到买家手中的一些列的行为给记录到区块链上,利用区块链的不可篡改,去中心化这样的特点来保证整个一溯源流程的安全可信。
29 | #### 场景描述
30 | 供应链溯源合约有以下几类参与方:
31 |
32 | - 商品厂商:保存于mapping(address => User) producerMap
33 | - 各级经销商:保存于mapping(address => User) retailerMap
34 | - 消费者:保存于mapping(address => User) customerMap
35 | 各类参与方均通过newUser方法进行**上链**登记。通过传递不同的Actor值来指定不同参与方。
36 |
37 | 厂商首先通过newProduct方法将出厂商品登记到区块链,随后商品分销到下一级经销商,接下来的环节,每一次商品的分销均在接收商品的经销商处调用retailerInnerTransfer方法将商品进行上链登记,最终商品在零售商处由消费者购买,调用fromRetailerToCustomer方法进行最终登记,完成整个出厂-多级分销-零售的流程。商品一旦由厂商登记上链,便可通过getCommodity查询到商品当前的分销信息,只有处于该分销路径上的参与方才允许查询。此外,通过addWhiteList可以为指定参与方添加顶级查询权限,被添加到WhiteList的参与方,即使不参与到某商品的分销路径中,也可查询到该商品的分销信息。
38 | #### 合约代码
39 | ```js
40 | contract SupplyChain {
41 | enum Actor{ Others, Producer, Retailer, Customer}
42 | enum Commoditytype{ Wine, Jewelry, Shrimp, Others}
43 | enum Phase{ Supplier, Producer, Dealer, Retailer, Customer}
44 |
45 | struct User{
46 | bytes32 ID;
47 | bytes32 name;
48 | Actor actor;
49 | }
50 |
51 | struct Commodity{
52 | bytes32 commodityID;
53 | bytes32 commodityName;
54 | uint produceTime;
55 | bytes32 producerName;
56 | uint[] timestamps;
57 | bytes32[] retailerNames;
58 | uint sellTime;
59 | bytes32 customerName;
60 | bool isBinding;
61 | address owner;
62 | }
63 |
64 | mapping(address => User) producerMap;
65 | mapping(address => User) retailerMap;
66 | mapping(address => User) customerMap;
67 | mapping(bytes32 => Commodity) commodityMap;
68 | mapping(address => bool) whiteList;
69 | address owner;
70 |
71 | function SupplyChain(){
72 | owner = msg.sender;
73 | whiteList[owner] = true;
74 | }
75 |
76 | function addWhiteList(address addr){
77 | whiteList[addr] = true;
78 | }
79 |
80 | function newUser(bytes32 ID, bytes32 name,Actor actor) returns(bool, string){
81 | User user;
82 |
83 | if(actor == Actor.Producer){
84 | user = producerMap[msg.sender];
85 | }else if(actor == Actor.Retailer){
86 | user = retailerMap[msg.sender];
87 | }else if(actor == Actor.Customer){
88 | user = customerMap[msg.sender];
89 | }else{
90 | return (false,"the actor is not belong");
91 | }
92 |
93 | if(user.ID != 0x0){
94 | return (false, "this ID has been occupied!");
95 | }
96 | user.ID = ID;
97 | user.name = name;
98 | user.actor = actor;
99 | return (true, "Success");
100 | }
101 |
102 | // this interface just for producer
103 | function newProduct(bytes32 commodityID, bytes32 commodityName,
104 | uint timestamp) returns(bool,bytes32){
105 | Commodity commodity = commodityMap[commodityID];
106 | if(commodity.commodityID != 0x0) {
107 | return (false,"The commodityID already exist!");
108 | }
109 | User user = producerMap[msg.sender];
110 | if(user.ID == 0x0) {
111 | return (false,"The producer don't exist!");
112 | }
113 | commodity.commodityID = commodityID;
114 | commodity.commodityName = commodityName;
115 | commodity.produceTime = timestamp;
116 | commodity.producerName = user.name;
117 | return (true,"Success,produce a new product");
118 | }
119 |
120 |
121 | // this interface just for retailer
122 | function retailerInnerTransfer(bytes32 commodityID,uint timestamp) returns(bool, string){
123 | Commodity commodity = commodityMap[commodityID];
124 | if(commodity.commodityID == 0x0) {
125 | return (false,"The commodityID don't exist!");
126 | }
127 | User user = retailerMap[msg.sender];
128 | if(user.ID == 0x0) {
129 | return (false,"The retailer don't exist!");
130 | }
131 | commodity.timestamps.push(timestamp);
132 | commodity.retailerNames.push( user.name );
133 | return (true,"Success");
134 | }
135 |
136 | function fromRetailerToCustomer(bytes32 commodityID,uint timestamp) returns(bool, string){
137 | Commodity commodity = commodityMap[commodityID];
138 | if(commodity.commodityID == 0x0) {
139 | return (false,"The commodityID don't exist!");
140 | }
141 | commodity.sellTime = timestamp;
142 | return (true,"Success,Has been sold");
143 | }
144 |
145 | // just for Supervision organization
146 | function getCommodityRecordsByWhiteList(bytes32 commodityID) returns(bool,string,
147 | bytes32 producerName,uint produceTime, bytes32[] retailerNames,uint[] retailerTimes
148 | , bytes32 customerName,uint sellTime){
149 | if(!whiteList[msg.sender]){
150 | return (false,"you has no access",producerName, produceTime, retailerNames, retailerTimes, customerName,commodity.sellTime);
151 | }
152 | Commodity commodity = commodityMap[commodityID];
153 | if(commodity.commodityID == 0x0){
154 | return (false,"The commodityID is not exist",producerName, produceTime, retailerNames, retailerTimes,customerName,commodity.sellTime);
155 | }
156 | return (true,"Success",commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName,commodity.sellTime);
157 | }
158 |
159 |
160 | function getCommodity(bytes32 commodityID,Actor actor) returns(bool,string,
161 | bytes32 producerName,uint produceTime,bytes32[] retailerNames,uint[] retailerTimes
162 | , bytes32 customerName,uint sellTime){
163 | Commodity commodity = commodityMap[commodityID];
164 | if(commodity.commodityID == 0x0){
165 | return (false,"The commodityID is not exist",producerName,produceTime,
166 | retailerNames,retailerTimes,customerName,sellTime);
167 | }
168 | User user;
169 | if(actor == Actor.Producer){
170 | user = producerMap[msg.sender];
171 | }else if(actor == Actor.Retailer){
172 | user = retailerMap[msg.sender];
173 | }else if(actor == Actor.Customer){
174 | user = customerMap[msg.sender];
175 | }else{
176 | return (false,"the actor is not belong",producerName,produceTime,
177 | retailerNames,retailerTimes,customerName,sellTime);
178 | }
179 | if(commodity.isBinding){
180 | if(commodity.owner != msg.sender){
181 | return (false,"warning,this commodity has been bound",producerName,produceTime,
182 | retailerNames,retailerTimes,customerName,sellTime);
183 | }else{
184 | (false,"has already bind",commodity.producerName,commodity.retailerNames,commodity.customerName);
185 | }
186 | }
187 | if(commodity.sellTime > 0 ) {
188 | commodity.isBinding = true;
189 | commodity.owner = msg.sender;
190 | commodity.customerName = user.name;
191 | }
192 | return (true,"Success",commodity.producerName,commodity.produceTime,commodity.retailerNames,commodity.timestamps,commodity.customerName,commodity.sellTime);
193 | }
194 | }
195 | ```
196 | ### 在使用sdk进行编译和部署
197 | 
198 | #### 对应的hpyerchain.properties的配置
199 | 由于在sdk上进行编译和部署则需要持久化编译产生的abi和bin已经部署后获得的合约地址
200 | 于是我们在配置文件中配置相关的文件去保存这些内容
201 | **文件内容**
202 |
203 | ```shell
204 | access_token=WVDSRC22PTGS3H4GMFQHGA
205 | # refresh_token=8ZEPJTSRV2CRCGMZHG4MPA
206 | hyperchain_user_address=0xc18b43373fc5ac8effbb5871937056e4bb3aa0ab
207 | # 是否重新编译和部署合约
208 | redeploy=true
209 | # 合约地址文件
210 | contract_address_path=contractAddress
211 | # Abi文件
212 | bin_path=BIN
213 | # ABI文件
214 | abi_path=ABI
215 | ```
216 |
217 | - 编译
218 | 使用sdk在编译时需要携带token,token存在7200秒的有效时间,超出了时间需要再次获取
219 | ```java
220 | //合约内容,一般会从合约文件中读取
221 | String contractContent = "
222 | contract Accumulator {
223 | uint32 sum = 0;
224 | function increment() {
225 | sum = sum + 1;
226 | }
227 | function getSum() returns(uint32) {
228 | return sum;
229 | }
230 | function add(uint32 num1,uint32 num2) {
231 | sum = sum+num1+num2;
232 | }
233 | }
234 | ";
235 | ```
236 |
237 | ABI
238 | ```json
239 | [{
240 | "constant": false,
241 | "inputs": [{
242 | "name": "num1",
243 | "type": "uint32"
244 | }, {
245 | "name": "num2",
246 | "type": "uint32"
247 | }],
248 | "name": "add",
249 | "outputs": [],
250 | "payable": false,
251 | "type": "function"
252 | }, {
253 | "constant": false,
254 | "inputs": [],
255 | "name": "getSum",
256 | "outputs": [{
257 | "name": "",
258 | "type": "uint32"
259 | }],
260 | "payable": false,
261 | "type": "function"
262 | }, {
263 | "constant": false,
264 | "inputs": [],
265 | "name": "increment",
266 | "outputs": [],
267 | "payable": false,
268 | "type": "function"
269 | }]
270 | //传入有效的token 进行调用
271 | CompileReturn compileReturn = api.compileContract(ACCESS_TOKEN, contractConten);
272 | ```
273 | - 部署
274 | ```java
275 | //部署合约并输出合约地址 需要传入 hyperchain账户地址
276 | DeployConReturn deployConReturn = api.deployContract(ACCESS_TOKEN,
277 | BIN,
278 | ADDRESS,
279 | (DevCallback) address -> System.out.println(address));
280 | ```
281 |
282 | ### 在开发者平台网页上进行部署合约
283 | 
284 | #### 对应的hyperchain配置
285 | 由于已经部署好了合约则只需闯入abi和合约地址
286 | ```shell
287 | access_token=WVDSRC22PTGS3H4GMFQHGA
288 | # refresh_token=8ZEPJTSRV2CRCGMZHG4MPA
289 | hyperchain_user_address=0xc18b43373fc5ac8effbb5871937056e4bb3aa0ab
290 | # 是否重新编译和部署合约
291 | redeploy=false
292 | ####已有部署好的合约
293 | #合约地址
294 | contract_address=0x07c3c7c4cc7820a169964b1fa5a951b94da618d7
295 | # abi(如果通过代码部署则不填写)
296 | abi=[{"constant":false,"inputs":[{"name":"num1","type":"uint32"},{"name":"num2","type":"uint32"}],"name":"add","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getSum","outputs":[{"name":"","type":"uint32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[],"payable":false,"type":"function"}]
297 | ```
298 | ### 调用合约函数
299 | ```java
300 | FuncParamReal funcParamReal1 = new FuncParamReal("uint32", 456);
301 | FuncParamReal funcParamReal2 = new FuncParamReal("uint32", 0);
302 | api.invokeContract(
303 | ACCESS_TOKEN,
304 | false,
305 | ADDRESS,
306 | CONTRACT_ADDRESS,
307 | ABI,
308 | list -> {
309 | System.out.println(list);
310 | },
311 | "add",
312 | funcParamReal1,
313 | funcParamReal2
314 | );
315 | ```
316 |
--------------------------------------------------------------------------------
/images/创建应用.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qulian-netease/minata/b03a554d3e5b299f7d7d8140d380cec195d8f5f0/images/创建应用.png
--------------------------------------------------------------------------------
/images/合约部署.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qulian-netease/minata/b03a554d3e5b299f7d7d8140d380cec195d8f5f0/images/合约部署.png
--------------------------------------------------------------------------------
/images/应用详情.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qulian-netease/minata/b03a554d3e5b299f7d7d8140d380cec195d8f5f0/images/应用详情.png
--------------------------------------------------------------------------------
/images/编译部署流程.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qulian-netease/minata/b03a554d3e5b299f7d7d8140d380cec195d8f5f0/images/编译部署流程.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | cn.hyperchain.application
8 | minata
9 | 1.0-SNAPSHOT
10 |
11 |
12 | org.springframework.boot
13 | spring-boot-starter-parent
14 | 2.0.2.RELEASE
15 |
16 |
17 |
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-aop
22 |
23 |
24 | junit
25 | junit
26 | 4.12
27 | compile
28 |
29 |
30 |
31 |
32 |
33 | redis.clients
34 | jedis
35 | 2.9.0
36 |
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-data-redis
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-web
45 |
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-test
50 | test
51 |
52 |
53 |
54 |
55 | io.springfox
56 | springfox-swagger2
57 | 2.7.0
58 |
59 |
60 |
61 | io.springfox
62 | springfox-swagger-ui
63 | 2.7.0
64 |
65 |
66 |
67 | com.alibaba
68 | fastjson
69 | 1.2.39
70 |
71 |
72 |
73 | io.jsonwebtoken
74 | jjwt
75 | 0.9.0
76 |
77 |
78 | org.springframework
79 | spring-test
80 | 5.0.6.RELEASE
81 | compile
82 |
83 |
84 | org.springframework.boot
85 | spring-boot-test
86 |
87 |
88 |
89 | cn.hyperchain
90 | qsnarksdk
91 | 1.0
92 | system
93 | ${project.basedir}/src/main/resources/lib/qsnarksdk-1.0-SNAPSHOT-jar-with-dependencies.jar
94 |
95 |
96 |
97 |
98 |
99 |
100 | org.springframework.boot
101 | spring-boot-maven-plugin
102 |
103 | true
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/Application.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | /**
7 | * @author sunligang
8 | * @date 2018/07/05
9 | */
10 |
11 | @SpringBootApplication
12 | public class Application {
13 | public static void main(String[] args) {
14 | SpringApplication.run(Application.class, args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/QsnarkTest.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application;
2 |
3 | import cn.hyperchain.application.common.utils.Logger;
4 | import cn.qsnark.sdk.exception.TxException;
5 | import cn.qsnark.sdk.rpc.QsnarkAPI;
6 | import cn.qsnark.sdk.rpc.function.FuncParamReal;
7 | import cn.qsnark.sdk.rpc.function.FunctionDecode;
8 | import cn.qsnark.sdk.rpc.returns.*;
9 | import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
10 | import org.junit.Test;
11 |
12 | import java.io.IOException;
13 | import java.io.UnsupportedEncodingException;
14 |
15 |
16 | /**
17 | * @author sunligang
18 | * @date 2018/07/05
19 | */
20 | public class QsnarkTest {
21 | private static QsnarkAPI api = new QsnarkAPI();
22 |
23 | private static Logger logger = Logger.Builder.getLogger(QsnarkTest.class);
24 |
25 | /**
26 | * 从开发者平台获取token,当然也可以通过sdk api获取
27 | */
28 | private final static String ACCESS_TOKEN = "RHMW6K5MNZGYFFL13MKJVQ";
29 | /**
30 | * hyperchain 账户地址
31 | */
32 | private final static String ADDRESS = "0xa06781871b39a22e0b0576a0eda9ceffda0560e3";
33 | private final static String BIN = "0x60606040526000805463ffffffff19169055341561001957fe5b5b610101806100296000396000f300606060405263ffffffff60e060020a6000350416633ad14af381146034578063569c5f6d146052578063d09de08a146078575bfe5b3415603b57fe5b605063ffffffff600435811690602435166087565b005b3415605957fe5b605f60a9565b6040805163ffffffff9092168252519081900360200190f35b3415607f57fe5b605060b6565b005b6000805463ffffffff808216850184011663ffffffff199091161790555b5050565b60005463ffffffff165b90565b6000805463ffffffff8082166001011663ffffffff199091161790555b5600a165627a7a723058205196f5c898c244d3ada034d11893c7a5d67acac307f8e5db125810804cf7bb690029";
34 | private final static String ABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"num1\",\"type\":\"uint32\"},{\"name\":\"num2\",\"type\":\"uint32\"}],\"name\":\"add\",\"outputs\":[],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"getSum\",\"outputs\":[{\"name\":\"\",\"type\":\"uint32\"}],\"payable\":false,\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"payable\":false,\"type\":\"function\"}]";
35 | private final static String CONTRACT_ADDRESS = "0xc878e596ecf53e9d761ff0664ea126e035644f66";
36 |
37 | /**
38 | * 创建账户
39 | */
40 | @Test
41 | public void createAccount() throws Exception {
42 | CreteAccountReturn creteAccountReturn =
43 | api.createAccount(ACCESS_TOKEN);
44 | logger.info(creteAccountReturn.getCode(),
45 | creteAccountReturn.getAddress(),
46 | creteAccountReturn.getId(),
47 | creteAccountReturn.getStatus(),
48 | creteAccountReturn.getTime());
49 | }
50 |
51 |
52 | @Test
53 | public void compile() throws IOException {
54 | String s = "contract Accumulator{ uint32 sum = 0; function increment(){ sum = sum + 1; } function getSum() returns(uint32){ return sum; } function add(uint32 num1,uint32 num2) { sum = sum+num1+num2; } }";
55 | CompileReturn compileReturn = api.compileContract(ACCESS_TOKEN, s);
56 | }
57 |
58 | /**
59 | * 合约部署
60 | */
61 | @Test
62 | public void deploy() throws IOException, InterruptedException {
63 | GetTxReciptReturn getTxReciptReturn = api.deploysyncContract(
64 | ACCESS_TOKEN,
65 | BIN,
66 | ADDRESS
67 | );
68 | logger.info(getTxReciptReturn);
69 | }
70 |
71 |
72 | /**
73 | * 合约维护
74 | */
75 | @Test
76 | public void maintainContract() throws IOException {
77 | //升级合约
78 | MainTainReturn upgradeMainTainReturn = api.maintainContract(ACCESS_TOKEN, ADDRESS, 1, BIN, CONTRACT_ADDRESS);
79 | //冻结合约
80 | MainTainReturn freezeMainTainReturn = api.maintainContract(ACCESS_TOKEN, ADDRESS, 2, BIN, CONTRACT_ADDRESS);
81 | //解冻合约
82 | MainTainReturn unFreezeMainTainReturn = api.maintainContract(ACCESS_TOKEN, ADDRESS, 3, BIN, CONTRACT_ADDRESS);
83 | }
84 |
85 |
86 | /**
87 | * 调用
88 | */
89 | @Test
90 | public void invoke() throws TxException, InterruptedException, IOException {
91 | FuncParamReal funcParamReal1 = new FuncParamReal("uint32", 456);
92 | FuncParamReal funcParamReal2 = new FuncParamReal("uint32", 0);
93 | api.invokeContract(
94 | ACCESS_TOKEN,
95 | false,
96 | ADDRESS,
97 | CONTRACT_ADDRESS,
98 | ABI,
99 | list -> {
100 | System.out.println(list);
101 | },
102 | "add",
103 | funcParamReal1,
104 | funcParamReal2
105 | );
106 | }
107 |
108 | @Test
109 | public void invoke2() throws TxException, InterruptedException, IOException {
110 | GetTxReciptReturn getTxReciptReturn = api.invokesyncContract(
111 | ACCESS_TOKEN,
112 | false,
113 | ADDRESS,
114 | CONTRACT_ADDRESS,
115 | ABI,
116 | "getSum"
117 | );
118 | logger.info(getTxReciptReturn.getRet());
119 | System.out.println(FunctionDecode.resultDecode("getSum", ABI, "0x00000000000000000000000000000000000000000000000000000000000001c8"));
120 | }
121 |
122 |
123 | @Test
124 | public void hexToString() throws UnsupportedEncodingException {
125 | String strDecoded = new String(ByteUtils.fromHexString("1a7468697320494420686173206265656e206f6363757069656421"), "UTF-8");
126 | logger.info(strDecoded);
127 | }
128 |
129 |
130 |
131 |
132 |
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/ServletInitializer.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application;
2 |
3 | import org.springframework.boot.builder.SpringApplicationBuilder;
4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
5 |
6 | /**
7 | * Created by superlee on 2017/11/3.
8 | * 启动类,继承 SpringBootServletInitializer 并重写 configure 方法
9 | * 部署到外部tomcat容器中
10 | */
11 | public class ServletInitializer extends SpringBootServletInitializer {
12 |
13 | @Override
14 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
15 | return application.sources(Application.class);
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/constant/Code.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.constant;
2 |
3 |
4 | /**
5 | * @author sunligang
6 | */
7 |
8 | public enum Code {
9 | SUCCESS(0, "成功"),
10 | INVOKE_FAIL(1, "失败");
11 |
12 |
13 |
14 |
15 | private int code;
16 | private String msg;
17 |
18 |
19 | Code(int code, String msg) {
20 | this.code = code;
21 | this.msg = msg;
22 | }
23 |
24 | public String getMsg() {
25 | return msg;
26 | }
27 |
28 | public void setMsg(String msg) {
29 | this.msg = msg;
30 | }
31 |
32 | public int getCode() {
33 | return code;
34 | }
35 |
36 | public void setCode(int code) {
37 | this.code = code;
38 | }
39 |
40 | public static String getMsgByCodeInt(int codeInt) {
41 | for (Code e : Code.values()) {
42 | if (e.getCode() == codeInt) {
43 | return e.msg;
44 | }
45 | }
46 | throw new IllegalArgumentException("未定义的code码:" + codeInt);
47 | }
48 |
49 | public static Code getCodeByCodeInt(int codeInt) {
50 | for (Code code : Code.values()) {
51 | if (code.getCode() == codeInt) {
52 | return code;
53 | }
54 | }
55 | throw new IllegalArgumentException("未定义的code码:" + codeInt);
56 | }
57 |
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/listener/StartupListener.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.listener;
2 |
3 | import cn.hyperchain.application.common.utils.ContractUtils;
4 | import cn.hyperchain.application.common.utils.Logger;
5 | import cn.hyperchain.application.common.utils.PropertiesUtils;
6 | import org.springframework.context.ApplicationListener;
7 | import org.springframework.context.event.ContextRefreshedEvent;
8 | import org.springframework.stereotype.Component;
9 |
10 | import java.util.Properties;
11 |
12 |
13 | /**
14 | * 在启动时根据配置是否需要重新部署合约
15 | * @author sunligang
16 | */
17 | @Component
18 | public class StartupListener implements ApplicationListener {
19 |
20 | private static Logger logger = Logger.Builder.getLogger(StartupListener.class);
21 | @Override
22 | public void onApplicationEvent(ContextRefreshedEvent event){
23 | //读取参数
24 | Properties properties = PropertiesUtils.getInstance("hyperchain.properties");
25 | boolean isReDploy= "true".equals(properties.getProperty("redeploy", "true"));
26 | if(isReDploy){
27 | logger.info("重新部署合约");
28 | ContractUtils.compileAndDeploy(properties.getProperty("access_token"));
29 | }else {
30 | logger.info("不重新部署合约");
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/response/BaseResult.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.response;
2 |
3 |
4 | import cn.hyperchain.application.common.constant.Code;
5 | import cn.hyperchain.application.common.utils.DateUtils;
6 |
7 | /**
8 | * Created by superlee on 2017/11/6.
9 | */
10 | @SuppressWarnings("unchecked")
11 | public final class BaseResult {
12 | private static final String EMPTY_DATA_INFO = "no data!";
13 | private int code;
14 | private String message;
15 | private T data = (T) EMPTY_DATA_INFO;
16 | private String date = DateUtils.now();
17 |
18 | public BaseResult() {
19 |
20 | this.data = (T) EMPTY_DATA_INFO;
21 | }
22 |
23 | public BaseResult(String msg) {
24 | this();
25 | this.code = 200;
26 | this.message = msg;
27 | }
28 |
29 | public BaseResult(int code, String message) {
30 | this.code = code;
31 | this.message = message + ":" + date;
32 | }
33 |
34 | public BaseResult(int code, String message, T data) {
35 | this.code = code;
36 | this.message = message + ":" + date;
37 | this.data = data;
38 | }
39 |
40 | // 与Code码交互
41 | public BaseResult(Code code) {
42 | this();
43 | this.code = code.getCode();
44 | this.message = code.getMsg() + ":" + date;
45 | }
46 |
47 | /**
48 | * 返回结果代码code和消息msg,不需要返回值
49 | *
50 | * @param code 结果类型
51 | */
52 | public final void returnWithoutValue(Code code) {
53 | this.code = code.getCode();
54 | this.message = code.getMsg();
55 | }
56 |
57 | /**
58 | * 返回结果代码code和消息msg,并返回值
59 | *
60 | * @param code 结果类型
61 | * @param object 返回值
62 | */
63 | public final void returnWithValue(Code code, T object) {
64 | returnWithoutValue(code);
65 | this.data = object;
66 | }
67 |
68 | public int getCode() {
69 | return code;
70 | }
71 |
72 | public void setCode(int code) {
73 | this.code = code;
74 | }
75 |
76 | public String getMessage() {
77 | return message;
78 | }
79 |
80 | public void setMessage(String message) {
81 | this.message = message;
82 | }
83 |
84 | public T getData() {
85 | return data;
86 | }
87 |
88 | public void setData(T data) {
89 | this.data = data;
90 | }
91 |
92 | @Override
93 | public String toString() {
94 | return "BaseResult{" +
95 | "code=" + code +
96 | ", message='" + message + '\'' +
97 | ", data=" + data +
98 | '}';
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/response/BaseResultFactory.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.response;
2 |
3 |
4 | import cn.hyperchain.application.common.constant.Code;
5 |
6 | /**
7 | * Created by superlee on 2017/11/7.
8 | * baseResult 工程方法
9 | */
10 | @SuppressWarnings("unchecked")
11 | public final class BaseResultFactory {
12 |
13 |
14 | public static BaseResult produceEmptyResult(Code code) {
15 | return new BaseResult(code);
16 | }
17 |
18 | public static BaseResult produceEmptyResult(int codeInt, String msg) {
19 | return new BaseResult(codeInt, msg);
20 | }
21 |
22 | public static BaseResult produceResult(int codeInt, String msg, Object data) {
23 | return new BaseResult(codeInt, msg, data);
24 | }
25 |
26 | public static BaseResult produceResult(Code code, Object data) {
27 | return new BaseResult(code.getCode(), code.getMsg(), data);
28 | }
29 |
30 | /**
31 | * 构建成功返回的数据(应该比较常用)
32 | * @param data
33 | * @return
34 | */
35 | public static BaseResult creatSuccessReult(Object data){
36 | BaseResult result = new BaseResult(Code.SUCCESS);
37 | result.setData(data);
38 | return result;
39 | }
40 |
41 | public static BaseResult creatSuccessReult(){
42 | return new BaseResult(Code.SUCCESS);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/utils/ContractUtils.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.utils;
2 |
3 | import cn.qsnark.sdk.rpc.QsnarkAPI;
4 | import cn.qsnark.sdk.rpc.returns.CompileReturn;
5 |
6 | import java.io.*;
7 | import java.nio.ByteBuffer;
8 | import java.nio.channels.FileChannel;
9 | import java.util.Properties;
10 |
11 | /**
12 | * solidity合约工具
13 | *
14 | * @author sunligang
15 | * @date 2018/06/14
16 | */
17 | public class ContractUtils {
18 |
19 | private static Logger logger = Logger.Builder.getLogger(ContractUtils.class);
20 |
21 | private static QsnarkAPI api = new QsnarkAPI();
22 | private static Properties properties = PropertiesUtils.getInstance("hyperchain.properties");
23 | private static String accessToken;
24 | private static String address;
25 | private static String abi;
26 | private static String bin;
27 | private static String contractAddress;
28 |
29 | static {
30 | //从配置文件中读取token
31 | accessToken = properties.getProperty("access_token");
32 | // abi
33 | abi = properties.getProperty("abi");
34 | //合约地址
35 | contractAddress = properties.getProperty("contract_address");
36 | // 账户地址
37 | address = properties.getProperty("address");
38 | }
39 |
40 |
41 | /**
42 | * 单个sol合约文件的编译和部署,并且合约文件需要位于/resource/contractSourceCode下
43 | *
44 | * @return 编译结果json
45 | */
46 | public static boolean compileAndDeploy(String token) {
47 | //合约文件夹
48 | String contractFolder = ContractUtils.class.getResource("/contractSourceCode").getPath();
49 | File folder = new File(contractFolder);
50 | String contractFile = "";
51 | File[] files = null;
52 | if (folder.isDirectory()) {
53 | files = folder.listFiles();
54 | assert files != null;
55 | if (files.length > 0) {
56 | contractFile = files[0].getPath();
57 | }
58 | }
59 |
60 | String contractContent = readFromFile(contractFile);
61 | //编译合约
62 | CompileReturn compile;
63 | try {
64 | compile = api.compileContract(token, contractContent);
65 | } catch (IOException e) {
66 | e.printStackTrace();
67 | return false;
68 | }
69 |
70 | bin = compile.getCts_bin();
71 |
72 | abi = compile.getCts_bin();
73 |
74 |
75 | try {
76 | contractAddress = api.deploysyncContract(accessToken,
77 | bin,
78 | address
79 | ).getContract_address();
80 | } catch (InterruptedException e) {
81 | e.printStackTrace();
82 | } catch (IOException e) {
83 | e.printStackTrace();
84 | return false;
85 | }
86 | //将bin和abi和合约地址保存到配置的文件中
87 | writeIntoFile(bin, properties.getProperty("bin_path"));
88 | writeIntoFile(abi, properties.getProperty("abi_path"));
89 | writeIntoFile(contractAddress, properties.getProperty("contract_address_path"));
90 | return true;
91 | }
92 |
93 | /**
94 | * 根据路径读取文件
95 | *
96 | * @param path 文件路径
97 | * @return
98 | */
99 | private static String readFromFile(String path) {
100 | File file = new File(path);
101 | FileReader fileReader = null;
102 | try {
103 | fileReader = new FileReader(file);
104 | } catch (FileNotFoundException e) {
105 | e.printStackTrace();
106 | }
107 | StringBuilder stringBuilder = new StringBuilder();
108 | char[] buffer = new char[128];
109 | int len;
110 | try {
111 | while ((len = fileReader.read(buffer)) != -1) {
112 | stringBuilder.append(buffer, 0, len);
113 | }
114 | } catch (IOException e) {
115 | e.printStackTrace();
116 | }
117 | return stringBuilder.toString();
118 | }
119 |
120 |
121 | /**
122 | * 将string写入到目的文件
123 | *
124 | * @param strSource
125 | * @param destPath
126 | */
127 | private synchronized static void writeIntoFile(String strSource, String destPath) {
128 | // 如果不存在则创建
129 | File file = new File(destPath);
130 | if (!file.exists()) {
131 | try {
132 | file.createNewFile();
133 | } catch (IOException e) {
134 | e.printStackTrace();
135 | }
136 | }
137 | FileChannel fileChannel = null;
138 | try {
139 | fileChannel = new FileOutputStream(destPath).getChannel();
140 | } catch (FileNotFoundException e) {
141 | e.printStackTrace();
142 | }
143 | ByteBuffer byteBuffer = ByteBuffer.allocate(128);
144 | byte[] info = strSource.getBytes();
145 | int index = 0;
146 | while (index < info.length) {
147 | byteBuffer.put(info, index, Math.min(info.length - index, 128));
148 | index += 128;
149 | byteBuffer.flip();
150 | try {
151 | fileChannel.write(byteBuffer);
152 | } catch (IOException e) {
153 | e.printStackTrace();
154 | }
155 | byteBuffer.clear();
156 | }
157 | }
158 |
159 | public static String getAbi() {
160 | return abi;
161 | }
162 |
163 |
164 | public static String getContractAddress() {
165 | return contractAddress;
166 | }
167 |
168 | /**
169 | * 获取账户地址
170 | *
171 | * @return
172 | */
173 | public static String getAddress() {
174 | return address;
175 | }
176 |
177 | }
178 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/utils/DateUtils.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Date;
5 |
6 | /**
7 | * 日期工具类
8 | *
9 | * @author faxiang
10 | * @version v1.0
11 | * @since 2017/12/20
12 | */
13 | public class DateUtils {
14 |
15 | private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
16 |
17 | /**
18 | * 获取当前时间(格式 yyyy-MM-dd HH:mm:ss)
19 | *
20 | * @return 时间字符串
21 | */
22 | public static String now() {
23 | return convertDate2String(DATE_PATTERN);
24 | }
25 |
26 | /**
27 | * 获取昨天的时间(格式 yyyy-MM-dd HH:mm:ss)
28 | *
29 | * @return 时间字符串
30 |
31 |
32 | /**
33 | * 转换当前日期为指定的格式
34 | *
35 | * @param pattern 格式
36 | * @return 日期字符串
37 | */
38 | public static String convertDate2String(String pattern) {
39 | return convertDate2String(new Date(), pattern);
40 | }
41 |
42 |
43 | /**
44 | * 转换日期为指定的格式
45 | *
46 | * @param date 待转换的日期对象
47 | * @param pattern 格式
48 | * @return 日期字符串
49 | */
50 | public static String convertDate2String(Date date, String pattern) {
51 | SimpleDateFormat df;
52 | String returnValue = "";
53 |
54 | if (date != null) {
55 | df = new SimpleDateFormat(pattern);
56 | returnValue = df.format(date);
57 | }
58 |
59 | return returnValue;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/utils/Logger.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.utils;
2 |
3 | import java.text.SimpleDateFormat;
4 | import java.util.Arrays;
5 | import java.util.Calendar;
6 |
7 | /**
8 | * @author sunligang
9 | */
10 | public class Logger {
11 |
12 | public static class Builder {
13 | public static Logger getLogger(Class> clazz) {
14 | return new Logger(clazz);
15 | }
16 | }
17 |
18 | private static final int COMPLETE_DISPLAY_PACKAGE_NAME = 2;
19 | private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
20 | private StringBuilder simpleClazzName = new StringBuilder();
21 |
22 | private Calendar calendar = Calendar.getInstance();
23 |
24 | Logger(Class> clazz) {
25 | String[] packageNameArray = clazz.getName().split("\\.");
26 | for (int i = 0; i < packageNameArray.length; i++) {
27 | if (i < packageNameArray.length - COMPLETE_DISPLAY_PACKAGE_NAME) {
28 | simpleClazzName.append(packageNameArray[i], 0, 1);
29 | } else {
30 | simpleClazzName.append(packageNameArray[i]);
31 | }
32 | if (i < packageNameArray.length - 1) {
33 | simpleClazzName.append(".");
34 | }
35 | }
36 | }
37 |
38 | public void info(Object info) {
39 | System.out.println(simpleDateFormat.format(calendar.getTime()) + "[" + simpleClazzName + "] info :" + info);
40 | }
41 |
42 | public void info(Object... info) {
43 | System.out.print(simpleDateFormat.format(calendar.getTime()) + "[" + simpleClazzName + "] info :");
44 | Arrays.stream(info).forEach(e -> {
45 | System.out.print(" " + e);
46 | });
47 | System.out.println();
48 | }
49 |
50 |
51 | public void blackLine() {
52 | System.out.println();
53 | }
54 |
55 | public void err(Object info) {
56 | System.err.println(simpleDateFormat.format(calendar.getTime()) + "[" + simpleClazzName + "] err :" + info);
57 | }
58 |
59 | public void warn(Object info) {
60 | System.err.println(simpleDateFormat.format(calendar.getTime()) + "[" + simpleClazzName + "] warm :" + info);
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/common/utils/PropertiesUtils.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.common.utils;
2 |
3 |
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.util.Properties;
7 |
8 | /**
9 | * @author sunligang
10 | * @date 2018/07/05
11 | */
12 | public class PropertiesUtils {
13 | public static Properties getInstance(String path) {
14 | Properties properties = new Properties();
15 | // 使用ClassLoader加载properties配置文件生成对应的输入流
16 | InputStream in = PropertiesUtils.class.getClassLoader().getResourceAsStream(path);
17 | // 使用properties对象加载输入流
18 | try {
19 | properties.load(in);
20 | } catch (IOException e) {
21 | e.printStackTrace();
22 | }
23 | return properties;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/config/Swagger2.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import springfox.documentation.builders.ApiInfoBuilder;
6 | import springfox.documentation.builders.ParameterBuilder;
7 | import springfox.documentation.builders.PathSelectors;
8 | import springfox.documentation.builders.RequestHandlerSelectors;
9 | import springfox.documentation.schema.ModelRef;
10 | import springfox.documentation.service.ApiInfo;
11 | import springfox.documentation.service.Parameter;
12 | import springfox.documentation.spi.DocumentationType;
13 | import springfox.documentation.spring.web.plugins.Docket;
14 | import springfox.documentation.swagger2.annotations.EnableSwagger2;
15 |
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by superlee on 2017/11/3.
21 | * swagger配置
22 | */
23 | @Configuration
24 | @EnableSwagger2
25 | public class Swagger2 {
26 | @Bean
27 | public Docket createRestApi() {
28 | return new Docket(DocumentationType.SWAGGER_2)
29 | .select()
30 | .apis(RequestHandlerSelectors.basePackage("cn.hyperchain.application.controller"))
31 | .paths(PathSelectors.any())
32 | .build()
33 | .apiInfo(apiInfo());
34 | }
35 |
36 | private ApiInfo apiInfo() {
37 | return new ApiInfoBuilder()
38 | .title("minata")
39 | .description("防伪溯源案例")
40 | .termsOfServiceUrl("暂无")
41 | .version("1.0")
42 | .build();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/contract/business/Actor.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.contract.business;
2 |
3 | /**
4 | * 参与方类型
5 | *
6 | * @author sunligang
7 | * @date 2018/07/09
8 | */
9 | public enum Actor {
10 | //生产商
11 | Producer,
12 | //各级经销商
13 | Retailer,
14 | //消费者
15 | Customer,
16 | //其他
17 | Others
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/contract/invoke/ContractInvoke.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.contract.invoke;
2 |
3 | import cn.hyperchain.application.common.constant.Code;
4 | import cn.hyperchain.application.common.response.BaseResult;
5 | import cn.hyperchain.application.common.response.BaseResultFactory;
6 | import cn.hyperchain.application.common.utils.ContractUtils;
7 | import cn.hyperchain.application.contract.business.Actor;
8 | import cn.qsnark.sdk.exception.TxException;
9 | import cn.qsnark.sdk.rpc.QsnarkAPI;
10 | import cn.qsnark.sdk.rpc.function.FuncParamReal;
11 | import cn.qsnark.sdk.rpc.function.FunctionDecode;
12 | import cn.qsnark.sdk.rpc.returns.GetTxReciptReturn;
13 | import org.springframework.stereotype.Component;
14 |
15 | import java.io.IOException;
16 | import java.io.UnsupportedEncodingException;
17 | import java.util.List;
18 |
19 |
20 | /**
21 | * @author sunligang
22 | * @date 2018/07/05
23 | */
24 | @Component
25 | public class ContractInvoke {
26 |
27 | private static QsnarkAPI api = new QsnarkAPI();
28 |
29 | private final static String FLAG_OK = "ok";
30 |
31 | /**
32 | * 对应于合约的newUser方法
33 | *
34 | * @return
35 | */
36 | public BaseResult newUser(String token, String invokeAddress, String ID, String name, Actor actor) {
37 | //构造参数
38 | FuncParamReal[] arrFunParamReal = new FuncParamReal[3];
39 | arrFunParamReal[0] = new FuncParamReal("bytes32", ID);
40 | arrFunParamReal[1] = new FuncParamReal("bytes32", name);
41 | //enum对应于string
42 | switch (actor) {
43 | case Producer:
44 | arrFunParamReal[2] = new FuncParamReal("uint8", 1);
45 | break;
46 | case Retailer:
47 | arrFunParamReal[2] = new FuncParamReal("uint8", 2);
48 | break;
49 | case Customer:
50 | arrFunParamReal[2] = new FuncParamReal("uint8", 3);
51 | break;
52 | case Others:
53 | arrFunParamReal[2] = new FuncParamReal("uint8", 0);
54 | break;
55 | default:
56 | break;
57 | }
58 | GetTxReciptReturn getTxReciptReturn = null;
59 | try {
60 | getTxReciptReturn = api.invokesyncContract(
61 | token,
62 | false,
63 | invokeAddress,
64 | ContractUtils.getContractAddress(),
65 | ContractUtils.getAbi(),
66 | "newUser",
67 | arrFunParamReal
68 | );
69 | } catch (IOException | TxException | InterruptedException e) {
70 | e.printStackTrace();
71 | }
72 |
73 | BaseResult baseResult = null;
74 |
75 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
76 | try {
77 | baseResult = BaseResultFactory.produceResult(
78 | Code.SUCCESS,
79 | FunctionDecode.resultDecode("newUser", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
80 | } catch (UnsupportedEncodingException e) {
81 | e.printStackTrace();
82 | }
83 | } else {
84 | try {
85 | baseResult = BaseResultFactory.produceResult(
86 | Code.INVOKE_FAIL,
87 | FunctionDecode.resultDecode("newUser", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
88 | } catch (UnsupportedEncodingException e) {
89 | e.printStackTrace();
90 | }
91 | }
92 | return baseResult;
93 | }
94 |
95 |
96 | /**
97 | * 添加白名单
98 | *
99 | * @return
100 | */
101 | public BaseResult addWhiteList(String token, String invokeAddress, String address) {
102 | //构造参数
103 | FuncParamReal[] arrFunParamReal = new FuncParamReal[1];
104 | arrFunParamReal[0] = new FuncParamReal("address", address);
105 | GetTxReciptReturn getTxReciptReturn = null;
106 | try {
107 | getTxReciptReturn = api.invokesyncContract(
108 | token,
109 | false,
110 | invokeAddress,
111 | ContractUtils.getContractAddress(),
112 | ContractUtils.getAbi(),
113 | "addWhiteList",
114 | arrFunParamReal
115 | );
116 | } catch (IOException | TxException | InterruptedException e) {
117 | e.printStackTrace();
118 | }
119 |
120 | BaseResult baseResult = null;
121 |
122 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
123 | try {
124 | baseResult = BaseResultFactory.produceResult(
125 | Code.SUCCESS,
126 | FunctionDecode.resultDecode("addWhiteList", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
127 | } catch (UnsupportedEncodingException e) {
128 | e.printStackTrace();
129 | }
130 | } else {
131 | try {
132 | baseResult = BaseResultFactory.produceResult(
133 | Code.INVOKE_FAIL,
134 | FunctionDecode.resultDecode("addWhiteList", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
135 | } catch (UnsupportedEncodingException e) {
136 | e.printStackTrace();
137 | }
138 | }
139 | return baseResult;
140 | }
141 |
142 |
143 | /**
144 | * 对应于合约的newProduct方法
145 | *
146 | * @return
147 | */
148 | public BaseResult newProduct(String token, String invokeAddress, String commodityID, String commodityName, int price ) {
149 | //构造参数
150 | FuncParamReal[] arrFunParamReal = new FuncParamReal[4];
151 | arrFunParamReal[0] = new FuncParamReal("bytes32", commodityID);
152 | arrFunParamReal[1] = new FuncParamReal("bytes32", commodityName);
153 | arrFunParamReal[2] = new FuncParamReal("uint256", System.currentTimeMillis());
154 | arrFunParamReal[3] = new FuncParamReal("uint256", price);
155 | GetTxReciptReturn getTxReciptReturn = null;
156 | try {
157 | getTxReciptReturn = api.invokesyncContract(
158 | token,
159 | false,
160 | invokeAddress,
161 | ContractUtils.getContractAddress(),
162 | ContractUtils.getAbi(),
163 | "newProduct",
164 | arrFunParamReal
165 | );
166 | } catch (IOException | TxException | InterruptedException e) {
167 | e.printStackTrace();
168 | }
169 |
170 | BaseResult baseResult = null;
171 |
172 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
173 | try {
174 | baseResult = BaseResultFactory.produceResult(
175 | Code.SUCCESS,
176 | FunctionDecode.resultDecode("newProduct", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
177 | } catch (UnsupportedEncodingException e) {
178 | e.printStackTrace();
179 | }
180 | } else {
181 | try {
182 | baseResult = BaseResultFactory.produceResult(
183 | Code.INVOKE_FAIL,
184 | FunctionDecode.resultDecode("newProduct", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
185 | } catch (UnsupportedEncodingException e) {
186 | e.printStackTrace();
187 | }
188 | }
189 | return baseResult;
190 | }
191 |
192 |
193 | /**
194 | * 对应于合约的retailerInnerTransfer方法
195 | *
196 | * @return
197 | */
198 | public BaseResult retailerInnerTransfer(String token, String invokeAddress, String commodityID) {
199 | //构造参数
200 | FuncParamReal[] arrFunParamReal = new FuncParamReal[2];
201 | arrFunParamReal[0] = new FuncParamReal("bytes32", commodityID);
202 | arrFunParamReal[1] = new FuncParamReal("uint256", System.currentTimeMillis());
203 | GetTxReciptReturn getTxReciptReturn = null;
204 | try {
205 | getTxReciptReturn = api.invokesyncContract(
206 | token,
207 | false,
208 | invokeAddress,
209 | ContractUtils.getContractAddress(),
210 | ContractUtils.getAbi(),
211 | "retailerInnerTransfer",
212 | arrFunParamReal
213 | );
214 | } catch (IOException | TxException | InterruptedException e) {
215 | e.printStackTrace();
216 | }
217 |
218 | BaseResult baseResult = null;
219 |
220 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
221 | try {
222 | baseResult = BaseResultFactory.produceResult(
223 | Code.SUCCESS,
224 | FunctionDecode.resultDecode("retailerInnerTransfer", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
225 | } catch (UnsupportedEncodingException e) {
226 | e.printStackTrace();
227 | }
228 | } else {
229 | try {
230 | baseResult = BaseResultFactory.produceResult(
231 | Code.INVOKE_FAIL,
232 | FunctionDecode.resultDecode("retailerInnerTransfer", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
233 | } catch (UnsupportedEncodingException e) {
234 | e.printStackTrace();
235 | }
236 | }
237 | return baseResult;
238 | }
239 |
240 |
241 | /**
242 | * 对应于合约的fromRetailerToCustomer方法
243 | *
244 | * @return
245 | */
246 | public BaseResult fromRetailerToCustomer(String token, String invokeAddress, String commodityID) {
247 | //构造参数
248 | FuncParamReal[] arrFunParamReal = new FuncParamReal[2];
249 | arrFunParamReal[0] = new FuncParamReal("bytes32", commodityID);
250 | arrFunParamReal[1] = new FuncParamReal("uint256", System.currentTimeMillis());
251 | GetTxReciptReturn getTxReciptReturn = null;
252 | try {
253 | getTxReciptReturn = api.invokesyncContract(
254 | token,
255 | false,
256 | invokeAddress,
257 | ContractUtils.getContractAddress(),
258 | ContractUtils.getAbi(),
259 | "fromRetailerToCustomer",
260 | arrFunParamReal
261 | );
262 | } catch (IOException | TxException | InterruptedException e) {
263 | e.printStackTrace();
264 | }
265 |
266 | BaseResult baseResult = null;
267 |
268 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
269 | try {
270 | baseResult = BaseResultFactory.produceResult(
271 | Code.SUCCESS,
272 | FunctionDecode.resultDecode("fromRetailerToCustomer", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
273 | } catch (UnsupportedEncodingException e) {
274 | e.printStackTrace();
275 | }
276 | } else {
277 | try {
278 | baseResult = BaseResultFactory.produceResult(
279 | Code.INVOKE_FAIL,
280 | FunctionDecode.resultDecode("fromRetailerToCustomer", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
281 | } catch (UnsupportedEncodingException e) {
282 | e.printStackTrace();
283 | }
284 | }
285 | return baseResult;
286 | }
287 |
288 | /**
289 | * 对应于合约的getCommodityRecordsByWhiteList方法
290 | *
291 | * @return
292 | */
293 | public BaseResult getCommodityRecordsByWhiteList(String token, String invokeAddress, String commodityID) {
294 | //构造参数
295 | FuncParamReal[] arrFunParamReal = new FuncParamReal[1];
296 | arrFunParamReal[0] = new FuncParamReal("bytes32", commodityID);
297 | GetTxReciptReturn getTxReciptReturn = null;
298 | try {
299 | getTxReciptReturn = api.invokesyncContract(
300 | token,
301 | false,
302 | invokeAddress,
303 | ContractUtils.getContractAddress(),
304 | ContractUtils.getAbi(),
305 | "getCommodityRecordsByWhiteList",
306 | arrFunParamReal
307 | );
308 | } catch (IOException | TxException | InterruptedException e) {
309 | e.printStackTrace();
310 | }
311 |
312 | BaseResult baseResult = null;
313 |
314 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
315 | try {
316 | baseResult = BaseResultFactory.produceResult(
317 | Code.SUCCESS,
318 | FunctionDecode.resultDecode("getCommodityRecordsByWhiteList", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
319 | } catch (UnsupportedEncodingException e) {
320 | e.printStackTrace();
321 | }
322 | } else {
323 | try {
324 | baseResult = BaseResultFactory.produceResult(
325 | Code.INVOKE_FAIL,
326 | FunctionDecode.resultDecode("getCommodityRecordsByWhiteList", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
327 | } catch (UnsupportedEncodingException e) {
328 | e.printStackTrace();
329 | }
330 | }
331 | return baseResult;
332 | }
333 |
334 | /**
335 | * 对应于合约的getCommodity方法
336 | *
337 | * @return
338 | */
339 | public BaseResult getCommodity(String token, String invokeAddress, String commodityID, Actor actor) {
340 | //构造参数
341 | FuncParamReal[] arrFunParamReal = new FuncParamReal[2];
342 | arrFunParamReal[0] = new FuncParamReal("bytes32", commodityID);
343 | //enum对应于string
344 | switch (actor) {
345 | case Producer:
346 | arrFunParamReal[1] = new FuncParamReal("uint8", 1);
347 | break;
348 | case Retailer:
349 | arrFunParamReal[1] = new FuncParamReal("uint8", 2);
350 | break;
351 | case Customer:
352 | arrFunParamReal[1] = new FuncParamReal("uint8", 3);
353 | break;
354 | case Others:
355 | arrFunParamReal[1] = new FuncParamReal("uint8", 0);
356 | break;
357 | default:
358 | break;
359 | }
360 | GetTxReciptReturn getTxReciptReturn = null;
361 | try {
362 | getTxReciptReturn = api.invokesyncContract(
363 | token,
364 | false,
365 | invokeAddress,
366 | ContractUtils.getContractAddress(),
367 | ContractUtils.getAbi(),
368 | "getCommodity",
369 | arrFunParamReal
370 | );
371 | } catch (IOException | TxException | InterruptedException e) {
372 | e.printStackTrace();
373 | }
374 |
375 | BaseResult baseResult = null;
376 |
377 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
378 | try {
379 | baseResult = BaseResultFactory.produceResult(
380 | Code.SUCCESS,
381 | FunctionDecode.resultDecode("getCommodity", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
382 | } catch (UnsupportedEncodingException e) {
383 | e.printStackTrace();
384 | }
385 | } else {
386 | try {
387 | baseResult = BaseResultFactory.produceResult(
388 | Code.INVOKE_FAIL,
389 | FunctionDecode.resultDecode("getCommodity", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
390 | } catch (UnsupportedEncodingException e) {
391 | e.printStackTrace();
392 | }
393 | }
394 | return baseResult;
395 | }
396 |
397 | /**
398 | * 监管部门(白名单)查询一批商品的价格
399 | * @param commodityIDs 商品id们
400 | * @return
401 | */
402 | public BaseResult getTotalPrice(String token, String invokeAddress, List commodityIDs){
403 | //构造参数
404 | FuncParamReal[] arrFunParamReal = new FuncParamReal[1];
405 | arrFunParamReal[0] = new FuncParamReal("bytes32[]", commodityIDs);
406 | GetTxReciptReturn getTxReciptReturn = null;
407 | try {
408 | getTxReciptReturn = api.invokesyncContract(
409 | token,
410 | false,
411 | invokeAddress,
412 | ContractUtils.getContractAddress(),
413 | ContractUtils.getAbi(),
414 | "getTotalPrice",
415 | arrFunParamReal
416 | );
417 | } catch (IOException | TxException | InterruptedException e) {
418 | e.printStackTrace();
419 | }
420 |
421 | BaseResult baseResult = null;
422 |
423 | if (FLAG_OK.equals(getTxReciptReturn.getStatus())) {
424 | try {
425 | baseResult = BaseResultFactory.produceResult(
426 | Code.SUCCESS,
427 | FunctionDecode.resultDecode("getTotalPrice", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
428 | } catch (UnsupportedEncodingException e) {
429 | e.printStackTrace();
430 | }
431 | } else {
432 | try {
433 | baseResult = BaseResultFactory.produceResult(
434 | Code.INVOKE_FAIL,
435 | FunctionDecode.resultDecode("getTotalPrice", ContractUtils.getAbi(), getTxReciptReturn.getRet()));
436 | } catch (UnsupportedEncodingException e) {
437 | e.printStackTrace();
438 | }
439 | }
440 | return baseResult;
441 | }
442 | }
443 |
--------------------------------------------------------------------------------
/src/main/java/cn/hyperchain/application/controller/ContractController.java:
--------------------------------------------------------------------------------
1 | package cn.hyperchain.application.controller;
2 |
3 | import cn.hyperchain.application.common.response.BaseResult;
4 | import cn.hyperchain.application.contract.business.Actor;
5 | import cn.hyperchain.application.contract.invoke.ContractInvoke;
6 | import io.swagger.annotations.ApiOperation;
7 | import io.swagger.annotations.ApiParam;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.web.bind.annotation.*;
10 |
11 | import java.util.List;
12 |
13 |
14 | /**
15 | * 合约操作
16 | *
17 | * @author sunligang
18 | * @date 2018/07/09
19 | */
20 | @RestController
21 | public class ContractController {
22 |
23 | @Autowired
24 | private ContractInvoke contractInvoke;
25 |
26 |
27 | /**
28 | * 添加白名单
29 | *
30 | * @param address 账户地址
31 | */
32 | @ApiOperation(value = "消费者发起的转移", notes = "防伪溯源案例")
33 | @RequestMapping(value = "/v1/whiteList", method = RequestMethod.POST)
34 | public BaseResult addWhiteList(
35 | @ApiParam(value = "accessToken") @RequestParam String token,
36 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
37 | @ApiParam(value = "账户地址") @RequestParam String address) {
38 | return contractInvoke.addWhiteList(token, invokeAddress, address);
39 | }
40 |
41 | /**
42 | * 创建一个参与方
43 | *
44 | * @param ID 参与方ID 注意需要是hyperchain账户
45 | * @param name 参与方姓名
46 | * @param actor 参与方类型
47 | * @return 创建用户是否成功
48 | */
49 | @ApiOperation(value = "增加一个参与方", notes = "防伪溯源案例")
50 | @RequestMapping(value = "/v1/user", method = RequestMethod.POST)
51 | public BaseResult newUser(@ApiParam(value = "accessToken") @RequestParam String token,
52 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
53 | @ApiParam(value = "参与方ID") @RequestParam String ID,
54 | @ApiParam(value = "参与方name") @RequestParam String name,
55 | @ApiParam(value = "参与方类型") @RequestParam Actor actor) {
56 | return contractInvoke.newUser(token, invokeAddress, ID, name, actor);
57 | }
58 |
59 | /**
60 | * 生产一件商品
61 | *
62 | * @param commodityID 商品ID
63 | * @param commodityName 商品name
64 | * @return 1.是否已经有了该商品 2.调用者是否是一个生产者 3.成功生产
65 | */
66 | @ApiOperation(value = "生产一件产品", notes = "防伪溯源案例")
67 | @RequestMapping(value = "/v1/product", method = RequestMethod.POST)
68 | public BaseResult newProduct(@ApiParam(value = "accessToken") @RequestParam String token,
69 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
70 | @ApiParam(value = "商品ID") @RequestParam String commodityID,
71 | @ApiParam(value = "商品name") @RequestParam String commodityName,
72 | @ApiParam(value = "商品价格") @RequestParam Integer price) {
73 | return contractInvoke.newProduct(token, invokeAddress, commodityID, commodityName, price);
74 | }
75 |
76 |
77 | /**
78 | * 经销商发起的商品转移
79 | *
80 | * @param commodityID 商品ID
81 | * @return 1.是否已经有了该商品 2.调用者是否是一个经销商 3.成功生产
82 | */
83 | @ApiOperation(value = "经销商发起的转移", notes = "防伪溯源案例")
84 | @RequestMapping(value = "/v1/retailerInnerTransfer", method = RequestMethod.POST)
85 | public BaseResult retailerInnerTransfer(
86 | @ApiParam(value = "accessToken") @RequestParam String token,
87 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
88 | @ApiParam(value = "商品ID") @RequestParam String commodityID) {
89 | return contractInvoke.retailerInnerTransfer(token, invokeAddress, commodityID);
90 | }
91 |
92 |
93 | /**
94 | * 消费者发起的商品转移
95 | *
96 | * @param commodityID 商品ID
97 | * @return 1.是否已经有了该商品 2.调用者是否是一个消费者 3.成功生产
98 | */
99 | @ApiOperation(value = "消费者发起的转移", notes = "防伪溯源案例")
100 | @RequestMapping(value = "/v1/fromRetailerToCustomer", method = RequestMethod.POST)
101 | public BaseResult fromRetailerToCustomer(@ApiParam(value = "accessToken") @RequestParam String token,
102 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
103 | @ApiParam(value = "商品ID") @RequestParam String commodityID) {
104 | return contractInvoke.fromRetailerToCustomer(token, invokeAddress, commodityID);
105 | }
106 |
107 | /**
108 | * 通过白名单获取商品记录
109 | *
110 | * @param commodityID 商品ID
111 | * @return 1.是否已经有了该商品 2.调用者是否是一个消费者 3.成功生产
112 | */
113 | @ApiOperation(value = "消费者发起的转移", notes = "防伪溯源案例")
114 | @RequestMapping(value = "/v1/getCommodityRecordsByWhiteList", method = RequestMethod.POST)
115 | public BaseResult getCommodityRecordsByWhiteList(
116 | @ApiParam(value = "accessToken") @RequestParam String token,
117 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
118 | @ApiParam(value = "商品ID") @RequestParam String commodityID) {
119 | return contractInvoke.getCommodityRecordsByWhiteList(token, invokeAddress, commodityID);
120 | }
121 |
122 |
123 | /**
124 | * 只获取当前账户的商品信息
125 | *
126 | * @param commodityID 商品ID
127 | * @param actor 账户类型
128 | * @return
129 | */
130 | @ApiOperation(value = "通过调用者账户去获取账户信息", notes = "防伪溯源案例")
131 | @RequestMapping(value = "/v1/getCommodity", method = RequestMethod.POST)
132 | public BaseResult getCommodity(
133 | @ApiParam(value = "accessToken") @RequestParam String token,
134 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
135 | @ApiParam(value = "商品ID") @RequestParam String commodityID,
136 | @ApiParam(value = "参与方类型") @RequestParam Actor actor) {
137 | return contractInvoke.getCommodity(token, invokeAddress, commodityID, actor);
138 | }
139 |
140 | /**
141 | * 获取商品的总价格
142 | *
143 | * @return
144 | */
145 | @ApiOperation(value = "通过商品ID列表获取总价格", notes = "防伪溯源案例")
146 | @RequestMapping(value = "/v1/totalPrice", method = RequestMethod.POST)
147 | public BaseResult getTotalPrice(
148 | @ApiParam(value = "accessToken") @RequestParam String token,
149 | @ApiParam(value = "调用者账户地址") @RequestParam String invokeAddress,
150 | @ApiParam(value = "商品ID列表") @RequestParam List commodityIDs) {
151 | return contractInvoke.getTotalPrice(token, invokeAddress, commodityIDs);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/resources/contractSourceCode/supplychain.sol:
--------------------------------------------------------------------------------
1 |
2 | library SafeMath{
3 |
4 | function mul(uint8 a , uint8 b ) internal returns( uint8 ){
5 | if(a == 0 ){
6 | return 0;
7 | }
8 | uint8 c = a * b ;
9 | assert(c /a == b );
10 | return c;
11 | }
12 | function div(uint256 a , uint256 b ) internal returns( uint256 ){
13 | uint256 c = a / b ;
14 | return c;
15 | }
16 | function sub(uint256 a , uint256 b ) internal returns( uint256 ){
17 | assert(b<=a);
18 | return a - b;
19 | }
20 | function add(uint256 a , uint256 b ) internal returns( uint256 ){
21 | uint256 c = a + b;
22 | assert(c >= a);
23 | return c;
24 | }
25 | }
26 |
27 | contract SupplyChain {
28 | using SafeMath for uint256;
29 | enum Actor{Others, Producer, Retailer, Customer}
30 | struct User {
31 | bytes32 ID;
32 | bytes32 name;
33 | Actor actor;
34 | }
35 |
36 | struct Commodity {
37 | uint256 price;
38 | bytes32 commodityID;
39 | bytes32 commodityName;
40 | uint produceTime;
41 | bytes32 producerName;
42 | uint[] timestamps;
43 | bytes32[] retailerNames;
44 | uint sellTime;
45 | bytes32 customerName;
46 | bool isBinding;
47 | address owner;
48 | }
49 |
50 | mapping(address => User) producerMap;
51 | mapping(address => User) retailerMap;
52 | mapping(address => User) customerMap;
53 | mapping(bytes32 => Commodity) commodityMap;
54 | mapping(address => bool) whiteList;
55 | address owner;
56 |
57 | modifier onlyOwner {
58 | if (msg.sender != owner)
59 | assert(false);
60 | _;
61 | }
62 | function SupplyChain(){
63 | owner = msg.sender;
64 | whiteList[owner] = true;
65 | }
66 |
67 | function addWhiteList(address addr){
68 | whiteList[addr] = true;
69 | }
70 |
71 | function newUser(bytes32 ID, bytes32 name, Actor actor) returns (bool, string){
72 | User user;
73 |
74 | if (actor == Actor.Producer) {
75 | user = producerMap[msg.sender];
76 | } else if (actor == Actor.Retailer) {
77 | user = retailerMap[msg.sender];
78 | } else if (actor == Actor.Customer) {
79 | user = customerMap[msg.sender];
80 | } else {
81 | return (false, "the actor is not belong");
82 | }
83 |
84 | if (user.ID != 0x0) {
85 | return (false, "this ID has been occupied!");
86 | }
87 | user.ID = ID;
88 | user.name = name;
89 | user.actor = actor;
90 | return (true, "Success");
91 | }
92 |
93 | function newProduct(bytes32 commodityID, bytes32 commodityName,
94 | uint timestamp, uint _price) returns (bool, bytes32){
95 | Commodity commodity = commodityMap[commodityID];
96 | if (commodity.commodityID != 0x0) {
97 | return (false, "The commodityID already exist!");
98 | }
99 | User user = producerMap[msg.sender];
100 | if (user.ID == 0x0) {
101 | return (false, "The producer don't exist!");
102 | }
103 | commodity.commodityID = commodityID;
104 | commodity.commodityName = commodityName;
105 | commodity.produceTime = timestamp;
106 | commodity.producerName = user.name;
107 | commodity.price = _price;
108 | return (true, "Success,produce a new product");
109 | }
110 |
111 |
112 | function retailerInnerTransfer(bytes32 commodityID, uint timestamp) returns (bool, string){
113 | Commodity commodity = commodityMap[commodityID];
114 | if (commodity.commodityID == 0x0) {
115 | return (false, "The commodityID don't exist!");
116 | }
117 | User user = retailerMap[msg.sender];
118 | if (user.ID == 0x0) {
119 | return (false, "The retailer don't exist!");
120 | }
121 | commodity.timestamps.push(timestamp);
122 | commodity.retailerNames.push(user.name);
123 | return (true, "Success");
124 | }
125 |
126 | function fromRetailerToCustomer(bytes32 commodityID, uint timestamp) returns (bool, string){
127 | Commodity commodity = commodityMap[commodityID];
128 | if (commodity.commodityID == 0x0) {
129 | return (false, "The commodityID don't exist!");
130 | }
131 | commodity.sellTime = timestamp;
132 | return (true, "Success,Has been sold");
133 | }
134 |
135 | function getCommodityRecordsByWhiteList(bytes32 commodityID) returns (bool, string,
136 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
137 | , bytes32 customerName, uint sellTime, uint price){
138 | if (!whiteList[msg.sender]) {
139 | return (false, "you has no access", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime, commodity.price);
140 | }
141 | Commodity commodity = commodityMap[commodityID];
142 | if (commodity.commodityID == 0x0) {
143 | return (false, "The commodityID is not exist", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime, commodity.price);
144 | }
145 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime, commodity.price);
146 | }
147 |
148 |
149 | function getCommodity(bytes32 commodityID, Actor actor) returns (bool, string,
150 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
151 | , bytes32 customerName, uint sellTime, uint price){
152 | Commodity commodity = commodityMap[commodityID];
153 | if (commodity.commodityID == 0x0) {
154 | return (false, "The commodityID is not exist", producerName, produceTime,
155 | retailerNames, retailerTimes, customerName, sellTime, price);
156 | }
157 | User user;
158 | if (actor == Actor.Producer) {
159 | user = producerMap[msg.sender];
160 | } else if (actor == Actor.Retailer) {
161 | user = retailerMap[msg.sender];
162 | } else if (actor == Actor.Customer) {
163 | user = customerMap[msg.sender];
164 | } else {
165 | return (false, "the actor is not belong", producerName, produceTime,
166 | retailerNames, retailerTimes, customerName, sellTime, price);
167 | }
168 | if (commodity.isBinding) {
169 | if (commodity.owner != msg.sender) {
170 | return (false, "warning,this commodity has been bound", producerName, produceTime,
171 | retailerNames, retailerTimes, customerName, sellTime, price);
172 | }
173 | }
174 | if (commodity.sellTime > 0) {
175 | commodity.isBinding = true;
176 | commodity.owner = msg.sender;
177 | commodity.customerName = user.name;
178 | }
179 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime, commodity.price);
180 | }
181 |
182 | function getTotalPrice(bytes32[] commodityIDs) returns (bool, string, uint256 totalPrice ){
183 | if (!whiteList[msg.sender]) {
184 | return (false, "you has no access", totalPrice);
185 | }
186 |
187 | for(uint8 i = 0; i < commodityIDs.length ; i++ ){
188 | Commodity commodity = commodityMap[commodityIDs[i]];
189 | if (commodity.commodityID == 0x0) {
190 | return (false, "The commodityID is not exist", totalPrice);
191 | }else{
192 | totalPrice.add(commodity.price);
193 | }
194 | }
195 | return (true, "get the total price ", totalPrice);
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/main/resources/hyperchain.properties:
--------------------------------------------------------------------------------
1 | # 是否重新编译和部署合约
2 | redeploy=false
3 |
4 | # 合约地址
5 | contract_address=0xdc58ca03fd1cd4c1515bff3f693e543e37848f02
6 | # abi
7 | abi=[{"constant":false,"inputs":[{"name":"ID","type":"bytes32"},{"name":"name","type":"bytes32"},{"name":"actor","type":"uint8"}],"name":"newUser","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityIDs","type":"bytes32[]"}],"name":"getTotalPrice","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"},{"name":"totalPrice","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityID","type":"bytes32"},{"name":"timestamp","type":"uint256"}],"name":"retailerInnerTransfer","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityID","type":"bytes32"},{"name":"commodityName","type":"bytes32"},{"name":"timestamp","type":"uint256"},{"name":"_price","type":"uint256"}],"name":"newProduct","outputs":[{"name":"","type":"bool"},{"name":"","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityID","type":"bytes32"},{"name":"timestamp","type":"uint256"}],"name":"fromRetailerToCustomer","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"addWhiteList","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityID","type":"bytes32"}],"name":"getCommodityRecordsByWhiteList","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"},{"name":"producerName","type":"bytes32"},{"name":"produceTime","type":"uint256"},{"name":"retailerNames","type":"bytes32[]"},{"name":"retailerTimes","type":"uint256[]"},{"name":"customerName","type":"bytes32"},{"name":"sellTime","type":"uint256"},{"name":"price","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"commodityID","type":"bytes32"},{"name":"actor","type":"uint8"}],"name":"getCommodity","outputs":[{"name":"","type":"bool"},{"name":"","type":"string"},{"name":"producerName","type":"bytes32"},{"name":"produceTime","type":"uint256"},{"name":"retailerNames","type":"bytes32[]"},{"name":"retailerTimes","type":"uint256[]"},{"name":"customerName","type":"bytes32"},{"name":"sellTime","type":"uint256"},{"name":"price","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]
8 |
--------------------------------------------------------------------------------
/src/main/resources/lib/qsnarksdk-1.0-SNAPSHOT-jar-with-dependencies.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qulian-netease/minata/b03a554d3e5b299f7d7d8140d380cec195d8f5f0/src/main/resources/lib/qsnarksdk-1.0-SNAPSHOT-jar-with-dependencies.jar
--------------------------------------------------------------------------------
/src/main/resources/参考合约/SafeMath.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.10;
2 |
3 | library SafeMath{
4 | function mul(uint256 a , uint256 b ) internal returns( uint256 ){
5 | if(a == 0 ){
6 | return 0;
7 | }
8 | uint256 c = a * b ;
9 | assert(c /a == b );
10 | return c;
11 | }
12 | function div(uint256 a , uint256 b ) internal returns( uint256 ){
13 | uint256 c = a / b ;
14 | return c;
15 | }
16 | function sub(uint256 a , uint256 b ) internal returns( uint256 ){
17 | assert(b<=a);
18 | return a - b;
19 | }
20 | function add(uint256 a , uint256 b ) internal returns( uint256 ){
21 | uint256 c = a + b;
22 | assert(c >= a);
23 | return c;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/resources/参考合约/supplychainNoSafeMath.sol:
--------------------------------------------------------------------------------
1 | contract SupplyChain {
2 | enum Actor{Others, Producer, Retailer, Customer}
3 | struct User {
4 | bytes32 ID;
5 | bytes32 name;
6 | Actor actor;
7 | }
8 |
9 | struct Commodity {
10 | bytes32 commodityID;
11 | bytes32 commodityName;
12 | uint produceTime;
13 | bytes32 producerName;
14 | uint[] timestamps;
15 | bytes32[] retailerNames;
16 | uint sellTime;
17 | bytes32 customerName;
18 | bool isBinding;
19 | address owner;
20 | }
21 |
22 | mapping(address => User) producerMap;
23 | mapping(address => User) retailerMap;
24 | mapping(address => User) customerMap;
25 | mapping(bytes32 => Commodity) commodityMap;
26 | mapping(address => bool) whiteList;
27 | address owner;
28 |
29 | modifier onlyOwner {
30 | if (msg.sender != owner)
31 | assert(false);
32 | _;
33 | }
34 | function SupplyChain(){
35 | owner = msg.sender;
36 | whiteList[owner] = true;
37 | }
38 |
39 | function addWhiteList(address addr){
40 | whiteList[addr] = true;
41 | }
42 |
43 | function newUser(bytes32 ID, bytes32 name, Actor actor) returns (bool, string){
44 | User user;
45 |
46 | if (actor == Actor.Producer) {
47 | user = producerMap[msg.sender];
48 | } else if (actor == Actor.Retailer) {
49 | user = retailerMap[msg.sender];
50 | } else if (actor == Actor.Customer) {
51 | user = customerMap[msg.sender];
52 | } else {
53 | return (false, "the actor is not belong");
54 | }
55 |
56 | if (user.ID != 0x0) {
57 | return (false, "this ID has been occupied!");
58 | }
59 | user.ID = ID;
60 | user.name = name;
61 | user.actor = actor;
62 | return (true, "Success");
63 | }
64 |
65 | // this interface just for producer
66 | function newProduct(bytes32 commodityID, bytes32 commodityName,
67 | uint timestamp) returns (bool, bytes32){
68 | Commodity commodity = commodityMap[commodityID];
69 | if (commodity.commodityID != 0x0) {
70 | return (false, "The commodityID already exist!");
71 | }
72 | User user = producerMap[msg.sender];
73 | if (user.ID == 0x0) {
74 | return (false, "The producer don't exist!");
75 | }
76 | commodity.commodityID = commodityID;
77 | commodity.commodityName = commodityName;
78 | commodity.produceTime = timestamp;
79 | commodity.producerName = user.name;
80 | return (true, "Success,produce a new product");
81 | }
82 |
83 |
84 | // this interface just for retailer
85 | function retailerInnerTransfer(bytes32 commodityID, uint timestamp) returns (bool, string){
86 | Commodity commodity = commodityMap[commodityID];
87 | if (commodity.commodityID == 0x0) {
88 | return (false, "The commodityID don't exist!");
89 | }
90 | User user = retailerMap[msg.sender];
91 | if (user.ID == 0x0) {
92 | return (false, "The retailer don't exist!");
93 | }
94 | commodity.timestamps.push(timestamp);
95 | commodity.retailerNames.push(user.name);
96 | return (true, "Success");
97 | }
98 |
99 | function fromRetailerToCustomer(bytes32 commodityID, uint timestamp) returns (bool, string){
100 | Commodity commodity = commodityMap[commodityID];
101 | if (commodity.commodityID == 0x0) {
102 | return (false, "The commodityID don't exist!");
103 | }
104 | commodity.sellTime = timestamp;
105 | return (true, "Success,Has been sold");
106 | }
107 |
108 | // just for Supervision organization
109 | function getCommodityRecordsByWhiteList(bytes32 commodityID) returns (bool, string,
110 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
111 | , bytes32 customerName, uint sellTime){
112 | if (!whiteList[msg.sender]) {
113 | return (false, "you has no access", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime);
114 | }
115 | Commodity commodity = commodityMap[commodityID];
116 | if (commodity.commodityID == 0x0) {
117 | return (false, "The commodityID is not exist", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime);
118 | }
119 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime);
120 | }
121 |
122 |
123 | function getCommodity(bytes32 commodityID, Actor actor) returns (bool, string,
124 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
125 | , bytes32 customerName, uint sellTime){
126 | Commodity commodity = commodityMap[commodityID];
127 | if (commodity.commodityID == 0x0) {
128 | return (false, "The commodityID is not exist", producerName, produceTime,
129 | retailerNames, retailerTimes, customerName, sellTime);
130 | }
131 | User user;
132 | if (actor == Actor.Producer) {
133 | user = producerMap[msg.sender];
134 | } else if (actor == Actor.Retailer) {
135 | user = retailerMap[msg.sender];
136 | } else if (actor == Actor.Customer) {
137 | user = customerMap[msg.sender];
138 | } else {
139 | return (false, "the actor is not belong", producerName, produceTime,
140 | retailerNames, retailerTimes, customerName, sellTime);
141 | }
142 | if (commodity.isBinding) {
143 | if (commodity.owner != msg.sender) {
144 | return (false, "warning,this commodity has been bound", producerName, produceTime,
145 | retailerNames, retailerTimes, customerName, sellTime);
146 | }
147 | }
148 | if (commodity.sellTime > 0) {
149 | commodity.isBinding = true;
150 | commodity.owner = msg.sender;
151 | commodity.customerName = user.name;
152 | }
153 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime);
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/resources/参考合约/supplychainWithSafeMath.sol:
--------------------------------------------------------------------------------
1 |
2 | library SafeMath{
3 |
4 | function mul(uint8 a , uint8 b ) internal returns( uint8 ){
5 | if(a == 0 ){
6 | return 0;
7 | }
8 | uint8 c = a * b ;
9 | assert(c /a == b );
10 | return c;
11 | }
12 | function div(uint256 a , uint256 b ) internal returns( uint256 ){
13 | uint256 c = a / b ;
14 | return c;
15 | }
16 | function sub(uint256 a , uint256 b ) internal returns( uint256 ){
17 | assert(b<=a);
18 | return a - b;
19 | }
20 | function add(uint256 a , uint256 b ) internal returns( uint256 ){
21 | uint256 c = a + b;
22 | assert(c >= a);
23 | return c;
24 | }
25 | }
26 |
27 | contract SupplyChain {
28 | using SafeMath for uint256;
29 | enum Actor{Others, Producer, Retailer, Customer}
30 | struct User {
31 | bytes32 ID;
32 | bytes32 name;
33 | Actor actor;
34 | }
35 |
36 | struct Commodity {
37 | uint256 price;
38 | bytes32 commodityID;
39 | bytes32 commodityName;
40 | uint produceTime;
41 | bytes32 producerName;
42 | uint[] timestamps;
43 | bytes32[] retailerNames;
44 | uint sellTime;
45 | bytes32 customerName;
46 | bool isBinding;
47 | address owner;
48 | }
49 |
50 | mapping(address => User) producerMap;
51 | mapping(address => User) retailerMap;
52 | mapping(address => User) customerMap;
53 | mapping(bytes32 => Commodity) commodityMap;
54 | mapping(address => bool) whiteList;
55 | address owner;
56 |
57 | modifier onlyOwner {
58 | if (msg.sender != owner)
59 | assert(false);
60 | _;
61 | }
62 | function SupplyChain(){
63 | owner = msg.sender;
64 | whiteList[owner] = true;
65 | }
66 |
67 | function addWhiteList(address addr){
68 | whiteList[addr] = true;
69 | }
70 |
71 | function newUser(bytes32 ID, bytes32 name, Actor actor) returns (bool, string){
72 | User user;
73 |
74 | if (actor == Actor.Producer) {
75 | user = producerMap[msg.sender];
76 | } else if (actor == Actor.Retailer) {
77 | user = retailerMap[msg.sender];
78 | } else if (actor == Actor.Customer) {
79 | user = customerMap[msg.sender];
80 | } else {
81 | return (false, "the actor is not belong");
82 | }
83 |
84 | if (user.ID != 0x0) {
85 | return (false, "this ID has been occupied!");
86 | }
87 | user.ID = ID;
88 | user.name = name;
89 | user.actor = actor;
90 | return (true, "Success");
91 | }
92 |
93 | function newProduct(bytes32 commodityID, bytes32 commodityName,
94 | uint timestamp, uint _price) returns (bool, bytes32){
95 | Commodity commodity = commodityMap[commodityID];
96 | if (commodity.commodityID != 0x0) {
97 | return (false, "The commodityID already exist!");
98 | }
99 | User user = producerMap[msg.sender];
100 | if (user.ID == 0x0) {
101 | return (false, "The producer don't exist!");
102 | }
103 | commodity.commodityID = commodityID;
104 | commodity.commodityName = commodityName;
105 | commodity.produceTime = timestamp;
106 | commodity.producerName = user.name;
107 | commodity.price = _price;
108 | return (true, "Success,produce a new product");
109 | }
110 |
111 |
112 | function retailerInnerTransfer(bytes32 commodityID, uint timestamp) returns (bool, string){
113 | Commodity commodity = commodityMap[commodityID];
114 | if (commodity.commodityID == 0x0) {
115 | return (false, "The commodityID don't exist!");
116 | }
117 | User user = retailerMap[msg.sender];
118 | if (user.ID == 0x0) {
119 | return (false, "The retailer don't exist!");
120 | }
121 | commodity.timestamps.push(timestamp);
122 | commodity.retailerNames.push(user.name);
123 | return (true, "Success");
124 | }
125 |
126 | function fromRetailerToCustomer(bytes32 commodityID, uint timestamp) returns (bool, string){
127 | Commodity commodity = commodityMap[commodityID];
128 | if (commodity.commodityID == 0x0) {
129 | return (false, "The commodityID don't exist!");
130 | }
131 | commodity.sellTime = timestamp;
132 | return (true, "Success,Has been sold");
133 | }
134 |
135 | function getCommodityRecordsByWhiteList(bytes32 commodityID) returns (bool, string,
136 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
137 | , bytes32 customerName, uint sellTime, uint price){
138 | if (!whiteList[msg.sender]) {
139 | return (false, "you has no access", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime, commodity.price);
140 | }
141 | Commodity commodity = commodityMap[commodityID];
142 | if (commodity.commodityID == 0x0) {
143 | return (false, "The commodityID is not exist", producerName, produceTime, retailerNames, retailerTimes, customerName, commodity.sellTime, commodity.price);
144 | }
145 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime, commodity.price);
146 | }
147 |
148 |
149 | function getCommodity(bytes32 commodityID, Actor actor) returns (bool, string,
150 | bytes32 producerName, uint produceTime, bytes32[] retailerNames, uint[] retailerTimes
151 | , bytes32 customerName, uint sellTime, uint price){
152 | Commodity commodity = commodityMap[commodityID];
153 | if (commodity.commodityID == 0x0) {
154 | return (false, "The commodityID is not exist", producerName, produceTime,
155 | retailerNames, retailerTimes, customerName, sellTime, price);
156 | }
157 | User user;
158 | if (actor == Actor.Producer) {
159 | user = producerMap[msg.sender];
160 | } else if (actor == Actor.Retailer) {
161 | user = retailerMap[msg.sender];
162 | } else if (actor == Actor.Customer) {
163 | user = customerMap[msg.sender];
164 | } else {
165 | return (false, "the actor is not belong", producerName, produceTime,
166 | retailerNames, retailerTimes, customerName, sellTime, price);
167 | }
168 | if (commodity.isBinding) {
169 | if (commodity.owner != msg.sender) {
170 | return (false, "warning,this commodity has been bound", producerName, produceTime,
171 | retailerNames, retailerTimes, customerName, sellTime, price);
172 | }
173 | }
174 | if (commodity.sellTime > 0) {
175 | commodity.isBinding = true;
176 | commodity.owner = msg.sender;
177 | commodity.customerName = user.name;
178 | }
179 | return (true, "Success", commodity.producerName, commodity.produceTime, commodity.retailerNames, commodity.timestamps, commodity.customerName, commodity.sellTime, commodity.price);
180 | }
181 |
182 | function getTotalPrice(bytes32[] commodityIDs) returns (bool, string, uint256 totalPrice ){
183 | if (!whiteList[msg.sender]) {
184 | return (false, "you has no access", totalPrice);
185 | }
186 |
187 | for(uint8 i = 0; i < commodityIDs.length ; i++ ){
188 | Commodity commodity = commodityMap[commodityIDs[i]];
189 | if (commodity.commodityID == 0x0) {
190 | return (false, "The commodityID is not exist", totalPrice);
191 | }else{
192 | totalPrice.add(commodity.price);
193 | }
194 | }
195 | return (true, "get the total price ", totalPrice);
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/main/resources/参考合约/testOverflow.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.10;
2 | contract Test {
3 | function testOverflow(uint8 _value1, uint8 _value2 ) returns(uint8) {
4 | uint8 ret ;
5 | ret = _value1 * _value2;
6 | return ret;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/resources/参考合约/useSafeMath.sol:
--------------------------------------------------------------------------------
1 | pragma solidity ^0.4.10;
2 |
3 | library SafeMath{
4 | function mul(uint8 a , uint8 b ) internal returns( uint8 ){
5 | if(a == 0 ){
6 | return 0;
7 | }
8 | uint8 c = a * b ;
9 | assert(c /a == b );
10 | return c;
11 | }
12 | function div(uint256 a , uint256 b ) internal returns( uint256 ){
13 | uint256 c = a / b ;
14 | return c;
15 | }
16 | function sub(uint256 a , uint256 b ) internal returns( uint256 ){
17 | assert(b<=a);
18 | return a - b;
19 | }
20 | function add(uint256 a , uint256 b ) internal returns( uint256 ){
21 | uint256 c = a + b;
22 | assert(c >= a);
23 | return c;
24 | }
25 | }
26 |
27 | contract Test {
28 | using SafeMath for uint8;
29 | function testOverflow(uint8 _value1, uint8 _value2 ) returns(uint8) {
30 | uint8 ret ;
31 | ret = _value1.mul(_value2);
32 | return ret;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/web/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------