Download as pdf or txt
Download as pdf or txt
You are on page 1of 50

以太坊与智能合约

www.juzix.net
以太坊与智能合约 www.juzix.net

 1. 以太坊的产生背景
 2. 以太坊的发展历程
 3. 以太坊的整体架构
 4. 以太坊数据结构
 5. 以太坊交易实现
 6. 以太坊区块实现
 7. 虚拟机和智能合约的发展
 8. 智能合约开发
 9. 以太坊虚拟机实现
 10. TheDao事件分析
 11. 以太坊2.0
 12. PlatON
1. 以太坊的产生背景 www.juzix.net

2009年,比特币出现之后, 2013年,Vitalik Buterin提出了 2014年,以太坊基金会成立,Vitalik、


出现各种类似比特币项目, 以太坊概念,一种能够被重编程 Gavin Wood和Jeffrey Wilcke创建了
运行在不同节点,每个项目 用以实现任意复杂计算功能的单 以太坊项目,作为下一代区块链系统。
重复、独立创建一个类似 一区块链 以太坊是一个有智能合约功能的公共
比特币的系统 区块链平台。以太坊类比操作系统,
智能合约类比应用
2. 以太坊的发展历程 www.juzix.net

2015年7月, 2016年3月, 2016年6月, 2017年10月, 2019年3月, 以太坊2.0,


边境 家园 The DAO事 拜占庭 君士坦丁堡 宁静(Serenity)
(Frontier), (Homestead), 件, (Byzantium), (Constantinople),
第一个版本 稳定性、易用 分叉ETH和 延迟引爆难度、 优化性能、承前
性 ETC 降低区块奖励 启后

大都会
(Metropolis)
3. 以太坊的整体架构 www.juzix.net
4. 以太坊数据结构-账户 www.juzix.net

外部账户和合约账户对比:
账户类型
账户构成 外部账户 合约账户
账户私钥 一般在以太坊的钱包客户端创建通过私钥 没有对应的账户私钥
算法创建生成,私钥由账户所有者自己保
管存储
账户地址 账户地址基于账户的私钥采用地址生成算 账户地址采用智能合约发布者的账户相关信息推
法推导得出 导得出
账户链上生成 通过账户私钥签名一笔交易发往以太坊的 通过外部账户向以太坊发布智能合约时创建生成
节点创建生成
账户存储 仅仅存储一个账户对应的交易序号和账户 除了外部账户的存储信息外,还存储该账户对应
的有效余额 的智能合约数据。这些智能合约的数据按照特定
的编码方式进行组织和存储

账户代码 无对应的账户代码 保存了智能合约的账户代码内容,该内容即为编


写的智能合约代码经过编译器编译后字节码信息
4. 以太坊数据结构-账户 www.juzix.net

账户对应的数据结构,定义在core/state/state_object.go文件中:
type Account struct {
Nonce uint64 // 账户交易序号
Balance *big.Int // 账户余额,单位是wei
Root common.Hash // 智能合约数据存储的默克尔树根节点hash值
CodeHash []byte // 智能合约二进制代码的hash值
}
type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account
data Account
db *StateDB
......
// Write caches.
trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
......
}
4. 以太坊数据结构-账户 www.juzix.net

执行交易的时候创建账户逻辑,core/state_transition.go:
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) {
......
if contractCreation {
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) // 这里将生成合约账户
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value) // 这里将在链上生成外部账户
}
......
}

从core/vm/evm.go中,可以看出通过caller地址和nonce生成合约地址
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr
common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
}
4. 以太坊数据结构-区块 www.juzix.net

区块:记录一段时间内发生的交易和状态结果的数据结构,是对当前账本的一次共识。
4. 以太坊数据结构-区块 www.juzix.net

# /core/types/block.go
type Block struct {
header *Header // 区块头信息
uncles []*Header // 叔区块头列表
transactions Transactions // 区块包含的交易列表
......
}
type Header struct {
ParentHash common.Hash // 父区块hash
UncleHash common.Hash // 叔区块列表hash
Coinbase common.Address // 矿工账户, 区块奖励和打包费用
Root common.Hash // 状态默克尔树根节点hash
TxHash common.Hash // 区块体交易列表的默克尔树根节点hash值
ReceiptHash common.Hash // 收据默克尔树根节点hash
Difficulty *big.Int // 区块难度值
Number *big.Int // 区块编号
Nonce BlockNonce // 区块随机数
...... // GasLimit 区块gas费用的上限, GasUsed区块交易执行已经消耗的gas费用总和
}
4. 以太坊数据结构-交易 www.juzix.net

