位置:芙蓉财经网 >> 区块链

更改虚拟货币代币的符号

2023年05月25日 16:47

工欲善其事,必先利其器。要想挖掘和分析智能合约的漏洞,你必须要先学会看懂智能合约。而目前智能合约中有很大一部分是发行代币的,那什么是代币,他们有什么标准呢?本文就是带领你入门,教会你看懂一个代币的智能合约。

以太坊代币

在以太坊系统中,存在作为基础货币的 Ether(以太),以及同样可以作为货币使用的 Token(代币)。

以太坊与其他加密货币的主要不同在于,以太坊不是单纯的货币,而是一个环境/平台。在这个平台上,任何人都可以利用区块链的技术,通过智能合约来构建自己的项目和DAPPS(去中心化应用)。

如果把以太坊理解成互联网,DAPPS则是在上面运行的网页。DAPPS是去中心化的,意味着它不属于某个人,而是属于一群人。DAPPS发布的方式通常是采用被称为 ICO 的众筹方式。简单来说,你需要用你的以太来购买相应DAPP的一些tokens。

一般有两种Token:

  1. Usage Tokens: 就是对应 DAPP 的原生货币。Golem 就是一个很好的例子,如果你需要使用 Golem 的服务,你就需要为其支付 Golem Network Token(GNT)。由于这种 Tokens 有货币价值,所以通常不会有其他的权益。
  2. Work Tokens: 此类 Tokens 可以标识你对于 DAPP 的某种股东权益。以 DAO tokens 为例,如果你拥有 DAO tokens,那么你有权就DAO是否资助某款 DAPP 来进行投票。

类比到股权,可以把 Usage Tokens 简单理解为普通流通股,可以与真实货币兑换,本身具有价值。而 Work Token,则大致相当于投票权。

为何需要Token:

不是有以太基础货币了,那为什么还需要 token 呢?可以想下现实生活的真实场景,在游乐场里,我们需要用现金兑换代币,然后用代币支付各种服务。 类比到以太坊,现金就是以太,代币就是 token,用 token 来执行合约中的各项功能。

以太坊Token标准

这个是本文学习的重点,所有遵循 ERC20 标准的函数,都要事先它定义的标准接口。搞懂这些,你也就能很快看懂一些智能合约代币的逻辑。

ERC-20 标准是在2015年11月份推出的,使用这种规则的代币,表现出一种通用的和可预测的方式。任何 ERC-20 代币都能立即兼容以太坊钱包(几乎所有支持以太币的钱包,包括Jaxx、MEW、imToken等),由于交易所已经知道这些代币是如何操作的,它们可以很容易地整合这些代币。这就意味着,在很多情况下,这些代币都是可以立即进行交易的。简单理解就是,ERC20是开发者在自己的tokens中必须采用的一套具体的公式/方法,从而确保不同DAPP的token与ERC20标准兼容。

ERC-20 标准规定了各个代币的基本功能,非常方便第三方使用,在开发人员的编程下,5 分钟就可以发行一个 ERC-20 代币。因为它可以快速发币,而且使用又方便,因此空投币和空气币基本上就是利用 ERC-20 标准开发的。基于 ERC-20 标准开发的同种代币价值都是相同的,它们可以进行互换。ERC-20 代币就类似于人民币,你的 100 元和我的 100 元是没有区别的,价值都是 100 元,并且这两张 100 元可以进行互换。有了这套标准,相当于全世界都使用人民币,而不用去别的国家还要计算汇率换成别的货币。想象下,每个Dapp都有不同格式的币,那对于这些应用的交互简直是种灾难。

etherscan上开源的 ERC20 标准的智能合约:https://etherscan.io/tokens

ERC20 Token标准接口:

contract ERC20 {

uint256 public totalSupply;

function balanceOf(address who) constant public returns (uint256);

function transfer(address to, uint256 value) public returns (bool);

function allowance(address owner, address spender) constant public returns (uint256);

function transferFrom(address from, address to, uint256 value) public returns (bool);

function approve(address spender, uint256 value) public returns (bool);

event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(address indexed owner, address indexed spender, uint256 value);

}

