0%

区块链实训5

了解区块链中的交易,了解智能合约的使用

第一部分:区块链中的交易

1. 交易的概念

在区块链中,交易是指两方之间价值的转移。这可以是货币、资产或数据的交换。交易的基本概念建立在信任与透明的基础上,通过区块链的分布式账本,所有参与者都可以验证交易的有效性,确保没有人能篡改历史记录。这种机制使得区块链特别适用于金融系统和需要去中心化信任的场景。

2. 交易的组成部分

每笔交易由以下几个主要组成部分构成:

  • 金额(Amount):表示转账的具体数量。
  • 交易ID:为每笔交易生成的唯一标识符,用于区分不同的交易。(hashNo)
  • 时间戳:记录交易的创建时间,确保时间线的透明性。

3. 交易的生命周期

交易的生命周期包括多个关键步骤:

  1. 创建交易:用户通过钱包软件输入交易的基本信息(接收方地址、金额等),并生成交易。
  2. 签名:交易使用用户的私钥进行数字签名,以证明其合法性。
  3. 广播:交易被发送到区块链网络,所有节点接收到该交易信息。
  4. 验证:节点验证交易的有效性,包括检查输入是否有效、签名是否正确等。
  5. 打包:经过验证的交易被矿工打包成区块,并与其他交易一起处理。
  6. 确认:矿工通过工作量证明等机制确认区块,交易被写入区块链。
  7. 最终性:交易一旦被多个区块确认后,便被认为是不可更改的,完成整个交易流程。

4. 交易实体类

项目地址:https://gitee.com/daitoulin/block_contract.git

实体类路径:block_contract/src/main/java/com/example/blockchain/entity/TradeObject.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package com.example.blockchain.entity;

public class TradeObject {

private String from;
private String to;
private String hashNo;
private String type;
private String imgUrl;
private String content;
private String blockIndex;
private String contentjson;
private String jsoncreatetime;
private String uId;
private String annexPath;
private String filePath;
private String blockHash;
private String createTime;
private String sign;
private String contractContent;
private String paramStr;
private String lastData;
private String objToString;
private String dataStr;


@Override
public String toString() {
return "TradeObject{" +
"from='" + from + '\'' +
", to='" + to + '\'' +
", type='" + type + '\'' +
", imgUrl='" + imgUrl + '\'' +
", content='" + content + '\'' +
", blockIndex='" + blockIndex + '\'' +
", contentjson='" + contentjson + '\'' +
", jsoncreatetime='" + jsoncreatetime + '\'' +
", uId='" + uId + '\'' +
", annexPath='" + annexPath + '\'' +
", filePath='" + filePath + '\'' +
", contractContent='" + contractContent + '\'' +
", paramStr='" + paramStr + '\'' +
", lastData='" + lastData + '\'' +
'}';
}

public String getFrom() {
return from;
}

public void setFrom(String from) {
this.from = from;
}

public String getTo() {
return to;
}

public void setTo(String to) {
this.to = to;
}


public String getHashNo() {
return hashNo;
}

public void setHashNo(String hashNo) {
this.hashNo = hashNo;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}

public String getImgUrl() {
return imgUrl;
}

public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getBlockIndex() {
return blockIndex;
}

public void setBlockIndex(String blockIndex) {
this.blockIndex = blockIndex;
}


public String getContentjson() {
return contentjson;
}

public void setContentjson(String contentjson) {
this.contentjson = contentjson;
}

public String getJsoncreatetime() {
return jsoncreatetime;
}

public void setJsoncreatetime(String jsoncreatetime) {
this.jsoncreatetime = jsoncreatetime;
}

public String getuId() {
return uId;
}

public void setuId(String uId) {
this.uId = uId;
}

public String getAnnexPath() {
return annexPath;
}

public void setAnnexPath(String annexPath) {
this.annexPath = annexPath;
}


public String getFilePath() {
return filePath;
}

public void setFilePath(String filePath) {
this.filePath = filePath;
}

public String getBlockHash() {
return blockHash;
}

public void setBlockHash(String blockHash) {
this.blockHash = blockHash;
}

public String getCreateTime() {
return createTime;
}

public void setCreateTime(String createTime) {
this.createTime = createTime;
}

public String getSign() {
return sign;
}

public void setSign(String sign) {
this.sign = sign;
}

public String getContractContent() {
return contractContent;
}

public void setContractContent(String contractContent) {
this.contractContent = contractContent;
}

public String getParamStr() {
return paramStr;
}

public void setParamStr(String paramStr) {
this.paramStr = paramStr;
}

public String getLastData() {
return lastData;
}

public void setLastData(String lastData) {
this.lastData = lastData;
}

public String getObjToString() {
return objToString;
}

public void setObjToString(String objToString) {
this.objToString = objToString;
}

public String getDataStr() {
return dataStr;
}

public void setDataStr(String dataStr) {
this.dataStr = dataStr;
}
}