交易是一条外部账户发送到区块链上另一账户的消息的签名数据包。交易定义在core/types/transaction.go中
type Transaction struct {
data txdata
......
}
type txdata struct {
AccountNonce uint64 // 用来区别同一用户发出的不同交易的标记
Price *big.Int // 表示发送者愿意支付给矿工的Gas价格
GasLimit uint64 // 交易允许消耗的最大Gas数量
Recipient *common.Address // 交易接受者的地址,如果为空表示是一个合约创建交易
Amount *big.Int // 发送者要转移给接受者的以太币数量
Payload []byte // 数据字段,如果存在,表明该交易是一个创建或者调用智能合约交易
V *big.Int // 签名数据V值,用于恢复公钥
R *big.Int // 签名的R值
S *big.Int // 签名的S值
......
}
5. 以太坊交易实现 www.juzix.net

节点A 节点B
客户端1 1.发送交易
HttpServer 交易 6.执行交易 区块
2.构造交易

客户端2 1.发送交易 交易队列

3.验证导入交易 5.添加交易到区块

交易队列 worker ethash


4.获取交易 7.打包区块 验证添加到队列

4.发送事件 P2P 5. 广播交易 P2P


5. 以太坊交易实现 www.juzix.net

PublicTransactionPoolAPI EthAPIBackend TxPool ProtocolManager

SendTransaction

SendTx
AddLocal
AddLocals AddRemotes

addTxs
addTxsLocke
dadd

requestPromoteExecutables

runReorg

发送 NewTxsEvent

BroadcastTxs
5. 以太坊交易实现 www.juzix.net

用户通过JSON RPC发起eth_sendTransaction请求,会调用PublicTransactionPoolAPI的SendTransaction:
#internal/ethapi/api.go
func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
......
if args.Nonce == nil {
// Hold the addresse's mutex around signing to prevent concurrent assignment of
// the same nonce to multiple accounts.
s.nonceLock.LockAddr(args.From)
defer s.nonceLock.UnlockAddr(args.From)
}
......
// Assemble the transaction and sign with the wallet
tx := args.toTransaction() //创建交易
signed, err := wallet.SignTx(account, tx, s.b.ChainConfig().ChainID) //对交易进行签名
if err != nil {
return common.Hash{}, err
}
return SubmitTransaction(ctx, s.b, signed) //提交到交易池
}
5. 以太坊交易实现 www.juzix.net

再看看SubmitTransaction函数:
#internal/ethapi/api.go
func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
......
}
#eth/api_backend.go
func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
return b.eth.txPool.AddLocal(signedTx)
}
#core/tx_pool.go
func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
......
errs, dirtyAddrs := pool.addTxsLocked(txs, local) //交易放入queue列表中
......
done := pool.requestPromoteExecutables(dirtyAddrs) //从queue中选取一些交易放入pending列表中等待执行
......
}
5. 以太坊交易实现 www.juzix.net

pool.addTxsLocked会调用到下面函数
#core/tx_pool.go
func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {
......
if err := pool.validateTx(tx, local); err != nil { //验证签名、余额、nonce等
log.Trace("Discarding invalid transaction", "hash", hash, "err", err)
invalidTxMeter.Mark(1)
return false, err
}
// If the transaction pool is full, discard underpriced transactions,当txpool已满,剔除掉低油价的交易
// 如果低于最低价,直接丢弃该交易返回。如果高于最低价,则从txpool中剔除一些低价的交易
if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue {
......
// New transaction is better than our worse ones, make room for it
drop := pool.priced.Discard(pool.all.Count()-int(pool.config.GlobalSlots+pool.config.GlobalQueue-1),
pool.locals)
for _, tx := range drop {
......
pool.removeTx(tx.Hash(), false)
}
}
5. 以太坊交易实现 www.juzix.net

// Try to replace an existing transaction in the pending pool


from, _ := types.Sender(pool.signer, tx) // already validated
//两个交易nonce相同,保留gas price高的那一笔
if list := pool.pending[from]; list != nil && list.Overlaps(tx) {
// Nonce already pending, check if required price bump is met
inserted, old := list.Add(tx, pool.config.PriceBump)
......
return old != nil, nil
}
// New transaction isn't replacing a pending one, push into queue,加入到queue列表中
replaced, err = pool.enqueueTx(hash, tx)
if err != nil {
return false, err
}
// 默认会保证本地交易优先被加到txpool中
.....
return replaced, nil
}
5. 以太坊交易实现 www.juzix.net

