加密社
2018年2月24日新闻,美图公司推出的第一个和区块链技术有关的项目叫做美链(英文缩写是BEC,全名叫做Beauty Chain)
这个项目发行的数字货币开始在一家叫OKEX的全球数字资产交易平台上可以买卖了。这个BEC币一共发了70亿个,价格曾经涨到了每个超过4美元,使得BEC的总价值一度超过了280亿美元。
不过,在同年4月22日的时候,因为BEC出现了一个很严重的安全问题,导致这个项目的市场价值几乎变成了零。
接下来我们来看具体是怎么回事
前言:在这里我们不过多的分析漏洞带来的经济损失及行业影响,纯技术角度来分析下漏洞本身。
1. 合约代码分析
在这里如果我们要分析代码,需要刨析合约代码的本身,至于BEC的合约代码,这在区块链上是开源的,所以我们可以看到。(在这里我对合约代码进行了消息注释,以防小白看不懂)
pragma solidity ^0.4.16;
/**
* @title SafeMath
* @dev 包含安全检查的数学运算函数,错误时会抛出异常
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b); // 防止乘法溢出
return c;
}
function div(uint256 a, uint256 b) internal constant returns (uint256) {
// assert(b > 0); // 当除数为0时,Solidity自动抛出异常
uint256 c = a / b;
// assert(a == b * c + a % b); // 这里不会有不成立的情况
return c;
}
function sub(uint256 a, uint256 b) internal constant returns (uint256) {
assert(b <= a); // 防止减法溢出
return a - b;
}
function add(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a + b;
assert(c >= a); // 防止加法溢出
return c;
}
}
/**
* @title ERC20Basic
* @dev ERC20接口的简化版本
* @dev 参见 https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public constant returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title Basic token
* @dev 标准代币的基本版本,不包含授权功能
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @dev 向指定地址转移代币
* @param _to 转移目标地址
* @param _value 转移的数量
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0)); // 验证目标地址不是零地址
require(_value > 0 && _value <= balances[msg.sender]); // 验证转移数量合法
// SafeMath.sub 在余额不足时会抛出异常
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev 获取指定地址的余额
* @param _owner 查询余额的地址
* @return 返回该地址拥有的代币数量
*/
function balanceOf(address _owner) public constant returns (uint256 balance) {
return balances[_owner];
}
}
/**
* @title ERC20 interface
* @dev 参见 https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Standard ERC20 token
*
* @dev 基本标准代币的实现
* @dev 基于 FirstBlood 的代码:https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev 从一个地址向另一个地址转移代币
* @param _from 转出地址
* @param _to 转入地址
* @param _value 转移的数量
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0)); // 验证目标地址不是零地址
require(_value > 0 && _value <= balances[_from]); // 验证转移数量合法
require(_value <= allowed[_from][msg.sender]); // 检查授权数量是否足够
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev 授权指定地址代表发送者花费指定数量的代币
*
* 注意:使用此方法修改授权金额时,应注意交易顺序风险。一种缓解方案是先将授权金额减少到0,