函数:

注意:非常重要的一点是调用者应该处理函数返回的错误,而不是假设错误永远不会发生。

  1. totalSupply: 返回token的总供应量
  2. balanceOf: 用于查询某个账户的账户余额
  3. tansfer: 发送 _value 个 token 到地址 _to
  4. transferFrom: 从地址 _from 发送 _value 个 token 到地址 _to
  5. approve: 允许 _spender 多次取回您的帐户,最高达 _value 金额; 如果再次调用此函数,它将用 _value 的当前值覆盖的 allowance 值。
  6. allowance: 返回 _spender 仍然被允许从 _owner 提取的金额。

事件 :

  1. event Transfer: 当 tokens 被转移时触发。
  2. event Approval: 当任何成功调用 approve(address _spender, uint256 _value) 后,必须被触发。

代币合约实例分析

talk is cheap show me the code 。前面给的函数说明是简单的概括,大家可能还似懂非懂,下面就将用实例说明。《AMR智能合约漏洞分析》这篇文章用实例讲解了智能合约的一种漏洞,合约代码在 https://etherscan.io/address/0x96c833e43488c986676e9f6b3b8781812629bbb5#code ,我们就以这个代码做个详细的分析。

开始一行行分析这个智能合约:

library SafeMath {

function mul(uint256 a, uint256 b) internal pure returns (uint256){

uint256 c = a * b;

assert(a == 0 || c / a == b);

return c;

}

function div(uint256 a, uint256 b) internal pure returns (uint256){

assert(b > 0);

uint256 c = a / b;

return c;

}

function sub(uint256 a, uint256 b) internal pure returns (uint256){

assert(b <= a);

return a - b;

}

function add(uint256 a, uint256 b) internal pure returns (uint256){

uint256 c = a + b;

assert(c >= a);

return c;

}

} 

这个比较简单,定义安全函数的库,用来防止整数溢出漏洞。

contract ERC20 {

uint256 public totalSupply;

function balanceOf(address who) constant public returns (uint256);

function transfer(address to, uint256 value) public returns (bool);

function allowance(address owner, address spender) constant public returns (uint256);

function transferFrom(address from, address to, uint256 value) public returns (bool);

function approve(address spender, uint256 value) public returns (bool);

event Transfer(address indexed from, address indexed to, uint256 value);

event Approval(address indexed owner, address indexed spender, uint256 value);

}

ERC20 标准接口,上一节说过了。

contract Ownable {

address owner;

// 把当前合约的调用者赋值给owner

function Ownable() public{

owner = msg.sender;

}

// 只有智能合约的所有者才能调用的方法

modifier onlyOwner(){

require(msg.sender == owner);

_;

}

// 合约的所有者可以把权限转移给其他用户

function transferOwnership(address newOwner) onlyOwner public{

require(newOwner != address(0));

owner = newOwner;

}

}

这个合约接口的功能是判断和修改该合约的所有者。其中函数 onlyOwner 用到了 modifiers(函数修改器) 关键字。函数修改器可以用来改变一个函数的行为,比如用于在函数执行前检查某种前置条件。如果你了解 python 的装饰器,这个就很容易理解了。还不理解?没关系,我们再详细说明下这个接口。首先你需要理解下这边的几个概念:

1. msg.sender 内置变量,代表当前调用该合约的账户地址。

2. Ownable() 函数,和合约接口同名,这是个构造函数,只能在创建合约期间运行,不能在事后调用。所以这个owner是创建该合约人的地址,无法被篡改,除非合约创始人授权。

3. 特殊字符串 _; 用来替换使用修改符的函数体。比如上述代码就是把 _; 替换成 transferOwnership ,也就是执行 transferOwnership 函数时候会先判断 require(msg.sender == owner);