pool.requestPromoteExecutables会调用下面函数
#core/tx_pool.go
func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirtyAccounts *accountSet, events
map[common.Address]*txSortedMap) {
......
//把交易从queue列表“提拔”到pending列表
promoted := pool.promoteExecutables(promoteAddrs)
for _, tx := range promoted {
......
events[addr].Put(tx)
}
......
// Notify subsystems for newly added transactions
if len(events) > 0 {
......
//发送Event
pool.txFeed.Send(NewTxsEvent{txs})
}
}
5. 以太坊交易实现 www.juzix.net

handler.go将把交易广播给其他节点。
#eth/handler.go
func (pm *ProtocolManager) txBroadcastLoop() {
for {
select {
case event := <-pm.txsCh:
pm.BroadcastTxs(event.Txs)

// Err() channel will be closed when unsubscribing.


case <-pm.txsSub.Err():
return
}
}
}
6. 以太坊区块实现 www.juzix.net

节点A 节点B
5.验证区块
BlockValidator body和状态 BlockChain 6.保存数据 LevelDB

BlockChain
4.验证区块头
3.写入区块链

worker ethash
Block 2.共识区块 验证执行区块

1..执行交易生成临时区块

8. 广播区块 P2P
7.发送事件 P2P
6. 以太坊区块实现 www.juzix.net

worker state_processor.go Ethash BlockChain ProtocolManager

commitNewWork

ApplyTransaction

commit
FinalizeAndAssemble

Seal

发送消息

WriteBlockWithState

发送NewMinedBlockEvent

BroadcastBlock
6. 以太坊区块实现 www.juzix.net

先看看miner/worker.go中的commitNewWork函数:
func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) {
......
pending, err := w.eth.TxPool().Pending() //取交易池中pending的交易
......
if len(localTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)
if w.commitTransactions(txs, w.coinbase, interrupt) { // 执行本地交易
return
}
}
if len(remoteTxs) > 0 {
txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs)
if w.commitTransactions(txs, w.coinbase, interrupt) { // 执行远程交易
return
}
}
w.commit(uncles, w.fullTaskHook, true, tstart) // 提交交易
}
6. 以太坊区块实现 www.juzix.net

commit函数中会发送taskCh消息,处理taskCh消息代码如下所示:
case task := <-w.taskCh:
......
if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { // 共识引擎共识区块
log.Warn("Block sealing failed", "err", err)
}

共识引擎共识成功后,resultCh会收到消息代码如下:
case block := <-w.resultCh:
......
stat, err := w.chain.WriteBlockWithState(block, receipts, task.state) // 写入区块链
......
w.mux.Post(core.NewMinedBlockEvent{Block: block}) // 发送事件给P2P模块
......
7. 虚拟机和智能合约的发展 www.juzix.net

与传统合约相比,区块链智能合约有如下优势:
1. 逻辑的明确性,不容易产生歧义。
2. 不可能被篡改。合约执行记录,可以作为永久凭证。
3. 合约执行的强制力可以保证。

Wasm:
EVM:
 支持流行的语言开发智能合约.
 只支持特定开发语言
 受益于十多年来LLVM编译器优化
 调试和测试比较困难
 高性能: 每秒5000多次执行
 有许多限制,如:堆栈(深度1024)、浮点数、小于
 各大公司不断发展,如: Google, Apple, Microsoft,
256位整数
Mozilla, and Facebook
 很少有人能够扩展EVM和所需的工具
 CPU 级别直接支持64位和32位整数运算
