实验1 会议报名登记系统的基本功能与实现

实验流程

系统功能要求:

合约参与方包含一个管理员以及其余参与者,管理员可以发起不止一个会议,并指定会议信息以及总人数。

参与者首先需要进行注册,将个人基本信息与以太坊地址相关联,并存储在合约上。

之后可进行报名,或委托他人为自己报名。

当会议报名人满时,该会议将不再可报名。

当合约内某些数据发生变化时,应能够触发事件(event)使前端重新获取并渲染数据,例如当某个会议报名人满时,应触发相应事件使前端及时更新可报名会议列表。

按功能要求与模板文件补全代码中的委托函数及为受托者报名函数。

delegate函数中建立受托人到其委托人的一个映射,然后在enrollFor函数中实现受托人为委托人报名功能,首先由username找到受托人,后续报名流程仿写enroll,更换报名对象即可,该部分代码如下:

function delegate(address addr) public{
trustees[addr].push(participants[msg.sender]);
}
function enrollFor(string memory username,string memory title) public returns(string memory){
uint index = 0;
for (uint i = 0; i < trustees[msg.sender].length; i++) {
if (keccak256(bytes(trustees[msg.sender][i].name)) == keccak256(bytes(username))) {
index = i;
break;
}
}
for (uint i = 0; i < conferences.length; i++){
if (keccak256(bytes(conferences[i].title)) == keccak256(bytes(title))){
require(conferences[i].current<conferences[i].max,"Enrolled full");
conferences[i].current = conferences[i].current+1;
if(conferences[i].current==conferences[i].max){
emit ConferenceExpire(title);
}
trustees[msg.sender][index].confs.push(title);
}
}
uint len = trustees[msg.sender][index].confs.length;
require(len>0,"Conference does not exist");
return trustees[msg.sender][index].confs[len-1];
}

练习1

  • 应在合约的哪个函数指定管理员身份?如何指定?

    在合约的构造函数中指定,用address public admin指定admin为管理员

  • 在发起新会议时,如何确定发起者是否为管理员?简述require()、assert()、revert()的区别。

    判别语句:require(msg.sender==admin,"permission denied");

    如下三个语句与if(msg.sender != owner) { throw; }效果等价

    if(msg.sender != owner) { revert(); } 

    assert(msg.sender == owner);

    require(msg.sender == owner);

    revert()处理与require()同样的类型,但是需要更复杂处理逻辑的场景,二者都会将剩余gas费返还给调用者,而assert()不返还gas费,所以一般很少用assert()

  • 简述合约中用memory和storage声明变量的区别。

    Storage 变量是指永久存储在区块链中的变量。Memory变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。

    状态变量(在函数之外声明的变量)默认为storage形式,并永久写入区块链;而在函数内部声明的变量默认是memory型的,它们函数调用结束后消失。
    image-20221020202338471

实验2 学习用Truffle组件部署和测试合约

安装Truffle和Ganache

按照教程按照即可

为合约编写测试文件

pragma solidity >=0.4.25 <0.7.0;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Enrollment.sol";

contract TestEnrollment{
//测试注册后,是否返回正确注册信息(仅测试name即可)
function testSignUp() public {
Enrollment en = new Enrollment();
(string memory name,) = en.signUp("alice","male");
(string memory expected_name,) = ("alice","male");
Assert.equal(name,expected_name,"signup failed");
}

//测试管理员添加新会议是否成功
function testNewConf() public{
Enrollment enroll = new Enrollment();
string memory expected = "conf1";
Assert.equal(enroll.newConference("conf1","beijing",30),expected,"new conference failed");
}

//请测试enroll函数,确保当用户报名后,其已报名会议列表中有该会议。提示:不要忘记先由管理员创建会议。
function testEnroll() public{
Enrollment en = new Enrollment();
string memory expected="conf1";
en.newConference("conf1","beijing",30);
Assert.equal(en.enroll("conf1"),expected,"Enrolled full");
}
}

进行测试truffle test

image-20221020203004359

用Ganache搭建私链

测试后重新搭建一条私链

image-20221020150810278

进行合约部署truffle migrate,成功部署

image-20221020151013332

合约部署后的记录

image-20221020151107896 image-20221020151126533 image-20221020151155170 image-20221020151220521

合约部署的主要流程包括以下4个步骤:

(1)启动一个以太坊节点

(2)编写智能合约

(3)编写后的智能合约经以太坊虚拟机的编译,成为计算机可运行的字节码

(4)合约发起用户将编译好的字节码文件通过发起交易的形式广播到区块链网络中,由矿工挖矿确认后即可将智能合约存入区块链中,并得到智能合约所在地址及调用合约所需接口

在节点触发智能合约条件需要使用合约时,区块链将调取智能合约字节码在本地运行,然后将运行结果再保存入区块链账本中。

另外,用户还可以通过web3接口调用智能合约,构建与之交互的Web应用,该应用由于完全构建于区块链之上,不需要第三方中介来提供服务,被称为去中心化应用(DAPP)。
img

实验3 利用Web3.js实现合约与前端的结合

配置Metamask

首先按照指导书配置Metamask

image-20221020204042069

在前端项目文件中配置合约信息

配置src/contracts/contract.js中的abi和合约地址

完成组件代码中交互代码

仿照SignUp完成其他组件的交互代码

delegate

submit(address) {
//调用合约

contract.methods.delegate(address)
.send({from:window.web3.eth.accounts[0]},function(err,res){console.log(res)}) //function中的res为方法返回值
.then((res)=>console.log(res)); //该res为交易执行完后的具体交易信息,如TxHash等

enrollFor

submit(username,title) {
//在此调用合约
contract.methods.enrollFor(username,title) //输入参数
.send({from:window.web3.eth.accounts[0]},function(err,res){console.log(res)}) //function中的res为方法返回值
.then(); //该res为交易执行完后的具体交易信息,如TxHash等

myConf

componentDidMount(){
//学习conflist/index.js该位置代码进行实现。

contract.methods.queryMyConf()
.call({from:window.web3.eth.accounts[0]},(err,res)=>{
//将返回的数组依次压入data中
this.setState({loading: true});
if(res != null){
for(var i=0;i<res.length;i=i+1){
data.push({'title': res[i]});
}
}
else{
data.push({'title': 'no'});
}
})
.then(()=>{
//更新状态,使页面数据重新渲染
this.setState({loading: false});
});

}

运行效果

npm installnpm start后,前端出现:

image-20221020204914717

绑定两个账户

image-20221020204937706

注册用户

image-20221020205128647

报名会议并支付花费后

image-20221020205235244

为usr2报名会议

image-20221020205305959

上课检查时候似乎是myconf写的有问题。。前端刷新不出来