status合约分析

in #cn7 years ago

status产生背景

传统社交网络一般有3个角色:

  • owner: 网络拥有者;目标是吸引用户, 把用户留在平台;
  • advertiser: 卖方; 购买用户数据,提供服务,owner可以提取价值;
  • user: 用户;只要用这个网络, 就必须选择相信owner和advertiser;

现在出现了社交经济网络,用户都是利益相关者, 自然形成有利于所有参与者的社交网络;
Status基于以太坊平台,用SNT代币来创建利益相关者的网络,提供不可逆的自由交易,点对点支付,加密点对点通信的入口; 在该网络中的服务都需要SNT代币来购买.
在status网络中, 任何给定的区块下, 可以简单地生成和父token相同余额的新token,目的是保留对早期支持者的公平,也不随着时间推移和项目演变,对测试新的SNT代币模式加以限制.

SNT应用举例

  1. 投票token, 当你投票时, 花费相应的token,可以使一个token代表一票; 比如投票选择软件方向的决策;
  2. github赏金机器人, 任何人对github问题创建赏金, 如果开发者实现了功能并提交到代码库, 赏金就给开发者. (可能会参考aragon,boardroom的项目管理).
  3. 用SNT创建半公开群聊, 比如活动组织者C用bancor发行一种token,充当音乐节门票,并允许持有者加入群聊;
  4. 购买推送服务;
  5. 花SNT注册用户名; DApp开发者B只授权status的注册用户可以进入讨论版发帖; (需要依赖uport);
  6. 表情服务,持有SNT作为卖家提供表情, 在表情市场售卖,用户可以花SNT购买;

status合约关系图

getImage.png

MiniME token对token的扩展

token 克隆

任何人可以从任何的token克隆一个token, 初始发行是父token在克隆开始block中的token数量.