7. 虚拟机和智能合约的发展 www.juzix.net

dapps:https://www.stateofthedapps.com/
优势:公开透明,去中心化,具有激励机制
8. 智能合约开发 www.juzix.net

智能合约使用Solidity编写,使用Solidity编译器编译成字节码。
Solidity提供了一个集成开发环境--Remix(http://remix.ethereum.org)

智能合约操作:
1. 创建合约:

编译 发起 部署 以太坊区
编写合约 字节码 交易
块链网络

2. 调用合约:

合约调用 调用 以太坊区
交易 块链网络
8. 智能合约开发 www.juzix.net

Solidity:Solidity是一种用于编写智能合约的高级语言,语法类似于JavaScript。(https://solidity.readthedocs.io)
1. Solidity中的合约与面向对象编程语言中的类很相似,在一个合约中可以声明多种成员,包括状态变量、函数、
函数修改器、事件等。同时,一个合约可以继承另一个合约。
2. 状态变量是永久存储在合约账户存储中的值,用于保存合约的状态。Solidity语言提供了多种类型的变量。
3. 函数是合约的执行单位,一个合约可能包含各种功能函数,它们相互调用,共同组成了合约的工作逻辑。
4. 函数修改器可用于改变函数的行为,在函数执行前插入其他逻辑。
5. 事件用于记录合约执行过程中发生的各种事件和状态变化。
contract Simple {
uint public data;
address public creater;
constructor() public {
creater = msg.sender;
}
modifier onlyCreater() {
require(msg.sender == creater);
_;
}
function testFun() onlyCreater public {
emit Transfer(msg.sender);
}
event Transfer(address indexed _from);
}
8. 智能合约开发 www.juzix.net

通过 http://remix.ethereum.org IDE和metamask演示ERC20合约的编写,部署和调用。合约部分代码如下:
contract TTEST {
string public name = "TTEST"; // token name
string public symbol = "TTEST"; // token symbol
uint256 public decimals = 6; // token decimals number
uint256 public totalSupply = 0; // token totalSupply
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
address public owner = address(0);
constructor (uint256 initialSupply) public {
owner = msg.sender;
totalSupply = initialSupply;
balanceOf[msg.sender] = initialSupply;
emit Transfer(address(0), msg.sender, initialSupply);
}
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_to != address(0)); // Prevent transfer to 0x0, use burn() instead
require(balanceOf[msg.sender] >= _value);
require(balanceOf[_to] + _value >= balanceOf[_to]); // Check for _value negative and add overflow
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}}
8. 智能合约开发 www.juzix.net

Truffle:合约编译、部署、测试。JavaScript编写的Node.js项目。
web3.js:https://web3js.readthedocs.io/en/1.0/,通过web3接口与节点交互。

安全性:
1. 由于我们将使用智能合约来持有代币
2. 所有智能合约都被公开的执行,源码很容易获得,代码中隐藏的Bug和漏洞也更容易被恶意攻击者发现。
9. 以太坊虚拟机实现 www.juzix.net

1. EVM是以太坊中的堆栈指令解释执行器,提供图灵完备的执行环境。

2. 由于智能合约的执行结果需要在以太坊的网络节点之间进行共识,因此EVM的所有指令执行必需具有确定性的输出。

3. EVM为了保证交易的可终止性,引入了执行器燃料Gas,按照执行指令的复杂度、所需要的内存空间等对每个
步骤进行计价,消耗指定数量的Gas,当交易所赋予的Gas消耗为零时则停止指令的执行,退出执行环境。

EVM虚拟机部分指令如下所示:
9. 以太坊虚拟机实现-指令 www.juzix.net

指令名 指令功能 执行参考


STOP 停止虚拟机的指令执行 stop()
ADD 实现两个数x和y的相加,返回x+y add(x, y)
MUL 实现两个数x和y的相乘,返回x*y mul(x,y)
SUB 实现两个数x和y的相减,返回x-y sub(x, y)
DIV 实现两个数x和y的相除,返回x/y div(x,y)
SDIV 有符号的除法运算 sdiv(x,y)
MOD 实现两个数x和y的取余,返回x%y mod(x,y)
SMOD 有符号的取模运算 smod(x,y)
ADDMOD 返回(x + y) % m addmod(x, y, m)
MULMOD 返回(x * y) % m mulmod(x, y, m)
EXP 实现两个数x和y的指数运算,返回x^y exp(x, y)
LT 逻辑运算小于返回1否则返回0 lt(x, y)
GT 逻辑运算大于返回1否则返回0 gt(x, y)
9. 以太坊虚拟机实现-指令 www.juzix.net

