EOSCrowdsale Contract Inspect

in #eos7 years ago (edited)

这两天 EOS 众筹正火,蹭个热点,技术面分析下 EOS的众筹合约

项目地址

https://github.com/EOSIO/eos-token-distribution

项目目录

├── Dappfile                  // dapphub 出品的智能合约开发工具
├── Makefile
├── bin
│   └── deploy                // 部署脚本
├── lib                       // 第三方依赖
│   ├── ds-auth               // 权限控制
│   ├── ds-exec
│   ├── ds-guard
│   ├── ds-math               // 数学运算
│   ├── ds-test               // 测试框架
│   ├── ds-token              // Token 框架
│   └── gnosis-multisig
└── src                       // 源码
        ├── eos.sol
        └── eos.t.sol

本项目使用 Dapphub 出品的智能合约开发框架 dapp 开发。Dapphub 开源了很多实用的库,大大简化了智能合约的开发成本。

代码分析

变量

DSToken  public  EOS;                  // EOS Token
uint128  public  totalSupply;          // Token 总量
uint128  public  foundersAllocation;   // 开发团队保留份额
string   public  foundersKey;          // 保留份额 Holder Address
uint     public  openTime;             // window 0 开始时间
uint     public  createFirstDay;       // window 0 供应量
uint     public  startTime;            // window 1 开始时间
uint     public  numberOfDays;         // window 总数
uint     public  createPerDay;         // 每日供应量

EOS 的 ICO 规则为:前五天为一个 window 总共发行 2 亿 Token。五天之后,每23个小时为一个window,发行 2 百万 Token。总共发行 10 亿 Token。

构造函数

// src/eos.sol

function EOSSale(
    uint     _numberOfDays,
    uint128  _totalSupply,
    uint     _openTime,
    uint     _startTime,
    uint128  _foundersAllocation,
    string   _foundersKey
) {
        ...

        // window 0 Token 供应量
    createFirstDay = wmul(totalSupply, 0.2 ether);

    // window 1 以及以后的每天 Token 供应量
    createPerDay = div(
        sub(sub(totalSupply, foundersAllocation), createFirstDay),
        numberOfDays
    );
    
    ...
}

wmuldapp_math 提供的一个方法。 dapp_math 中定义两种精度:WADRAY ,分别代表 18 位和 27 位的精度。以 w 开头表示该运算精度为 18 位。

由于 solidity 里面没有 float 类型,想要乘以 0.2 可以通过 乘以 0.2 ether 实现。

初始化 Token

// src/eos.sol

function initialize(DSToken eos) auth {
    ...

    EOS = eos;
    // 设置供应总量
    EOS.mint(totalSupply);

    // 保留 Token 发往 0xb1 这个地址
    EOS.push(0xb1, foundersAllocation);
    keys[0xb1] = foundersKey;
}

mint 方法是 dapp_token 提供,用来设置 Token 供应总量。

开发团队保留的份额被发送到 0xb1 。这个地址是无人可用的,等于丢掉此数量的 Token ,但是又保证 Token 总供应量不变。EOS 团队不需要这个代币,因为在 EOS 上线之后,现在的 Token 会兑换为 EOS 链上的 Token。

window time

// Each window is 23 hours long so that end-of-window rotates
// around the clock for all timezones.
function dayFor(uint timestamp) constant returns (uint) {
        return timestamp < startTime
            ? 0
            : sub(timestamp, startTime) / 23 hours + 1;
}

前五天都是 window 0 。之后每23小时为一个 window 。这么做的好处是让每个 window 的开始时间是滚动的,不同时区的投资者更方便参与。

购买逻辑

function buyWithLimit(uint day, uint limit) payable {
    // 限制时间
    assert(time() >= openTime && today() <= numberOfDays);
    // 最小购买额度 0.01 ether
    assert(msg.value >= 0.01 ether);

    // 购买记录
    userBuys[day][msg.sender] += msg.value;
    dailyTotals[day] += msg.value;
}

Token 兑换

function claim(uint day) {
    // 防止重复兑换
    if (claimed[day][msg.sender] || dailyTotals[day] == 0) {
        return;
    }

    // This will have small rounding errors, but the token is
    // going to be truncated to 8 decimal places or less anyway
    // when launched on its own chain.

    var dailyTotal = cast(dailyTotals[day]);
    var userTotal  = cast(userBuys[day][msg.sender]);
    // 指定 window 的 Token 供应量除以此 window 的 eth 总量
    // 得到兑换比例
    var price      = wdiv(cast(createOnDay(day)), dailyTotal);
    // 兑换比例乘以指定 window 中此用户支付的 eth 数量得到兑换总量
    var reward     = wmul(price, userTotal);
    // 记录兑换标志
    claimed[day][msg.sender] = true;
    // 执行转账
    EOS.push(msg.sender, reward);
}