下面把这个合约加一个打印owner的函数,然后放到 remix 调试,这样更直观理解。

pragma solidity ^0.4.24;

contract Ownable {

address owner;

// 把当前合约的调用者赋值给owner

function Ownable() public{

owner = msg.sender;

}

function CurrentOwner() public returns (address){

return owner;

}

// 只有智能合约的所有者才能调用的方法

modifier onlyOwner(){

require(msg.sender == owner);

_;

}

// 合约的所有者可以把权限转移给其他用户

function transferOwnership(address newOwner) onlyOwner public{

require(newOwner != address(0));

owner = newOwner;

}

}

a) 使用账户 A 创建合约,则 owner 则是 A 的地址,切换到用户 B 点击 onlyOwner 函数,看到owner的值是账户 A 的地址。这时候如果点击 transferOwnership 会报错,因为这个函数被 onlyOwner 修饰了,会先判断当前调用合约的是否是合约所有者。当前合约所有者是账户 A,合约调用者账户 B 是没权限转移权限的。

一文读懂以太坊代币合约

b) 把账户切换到 A,transferOwnership 地址填账户 B,这时候你就可以把合约所有者权限转移给账户 B 了。而再一次执行,发现提示错误了,因为此时合约所有者已经是账户 B,账户 A 没权限。

一文读懂以太坊代币合约

contract StandardToken is ERC20 {

using SafeMath for uint256;  // 使用 SafeMath 函数库

mapping (address => mapping (address => uint256)) allowed;  // 类比二维数组

mapping(address => uint256) balances;  // 类比一维数组

   // 把合约调用者的余额转移 _value 个tokens给用户 _to

function transfer(address _to, uint256 _value) public returns (bool){

assert(0 < _value);

assert(balances[msg.sender] >= _value);

balances[msg.sender] = balances[msg.sender].sub(_value);

balances[_to] = balances[_to].add(_value);

emit Transfer(msg.sender, _to, _value);

return true;

}

   // 查询 _owner 账户的余额

function balanceOf(address _owner) constant public returns (uint256 balance){

return balances[_owner];

}

   // 从地址 _from 转移 _value 个 tokens 给地址 _to

function transferFrom(address _from, address _to, uint256 _value) public returns (bool){

uint256 _allowance = allowed[_from][msg.sender];

assert (balances[_from] >= _value);

assert (_allowance >= _value);

assert (_value > 0);

balances[_to] = balances[_to].add(_value);

balances[_from] = balances[_from].sub(_value);

allowed[_from][msg.sender] = _allowance.sub(_value);

emit Transfer(_from, _to, _value);

return true;

}

// 允许 _spender 多次取回您的帐户,最高达 _value 金额; 如果再次调用此函数,它将用 _value 的当前值覆盖的 allowance 值

function approve(address _spender, uint256 _value) public returns (bool){

require((_value == 0) || (allowed[msg.sender][_spender] == 0));

allowed[msg.sender][_spender] = _value;

emit Approval(msg.sender, _spender, _value);

return true;

}

// 返回 _spender 仍然被允许从 _owner 提取的金额

function allowance(address _owner, address _spender) constant public returns (uint256 remaining){

return allowed[_owner][_spender];

}

} 

这边逻辑不复杂,有个概念可能不太好理解,这里详细说明下。allowed 这个变量(类比成二维数组),是用来存取授信的额度,在 approve 函数中定义。

allowed[msg.sender][_spender] = _value;

这个 msg.sender 是当前合约调用者,_spender 是被授权人,额度是 _value 。可以通俗的理解成,银行(msg.sender)给用户( _spender) 授权了 _value 额度的 tokens 。在银行转账,相应的额度也会减少,而用户在此银行最多可以转被授权的 _value 个 tokens,不同的银行(msg.sender)可以给用户(_spender)授信不同的额度(_value)。