SLT 逻辑运算小于返回1否则返回0,有符号的数比较 slt(x, y)


SGT 逻辑运算大于返回1否则返回0,有符号的数比较 sgt(x, y)
EQ 逻辑运算等于返回1否则返回0 eq(x, y)
ISZERO x为0值返回1否则返回0 iszero(x)
AND 逻辑与运算,返回x & y and(x, y)
OR 逻辑或运算,返回x | y or(x, y)
XOR 逻辑异或运算,返回x ^ y xor(x, y)
NOT 逻辑非运算,返回~x not(x, y)
BYTE 取x的n字节内容,n需要小于32 byte(n, x)
SHA3 对内存数据从p开始的n字节进行hash运算,返回计算结果 sha3(p, n)
ADDRESS 返回当前合约的地址 address
BALANCE 获取指定地址在区块链上的账户余额,单位为wei balance(a)
ORIGIN 获取当前执行交易的发送方 origin
当前指令的调用方,如果跨合约调用返回调用合约地址,否 caller
CALLER 则返回origin
CALLVALUE 当前调用所转移的以太币金额,单位为wei callvalue
9. 以太坊虚拟机实现-指令 www.juzix.net

CALLDATALOAD 返回当前调用data数据的指定位置开始的32字节内容 calldataload(p)


CALLDATASIZE 返回当前调用data数据的字节数 calldatasize
从当前调用data数据中拷贝从f开始的s字节至内存的t位置 calldatacopy(t, f, s)
CALLDATACOPY

CODESIZE 返回当前合约的代码字节数 codesize


CODECOPY 从当前合约代码中拷贝从f开始的s字节至内存的t位置 codecopy(t, f, s)
GASPRICE 返回当前执行交易中的gasprice gasprice
EXTCODESIZE 查询指定地址的合约代码大小 extcodesize(a)
EXTCODECOPY 从指定合约a的代码中拷贝从f开始的s字节至内存的t位置 extcodecopy(a, t, f, s)
RETURNDATASIZE 最后一条指令返回的数据字节数 returndatasize
从最后的指令返回数据中拷贝从f开始的s字节至内存的t位置 returndatacopy(t, f, s)
RETURNDATACOPY

BLOCKHASH 查询指定块高的区块hash值 blockhash(b)


COINBASE 查询当前区块的打包账户 coinbase
TIMESTAMP 查询当前区块的打包的UTC时间 timestamp
NUMBER 返回当前区块的块高 number
DIFFICULTY 查询当前区块的难度值 difficulty
9. 以太坊虚拟机实现-堆栈执行器 www.juzix.net

以太坊的EVM虚拟机是一种堆栈指令解释执行的执行器,通过从区块链的数据库中加载智能合约的二进制代码,
从头开始执行指令,结合交易请求的相关参数和相关的跳转指令来实现智能合约执行流程。
开始 判断并调整
堆栈空间

初始化指令 判断指令
信息 类型

调用设置的
拷贝合约代 指令执行

码 Callback

优化合约代 结束
码和跳转指 更新内存空
令 间

获取下一条 还存在有 扣减执行费


执行指令 效指令 用

获取指令参
执行指令

9. 以太坊虚拟机实现-执行费用 www.juzix.net

1. 为了能够激励节点有足够的动力来维护以太坊网络的稳定运行,对提供节点算力的用户需要支付一定的奖励,这种
奖励来源于两份方面:一是单纯的区块打包奖励;一是打包交易的手续费。

2.交易手续费在以太坊中通过交易执行的复杂度来定价,这种定价取决于执行交易需要消耗多少算力。以太坊的交易
手续费也从一定层度上保证了智能合约不会出现执行后无法终止的情况,通过经济的手段防止了恶意的交易在区块链
网络上传播从而引起网络瘫痪。

