目录
“区块链技术应用” 赛项赛卷(5卷)
模块一:区块链产品方案设计及系统运维(35分)
任务1-1:区块链产品需求分析与方案设计
任务1-2:区块链系统部署与运维
任务1-3:区块链系统测试
模块二:智能合约开发与测试(30分)
任务2-1:智能合约设计
任务2-2:智能合约开发
任务2-3:智能合约测试
模块三:区块链应用系统开发(30分)
任务3-1:区块链电子签章前端功能开发
任务3-2:区块链应用系统后端开发
2023年全国职业院校技能大赛
高职组
任
务
书
背景描述
电子签章可实现与纸质文件盖章操作相似的可视效果,以保障数据来源的真实性、数据完整性以及签名人行为的不可否认性。
传统的电子签章系统是基于中心化的,也就是数据是集中存储在中心数据库中,这就导致传统电子签章使用记录存在被篡改、伪造的风险。
而区块链电子签章系统在传统电子签章系统的基础上,可以借助于区块链技术,用不可篡改、不可抵赖的方式记录电子签章的整个流转过程,如领取、使用、查询等,从而避免传统电子签章系统存在的潜在风险。
某市政务部门拟开发一款区块链电子签章系统,该区块链电子签章系统包含为印章使用者、监管、平台运营方的管理功能,场景开放平台、印章底层引擎和区块链基座。场景开放平台面向各类政务系统与第三方电子签名服务商提供电子签章相关的开放能力。场景开放平台对上提供相关的基础能力,对下连接区块链基座。
选手完成本模块的任务后,将任务中设计结果、运行代码、运行结果等截图粘贴至客户端桌面【区块链技术应用赛\重命名为工位号\模块一提交结果.docx】中对应的任务序号下。
本任务需要依据项目背景完成需求分析与方案设计,具体要求如下:
1.依据项目背景中给定的区块链电子印章系统业务架构图,进行业务分析,,使用工具展现本系统的基本设计概念和处理流程,要求分为区块链电子印章业务平台和支撑平台两个部分;
2.根据电子印章系统设计,在系统中分别包括用户和签章管理功能。根据描述,设计区块链电子签章系统的总体功能概览图;
3.根据电子印章系统设计,请设计区块链电子签章系统通过智能合约确保用户隐私和数据保密的方案;
4.根据电子印章系统设计,请设计区块链电子签章系统中的身份验证和授权管理具体方案。
根据项目需求背景完善方案设计,形成完整的概要设计说明书,并保存至【区块链技术应用赛\重命名为工位号】下。
围绕电子签章区块链平台部署与运维需求,进行项目相关系统、节点以及管理工具的部署工作。通过监控工具完成对网络、节点服务的监控。最终利用业务需求规范,完成系统日志、网络参数、节点服务等系统结构维护。
1. 登陆Linux服务器,安装并部署单机、四机构、三群组、八节点的星形组网拓扑区块链系统;
2. 登陆Linux服务器,安装并部署区块链系统控制台,检查部署控制台是否正常运行;
3. 登录Linux服务器,部署区块链管理前置平台;
4. 登陆Linux服务器,使用终端生成新的节点,并且将该节点加入对应群组然后启动节点。
子任务1-2-1: 登陆Linux服务器,安装并部署单机、四机构、三群组、八节点的星形组网拓扑区块链系统,具体工作内容如下:
子任务1-2-2:登陆Linux服务器,安装并部署区块链系统控制台,检查部署控制台是否正常运行,具体工作内容如下:
子任务1-2-3:登录Linux服务器,部署区块链管理前置平台,具体内容如下
子任务1-2-4:登陆Linux服务器,使用终端生成新的节点,并且将该节点加入对应群组然后启动节点,具体内容如下:
设计对区块链系统的测试流程;结合实际业务需求,调用部署的智能合约中进行系统测试、性能测试等;根据业务需求,分析并且修复给定智能合约中的安全漏洞。利用模拟业务和测试工具来完成对区块链系统服务数据的测试。
1. 基于给定脚本完成区块链管理平台部署以及结果验证,最后将执行结果截图保存。
有如下智能合约:
contract EtherGame {
uint public targetAmount = 7 ether;
address public winner;
function deposit() public payable {
require(msg.value == 1 ether, "You can only send 1 Ether");
uint balance = address(this).balance;
require(balance <= targetAmount, "Game is over");
if (balance == targetAmount) {
winner = msg.sender;
}
}
function claimReward() public {
require(msg.sender == winner, "Not winner");
(bool sent, ) = msg.sender.call{value: address(this).balance}("");
require(sent, "Failed to send Ether");
}
}
contract Attack {
EtherGame etherGame;
constructor(EtherGame _etherGame) {
etherGame = EtherGame(_etherGame);
}
function attack() public payable {
address payable addr = payable(address(etherGame));
selfdestruct(addr);
}
}
选手完成本模块的任务后,将任务中设计结果、运行代码、运行结果等截图粘贴至客户端桌面【区块链技术应用赛\重命名为工位号\模块二提交结果.docx】中对应的任务序号下。
根据区块链电子签章应用需求分析和方案设计文档的描述,编写该区块链产品的智能合约功能需求文档,以及设计该智能合约UML图,具体要求如下:
1.编写区块链电子签章智能合约功能需求文档
根据区块链电子签章产品项目背景和需求分析,编写该区块链电子签章产品的智能合约功能需求文档。
2.完成区块链电子签章智能合约的设计图
设计区块链电子签章智能合约接口,画出区块链电子签章智能合约的角色UML用例图,以及画出区块链电子签章智能合约关系的时序图。
使用Solidity语言完成智能合约开发、部署和调用,要求如下:
1.个人签章信息接口编码
(1)编写个人签章智能合约的实体接口,完成实体通用数据的初始化,实现签章和用户实体信息上链的功能;
struct SealInfo {
//①姓名
//②身份证
//③印章数据
//④创建印章时间戳
}
(2)编写签章信息上链的接口,实现Seal合约的构造函数;
//@dev构造函数
// @param _name姓名
// @param _cardId身份证号码
// @param _data 个人印章数据
// @param _datetime 账户注册时间
constructor(string _name,string _cardId,string _data,string _datetime) public {
//①用户姓名
//②用户身份ID
//③用户数据
//④时间戳
isUsed = true;
}
(3)基于给定的智能合约代码以及注释,完成ElectronicSeal合约判断多人签章文件编号是否存在的函数。
//@dev 判断多人签名文件编号是否存在
//@param _code 签章文件编号
//@return bool true:存在 false:不存在
function isExistMultiCode(string _code) view internal returns(bool) {
//①遍历所有多人签章文件编号并与传入签章文件编号参数进行比较
//②返回不存在
}
2.电子印章接口编码
(1)基于给定的智能合约代码以及注释,完成ElectronicSeal合约获取多人签章信息函数;
//@dev 获取多人签章信息
//@param _code 签章文件编号
//@return (签章文件摘要、多人签章发起人账户地址、签章文件编号、签章人数、签章账户地址列表)
function getMultiSingInfo(string _code) public view onlyPromoterOrSinger returns(string,address,string,uint,address[] memory) {
//①获取多人签章合约对象实例
//②返回多人签章信息
}
(2)基于给定的智能合约代码以及注释,完成ElectronicSeal合约多人签章函数。
//@dev 多人签章
//@param _address 签章人账户地址
//@param _hash 签章文件摘要
//@param _datatime 签章时间戳
function doMultiSign(address _address,string _hash,string _datetime) public onlyPromoter {
//①判断传入的签章人账户地址不能为空
//②获取多人签章合约对象实例
//③获取当前等待签章账户地址
//④判断合约调用者是否为等待签章人账户地址
//⑤进行签章
}
(1)解决代码错误和警告,正确编译并部署合约,成功获取部署的合约地址和ABI;
(2)调用区块链电子签章智能合约的接口,完整验证业务流程。
根据已完成的智能合约,针对开发功能开展相关合约测试工作,具体工作内容如下:
1.请基于前端系统的开发模板,在注册页面中添加对应的代码逻辑,实现对前端系统的注册功能,并测试功能完整性,示例页面如下;
题目的具体要求如下:
register.html
代码片段1:
选手填写部分>
代码片段2:
代码片段3:
methods: {
submit() {
if (this.user.username == '') {
alert("用户名不能为空")
return
}
if (this.user.password == '') {
alert("密码不能为空")
return
}
if (this.user.chainAccount == '') {
alert("区块链账户地址不能为空")
return
}
if (this.user.name == '') {
alert("姓名不能为空")
return
}
if (this.user.cardId == '') {
alert("身份证号码不能为空")
return
}
if (this.user.phone == '') {
alert("电话号码不能为空")
return
}
选手填写部分
}
}
2.请基于前端系统开发模板,在登录页面中添加对应的代码逻辑,实现前端系统登录功能,并测试功能完整性,示例页面如下:
本题目的具体要求如下:
login.html
代码片段1:
代码片段2:
//创建Vue实例
new Vue({
el: '#app', //el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
data: { //data中用于存储数据,数据供el所指定的容器去使用,值我们暂时先写成一个对象。
user: {
选手填写部分
},
tip: false,
msg: ""
},
methods: {
submit() {
if (this.user.username == '') {
this.msg = "用户名不能为空"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
return
}
if (this.user.password == '') {
this.msg = "密码不能为空"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
return
}
axios.post("http://localhost/user/login", this.user).then(response => {
if (response.data.resultCode == 200) {
this.msg = "登录成功"
this.tip = true
setTimeout(() => {
选手填写部分
}, 1000)
选手填写部分
} else {
this.msg = "登录失败"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
}
})
},
}
})
3.请基于前端系统的开发模板,在个人印章页面中添加对应的页面代码逻辑,实现对前端系统的个人印章功能,并测试功能完整性,示例页面如下:
具体要求如下:
myseal.html
代码片段1:
代码片段2:
methods:{
logout() {
选手填写部分
}
}
代码片段3:
// 请求发前拦截,header中添加 token
axios.interceptors.request.use(config => {
config.headers.Authorization =选手填写部分
return config
})
代码片段4:
axios.get("http://localhost/user/seal").then(response => {
if (response.data.resultCode == 200) {
this.seal =选手填写部分
}
})
4.请基于前端系统的开发模板,请在文件签章页面中添加对应的页面代码逻辑,实现对前端系统的文件签章业务功能,并测试功能完整性,示例页面如下:
具体要求如下:
signature.html
代码片段1:
signature() {
if (this.status == 0) {
this.msg = "请上传文档"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
return
}
this.status = 2
var canvas;
canvas =选手填写部分
canvas.width = $("#poster").width()
canvas.height = $("#poster").height()
var context = canvas.getContext("2d");
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#fff";
context.fill();
代码片段2:
uploadSeal() {
if (this.status != 2) {
this.msg = "文档未上传或未签章"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
return
}
var posterImg = document.getElementById('poster');
var imgBase64 = posterImg.getAttribute("src");
axios.post("选手填写部分",
{
"filename": this.filename,
"imgBase64": imgBase64,
}
).then(response => {
if (response.data.resultCode == 200) {
this.msg = "签章文档上链成功"
this.tip = true
setTimeout(() => {
this.tip = false
window.location.href = "main.html";
}, 1000)
} else {
this.msg = "签章文档上链失败"
this.tip = true
setTimeout(() => {
this.tip = false
}, 1000)
}
})
}
代码片段3:
mounted() {
// 请求发前拦截,header中添加 token
axios.interceptors.request.use(request => {
request.headers.Authorization = localStorage.getItem("Authorization")
return request
})
//响应拦截器(认证授权)
axios.interceptors.response.use(response => {
if (response.data.resultCode == 401) {
//跳转页面
window.location.href = "login.html";
}
return response
})
//TODO:【5-1-4-3】请补充向服务器发送获取个人印章请求代码
axios.get("选手填写部分").then(response => {
if (response.data.resultCode == 200) {
this.seal =选手填写部分
}
})
}
5.请基于前端系统开发模板,在文件验章页面中添加对应的页面代码逻辑,实现对前端系统的文件验章业务功能,并测试功能完整性,示例页面如下:
具体要求如下:
verity.html
代码片段1:
代码片段2:
async verify(event) { //同步发送 axios 请求(获取数据后弹出模态框)
if (this.status == 0) {
this.msg = "请上传文档"
this.tip = true
event.stopPropagation() //阻止事件冒泡(防止弹出模态框)
setTimeout(() => {
this.tip = false
}, 1000)
return
}
await axios.post("选手填写部分", {"imgBase64":this.contract}).then(response => {
if (response.data.resultCode == 200) {
this.verifyInfo = response.data.data
}
})
}
1.基于后端系统的开发模板,补充对应的代码逻辑,完成后端代码逻辑,实现对后端系统的注册功能,并测试功能完整性,题目具体要求如下:
userService.java
代码片段1:
public boolean register(RegisterBean registerBean) {
//校验用户名或账户地址是否重复
QueryWrapper
queryWrapper.eq("username", registerBean.getUsername())
.or().eq("chain_account",registerBean.getChainAccount());
UserEntity userOne =选手填写部分
if (userOne != null) {
log.info("注册失败:用户名或账户地址重复");
return false;
}
//拷贝对象
UserEntity userEntity = new UserEntity();
BeanUtils.copyProperties(registerBean, userEntity);
userEntity.setPassword(选手填写部分);//md5加密密码
//生成印章(Base64)
byte[] buffer = Seal.getSealImg(registerBean.getUsername(), registerBean.getName());
Encoder encode = Base64.getEncoder();
String sealImg =选手填写部分
userEntity.setSealImg(sealImg);
//用户注册信息上链
List funcParams = new ArrayList<>();
funcParams.add(registerBean.getChainAccount());
funcParams.add(registerBean.getName());
funcParams.add(registerBean.getCardId());
funcParams.add(String.valueOf(System.currentTimeMillis()));
JSONObject res = HttpUtils.writeContract("addSealAccount", funcParams);
if (res == null) {
log.info("注册失败:用户注册信息上链失败");
return false;
}
int result =选手填写部分
return result > 0 ? true : false;
}
2.基于后端系统的开发模板,补充代码完成对应的后端代码逻辑,实现后端系统登录功能,并测试功能完整性,具体要求如下:
UserController.java
代码片段1:
public AjaxResult
log.info(loginBean.toString());
if (userService.isLogin(loginBean)) {
//创建
String token = TokenUtil.sign(loginBean.getUsername(),String.valueOf(System.currentTimeMillis()));
AjaxResult
result.setData(选手填写部分);
return result;
} else {
return new AjaxResult
}
}
UserService.java
代码片段1:
public boolean isLogin(LoginBean loginBean) {
QueryWrapper
//TODO:请补充 queryWrapper 添加用户名和密码两个数据库查询条件代码
queryWrapper.eq(选手填写部分)
.eq(选手填写部分);
//TODO:请补充数据库查询登录用户信息代码
UserEntity userEntity =选手填写部分
return userEntity != null ? true : false;
}
3.请基于后端系统的开发模板,补充代码完成对应的后端代码逻辑,实现后端系统账户信息查看功能,并测试功能完整性,具体要求如下:
UserController.java
代码片段1:
@ApiOperation(value = "用户信息",notes = "获取用户详细信息")
@GetMapping("/user/info")
public AjaxResult
String token =选手填写部分
String username =选手填写部分
RegisterBean registerBean = userService.getUser(username);
AjaxResult
result.setData(registerBean);
return result;
}
UserService.java
代码片段1:
public RegisterBean getUser(String username) {
QueryWrapper
queryWrapper.eq("username", username);
UserEntity userEntity =选手填写部分
RegisterBean registerBean = new RegisterBean();
选手填写部分
return registerBean;
}
4.请基于后端系统的开发模板,补充代码完成对应的后端代码逻辑,实现对后端系统的文件签章功能,并测试功能完整性,具体要求如下:
TokenInterceptor.java
代码片段1:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setCharacterEncoding("utf-8");
String token = request.getHeader("Authorization");
log.info("token:"+token);
if (token != null) {
boolean result =选手填写部分
if (result) {
log.info("通过拦截器");
return true;
}
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try {
JSONObject json = new JSONObject();
json.put("message", "认证失败,未通过拦截器");
json.put("resultCode", "401");
response.getWriter().append(json.toJSONString());
log.info("认证失败,未通过拦截器");
} catch (Exception e) {
e.printStackTrace();
response.sendError(500);
return false;
}
return false;
}
SealService.java
代码片段1:
public boolean signature(ContractBean contractBean, String chainAccount) {
//获取签名文档uuid编码
String code =选手填写部分
SealEntity sealEntity = new SealEntity();
BeanUtils.copyProperties(contractBean, sealEntity);
String[] str = contractBean.getFilename().split("\\.");
sealEntity.setFilename(str[0]);
sealEntity.setType(str[1]);
sealEntity.setCode(code);
//签章记录上链
try {
//获取印章文档Hash值
MessageDigest messageDigest =选手填写部分
byte[] bytes = messageDigest.digest(contractBean.getImgBase64().getBytes("UTF-8"));
String hash = Hex.toHexString(bytes);
log.info("hash:" + hash);
List funcParams = new ArrayList<>();
funcParams.add(chainAccount);
funcParams.add(hash);
funcParams.add(code);
funcParams.add(String.valueOf(System.currentTimeMillis()));
JSONObject res =选手填写部分
if (res == null) {
log.info("印章记录上链失败");
}
} catch (Exception e) {
e.printStackTrace();
}
int result = sealMapper.insert(sealEntity);
return result > 0 ? true : false;
}
5.请基于后端系统的开发模板,补充代码完成对应的后端代码逻辑,实现对后端系统的文件验章功能,并测试功能完整性,具体要求如下:
SealController.java
代码片段1:
public AjaxResult
String imgBase64 = imgBase64Bean.getImgBase64();
String token = request.getHeader("Authorization");
String username = TokenUtil.getLoginName(token);
RegisterBean registerBean = userService.getUser(username);
VerifyBean verifyBean =选手填写部分
if (verifyBean == null){
return new AjaxResult
}
AjaxResult
result.setData(verifyBean);
return result;
}
SealService.java
代码片段1:
public VerifyBean verify(String chainAccount, String imgBase64) {
VerifyBean verifyBean = new VerifyBean();
verifyBean.setChainAccount(chainAccount);
//获取账户信息
List params = new ArrayList();
params.add(chainAccount);
JSONArray accountInfo =选手填写部分
if (accountInfo == null) {
log.info("获取区块链账户信息失败");
return null;
}
List
String name = list.get(0);
String cardId = list.get(1);
verifyBean.setName(name);
verifyBean.setCardId(cardId);
try {
//获取印章文档Hash值
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] bytes = messageDigest.digest(imgBase64.getBytes("UTF-8"));
String hash = Hex.toHexString(bytes);
log.info("hash:"+hash);
//获取印章信息
List funcParams = new ArrayList();
funcParams.add(chainAccount);
funcParams.add(hash);
JSONArray sealInfo =选手填写部分
if (sealInfo == null) {
log.info("获取区块链印章信息失败");
return null;
}