function createCloneToken(
    string _cloneTokenName,
    uint8 _cloneDecimalUnits,
    string _cloneTokenSymbol,
    uint _snapshotBlock,
    bool _isConstant
    ) returns(address) {

token余额查询

查询token发行量和每个人的余额变化的历史:

function totalSupplyAt(uint _blockNumber) constant returns(uint)
function balanceOfAt(address _holder, uint _blockNumber) constant returns (uint)

token的控制器

控制token销售,销毁, 交易冻结.

function generateTokens(address _holder, uint _value) onlyController

function destroyTokens(address _holder, uint _value) onlyController

function enableTransfers(bool _transfersEnabled) onlyController

status中的token

SNT分配

29%用于储备(multisig)
20%来自现场团队和创始人(multisig,2年归属合同,6个月悬崖)
剩余的51%在初始贡献期本身和SGT之间分配,SGT <=总供给的10%。

SGT 白名单

地址可以列入白名单,并保证参与达到最大数量,忽略动态上限。发送ETH到智能合约地址应该没有什么不同,不管是否列入白名单。状态创世SGT是一个MinimeToken,总共提供5亿个,不超过总供应量的10%。即。如果分配了2.5亿SGT,则SGT将占总供应量的5%。 SGT可以在贡献期后兑换为SNT。

动态上限

一个曲线,指定特定块间隔的一系列隐藏的上限,可以在贡献期间随时显示。在完成众筹之前,必须披露整个曲线。只要整个曲线已经被揭示,可以在曲线期间的任何时刻完成众筹。白名单地址忽略上限。

SNT 杂项

SNT在投放期后1周不可转让,并以每1个ETH兑换10,000 SNT进行铸造。

多重签名钱包

社区钱包

签名者有5个; 需要3个以上签名同意;
社区钱包拥有SNTtoken,并加入广泛的社区确保status的使命得以实施。
管理29%的保留SNT的分配;
解决status研究与开发中的假设"死锁"问题,以确保资源不会被锁定,项目可以继续进行。

开发和研究钱包

签名者有3个; 需要2个以上签名同意;
持有研究与开发用的以太基金和SNTtoken。

合约流程分析

1. 合约部署和初始化

  1. 按循序部署合约,记录合约地址;
  2. 设置SNT的controller为statusContribution合约;
  3. 设置3条隐藏曲线,上限分别是1000ether,21000ether,61000ether。
  4. 执行statusContribution.initialize(...);
    1. 保存前面部署到相关的合约地址到变量中;其中包括接受用户ether的账户地址等。

2. 用户购买SNT(ICO)

  1. 有2种途径购买SNT。
    1. 用户向statusContribution合约地址转账;
    2. 用户向 SNT合约地址转账,(通过SNT的controller购买,即向statusContribution合约);
  2. 用户向合约地址转账, 转账金额进入该合约的balance中;
  3. 然后执行该合约的fallback函数,fallback通过proxyPayment(msg.sender)将转账金额转移到指定的账户中;
    1. 如果是白名单用户,根据该白名单用户配置的上限购买SNT(buyGuaranteed);
    2. 普通用户通过buyNormal购买;
      1. 检查gasprice,不能大于50GWei;防止有用户出高价“插队”;
      2. 也不允许合约用户进行购买的行为;
      3. 先控制购买频率(间隔至少100个block);
      4. 计算本次可以购买的最大金额(单位wei);
      5. 如果最大金额比用户转账的金额还大,那么全部金额用来购买SNT;
      6. 否则只能最多购买最大金额,剩下的退还给用户;
      7. 计算可以购买的SNT(wei*10000);
      8. SNT合约发售token给该用户;
      9. 购买资金转移到指定账户上;

3. 动态上限合约分析

3.1 setHiddenCurves 设置隐藏曲线

  • 实现将计算好的曲线hash保存在curves.hash中;
  • 一共有3条曲线,7个“占位”用的hash, 共10个hash;

3.2 revealMulti 揭露一条曲线

  • 需要输入该曲线上限,斜率因子,允许单次购买的最小金额, salt;
  • 计算hash,如果是待揭露的曲线的hash, 那么揭露该曲线;
  • 揭露的信息保存在curves数组中, 并更新揭露的曲线条数;

3.3 moveTo 移动曲线

  • 只有创建者才有权移动曲线;
  • 移动必须是在已揭露的曲线上移动,并且一次只能向前移动1条;

3.4 toCollect 计算这次交易允许用户购买的最大的金额(单位wei)

  1. 需要输入当前已经筹集的资金数(单位wei)
  2. 如果没有曲线揭露, 那么允许购买的资金也是0;
  3. 如果当前已筹集金额的大于当前曲线上限
    1. 先移到下一条曲线;
    2. 如果下一条曲线还没有揭露, 不允许用户再购买(返回0);
    3. 如果下一条已揭露,但是当前已筹集金额比下一条的上限还大,那么也不允许购买;
  4. 否则计算当前可以购买的绝对金额(上限减去已募集资金数);
  5. 再计算真正允许用户购买的金额(绝对金额除以30);
  6. 在已经筹集金额接近上限时,计算出来的真正可购买金额可能会比允许购买的最小金额还要小,那么根据计算出的绝对金额和允许购买的最小金额比较,如果前者大于后者,那么返回允许购买的最小金额, 后者返回计算出来的绝对金额。

3.5 动态上限曲线的使用

  1. 先计算曲线hash,并保管好salt;3条曲线上限依次变大(1000,21000,61000);
  2. 把hash保存到合约中;
  3. 创建者选择合适的时机,依次揭露3条曲线;
    1. 筹集资金达到上限后,会自动触发进入下一跳已揭露的曲线;
    2. 如果募集参与的人少,创建者可以手动移动到下一条曲线, 1. 提高单次购买上限,吸引用户购买;
  4. 在用户每笔购买交易中动态计算本次可购买的最大金额,然后根据用户支付的以太币数量,如果超过最大金额, 只能购买最大金额, 剩下的返还给用户,否则全部以太币用于购买SNT。

4 ICO完成的处理

  1. 只有合约创建者才能提前结束ICO,或者在ICO截止时间之后结束;
  2. 曲线必须全部都已揭露才能结束;
  3. 如果全部曲线已揭露,并且还没有到结束的时间,那么只有当众筹资金超过最后一天曲线的上限,才允许结束ICO。
  4. 发售预留的token;
    1. 计算实际SGT的token数, 如果超过10%,则限制上限为10%,否则按照实际比例发售;剩下的token公开发售;
      1. 公开发售 = 41% + (10% - % to SGT holders)
    2. 发售给开发者的20%token;
    3. 发售预留token,占29%;
  5. 把SNT的controller切为SNTplaceHolder合约地址;
    getImage.png

SNT的交易

SNT合约实现标准的ERC20接口,交易所通过标准接口进行用户间交易。

contract ERC20Token {
    ///发行的token总量
    uint256 public totalSupply;

    //owner的余额
    function balanceOf(address _owner) constant returns (uint256 balance);

    /// @notice send `_value` token to `_to` from `msg.sender`
    function transfer(address _to, uint256 _value) returns (bool success);

    /// @notice 交易的发起者发送 `_value` token 从`_from` 给 `_to` .
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);

    /// @notice `msg.sender` 授权 `_spender` 可以花最多的 `_value` tokens
    function approve(address _spender, uint256 _value) returns (bool success);

    //返回允许spender可以花owner的token数目
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);

    //token发送事件
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    //授权事件
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