3.以太坊的VM除了执行指令消耗Gas外,还对存储空间的使用也计入可Gas的计价范畴。消耗的存储空间越大,那么
需要支付的Gas费用也会越高。这个存储空间包括内存空间和磁盘空间,磁盘空间指的是智能合约的Storage变量。
9. 以太坊虚拟机实现-源码 www.juzix.net

先看看虚拟机解释器文件core/vm/interpreter.go文件的Run函数:
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
......
for atomic.LoadInt32(&in.evm.abort) == 0 { //循环取每一个指令
......
op = contract.GetOp(pc)
......
if !contract.UseGas(operation.constantGas) { // 计算指令gas消耗
return nil, ErrOutOfGas
}
......
if operation.dynamicGas != nil { // 计算内存gas消耗
cost, err = operation.dynamicGas(in.gasTable, in.evm, contract, stack, mem, memorySize)
if err != nil || !contract.UseGas(cost) {
return nil, ErrOutOfGas
}
}
......
res, err = operation.execute(&pc, in, contract, mem, stack) // 执行指令
......
}
return nil, nil
}
9. 以太坊虚拟机实现-源码 www.juzix.net

再看看PUSH指令实现core/vm/instructions.go文件的opPush1函数:
func opPush1(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
var (
codeLen = uint64(len(contract.Code))
integer = interpreter.intPool.get()
)
*pc += 1
if *pc < codeLen {
stack.push(integer.SetUint64(uint64(contract.Code[*pc])))
} else {
stack.push(integer.SetUint64(0))
}
return nil, nil
}
指令定价级别(core/vm/gas.go):
const (
GasQuickStep uint64 = 2
GasFastestStep uint64 = 3
GasFastStep uint64 = 5
GasMidStep uint64 = 8
GasSlowStep uint64 = 10
GasExtStep uint64 = 20
)
10. TheDao事件分析 www.juzix.net

The Dao可谓是以太坊上的一个明星项目,因为该项目通过公开募集的方式众筹到了1.6亿美元,一跃成为当时最大
的众筹项目而出名。将该项目超过30%的募集资金多达360多万的以太币转移到了黑客指定的账户中。

The Dao合约的splitDAO函数是这个攻击漏洞的主入口,该函数的目的是从原有的Dao中分裂出一个小规模的子Dao,
攻击者利用了这一可行的提取以太币的机制,通过创建子Dao来将以太币持续的转入指定的账户。
splitDao的相关漏洞代码如下:
10. TheDao事件分析 www.juzix.net

function splitDAO(uint _proposalID, address _newCurator) noEther


onlyTokenholders returns (bool _success)
{
...... // 省略部分合法性校验的代码
// 转移以太币并创建新的代币
uint fundsToBeMoved =
(balances[msg.sender] * p.splitData[0].splitBalance) /
p.splitData[0].totalSupply;
if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false)
throw;
...... // 省略部分代码
// 转移以太币至指定的账户,销毁当前合约中的DAO代币
Transfer(msg.sender, 0, balances[msg.sender]);
withdrawRewardFor(msg.sender);
totalSupply -= balances[msg.sender];
balances[msg.sender] = 0;
paidOut[msg.sender] = 0;
return true;
}
10. TheDao事件分析 www.juzix.net

在上述代码中,先调用withdrawRewardFor函数转移以太币给交易发送方,然后再通过设置totalSupply、balances、
paidOut等参数来销毁对应的DAO代币。这可能导致多次转出以太币至指定的账户。
通过查看withdrawRewardFor的代码也存在相关的漏洞,withdrawRewardFor代码如下:
function withdrawRewardFor(address _account) noEther internal returns (bool _success)
{
if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account])
throw;

uint reward =
(balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account];
if (!rewardAccount.payOut(_account, reward))
throw;
paidOut[_account] += reward;
return true;
}
在上述代码中,对paidOut的更新发生在payout方法的调用之后,同样意味着可以通过竞争的方式多次调用
payout方法。payout方法的实现刚好又满足了这样的情况。下面给出payout的方法实现。
10. TheDao事件分析 www.juzix.net

function payOut(address _recipient, uint _amount) returns (bool)


