Bitcoin and Blockchain

这是一篇越在2016年左右记录的笔记, 仅仅分享

比特币

比特币作为区块链的元祖, 引入了许多基本概念, 能作为学习区块链的开端.

目的

比特币的意图是实现一个去中心化的货币系统, 比特币的发行和支付由参与网络的所有节点所控制.

从原理上说, 所有节点都完全独立, 也就是说: 每个节点都拥有完整的货币数据,包括发行记录及支付记录; 每个节点都可以发行货币,也可以发起支付.

比特币主要解决了这个问题: 如何让网络中的所有节点都可以"彼此不信任"的"达成共识" . 彼此不信任是指:对于任意节点X而言, 其他节点彼此相同,没有特殊的"可信"节点(或者说,你无法判断某个节点是否可靠). "达成共识"是指, 所有节点最终都持有相同的货币数据, 主要是发行记录和支付记录.

"达成共识"的结果是所有人都持有相同的数据,但是并不是说相同的数据就是正确的数据,这些相同的数据也可能是被攻击后产生的数据. 比特币的设计只是大概率的能避免被攻击而已.

除了上述问题外, 比特币及相关的其他工具都是服务于安全和性能. 这两个部分涉及的细节很多,但是都不太重要.

理解比特币的工作流程,主要需要了解四个组件: 账户, 交易, 打包(挖矿), 共识

基本架构

  • 一般而言,任何一种区块链都由以下核心组件
    • 用于传播block及transaction的P2P网络
    • 用于保证chain唯一性的"consensus rules",一般是“某种POW算法 + 规则”。
    • 按照chain来组织的blocks,该chain是所有节点consensus通过的。
    • 鼓励节点维持chain增长的机制,一般就是创造block的奖励,即“挖矿奖励”
    • 对上述功能的开源实现,因为去中心必须保证彼此透明。

账户class User

  • 比特币的账户系统逻辑上就是一般的账户系统,保证只有用户本人能操作自己相关的资产. 可以由以下两个QA做概述.

  • Q: 如何在比特币系统中区分不同实体, 也就是如何创建自己的账号?

    • A: 在比特币中,你可以选择任意一个定长字符串作为你的账户, 且不需要密码. 为了避免因同名产生的账户被盗, 比特币建议: 设置尽可能好的随机账户名(选择可靠的随机数发生器), 保护好它,不要泄露, 如果某人确实随机的和你一样,从而能操作你的财产,那这一定是"天意".
  • Q: 不能把我的账户泄露出去,那我怎么在比特币系统中使用它?

    • A: 比特币的想法: 嗯,用户可以任意选择账户名,又要安全的和外部交互,做验证等操作, 这个问题怎么这么眼熟? 嗯,对,这不就可以用公私钥解决吗,用户选择的账户名作为私钥就行了. 但是关于这个系统可以写一本书啊. 比特币的答案: 你可以搜索一下公私钥机制,我只能说这么多了.
  • 具体实现中, 私钥字符串决定了账户, 由用户自己生成, 用于付款,自然要保管好. 公钥字符串由私钥生成, 用于收款,可以在外部分发. 公钥私钥一一对应.

交易class Trans

class Trans{
std::vector<Trans *> source;
std::map<User* ,int> target;// 向User转账的数额.
}
  • 每个交易Trans都包含std::vector<Trans * > source std::map<User *,int> target
    • 对于用户XX,它想要发起交易,则它要花的钱,必须是从某个地方转给它的. 换言之,若用户XX向YY等发起了一笔交易tran_x_to_y,那么tran_x_to_y.source[i].target都必须包含XX.
    • 对于tran_x_to_y,它必须要将tran_x_to_y.source中得到的所有费用通过tran_x_to_y.target转移出去,否则,剩余的部分将作为"手续费"交给矿工.
  • 最后, 比特币中, 账户之间的交易就是常规的"货币支付", 此时source是由之前由某个用户创建的交易; 从虚空产生的交易就是"货币发行",此时的source是虚空,有时称为创币交易,或coinbase交易. 总而言之,可以认为比特币系统中的货币数据就是 "交易记录". 后文只使用交易这个词了.
  • 比特币网络自身并不持有余额数据,余额可以通过所谓的UTXO计算出来,账户的UTXO是指:转账到某个账户,还没有被后续其他交易引用的比特币.
  • 矿工打包交易时,会执行交易对应的Transaction Script,以验证交易是合法的,比特币的设计中,基本可以保证只有私钥的持有者才能创建合法的转出交易.

