128

Imkey.pro 智能合约代码解读

gaom094 · 于 发布 · 929 次阅读

最近被一篇出自http://www.bcgeek.cn/index.php?s=/news/index/detail/id/59.htmlImKey:的文章“ 一款不怕私钥丢失的神器”给吸引住了。
什么?私钥丢了还能找回?这完全违背“自己财产自己负责”的去中心精神啊。于是笔者抱着好奇的心态,去试用了一下这款号称不怕私钥丢失的产品(http://imkey.pro/eth/index.html
经过笔者的实际使用后,才终于明白这是一款基于以太坊智能合约开发的保险箱(这是笔者的命名,不清楚这款产品背后的设计人员的真实想法)。于是笔者深入分析了一下这款产品的核心智能合约的代码(https://etherscan.io/address/0x49102622791a58abe990a0ac68ec3f4366c5b2b1#code
感兴趣的读者也可以自行阅读源码哈。
这份合约的代码给笔者的感觉是极其精简,抛开一大段的事件定义和数学库定义,我们直接进入核心主题(也即核心功能部分)。
首先合约中定义了四种状态
//State
enum State {
Normal, Report, Verify, Lock
}

从字面上理解,应该是普通状态、挂失中状态、验证状态、锁定状态。

合约中有个payable的充值函数
//deposit
function () public payable
{
require(states[msg.sender] == State.Normal);
balances[msg.sender] = balances[msg.sender].add(msg.value);
emit onEventDeposit(msg.sender, msg.value.div(100000000000000));
}

这段代码非常简单,就是你用普通状态帐号向合约中充入多少以太币,合约中就记录着你的资产余额。
然后合约中有两个提现函数,一个命名为withdraw,代码如下
//withdraw
function withdraw(address to, uint256 value) public
{
require(states[msg.sender] != State.Lock);
require(balances[msg.sender] >= _value.add(withdraw
fee));
balances[msg.sender] = balances[msg.sender].sub(
value.add(withdraw_fee));
_to.transfer(_value);
owner.transfer(withdraw_fee);
emit onEventWithdraw(msg.sender, _to, _value.div(100000000000000));
}

这里可以看到对于非锁定状态的帐号可以直接用自己的私钥从合约的余额中提现,这个操作感觉跟普通以太币的转账没什么太大的本质区别(只有私钥所有者才能动用资产),只是产品设计人员在这里增加了一笔0.0006eth的手续费(不过笔者感觉这个额度还是蛮厚道的)。但这里笔者唯一的疑问在于帐号为什么会有锁定状态呢?什么条件会锁定账户呢?由谁来锁定账户呢?相信后面会有答案。

紧接着,是第二个提现函数,代码设计人员将其命名为withdrawloss
//withdraw for loss
function withdrawloss(address from, address to) public
{
require(to == msg.sender);
require(tos[
from] == _to);
require(states[
from] == State.Verify);
require(states[
to] == State.Normal);
//check verify time
require(now >= verifytimes[from] + 5 days);
require(balances[
from] >= withdraw_fee_lost);

emit onEventWithdrawLost(_from, _to, balances[_from].div(100000000000000));

owner.transfer(withdraw_fee_lost);
balances[to] = balances[to].add(balances[_from]).sub(withdraw_fee_lost);
balances[
from] = 0;
states[
from] = State.Normal;
verifytimes[from] = 0;
tos[
from] = 0;
}

笔者从字面上的意思结合产品原型中的有个挂失后提现的功能页面,联想到这应该是挂失后提现的操作,为了更好的理解这一大段的状态判定逻辑,笔者建议先阅读下面的挂失操作的代码。

//report
function report(address from, address to, bytes sign) public
{
require(_to == msg.sender);
require(states[
from] == State.Normal);
require(balances[
to] >= report_lock);
require(states[
to] == State.Normal);
signs[from] = _sign;
tos[_from] = _to;
states[
from] = State.Report;
states[_to] = State.Lock;

emit onEventReport(_from, _to);
}

首先在挂失操作中要传入三个参数,已经丢失私钥的地址,私钥丢失后通过挂失要转移资产的新地址,一个命名为_sign的数据(从字面理解就是签名数据,对应产品原型中的身份数据)。
这段代码主要功能就是做一些状态判定和状态设置,比如将待挂失地址设置为挂失状态,新地址由普通状态进入锁定状态(跟取现操作时需要判定非锁定状态的逻辑对应起来理解,就是这个新的地址会处于不能提现状态),同时设计人员做了一个强制要求,就是做挂失操作的时候需要新地址要存入0.1ETH的保证金,只有被验证通过,保证金会退还给用户。笔者认为这里的设计很明显是在提高恶意用户做恶意挂失操作的成本,而且一旦发现是恶意挂失操作的话,这0.1ETH会被系统管理员账号没收。

挂失成功后的验证函数(代码中命名为verify),要传入一个_id参数,这个_id从这行代码bytes32 hash = keccak256(_id) 来看,笔者猜测应该是平台在做身份生成的时候,生成的一个随机数,然后用这个随机数的hash值交由用户去做签名,理论上只有平台拥有这个数据,其他恶意用户是没有这个数据的。而且这个设定跟挂失操作时需要保证金的设计结合,感觉完美解决了恶意用户的挂失行为。

我们再回到挂失后提现操作的代码处,我们就不难理解,挂失后只能由挂失时提供的新地址才能提现(而这个新地址传入的签名数据是经过合约验证通过了的,这也意味着新地址必然有挂失地址的私钥签名数据,只能是同一人操作)。另外这个合约中有个非常巧妙的设计就是,挂失通过验证后,还需要等待5天的时间才能从合约中提现。这个延时提现的功能感觉就是整个智能合约的核心,这个时间窗口给平台方、所有用户都提供了足够的审计时间(另外在产品使用过程中,笔者发现有个订阅功能,经过笔者的测试,所有订阅过的地址的任何操作变化,平台都会发送通知邮件)。

另外在阅读整个代码过程中,笔者一直很好奇在合约的开始的时候有做owner账户(管理员账户)的设定,但自始至终都没有体现出owner的权限,终于在代码的最后出现了owner操作

// reset the user's state for some malicious attacks
function resetState(address from) public onlyOwner
{
require(states[from] == State.Report || states[from] == State.Lock);
if(states[_from] == State.Report) {
states[
from] = State.Normal;
verifytimes[_from] = 0;
tos[
from] = 0;
emit onEventReset(from);
} else if(states[_from] == State.Lock) {
states[
from] = State.Normal;
balances[
from] = balances[_from].sub(report_lock);
owner.transfer(report_lock);
emit onEventUnlock(_from);
}
}
但这里的owner只做一件事情,对恶意挂失行为的处理,恶意挂失方被没收保证金,被恶意挂失方恢复状态。相比很多合约里owner能直接挪用用户资产、设定有害参数等行为,这里owner做的事情合情合理啊。

最后笔者总结一下,imkey.pro 提供的这个智能合约其实也完全实现了一种去中心化的私钥丢失后帮你找回资金的方案。如果你对平台保管身份数据功能不放心,你完全可以自己生成身份ID和签名数据,并完全本地保存。当然这种去中心化方案只是笔者自己总结出来的哈,不知道平台方会不会有意见,哈哈。以前你的私钥只有一份,通过这个智能合约,你相当于有两份数据,一份私钥,一份签名数据,做两处保存,私钥丢失了还可以用签名数据找回资产,签名数据丢失了,可以通过私钥再次生成签名,这样,你的资产安全就更有保障了啊。

  • 暂无回复。