把 allowd 的概念理解了,allowance 函数也就很好理解了,第一个参数 _owner 类比成银行,第二个参数 _spender 类比成用户,这个函数就用来查询用户(_spender)在银行(_owner)剩余的额度(tokens)。通过上述的讲解,可以知道 transfer 和 transferfrom 函数区别如下:

1. transfer 是把当前合约调用者的 tokens 转移给其他人

2. transferFrom 则是可以把 ”银行“ 授信额度的钱(tokens)转给自己或者他人,转移的是 “银行” 的 tokens

contract Ammbr is StandardToken, Ownable {

string public name = '';

string public symbol = '';

uint8 public decimals = 0;

uint256 public maxMintBlock = 0;

event Mint(address indexed to, uint256 amount);

// 给地址 _to 初始化数量 _amount 数量的 tokens,注意 onlyOwner 修饰,只有合约创建者才有权限分配

function mint(address _to, uint256 _amount) onlyOwner public returns (bool){

assert(maxMintBlock == 0);

totalSupply = totalSupply.add(_amount);

balances[_to] = balances[_to].add(_amount);

emit Mint(_to, _amount);

maxMintBlock = 1;

return true;

}

// 转帐操作,可以同时转给多个人

function multiTransfer(address[] destinations, uint[] tokens) public returns (bool success){

assert(destinations.length > 0);

assert(destinations.length < 128);

assert(destinations.length == tokens.length);

uint8 i = 0;

uint totalTokensToTransfer = 0;

for (i = 0; i < destinations.length; i++){

assert(tokens[i] > 0);

totalTokensToTransfer += tokens[i]; // 存在溢出

}

assert (balances[msg.sender] > totalTokensToTransfer);

balances[msg.sender] = balances[msg.sender].sub(totalTokensToTransfer);

for (i = 0; i < destinations.length; i++){

balances[destinations[i]] = balances[destinations[i]].add(tokens[i]);

emit Transfer(msg.sender, destinations[i], tokens[i]);

}

return true;

}

// 构造函数,可选

function Ammbr(string _name , string _symbol , uint8 _decimals) public{

name = _name; // 设定代币的名字,比如: MyToken

symbol = _symbol; // 返回代币的符号,比如: ARM

decimals = _decimals; // 设置 token 的精度

}

}

现在来详细说明下 decimals 这个参数。首先我们来理解下常说的以太(ether)到底是怎么换算的。在以太坊交易中,最小的单位是 wei ,1 ether = 10^18 wei 。单位换算在线地址: https://converter.murkin.me/

ether单位对照表:

一文读懂以太坊代币合约

调用合约转发 Token 的时候,传入的值是要转发的 Token 数乘上精度(默认decimals=18),比如转1个Token,传入合约的值是1000000000000000000 wei

代币(Token)参数对照表:

一文读懂以太坊代币合约

前面把我认为的难点、疑惑点都说完了,想必大家看懂这个合约也没什么难度。看懂合约后,如果看过以太坊智能合约安全漏洞入门之类的文章,应该一看就能看出 multiTransfer 存在溢出漏洞。原理就是 totalTokensToTransfer 没有使用安全函数,可以导致整数上溢出。详情可参考 这篇文章 。下面我们就手动调试下这个漏洞

1. 首先运行 mint 函数,给账户 A 初始化 100000 个tokens

一文读懂以太坊代币合约

2. 向账户 B,C 分别充值 57896044618658097711785492504343953926634992332820282019728792003956564819968, 相加得 115792089237316195423570985008687907853269984665640564039457584007913129639936,而账户 unit256 最大值15792089237316195423570985008687907853269984665640564039457584007913129639935,导致溢出 totalTokensToTransfer 的值为0

注意: 在 remix 调试时候,传入的数组可以用 [......, ......] 表示,但是地址必须用双引号包裹,传入的数字如果较大也必须用双引号包裹,否则会报错

一文读懂以太坊代币合约

一文读懂以太坊代币合约

