参考教程:北大肖臻老师
记账(挖矿)过程
记账(挖矿)就是计算Nonce(随机数)的过程,让Nonce匹配给定的Hash值,Hash值前面的0越多,则说明难度越大
-
相同数据生成的哈希值不变,不同数据哈希值不同
-
哈希加密算法不可逆,知道数据可以推出哈希值,但知道哈希值无法推出源数据内容(非对称加密)
/*
判断生成的Hash值是否符合难度
*/
function isValidHashDifficulty(hash, difficulty){
for(var i = 0, b = hash.length; i < b; i++){
if(hash[i] != '0'){
break;
}
}
return i >= difficulty;
}
/*
通过不断测试Nonce来找到符合条件的Hash值
*/
let nonce = 0;
let hash;
let input;
let difficulty = 3;
while(!isValidHashDifficulty(hash, difficulty)){
nonce = nonce + 1;
input = index + previousHash + timestamp + data + nonce;
hash = CryptoJS.SHA256(input)
}
比如现在有一笔交易需要写入区块中,那么当前分布式网络节点就会去计算“交易数据+一些其他数据+Nonce”的哈希值,若算出的哈希值(00asdjkh213jk)不匹配难度(假设难度为3),那么让Nonce加1,继续计算哈希值,直到哈希值满足难度为止,比如000sajdh213hiuh
硬分叉 & 软分叉
共识算法
PoW(Proof of Work)
工作量证明,GHOST协议
PoS(Proof of Stake)
股权证明,Casper协议
Comparison Between BTC & ETH
出块时间
-
BTC的出块时间大约是10分钟产生一个新区块
-
BTC出块时间慢,交易效率低,若使用BTC作为支付工具,则转账的BTC最多可能需要10分钟才能到账(被确认),因为每一笔交易都是记录在区块里的,只有当记录该笔交易的区块被挖出且被全网节点确认之后才算合法,此时转账才是有效的
-
BTC虽然出块时间慢,但恰是较长的出块时间保证了BTC系统的稳定性。
-
-
ETH的出块时间大约是10秒钟产生一个新区块
-
ETH出块时间快,交易效率高。
-
ETH出块时间虽然块,但存在一定程度上的不稳定性。试想两个算力不同的节点A与B(A:10,B:100),若在ETH系统中,当B挖出新区块并广播全网节点时,A可能还未挖出新区块,当B新挖出的区块得到全网确认后继续挖下一个新区块时,A可能才挖出新区块,但此时显然A挖出的新区块就被抛弃了,这样有算力优势的节点会越来越有优势,造成一定程度的中心化。
-
交易对象
-
BTC:UTXO
-
ETH:Account
比特币
BitCoin(BTC),非图灵完备
密码学原理
BTC用到了密码学中的哈希和签名
哈希(Hash)
三个性质:Collision resistance(防止哈希碰撞)、Hiding(根据哈希无法推出原输入值)、Puzzle Friendly(Difficult to Solve but Easy to verify)
-
MD5:不安全,可以人为制造Hash Collision
-
SHA-256(Secure Hash Algorithm)
对称加密 & 非对称加密
Symmetrical Encryption & Asymmetric Encryption
-
对称加密:单密钥。通信双方商定一个共同的密钥,在传输信息时用密钥加密,接收方收到时再用密钥解密。但密钥的分发过程较繁琐,且容易被窃听。
-
非对称加密:密钥对(公钥和私钥)。每个通信节点都拥有一对公钥和私钥,公钥可公开,私钥存于本地。当发送方给某一接收方发送信息时,用接收方的公钥对信息进行加密,接收方收到信息后用自己保存在本地的私钥进行解密。
签名(Signature)
验证交易的真实性,即是否由“本人”发出
- 当某节点发起一笔转账交易时,用自己的私钥给交易签名,而后发布到区块链上等待矿工打包交易。当矿工打包交易时,需验证这笔交易是否的确由该节点发出,所以需要用该节点的公钥去验证这笔交易的源头真实性。
数据结构
哈希指针
后一个块的哈希指针指向前一个块,当某个块被篡改时,其后的所有块都会被改动
Merkle Tree
默克尔树(其实就是把二叉树的指针改成哈希指针)
-
Block:Block Header + Block Body
-
Block Header
-
version
-
hash of previous block header
-
merkle root hash
-
target
-
nonce
-
-
Block Body
- transaction list
-
全节点(Full Node):保存整个区块的内容
-
轻节点(Light Node):只保存Block Header
-
-
Merkle Proof
协议
双花攻击
女巫攻击
共识
Consensus
实现
UTXO
Unspent Transaction Output
-
每笔交易都需满足Input = Output,所以要知道某一地址的账户余额,只需查询UTXO中,转入该地址的交易额总量
-
例子:Coinbase => A(10),A(10) => B(4) + A(6)
- 那么此时UTXO中保存的条目就是“未花费的输出”,即A(10) => B(4) + A(6),此时对UTXO中的交易输出进行查询即可得知A和B地址的余额
Six Confirmation
当前块挖出后收到的确认为One Confirmation,其后紧跟的一个块挖出后得到的是Two Confirmation,BTC设计当前块在Six Confirmation后被篡改的可能性才很低(因为可能有恶意节点分叉攻击,恶意构造最长链覆盖主链)
挖矿难度
挖矿:Hash(Block Header) <= Target
- d i f f i c u l t y = d i f f i c u l t y _ 1 _ t a r g e t t a r g e t _ n o w difficulty = \frac{difficulty\_1\_target}{target\_now} difficulty=target_nowdifficulty_1_target
难度调整
2016个区块调一次
- T a r g e t = T a r g e t ∗ A c t u a l T i m e E x p e c t e d T i m e Target = Target * \frac{Actual Time}{Expected Time} Target=Target∗ExpectedTimeActualTime
挖矿
-
矿池
- 矿工工作量证明Share:Almost Valid Block
BTC脚本
分叉
-
State Fork
临时性分叉
-
Fork Attak
-
Deliberate Attack
-
-
Protocol Fork
协议升级造成的分叉,假设新节点占51%以上
-
Hard Fork
硬分叉:新节点认可旧节点的协议,旧节点不认可新节点的协议
-
Soft Fork
软分叉:新节点不认可旧节点的协议,旧节点认可新节点的协议
-
加密和哈希
加密的目的是为了保证传输的可靠性以及接收方的可解密性,所以加密算法是不会造成信息丢失的。但哈希不同,以BTC中的哈希算法SHA-256为例,无论是多少位的输入,输出均为256位的哈希,这个过程是不可逆的,会造成信息的丢失。(如果可逆,那这个算法就是一个无敌的压缩算法了,因为多大的数据都能压到256位并且能回解出原数据)
以太坊
Ethereum(ETH),图灵完备
个人对于以太坊以及基于以太坊的各种代币的理解
以太坊账户之间的交易是在Ethereum的主链上进行的,例如一个账户要给另一个账户转账,那么就(利用MetaMask等钱包)向目标账户地址发起一个Transaction,value参数设置为转账数目即可。
基于以太坊的各种代币(如HT、BNB等)其实本质上就是Ethereum主链上布署的一个合约,该合约包含了代币本身之间互相交易的solidity代码。如要在代币之间进行交易,则就其本质上来说,是向该代币的合约地址发送Transaction,data参数设置为合约代码需要调用的函数选择器以及参数,进而调用合约内部的交易函数,进行Transfer,所以代币之间的转账其实是发生在代币合约内部,代币转账在以太坊主链上Transaction的value其实为0,Tranfer的参数才是真正的代币交易数目。而且各用户所拥有的代币余额其实都存储在该代币合约的存储空间中。
发展阶段
-
Frontier
-
Homestead
-
Metropolis
-
Byzantium
-
Constantinople
-
-
Saerenity
组成部分
-
P2P Network
-
Transaction
-
EVM
以太坊虚拟机
-
Blockchain
Google的LevelDB
-
Client
-
Go-Etheruem(Geth)
-
Parity
-
重要概念
-
Account
-
EOA(Externally Owned Account,外部账户):存储和代码均为空
-
Contract Account(合约账户):包含存储和代码
-
-
Address
-
Transaction
-
Gas
-
Gas Limit
防止ETH程序无限循环消耗资源
-
Gas Used
-
状态树(全局共享)
addr(160 bit) => state
Trie
字典树
Patricia Tree
路径压缩字典树
Merkle Patricia Tree
MPT
交易树 & 收据树(各个区块局部共享)
MPT
Bloom Filter
挖矿产出
GHOST协议
幽灵协议(Greedy Heaviest Observed SubTree)
-
区块奖励(Block Reward)
- 每产生一个新区块就有一笔固定的奖励给矿工。初始是5个Ether,现在是3个
-
叔块奖励(Uncle Reward)
解决“临时性分叉”
- 由于ETH产生区块的速度较快(约10s产生一个区块),但产生的区块需要得到全网节点的确认才算合法区块,而全网确认的时间较长,在全网确认的时间段内,很有可能已经又产生了同高度的新区块,这时就出现了临时性分叉,如下图所示。
- 而区块链在未分叉的情况下只承认最长的链,所以最长链上的区块才会被最终承认。如下图所示,Block #3被抛弃,Block #2被最终确认。但如果仅有区块奖励的话,对于Block #3上的矿工很不公平,所以才有了叔块奖励去奖励类似于出现临时分叉时Block #3上的矿工。(Block #3在ETH中是合法的区块,而在BTC中是废块。在ETH中,保留诸如Block #3的叔块有利于提升整个ETH系统的可靠性,因为每产生一个新区块是需要得到全网节点的确认的,节点越多,就需要得到越多节点的确认,所以更可靠)
-
叔块引用奖励(Uncle Referencing Reward)
挖矿收入
-
普通区块收入
-
固定奖励
-
区块内包含的所有程序的gas花费的总和
-
若普通区块引用了叔块,则每引用一个叔块可以得到固定奖励的1/32
-
-
叔块收入
- 叔块奖励 = (叔块高度 + 8 - 引用叔块的区块高度) * 普通区块奖励 / 8
挖矿算法
ethash
-
Memory Hard
-
ASIC Resistance
难度调整
每个区块都有可能调整
D ( H ) ≡ { D 0 = 131072 if H i = 0 max ( D 0 , P ( H ) H d + x × ς 2 ) + ϵ otherwise D(H) \equiv \begin{cases}D_{0} = 131072 & \text { if } \quad H_{\mathrm{i}}=0 \\ \max \left(D_{0}, P(H)_{H_{\mathrm{d}}}+x \times \varsigma_{2}\right)+\epsilon & \text { otherwise }\end{cases} D(H)≡{D0=131072max(D0,P(H)Hd+x×ς2)+ϵ if Hi=0 otherwise
-
D(H)是本区块的难度,由基础部分 P ( H ) H d + x × ς 2 P(H)_{H_{\mathrm{d}}}+x \times \varsigma_{2} P(H)Hd+x×ς2和难度炸弹部分 ϵ \epsilon ϵ相加得到
-
P ( H ) H d \mathrm{P}(H)_{H_{d}} P(H)Hd为父区块的难度,每个区块的难度都是在父区块难度的基础上进行调整
-
x × ζ 2 x \times \zeta_{2} x×ζ2用于自适应调节出块难度,维持稳定的出块速度
x ≡ ⌊ P ( H ) H d 2048 ⌋ ς 2 ≡ max ( y − ⌊ H s − P ( H ) H s 9 ⌋ , − 99 ) \begin{gathered} x \equiv\left\lfloor\frac{P(H)_{H_{\mathrm{d}}}}{2048}\right\rfloor \\ \varsigma_{2} \equiv \max \left(y-\left\lfloor\frac{H_{s}-P(H)_{H_{\mathrm{s}}}}{9}\right\rfloor,-99\right) \end{gathered} x≡⌊2048P(H)Hd⌋ς2≡max(y−⌊9Hs−P(H)Hs⌋,−99)
-
x x x 是调整的单位, ς 2 \varsigma_{2} ς2 为调整的系数。
-
y y y 和父区块的uncle数有关。如果父区块中包括了uncle ,则 y y y 为2, 否则 y \mathrm{y} y 为 1 。
- 父块包含uncle时难度会大一个单位,因为包含uncle时新发行的货币量大, 雴㚻适当提高难度以保持货币发行量稳定。
-
难度降低的上界设置为-99,主要是应对被黑客攻击或其他目前想不到的黑天鹅事件
y − ⌊ H s − P ( H ) H s 9 ] y-\left\lfloor\frac{H_{\mathrm{s}}-P(H)_{H_{\mathrm{s}}}}{9}\right] y−⌊9Hs−P(H)Hs]
- H s H_{s} Hs是本区块的时间戳, P ( H ) H S P(H)_{H_{S}} P(H)HS是父区块的时间戳,均以秒为单位,并规定 H s > P ( H ) H S H_{s}>P(H)_{H_{S}} Hs>P(H)HS
ϵ ≡ ⌊ 2 ⌊ H i ′ ÷ 100000 ⌋ − 2 ] H i ′ ≡ max ( H i − 3000000 , 0 ) \begin{aligned} \epsilon & \equiv\left\lfloor 2^{\left\lfloor H_{\mathrm{i}}^{\prime} \div 100000\right\rfloor-2}\right] \\ H_{\mathrm{i}}^{\prime} & \equiv \max \left(H_{\mathrm{i}}-3000000,0\right) \end{aligned} ϵHi′≡⌊2⌊Hi′÷100000⌋−2]≡max(Hi−3000000,0)
-
设置难度炸弹的原因是要降低迁移到PoS协议时发生Fork的风险:到时挖矿难度非常大,所以矿工有意愿迁移到PoS协议
-
ϵ \epsilon ϵ 是2的指数函数, 每十万个块扩大一倍, 后期增长非常快,这就是难度“炸弹”的由来。
-
H i ′ H_{i}^{\prime} Hi′ 称为fake block number, 甴真正的block number H i H_{i} Hi 减少三百万得到。这样做的原因是低估了PoS协议的开发难度, 需要延长大概一年半的时间(EIP100)。
权益证明
PoS(Proof of Stake)
挖矿算法完全无意义,只是为了抢夺打包交易的记帐权,挖矿算法对记帐完全没有帮助
- 不挖矿,谁持有币多谁权力大
智能合约
Smart Contract
The DAO
Decentralized Autonomous Organization
- 重入攻击
美链
Beauty Chain
以太币单位
- 1 ether = 1 0 18 10^{18} 1018 wei
私钥、公钥、地址
-
私钥(Private Key):256位的随机数,用于发送以太的交易中创建签名来证明自己对资金的所有权
-
公钥(Public Key):由私钥通过椭圆曲线加密secp256k1算法单向生成的512位(64字节)数
-
地址(Address):由公钥的keccak-256单向哈希,取最后20个字节(160位)派生出来的标识符
转账
例如发送1个ETH,还需算上手续费(发送方付)= Gas Num * Gas Price,发送方总消耗为(1 + Gas Num * Gas Price) ETH,而手续费最终是奖励给挖出该笔交易所在区块的矿工
全节点 & 轻节点
合约创建
给地址0x0发送一笔不带value的特殊交易创建合约(0x0既不是EOA也不是CA)
向EOA或合约传递value
传递value值,即转账给对方
向EOA或合约传递data
-
发给EOA:
-
发给合约:32字节的十六进制序列化编码
-
函数选择器:函数原型的Keccak256哈希的前4个字节,这允许EVM明确地识别将要调用的函数
-
函数参数:根据EVM定义的各种基本类型的规则进行编码
-
EVM
以太坊虚拟机
Geth
以太坊客户端
CMD
geth --datadir ./private/data init ./private/genesis.json //初始化创世区块
geth --datadir ./private/data --networkid 15 console //启动
geth --datadir ./private/data --networkid 15 console 2>./private/output.log //重定向启动
geth --datadir ./private/data --networkid 15 --rpc console 2>./private/output.log //可通过MetaMask Localhost 8545连接本地私链
geth --datadir ./private/data --dev console //开发者模式(无需.json配置文件,自动创建私链,自动挖矿)
--allow-insecure-unlock //HTTP连接允许解锁账户
--nodiscover //不寻找P2P节点
--rpcapi db,eth,net,web3,personal,web3 //开放给web3.js远程调用的api
Console-web3
web3.fromWei(int num) //wei => ether
web3.toWei(int num) //ether => wei
Console-personal
personal.newAccount() //创建新账户
personal.unlockAccount(string account) //解锁账户
Console-eth
eth.blockNumber //获取当前链区块数
eth.getBalance(string account) //获取当前账户余额
eth.coinbase //查看当前默认挖矿账户
eth.mining //查看当前是否在挖矿
eth.sendTransaction({from: string account, to: string account, value: int num}) //交易
Console-miner
miner.start(int thread) //开始挖矿(参数为线程数)
miner.stop() //停止挖矿
miner.setEtherbase(string account) //设置挖矿进账主账户
Web3.js@1.4.0
合约部署
var abiArray = xxx //.abi
var byteCode = xxx //.bin
var myContract = new web3.eth.Contract(abiArray)
myContract.deploy({
data: byteCode,
arguments: [arg1, arg2, ...] //The arguments of Constructor of Contract
}).send({
from: xxx,
gas: xxx,
gasPrice: xxx
}).then(instance => {
console.log(instance.options.address) //The allocated address of contract
})
myContract.options.address = instance.options.address
合约函数调用
.<FUNC_NAME>() //函数名直接调用
.call() //有view字段,函数不更改状态,故不发送交易(默认调用call())
.sendTransaction() //无view字段,函数可更改状态,故可发送交易(默认调用sendTransaction())
ganache-cli
以太坊客户端