打包

  • Q: 节点发出交易Trans后,如何被其余节点接受到自己的本地数据库中?
  • A: 综合考虑性能和安全性, 比特币使用打包机制. 在网络中的节点可以自愿作为打包节点,它们将自己收到的交易打成一个Block,并广播出去,其他节点则按共识原则来记录收到的Block
  • 为了服务于共识机制, 打包时不但需要收集Trans,还要保证自己给出的Block在数值上满足一个要求,这是一个费时费力的工作.
  • 为了激励打包者, 每打一个包, 打包者被允许自己构造,并添加一个Coinbase交易到自己将要打的包中,添加这个coinbase交易后,打包者将开始尝试使这个包满足要求.这另一方面还解决了货币发行的问题.
    • 一般而言,这个conbase交易的target就是打包者自己的账户地址,但是从技术上说,target是由打包者任意决定的.
  • 满足要求的算法可以近似为bool isOK(Chain,Block), Chain就是区块链当前的状态.
    • 一般而言,如果Chain改变了(例如你正在打包时,外界告诉你区块链更新了),那么返回值一定会变成false.
    • Block内容大致确定后,Block内能修改的包括:交易列表,交易顺序,Nonce值,Conbase交易(coinbase交易是打包者构造的,自然可以修改).打包者的目标就是修改Block,使得isOK(Block)返回true,一般而言,只要修改包中的int Nonce成员,在2^32位空间内穷举就可以找到合适的解.
  • 实践中,打包者被称为"矿工", 尝试打包的过程就是"挖矿", "给出某个包对应的Nonce"这个操作一般被称为Proof Of Work, 验证Nonce是否满足要求的算法isOK称为PoW算法

共识机制

当一个节点收到一个Block时, 是否要接受这个Block? 网络中的每个节点都彼此独立的面对这个问题,在解决这个问题的同时,还必须间接的保证所有节点最终记录相同的结果. 这就是共识需要解决的问题.

  • 比特币的方案:
  • 每个Block都必须是isOk的,否则直接被拒绝,换言之,你不能随意的构造Block干扰系统,必须要花费精力.
  • 打包节点只基于最长的链进行计算,以消除分歧. 例如,某个节点A先得到了b1,后得到了b2,二者产生分歧链,由于b1先到,所以矿工将基于b1分支进行打包,之后,又突然收到了b3,而b3又基于b2打包,那么节点A认为b2-b3更长,所以重新开始基于b2-b3分支打包,如此直到永远. 按照这样的设计,所有打包节点记录的链中,临近几个block可能是不同的,一般6个块之前的链就相同了,相当于6个块之前的链是有共识的.
  • 比特币的策略为:假定系统目前已经是安全的,也就是达成共识的那条主链是最长的, 那么, 必然有最多的矿工在主链上工作, 导致这条链增长最快,从而能保持最长的优势,从而保证它还是主链. 在支链上工作的矿工都是势单力薄的,支链增长慢,很快就会被矿工抛弃.

共识机制仅仅是从理想上,从概率上缓解了"共识问题", 它的想法是:只要好人多,比特币就能正常运作. 比特币共识机制一方面奖励好人,另一方面让作恶很难,且作恶容易血本无归,所以很大概率上能保证好人更多.

Ethereum

比特币中,发出的Transaction在被打包时,会触发Transaction Script的执行. ETH 希望能扩展这种执行代码的能力,使其能够执行更复杂的代码.
从而实现General Purpose Block Chain, 或Block Chain 2.0