一文读懂以太坊代币合约

总结:

其实智能合约的代码比平常分析逆向的程序代码都简单多了,只要你掌握 ERC20 的标准几个接口,了解函数修改器的概念以及一些以太坊基本的概念,相信看懂一个 ERC20 标准的合约并不难。也希望大家能把上述分析自己动手实践一下,之前我很多地方也有疑问,通过动手实践很快就明白问题的所在了。如果学习有捷径的话就是多动手,多调试。Solidity 没有打印的函数,有时候会给调试带来不便,下面补个 Solidity 调试的代码,方便大家打印变量值。

pragma solidity ^0.4.21;

//通过log函数重载,对不同类型的变量trigger不同的event,实现solidity打印效果,使用方法为:log(string name, var value)

contract Console {

event LogUint(string, uint);

function log(string s , uint x) internal {

emit LogUint(s, x);

}

event LogInt(string, int);

function log(string s , int x) internal {

emit LogInt(s, x);

}

event LogBytes(string, bytes);

function log(string s , bytes x) internal {

emit LogBytes(s, x);

}

event LogBytes32(string, bytes32);

function log(string s , bytes32 x) internal {

emit LogBytes32(s, x);

}

event LogAddress(string, address);

function log(string s , address x) internal {

emit LogAddress(s, x);

}

event LogBool(string, bool);

function log(string s , bool x) internal {

emit LogBool(s, x);

}

}

把文件保存成 Console.sol ,其他程序中引用这个文件即可。具体用法如下

一文读懂以太坊代币合约

以上是全文内容,如果文中说的有误,或者大家有什么更好的想法,欢迎大家和我交流。

参考:

http://yinxiangblog.com/?id=10

https://github.com/ethereum/EIPs/issues/20

https://blog.csdn.net/diandianxiyu_geek/article/details/78082551

https://theethereum.wiki/w/index.php/ERC20_Token_Standard

http://www.cnblogs.com/huahuayu/p/8593774.html

https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.wqhvh2y0obwt

脉搏地址:https://www.secpulse.com/

微博地址:https://weibo.com/311057789/home?wvr=5

推荐阅读

虚拟货币合约交易软件下载 巅峰极速合约150个合约币怎么获得
数字货币横盘久了是跌还是涨(数字货币趋势狂人:这次爆仓的人要永远离开币圈了,冬天开始了)
2019虚拟货币政策规定,2021冻结虚拟货币账户的规定
虚拟币现货合约是什么平台(虚拟币现货合约是什么平台的)
虚拟币哪家合约平台最好,虚拟币哪家合约平台最好用
网页电影
中国股指期货合约(中国股指期货合约乘数一览表)
2013年期货投资报告(if2013期货)
豆粕期货计量单位?豆粕期货报价单位
1810合约meg期货(期货合约2201)

标签: 函数 合约 代币

文章来源: summer
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至405936398@qq.com 举报,一经查实,本站将立刻删除。
相关资讯
虚拟币交易平台app活动 正规的虚拟币交易平台有哪些?
虚拟币交易平台app活动 正规的虚拟币交易平台有哪些? 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...

虚拟快币充值平台官网入口,虚拟快币充值平台官网入口下载
虚拟快币充值平台官网入口,虚拟快币充值平台官网入口下载 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...

整治虚拟货币挖矿省份排名?欧美国家为什么没有禁止?
整治虚拟货币挖矿省份排名?欧美国家为什么没有禁止? 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...

虚拟货币与虚拟银行学?什么是虚拟币账户
虚拟货币与虚拟银行学?什么是虚拟币账户 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...

加强虚拟货币 关于进一步规范和处置虚拟货币交易
加强虚拟货币 关于进一步规范和处置虚拟货币交易 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...

虚拟币 平台?虚拟币平台
虚拟币 平台?虚拟币平台 1970-01-01

// 类比一维数组   // 把合约调用者的余额转移 _value 个tokens给用户 _to function tr...