5. 交易相关接口

  1. 获取交易实体类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @RequestMapping(value = "/data/getTradeObject", method = {RequestMethod.POST})
    public ResponseEntity<JSONObject> getTradeObject(@RequestBody TradeBO tradeBO) throws Exception {
    JSONObject jo = new JSONObject();

    if ("".equals(tradeBO.getContent()) || tradeBO.getContent() == null){
    jo.setCode("-1");
    jo.setMsg("交易content值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if ("".equals(tradeBO.getFrom()) || tradeBO.getFrom() == null){
    jo.setCode("-1");
    jo.setMsg("交易from值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if ("".equals(tradeBO.getPrivateKey()) || tradeBO.getPrivateKey() == null){
    jo.setCode("-1");
    jo.setMsg("私钥privateKey不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }

    BigInteger pri = new BigInteger(tradeBO.getPrivateKey(), 16);

    TradeObject tradeObject = new TradeObject();
    tradeObject.setFrom(tradeBO.getFrom());
    tradeObject.setTo("system");
    tradeObject.setType("1");
    tradeObject.setContent(tradeBO.getContent());
    tradeObject.setJsoncreatetime(DateUtils.getTime());
    tradeObject.setObjToString(tradeObject.toString());

    Sign.SignatureData signatureData = EthUtils.signMessage(tradeObject.toString(),pri);
    String sign = EthUtils.getSignStr(signatureData);
    tradeObject.setSign(sign);

    jo.setO(tradeObject);
    jo.setMsg("签名成功");
    jo.setCode("1");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
  2. 交易接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    @RequestMapping(value = "/data/trade", method = {RequestMethod.POST})
    public ResponseEntity<JSONObject> trade(@RequestBody TradeObject tradeObject) {
    JSONObject jo = new JSONObject();
    List<Pending> pes = PendingServiceImpl.queryPendings();


    if ("".equals(tradeObject.getFrom()) || tradeObject.getFrom() == null){
    jo.setCode("-1");
    jo.setMsg("交易from值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if("".equals(tradeObject.getTo()) || tradeObject.getTo() == null){
    jo.setCode("-1");
    jo.setMsg("交易to值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if ("".equals(tradeObject.getObjToString()) || tradeObject.getObjToString() == null){
    jo.setCode("-1");
    jo.setMsg("交易objToString值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if ("".equals(tradeObject.getSign()) || tradeObject.getSign() == null){
    jo.setCode("-1");
    jo.setMsg("交易sign值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    if ("".equals(tradeObject.getType()) || tradeObject.getType() == null){
    jo.setCode("-1");
    jo.setMsg("交易type值不能为空");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }


    String no = PendingServiceImpl.genTradeNo(tradeObject);
    tradeObject.setHashNo(no);


    for (Pending p : pes) {
    if (p.getOrderNo().equals(no)) {
    jo.setCode("-1");
    jo.setMsg("交易已存在");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }
    }

    Sign.SignatureData signatureData = EthUtils.stringToSignatureData(tradeObject.getSign());
    //验证钱包地址
    boolean isValid = EthUtils.verifySignature(tradeObject.getObjToString(), signatureData, tradeObject.getFrom());
    if (!isValid){
    jo.setCode("-1");
    jo.setMsg("验签失败,请重新签名");
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);
    }


    String body = new Gson().toJson(tradeObject);
    try {
    PendingServiceImpl.validateTradeNo(tradeObject);
    Pending pending = new Pending();
    pending.setTradeBody(body);
    pending.setCreateTime(tradeObject.getJsoncreatetime());
    pending.setOrderNo(tradeObject.getHashNo());
    pending.setTradeType(tradeObject.getType());
    PendingServiceImpl.save(pending);
    for (String port : map.getFs().keySet()) {
    Friends f = map.getFs().get(port);
    String ip = f.getIp();
    String url = "http://" + ip + ":8001/data/trade";
    restTemplate.postForEntity(url, tradeObject, TradeObject.class);
    }
    jo.setCode("1");
    jo.setMsg("成功");

    } catch (Exception e) {
    System.out.println(e.getMessage());
    jo.setCode("-1");
    jo.setMsg("失败");
    }
    return new ResponseEntity<JSONObject>(jo, HttpStatus.OK);

    }

交易接口参数展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"from": "0xa0cc1fc72ced8facab08ebe5d6ad5d806ddbd8aa",
"to": "system",
"hashNo": null,
"type": "1",
"imgUrl": null,
"content": "good good study,day day up",
"blockIndex": null,
"contentjson": null,
"jsoncreatetime": "2024-10-22 10:41:14",
"uId": null,
"annexPath": null,
"filePath": null,
"blockHash": null,
"createTime": null,
"sign": "fba08b9a782606b5cfd40c68b24604df2c669bbfa78440b6848ffd826c684b5c58e2b4c7a1db3805dd1e31bb30da233af80cca4edd0cca73e8f8e4dddb452d551c",
"contractContent": null,
"paramStr": null,
"lastData": null,
"objToString": "TradeObject{from='0xa0cc1fc72ced8facab08ebe5d6ad5d806ddbd8aa', to='system', type='1', imgUrl='null', content='good good study,day day up', blockIndex='null', contentjson='null', jsoncreatetime='2024-10-22 10:41:14', uId='null', annexPath='null', filePath='null', contractContent='null', paramStr='null', lastData='null'}",
"dataStr": null
}

image-20241026143316698

上链完的普通交易详情如图所示

6. 实现交易

我们启动项目,访问8001端口的/data/getWallet,获取钱包地址和公私钥

1
2
3
4
5
6
7
8
9
{
"code": "1",
"msg": "生成钱包成功",
"o": {
"publicKey": "2e58fdca4a243486b9ecfd9287e3c56258eaee31761025ebc4acbe636e5c767b453b5465b755b22c1cb572c32e7e98241193b7e8dbe6ed2b6d3eb81668883772",
"privateKey": "ced6cc51ee0d2063211564e53012aaac4c0b4978e8cc22a11ed998de4e4bcc3b",
"address": "0xed6d1bcb771e59765ab65f43b9406669428af057"
}
}

image-20241026163252655

发送交易信息

1
2
3
4
5
{
"privateKey":"ced6cc51ee0d2063211564e53012aaac4c0b4978e8cc22a11ed998de4e4bcc3b",
"from":"0xed6d1bcb771e59765ab65f43b9406669428af057",
"content":"test"
}

image-20241026165533515

成功创建和签名交易对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"code": "1",
"msg": "签名成功",
"o": {
"from": "0xed6d1bcb771e59765ab65f43b9406669428af057",
"to": "system",
"hashNo": null,
"type": "1",
"imgUrl": null,
"content": "test",
"blockIndex": null,
"contentjson": null,
"jsoncreatetime": "2024-10-26 16:55:19",
"uId": null,
"annexPath": null,
"filePath": null,
"blockHash": null,
"createTime": null,
"sign": "e653a51fb50421d0e58143342ce0b6c945378581331c247235d5121010926b5560db66b042bc2f01a4a4ff6e9cc2edc3af164a2608e7068cb507f099cd237c971b",
"contractContent": null,
"paramStr": null,
"lastData": null,
"objToString": "TradeObject{from='0xed6d1bcb771e59765ab65f43b9406669428af057', to='system', type='1', imgUrl='null', content='test', blockIndex='null', contentjson='null', jsoncreatetime='2024-10-26 16:55:19', uId='null', annexPath='null', filePath='null', contractContent='null', paramStr='null', lastData='null'}",
"dataStr": null
}
}

发送交易信息进行交易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"from": "0xed6d1bcb771e59765ab65f43b9406669428af057",
"to": "system",
"hashNo": null,
"type": "1",
"imgUrl": null,
"content": "test",
"blockIndex": null,
"contentjson": null,
"jsoncreatetime": "2024-10-26 16:55:19",
"uId": null,
"annexPath": null,
"filePath": null,
"blockHash": null,
"createTime": null,
"sign": "e653a51fb50421d0e58143342ce0b6c945378581331c247235d5121010926b5560db66b042bc2f01a4a4ff6e9cc2edc3af164a2608e7068cb507f099cd237c971b",
"contractContent": null,
"paramStr": null,
"lastData": null,
"objToString": "TradeObject{from='0xed6d1bcb771e59765ab65f43b9406669428af057', to='system', type='1', imgUrl='null', content='test', blockIndex='null', contentjson='null', jsoncreatetime='2024-10-26 16:55:19', uId='null', annexPath='null', filePath='null', contractContent='null', paramStr='null', lastData='null'}",
"dataStr": null
}

image-20241026165902029

我们直接开挖http://127.0.0.1:8001/starMining,在浏览器打开

image-20241026172149944

可以看到挖到的是我们前面进行的test交易

image-20241026171947590

第二部分:智能合约

1. 智能合约定义

1.1. 什么是智能合约:

智能合约是一种自执行的合约,协议条款直接写入代码中,并存储在区块链上。它能够在特定条件被满足时,自动执行合约的条款。这种合约确保交易的透明性和可信性,因为所有的操作都记录在区块链上,且不可篡改。

1.2. 与传统合约的比较:

  1. 执行方式
  • 传统合约:通常需要第三方(如律师)来执行和执行条款。
  • 智能合约:自动在区块链上执行,无需中介。
  1. 透明性与安全性
  • 传统合约:可能会因为人为错误或欺诈而受到影响,透明度较低。
  • 智能合约:所有交易记录透明,且由于区块链的不可篡改性,更加安全。
  1. 成本与效率
  • 传统合约:涉及中介费用,处理时间较长。
  • 智能合约:降低了交易成本,提高了执行效率,因为省去了中介的参与。

2. 工作原理

2.1. 区块链技术基础

  1. 区块链概念
    • 区块链是一种去中心化的分布式账本技术,能够安全地记录交易和信息。
    • 数据以“区块”的形式存储,每个区块包含一组交易记录,并通过加密技术连接成链。
  1. 去中心化
    • 区块链网络没有单一控制点,所有节点共同维护账本。
    • 去中心化增强了系统的安全性和抗攻击能力。
  1. 共识机制
    • 为了确保所有节点对账本的一致性,区块链使用共识机制(如工作量证明、权益证明等)来验证和确认交易。
  1. 不可篡改性
    • 一旦数据被添加到区块链上,就几乎不可能被修改或删除,确保了交易的真实性和可靠性。

2.2. 如何执行智能合约

  1. 合约的编写
    • 智能合约通常用特定的编程语言(如Solidity)编写,代码中定义了合约的条款和条件。
  1. 部署到区块链
    • 编写好的智能合约被部署到区块链上,成为网络的一部分。部署后,合约的地址会被记录,任何人都可以与其交互。
  1. 触发条件
    • 智能合约在特定条件下被触发。例如,某项交易的完成、主动调用,时间的到达等。
  1. 自动执行
    • 一旦条件满足,智能合约将自动执行合约中的指令,如转账、更新数据等。这一过程不需要任何人的干预。
  1. 记录与验证
    • 执行的结果会被记录在区块链上,并通过共识机制进行验证,确保每个节点都有相同的信息。
  1. 反馈与更新
    • 执行完毕后,智能合约可以生成反馈信息,告知相关方执行结果。若合约设计中包括更新逻辑,合约状态也可以相应更新

3. 常用语言与平台

3.1. Solidity简介

  1. 编程语言
    • Solidity是一种专门为智能合约开发而设计的高层编程语言。它受到了JavaScript、Python和C++等语言的影响,使得其语法相对易于理解。
  1. 特性
    • 类型安全:支持静态类型检查,减少运行时错误。
    • 合约结构:允许定义合约、库和接口,便于代码的组织和重用。
    • 事件:提供事件日志功能,便于在区块链上跟踪状态变化和交易。
  1. 开发工具
    • 常用的开发环境包括Remix、Truffle和Hardhat,这些工具提供了测试、编译和部署的功能,使得开发者能够高效地构建和管理智能合约。

3.2. 以太坊平台

  1. 概述
    • 以太坊是一个开放的区块链平台,支持智能合约和去中心化应用(DApp)的开发。它由Vitalik Buterin于2015年推出,成为最流行的智能合约平台之一。
  1. 虚拟机
    • 以太坊使用以太坊虚拟机(EVM)来执行智能合约,EVM是一个完全去中心化的执行环境,确保合约在全球所有节点上的一致性。
  1. 原生代币
    • 以太坊平台的原生代币是以太(ETH),用于支付交易费用和计算资源。用户在调用智能合约时需要支付“Gas”费用。
  1. 生态系统
    • 以太坊拥有丰富的生态系统,包括去中心化金融(DeFi)、非同质化代币(NFT)、游戏和社交平台等,吸引了大量开发者和用户。
  1. 发展方向
    • 以太坊已经进行2.0版本更新,采用权益证明(PoS)机制,提高交易速度和降低能耗,同时增强安全性和可扩展性。

4. 智能合约应用场景

4.1. 金融服务

  • 去中心化金融(DeFi)

    • 智能合约使得金融产品(如借贷、交易所、衍生品等)可以在去中心化平台上自动执行,无需传统金融中介。用户可以直接在区块链上进行交易,降低费用和提高效率。
  • 自动化清算与结算

    • 在金融交易中,智能合约可以自动处理结算流程,确保交易各方在条件达成时自动进行资金转移和清算,减少人工干预和错误。
  • 保险理赔

    • 智能合约可用于保险行业,自动验证理赔请求并在条件满足时自动支付。这可以加快理赔流程,提高客户满意度。

4.2. 供应链管理

  • 透明度与追踪

    • 智能合约可以记录每个供应链环节的信息,如原材料来源、生产过程、运输状态等。所有参与方都可以实时查看数据,增强透明度和信任。
  • 自动执行合同条款

    • 在供应链中,智能合约能够自动触发付款和交货等操作。例如,当货物到达指定地点时,系统会自动释放付款,确保交易顺利进行。
  • 防止伪造与欺诈

    • 通过在区块链上记录产品的每个环节,智能合约可以帮助品牌防止伪造和欺诈行为,维护品牌声誉。

4.3. 投票系统

  • 安全与透明

    • 智能合约可以用于构建去中心化的投票系统,确保每一票的记录都是安全和透明的。投票结果可在区块链上实时更新,确保投票过程的公正性。
  • 减少舞弊行为

    • 由于区块链的不可篡改性,智能合约能够有效防止投票舞弊,确保每位合格选民只能投一次票,且投票记录无法被修改。
  • 快速结果统计

    • 智能合约能够自动计算和发布投票结果,减少传统投票系统中的人工统计时间,提高效率

5. 以太坊智能合约

访问下面任一地址即可

1
2
3
https://remix.ethereum.org/

https://remix.ethereum.org/#lang=en&optimize=false&runs=200&evmVersion=null&version=soljson-v0.8.26+commit.8a97fa7a.js

来到合约界面

image-20241026144008306

contracts文件夹下有3个.sol文件,就是官方给初学者准备的3个最基础的合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.8.2 <0.9.0;

/**
* @title Storage
* @dev Store & retrieve value in a variable
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
*/
contract Storage {

uint256 number;

/**
* @dev Store value in variable
* @param num value to store
*/
function store(uint256 num) public {
number = num;
}

/**
* @dev Return value
* @return value of 'number'
*/
function retrieve() public view returns (uint256){
return number;
}
}

Solidity 合约通常包含以下几个主要部分:

  1. SPDX 许可标识:指定代码的开源许可。
  2. pragma 指令:声明 Solidity 版本。
  3. 导入语句:引入其他合约或库。
  4. 合约声明:使用 contract 关键字。
  5. 状态变量:存储在区块链上的持久数据。
  6. 函数:合约的可执行代码单元。

合约写好了我们对合约进行编译

image-20241026144509165

编译完后,点击左侧部署,来到部署页面,点击deploy,在最下方会出现Deployed Contracts

image-20241026144411259

点击收缩符号,就可以看到合约里拥有的方法,点击就可以对合约方法进行调用。

image-20241026144552743

6. 智能合约系统

项目是根据开源项目xxl-job进行修改完成的,项目地址:https://gitee.com/daitoulin/contract_admin.git

我们先配置数据库

image-20241026145656398

导入前面第一部分区块链交易学习的项目代码中的sql文件

image-20241026160726959

我们查看下,成功导入

image-20241026160801151

6.1. 启动项目

application.properties配置文件(在contract_admin/xxl-job-admin/src/main/resources/application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
### web
server.port=7896
server.servlet.context-path=/xxl-job-admin

### actuator
management.server.servlet.context-path=/actuator
management.health.mail.enabled=false

### resources
spring.mvc.servlet.load-on-startup=0
spring.mvc.static-path-pattern=/static/**
spring.resources.static-locations=classpath:/static/

### freemarker
spring.freemarker.templateLoaderPath=classpath:/templates/
spring.freemarker.suffix=.ftl
spring.freemarker.charset=UTF-8
spring.freemarker.request-context-attribute=request
spring.freemarker.settings.number_format=0.##########
spring.freemarker.settings.new_builtin_class_resolver=safer

### mybatis
mybatis.mapper-locations=classpath:/mybatis-mapper/*Mapper.xml
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.type-aliases-package=com.xxl.job.admin.core.model

### xxl-job, datasource
spring.datasource.url=jdbc:mysql://localhost:3306/xxl_job?useUnicode=true&useSSL=false&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

### datasource-pool
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.maximum-pool-size=30
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=900000
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.validation-timeout=1000

### xxl-job, email
spring.mail.host=smtp.qq.com
spring.mail.port=25
spring.mail.username=xxx@qq.com
spring.mail.from=xxx@qq.com
spring.mail.password=xxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory

### xxl-job, access token
xxl.job.accessToken=abbbq.bbc!

### xxl-job, i18n (default is zh_CN, and you can choose "zh_CN", "zh_TC" and "en")
xxl.job.i18n=zh_CN

## xxl-job, triggerpool max size
xxl.job.triggerpool.fast.max=200
xxl.job.triggerpool.slow.max=100

### xxl-job, log retention days
xxl.job.logretentiondays=15

node.ip=192.168.132.1

开启节点项目,观察节点是否成功注册到合约系统

image-20241026161440294

访问http://127.0.0.1:7896/xxl-job-admin/后,使用admin:123456登录

image-20241026161523266

6.2. 创建合约

点击任务管理,然后点击右上角新增

需要填写以下内容

1
2
3
4
5
6
任务描述:随意填写
负责人:随意填写
//私钥和钱包地址前面的在在交易项目已经获取
私钥:区块链项目那边生成的ETH钱包私钥
钱包地址:区块链项目那边生成的ETH钱包地址
运行模式:选择GLUE(Java)我们要用java语言来写智能合约

image-20241026163651849

新增此合约对应的接口,检查上述输入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@RequestMapping("/add")
@ResponseBody
public ReturnT<String> add(XxlJobInfo jobInfo) {


jobInfo.setScheduleType("NONE");

String privateKey = jobInfo.getPrivateKey();
String walletAddress = jobInfo.getWalletAddress();

if (privateKey == null || "".equals(privateKey)){
return new ReturnT<String>(ReturnT.FAIL_CODE,"私钥不能为空");
}

if (walletAddress == null || "".equals(walletAddress)){
return new ReturnT<String>(ReturnT.FAIL_CODE,"钱包地址不能为空");
}

Credentials credentials = Credentials.create(privateKey);
// 获取 Ethereum 地址
String address = credentials.getAddress();

if (!address.equals(walletAddress)){
return new ReturnT<String>(ReturnT.FAIL_CODE,"私钥与钱包地址不匹配,请检查后再试");
}


TContract tContract = new TContract();
tContract.setContractContent(jobInfo.getGlueSource());

tContract.setWalletAddress(walletAddress);
String time = DateUtil.formatDateTime(new Date());
tContract.setCreateTime(time);


String s = encryptSHA1(walletAddress + time);
tContract.setContractAddress(s);
System.out.println(s);

ReturnT<String> add = xxlJobService.add(jobInfo);
tContract.setJobId(add.getContent());
contractDao.save(tContract);

System.out.println("jobId:" + add.getContent());

return add;
}

6.3. 修改合约内容

点击GLUE IDE到合约修改页面

image-20241026164040215

合约内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.xxl.job.service.handler;

import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.IJobHandler;
import java.util.*;
import com.example.blockchain.service.impl.ContractServiceImpl;
import javax.annotation.Resource;
import com.example.blockchain.entity.TradeObject;
import com.google.gson.Gson;
import com.example.blockchain.entity.TContract;

public class DemoGlueJobHandler extends IJobHandler {

@Resource
private ContractServiceImpl contractServiceImpl;


@Override
public void execute() throws Exception {
XxlJobHelper.log("XXL-JOB, Hello World.");
String Param = XxlJobHelper.getJobParam();
HashMap<String, Object> map = contractServiceImpl.jsonToMap(Param);
System.out.println(map);
XxlJobHelper.log(map.toString());
TradeObject trade = new Gson().fromJson(map.get("tradeObject"),TradeObject.class);
TContract contract = new TContract();
String data = "";

trade.setDataStr(data);
contract.setId(map.get("contractId"));
contract.setData(data);
contract.setLastData(trade.getLastData());
contractServiceImpl.setData(contract);
contractServiceImpl.toChain(trade);
}

}

6.4. 调用合约

在需要执行的合约点击右边的操作,点击执行一次。然后在任务参数这里我们需要输入privateKey,以及你想传递给合约的参数,需要以json形式书写。

1
2
3
4
{
"privateKey":"ced6cc51ee0d2063211564e53012aaac4c0b4978e8cc22a11ed998de4e4bcc3b",
"test":"123456"
}

image-20241026164544315

6.5. 合约案例

  1. 最简单的存储参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    package com.xxl.job.service.handler;

    import com.xxl.job.core.context.XxlJobHelper;
    import com.xxl.job.core.handler.IJobHandler;
    import java.util.*;
    import com.example.blockchain.service.impl.ContractServiceImpl;
    import javax.annotation.Resource;
    import com.example.blockchain.entity.TradeObject;
    import com.google.gson.Gson;
    import com.example.blockchain.entity.TContract;

    public class DemoGlueJobHandler extends IJobHandler {

    @Resource
    private ContractServiceImpl contractServiceImpl;


    @Override
    public void execute() throws Exception {
    XxlJobHelper.log("XXL-JOB, Hello World.");
    String Param = XxlJobHelper.getJobParam();
    HashMap<String, Object> map = contractServiceImpl.jsonToMap(Param);
    System.out.println(map);
    XxlJobHelper.log(map.toString());
    TradeObject trade = new Gson().fromJson(map.get("tradeObject"),TradeObject.class);
    TContract contract = new TContract();
    String data = "";

    data = map.get("printData");



    trade.setDataStr(data);
    contract.setId(Integer.valueOf(map.get("contractId")));
    contract.setData(data);
    contract.setLastData(trade.getLastData());
    contractServiceImpl.setData(contract);
    contractServiceImpl.toChain(trade);
    }

    }

    按前文执行一次调用合约,然后区块浏览器查看上链数据

  2. 多函数调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package com.xxl.job.service.handler;

    import com.xxl.job.core.context.XxlJobHelper;
    import com.xxl.job.core.handler.IJobHandler;
    import java.util.*;
    import com.example.blockchain.service.impl.ContractServiceImpl;
    import javax.annotation.Resource;
    import com.example.blockchain.entity.TradeObject;
    import com.google.gson.Gson;
    import com.example.blockchain.entity.TContract;

    public class DemoGlueJobHandler extends IJobHandler {

    @Resource
    private ContractServiceImpl contractServiceImpl;


    @Override
    public void execute() throws Exception {
    XxlJobHelper.log("XXL-JOB, Hello World.");
    String Param = XxlJobHelper.getJobParam();
    HashMap<String, Object> map = contractServiceImpl.jsonToMap(Param);
    System.out.println(map);
    XxlJobHelper.log(map.toString());
    TradeObject trade = new Gson().fromJson(map.get("tradeObject"),TradeObject.class);
    TContract contract = new TContract();
    String data = "";


    String method = map.get("methodName");
    if(method.equals("initMethod")){
    String initString = initMethod();
    data = initString;
    }
    if(method.equals("others")){
    data = "others method"
    }


    trade.setDataStr(data);
    contract.setId(Integer.valueOf(map.get("contractId")));
    contract.setData(data);
    contract.setLastData(trade.getLastData());
    contractServiceImpl.setData(contract);
    contractServiceImpl.toChain(trade);
    }


    public String initMethod(){
    return "initMethod";
    }


    }
  3. 抽奖程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    package com.xxl.job.service.handler;

    import com.xxl.job.core.context.XxlJobHelper;
    import com.xxl.job.core.handler.IJobHandler;
    import java.util.*;
    import com.example.blockchain.service.impl.ContractServiceImpl;
    import javax.annotation.Resource;
    import com.example.blockchain.entity.TradeObject;
    import com.google.gson.Gson;
    import com.example.blockchain.entity.TContract;

    public class DemoGlueJobHandler extends IJobHandler {

    @Resource
    private ContractServiceImpl contractServiceImpl;


    @Override
    public void execute() throws Exception {
    XxlJobHelper.log("XXL-JOB, Hello World.");
    String Param = XxlJobHelper.getJobParam();
    HashMap<String, Object> map = contractServiceImpl.jsonToMap(Param);
    System.out.println(map);
    XxlJobHelper.log(map.toString());
    TradeObject trade = new Gson().fromJson(map.get("tradeObject"),TradeObject.class);
    TContract contract = new TContract();
    String data = "";


    List<String> participants = map.get("participants");


    // 生成随机数并确定中奖者
    Random random = new Random();
    int index = random.nextInt(participants.size());
    String winner = participants.get(index);
    data = winner;


    trade.setDataStr(data);
    contract.setId(Integer.valueOf(map.get("contractId")));
    contract.setData(data);
    contract.setLastData(trade.getLastData());
    contractServiceImpl.setData(contract);
    contractServiceImpl.toChain(trade);
    }

    }