Transaction In ETH

  • ETH中有一个ETH VIRTUAL MACHINE, 可以认为是一个包含巨大STATE的单线程执行机,每来一个Block,EVM就顺序解读BLockTransaction的内容,并更新STATE中的值.

  • ETH中,Account的意义更加明显,Account字符串对应了某个EVM STATE中对象的地址, 由于这种对应是唯一的,所以ETH中可以将Account,AddressObject这三个词互换,有时候能帮助你理解ETH的论述.

  • 比特币需要通过Transaction历史来查询余额数据, 而ETH 中的所有数据都是客观存在的,这些数据都是属于某个Account,可以提高工作效率.

  • 由于ETH记录了具体的状态,所以交易中可以只需要一个from和一个to,一个典型的ETH交易结构如下

    struct Transaction{
    nonce;//这个值和`from`绑定,表明是用户发出的第nonce个Transaction
    gas_limit;// 用户愿意支付的gas数上限.
    to // 发送目标
    value // 附带的ETH金额 (可选)
    data // 附带的相关数据 (可选)
    v_r_s // 代表了交易的发起方,相当于`from`
    }
  • Transaction的行为大致可以分为三类:

    • 常规的资产转移Transaction,此时from,to是ETH用户,主要用于转移资产.在矿工打包Transaction时,会进行余额的更新.
    • 创建新可执行对象的Transaction,此时的from是ETH用户,to0x00,在Transaction被矿工打包时,矿工会更新to为可执行对象的具体地址.
    • 触发可执行对象的Transaction,此时的from是用户或另一个可执行对象,to是一个可执行对象,用于触发某个函数的执行.在矿工打包Transaction时,会执行对应的代码,并根据运行结果更新EVM中相关STATE的值.
      • 常规用户在验证Block Chain的过程中,也会把这些函数执行一遍.
      • 由于执行代码需要开销,为了避免攻击,同时激励矿工,交易发起人需要向矿工支付gas.
      • 执行中任何出错都将导致EVM回滚到transaction触发之前的状态,但是gas则会被矿工拿走.(不良的交易也会被打包,而不是被丢弃)
  • 为了应对概念上的变化,ETH中将可执行对象称为Contract Object,称Contract Object的地址为Contract Account

    • Contract AccountContract Object意义相同,都代表了可执行对象,额外持有了一些局部变量和一些代码,用于供外部触发.
    • 普通账户一般称为Extern Owned Account,Extern是相对于EVM而言的,对EVM而言,extern就是指ETH的用户;
    • 相比之下Contract Account常常被称为Owned By Contract Code,意思是普通账户由Extern所有,可以被Extern修改, 而Contract只能直接被Contract Code操作,且Contract Code在被创建后就不能被Extern修改了
  • Contract可执行对象的执行

    • 向合约账户发送带有执行参数的Transcation就能触发合约账户中某个函数的执行.
    • 合约账户只能被Transaction激活,不会主动的运行.
    • 合约账户可以通过析构被销毁,这将导致其中的所有函数都不能执行.
    • 合约账户的代码可以发起新的Transaction,一般被称为Message

  • ETH的Block还有若干个叔区块, 叔区块是不在主链上的块, 只能被整个主链引用一次.用于补偿没有进入主链的块.
  • ETH在开发初期的roadmap就有four stage,且一些重大变化会造成不可回滚,即hard forks,目前已经有若干重要的Hard fork实现了,其中两次比较重要的是
    • Ice Age: 使用指数难度,以准备过渡到POS的consensus机制
    • DAO: 因为hacked DAO事件回滚导致的硬分叉.
  • ETH中,底层的代码是类似于机器码的EVM CODE,有其对应的汇编语言,以及若干种高级语言,高级语言中比较流行的是Solidity,这是一种类似于C++的语言,写Contract代码时的主要内容就是定义一个class.将class编译后的EVM Code发送到0x00就能创建该class的实例.