出价信息的数据结构
现在,设计一个新的数据结构来保存买家的密封出价。可以创建一个结构体() 来保存密封出价信息:
注意,结构体里面的字段是竞价者支付给合约的保证金,而不是其真实出价: 真实出价目前是不可知的,我们暂时只知道转入合约的保证金数量,用它来填充 字段。
为了方便在拍卖结束后筛选赢家、返还投标保证金等操作,我们可以在 结构构中加入一个新的成员,用来记录一个商品的所有竞价信息:
状态是一个嵌套的映射表,它将竞价者的账户地址映射到买家对该商品的 密封出价表中。为了便于理解,同样不妨把它视为如下的二维表格:
提交密封出价
在竞价期间,买家调用合约的密封出价方法来提交其出价哈希值。 方法定义如下
由于买家调用方法提交密封出价时,需要同时向合约转入保证金,因此 需要为这个方法声明关键字。
密封出价方法的实现逻辑非常简单直白 —— 检查是否满足执行条件,然后保存密封出价信息:
参考实现代码如下:
注意全局变量,它表示当前块的时间戳。也就是说,当 函数被调用时,一笔交易将被创建, 这笔交易会被矿工打包到一个块里。每个块都有一个对应的时间戳,用来声明这个块被挖出来的时间。 就等同于那个时间戳。
表示买家调用合约的方法出价时支付的保证金数额,显然,我们需要这个保证金 要高于卖家设定的起拍价格才有意义。
揭示真实出价
在拍卖过了截止时间之后,买家需要调用合约的方法来提交之前被密封 (未透露)的真实出价。方法定义如下:
为了验证买家是否虚报其在竞价期间的真实出价,买家除了提交参与竞价的商品编号和 声称的真实出价外,还需要传入提交密封出价时所选用的密文。
与相比,函数的实现稍微要复杂一点,我们先看一下逻辑流程:
依照惯例,我们还是先检查是否满足运行条件。只有在拍卖截止后才可以 揭示真实出价。然后利用买家提交的出价和密文,通过哈希值的重算和比较 来确认买家曾经在竞价期间提交过该出价。 一旦确定了生成的出价有效,就可以更新竞价结果了,这包括选出新的最高出价人、 最高出价和次高出价。 如果买家不可能胜出(不是最高出价人),则将其保证金原路返还。
可供参考的实现代码如下:
在所有情况中,我们会返还保证金与实际出价的差额,也就是,如果 Alice 出价 10 美元,但 是交了15 美元的保证金,在揭示出价以后,将会返回给 Alice 5 美元。
哈希算法
在之前,我们谈到了对出价信息(出价金额、密文)进行哈希计算。 是一个密码学上的哈希函数, 对于任何长度的任意字符串,它都可以生成一个32字节字符串。
让我们来看一下为什么这对我们的场景十分有用(生成密封出价)。为了生成一个密封出价,我们 将在前端使用库的函数。如果 Alice 想要生成一个出价,只需要 在前端调用 函数,传入她的出价和密文即可:
这个输出就是要传入 函数的 字符串。任何看到该字符串的人都不可能知道 Alice 的真实出价是 10.5美元。
字符串转整数
由于在揭示出价时,买家声称的出价是一个字符串,因此我们实现一个简单的函数 将其转换为整型:
嗯,这可能有点让人意外,居然没有内置实现从字符串到整数的转换。 另外一点,由于不需要从合约外部调用这个方法,因此它的可视性声明为。
获取竞价结果
现在我们实现两个简单的方法,分别返回最高出价者信息和出价总人数。 这些信息将会用于在网页显示竞价结果,同时也有助于在 truffle 控制台进行测试。
获取竞价结果
函数用来返回竞价结果,这包括最高价买家的账户地址、 最高出价和次高出价:
我们使用关键字来指定变量的存储位置,是为了在函数执行完毕时 自动清理该变量。
获取参与者总人数
函数用来返回合约记录的竞价参与人数,也就是状态的值:
控制台与脚本交互测试
首先确保在终端1#已经启动了节点仿真器:
然后编译并部署修改后的合约,进入truffle终端:
注意,由于我们要重新部署合约,因此在部署合约时,使用了参数。 由于合约的竞价依赖于历史状态,因此在你尝试运行下面的代码之前,记得一定 要重新启动节点仿真器,或者使用参数。
添加商品测试数据
向区块链插入一个商品,并将拍卖结束时间设置为从现在算起 200 秒:
密封出价
提供了 10 个测试账户,让我们用不同的账户出价。我们使用 包的函数计算哈希值:
检查账户余额
当你对商品出价以后,使用 web3.eth.getBalance 检查 accounts[0] 和 accounts[1] 的余额:
你会注意到它们的余额大概是 97 ETH 和 96 ETH。这两个账户的出价分别 2 ETH 和 3 ETH, 但是各自支付了 3 ETH 和 4 ETH的保证金。如果代码如期工作,当我们揭示出价时,差价应该返还给 这些账户。
揭示出价
前面已经设置拍卖在200 秒后结束,让我们等到拍卖结束,然后揭示真实出价:
查看拍卖结果
现在调用合约的方法来查看是谁赢得了拍卖:
你应该可以看到 为最高出价 3 ETH 的出价者(赢家),次高出价是 2 ETH。
此时,所有失败的出价者将会收到返还的保证金。赢家的出价资金仍然在合约账户里。 在后续的课程中,我们将会使用托管合约来管理赢家的资金。
在预置代码中,是等价的测试验证脚本,你可以拷贝到 项目目录直接运行:
领取专属 10元无门槛券
私享最新 技术干货