status合约安全举例

根据https://www.kingoftheether.com/contract-safety-checklist.html 分析status合约的安全防御:

异常数据输入处理

 ) public onlyOwner {
        // Initialize only once
        require(address(SNT) == 0x0);

        SNT = MiniMeToken(_snt);
        require(SNT.totalSupply() == 0);
        require(SNT.controller() == address(this));
        require(SNT.decimals() == 18); 

函数的访问控制

  1. 通过修改器实现,比如:
    function generateTokens(address _owner, uint _amount
    ) onlyController returns (bool) {
    

只有controller才能生成token, 合约创建者如果不是controller,也没有权限生成token.

  1. 通过constant限制get函数不能修改storage的数据:
    function getValueAt(Checkpoint[] storage checkpoints, uint _block
    ) constant internal returns (uint) {
    

已知潜在安全问题的修复

    function approve(address _spender, uint256 _amount) returns (bool success) {
        if (!transfersEnabled) throw;

        // To change the approve amount you first have to reduce the addresses`
        //  allowance to zero by calling `approve(_spender,0)` if it is not
        //  already 0 to mitigate the race condition described here:
        //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
        if ((_amount!=0) && (allowed[msg.sender][_spender] !=0)) throw;

不要使用tx.origin

用msg.sender

计算溢出的检查

比如safeMath.sol:
乘法:计算出结果后,通过除法判断结果是否溢出.

function mul(uint a, uint b) internal returns (uint) {
    uint c = a * b;
    assert(a == 0 || c / a == b);
    return c;
  }

除法: 不检查除数是否等于0, 因为solidity会检查并throw;
减法: 不允许小减大;
加法: 判断是否溢出:

function add(uint a, uint b) internal returns (uint) {
    uint c = a + b;
    assert(c >= a);
    return c;
  }

直接判断是否溢出,如下:

    function generateTokens(address _owner, uint _amount
    ) onlyController returns (bool) {
        uint curTotalSupply = getValueAt(totalSupplyHistory, getBlockNumber());
        if (curTotalSupply + _amount < curTotalSupply) throw; // Check for overflow

Coin Marketplace

STEEM 0.19
TRX 0.14
JST 0.030
BTC 59479.71
ETH 3174.48
USDT 1.00
SBD 2.44