注册 EOS 共钥

function register(string key) {
    // 众筹结束之后不再提供注册
    assert(today() <=  numberOfDays + 1);
    assert(bytes(key).length <= 64);

    keys[msg.sender] = key;
}

EOS 团队要求投资者在众筹结束之前自行生成 EOS 公私钥,并将生成的共钥注册在合约中。这样在 EOS 正式上线之后,用户可以兑换 EOS 上的 Token 。

ETH 转移

function collect() auth {
    // window 0 不能转移
    assert(today() > 0);
    // 将 eth 转移给调用者
    exec(msg.sender, this.balance);
}

auth 是 dapp_auth 提供的权限控制方法,保证 collect 函数只能被合约 owner 执行。

安全性分析

EOS 众筹合约使用了 dapp_auth 提供的权限控制功能。

// src/eos.sol
// 继承 DSAuth
contract EOSSale is DSAuth {
}

// lib/ds-auth/src/auth.sol
contract DSAuth is DSAuthEvents {
    DSAuthority  public  authority;
    address      public  owner;

    function DSAuth() {
        owner = msg.sender;
    }
}

DSAuth 提供的默认权限控制是基于 owner 的。默认初始化行为将合约创建者设置为 owner 。同时提供 setOwner 函数,可以转移控制权。

function setOwner(address owner_)
    auth
{
    owner = owner_;
    LogSetOwner(owner);
}

setOwner 也需要使用 auth 验证权限。

modifier auth {
    assert(isAuthorized(msg.sender, msg.sig));
    _;
}

function isAuthorized(address src, bytes4 sig)
    internal returns (bool)
{
    // 如果调用者是合约自己,通过。
    if (src == address(this)) {
        return true;
    // 如果调用者是 owner ,通过。
    } else if (src == owner) {
        return true;
    // 如果 authority 未设置,失败。
    } else if (authority == DSAuthority(0)) {
        return false;
    } else {
        // 检查调用者是否有权限。
        return authority.canCall(src, this, sig);
    }
}

在默认行为下 authority 未设置,所以只有 owner 验证。

可以看到,这个合约还可以通过设置自定义的 DSAuthority 来扩展权限验证的逻辑。

contract DSAuthority {
    function canCall(
        address src, address dst, bytes4 sig
    ) constant returns (bool);
}

contract DSAuth is DSAuthEvents {
    function setAuthority(DSAuthority authority_)
        auth
    {
        authority = authority_;
        LogSetAuthority(authority);
    }
}

我们只需要在自己的合约中继承 DSAuthority ,实现 canCall 方法。
然后将实例传入 setAuthority 就可以设置特定的权限逻辑。

商业价值分析

EOS 的主要特性:

  • 免费试用
  • 合约可以升级
  • 出块速度快,达到平均 1.5s
  • 串行/并行性能强,可以达到百万级交易处理规模
  • 账户系统,权限系统等
  • 可插拔的合约虚拟机

EOS 更方便创建大型高频的分布式应用。

Sort:  

Nice

Very cool post, I can learn a lot of new things from you :D

What is this? xd :D

Postnya very useful and hope you are lucky for future in this steemit :)
But do not forget the upvote on my blog too :D
#Thank's

Toooo long to read everything :D

不错。建议加几个标签 #cn #cn-programming

很赞,楼主深入研究过dapp提供的api啊

Resteem and UpVote with pleasure
(ᵔᴥᵔ) from @burundel

with translation and English all is good .... upvoted

強大的技術貼

有深度的技术贴,棒棒哒

Please where can I find an English version so I can learn more about it. Thanks

i agree.. everything is correct

This post not only gives you upvotes but also this great post will give you blessings and wishes of people because with this writing many people will understand the reason for lost payment, steemit community do not forget comments, upvoting, replies, and follow me @jakir

写的太好了,认真看了两遍,学到很多,谢谢博主!

universal language, programming code

Thanks for sharing, nice

Great input. Thank you for sharing!

Thank you very much for this post.

Great topic
I look forward to more topic

Programming codes are used by all and makes your post valuable to us Thanks

Good post

HEY! GREAT WORK SIR!

You're THE KINDA MAN WHO WOULD LOVE TO KNOW WHO the freedom WHALE IS, right?:)

I exposed him, feel free to check !

Im asian but not chinese. whats the point of being asian, right?

some english translations please? thanks

Thank you for the translation and info :-)

i can see the script but cant understand, anyhow good post with good motions.

Good information!