{
if (msg.sender != owner || msg.value > 0 || (payOwnerOnly && _recipient != owner))
throw;
if (_recipient.call.value(_amount)()) {
PayOut(_recipient, _amount);
return true;
} else {
return false;
}
}

上述代码的实现主要是通过call方法的调用向指定的账户转移以太币。攻击则是从这个点开始的:
1. 在以太坊中根据solidity的规范,如果向一个合约账户发送不带任何参数的请求,则fallback函数将在找不到对应的
处理函数时自动执行。上述代码中的call方法对应的地址为一个钱包合约地址,并且实现了一个fallback函数,在
fallback函数中再次调用splitDao方法,进入这个递归循环调用的过程。

2. 由于上述代码中的call方法没有明确的指定Gas费用,意味着当前剩余的Gas会被用于这次Call调用,因此递归调用
的次数就受限于攻击者发送原始交易时指定的Gas。
10. TheDao事件分析 www.juzix.net

从上述代码实现来看,这个攻击漏洞主要存在如下两个问题:
1. 合约代码不合理的执行次序
在展示的代码中,可以看到两处的代码执行顺序存在问题,账户余额的变更和账户转账操作应该遵循先变更账户后
转账的逻辑。
2. 未知代码不受限制的执行
在智能合约中要谨慎使用call或者delegatecall操作,因为这样的操作可能会导致一些未知的恶意代码执行,
需要确保被调用地址的合约实现逻辑是正确合法的,同时也最好限制有效的Gas费用,注意fallback方法的执行。

由于The Dao的合约机制,攻击者转移控制的以太币需要在一定的时间后才可以被拿走,这给该次的事件留下了
一定的缓冲处理时间。最终的机制是通过硬分叉的机制,将The Dao智能合约中的以太币转移到了一个新的账户中,
进而返还给众筹的参与者。最后以太坊社区未能达成统一的意见,系统在1920000区块节点分叉成以太坊经典ETC和
现有的以太坊ETH。
11. 以太坊2.0 www.juzix.net

以太坊执行交易的性能一直成为整个系统的瓶颈。分片(sharding)技术就是为了解决所有区块链面临的扩展性问题。
分片之后,每个节点只需要存储、处理一部分交易,从而解决区块链面临的扩展性问题。跨分片交易会引入复杂度。

POW由于消耗大量的算力和电力,以太坊的POS共识协议称为Casper。可以更加去中心化,高能效,经济安全。

以太坊的一位开发者曾经表示:“其实在最开始,大家并没有对以太坊虚拟机做过很深入的规划”。“以太坊虚拟机只
不过被当作像瑞士军刀那样的工具看待-----用处不小,但并不完美”。
eWASM 将是以太坊版本的 WASM (WebAssembly 的缩写)。eWASM 不确定时间。
11. 以太坊2.0 www.juzix.net

2019 2020 2021


Phase 0 -Beacon Chain, Phase 1 - Shard Chains, Phase 2 - State Execution,
Casper FFG 分片链 eWASM
12. PlatON www.juzix.net

官网:https://platon.network
12. PlatON www.juzix.net

使用流行的WASM 库支持:
特性:
 C++ 14 标准
 支持多种语言.
 JSON 库
 每秒5000多次执行.
 以太坊RLP协议
 wasm合约, 隐私合约和可验证合约具有一致性开发体验.
 部分boost库
12. PlatON www.juzix.net

通过可验证计算传递信任:
计算节点产生证明 (𝛑𝛑)
𝛑𝛑的验证比计算 f(x)要快很多

PlatON将计算与共识分离:
计算网络增加了区块链的无信任
计算能力.
区块链仅负责验证.
12. PlatON www.juzix.net

 隐私合约使用高级语言编写,编译成WASM代码部署到区块链上.
 具有预付计算费用的交易触发隐私合约的执行,数据提供者本地提供输入数据.
 结果返回到区块链验证,验证通过就结算费用.
12. PlatON www.juzix.net

共识机制:PPOS + CBFT

节点质押,用户
利用VRF选取25名 利用CBFT共 结算周期获取
委托,选取101名
当前轮验证人节 识轮流出块。 质押奖励
验证人
点 获得出块奖励
为了数据的流动

矩阵元

www.juzix.net

You might also like