diff --git a/Basic/account.md b/Basic/account.md deleted file mode 100644 index 96b52fe..0000000 --- a/Basic/account.md +++ /dev/null @@ -1,170 +0,0 @@ -# UTXO与普通账户模型 -- [UTXO与普通账户模型](#utxo%E4%B8%8E%E6%99%AE%E9%80%9A%E8%B4%A6%E6%88%B7%E6%A8%A1%E5%9E%8B) - - [普通账户模型](#%E6%99%AE%E9%80%9A%E8%B4%A6%E6%88%B7%E6%A8%A1%E5%9E%8B) - - [UTXO 模型](#utxo-%E6%A8%A1%E5%9E%8B) - - [账户余额模型与 UTXO 的比较](#%E8%B4%A6%E6%88%B7%E4%BD%99%E9%A2%9D%E6%A8%A1%E5%9E%8B%E4%B8%8E-utxo-%E7%9A%84%E6%AF%94%E8%BE%83) - - [区块链中的 UTXO 模型](#%E5%8C%BA%E5%9D%97%E9%93%BE%E4%B8%AD%E7%9A%84-utxo-%E6%A8%A1%E5%9E%8B) - - [UTXO 的特性及缺点](#utxo-%E7%9A%84%E7%89%B9%E6%80%A7%E5%8F%8A%E7%BC%BA%E7%82%B9) - -区块链网络中有两种记账模式,除了 UTXO 模型还有 Account Based 结构,也就是普通账户模型,也叫账户余额模型,**前者在比特币系的数字货币中被广泛使用,后者更多是用在智能合约型的区块链上**。 - -## 普通账户模型 -我们先从传统的账户模型出发来聊聊是如何记账的,假设我们现在有一个支付系统,在这个支付系统中有Alice和Bob两个账户,Alice账户里有 100 万,现在要转账给Bob 10 万,这其中涉及的操作是这样的: -1. 检查Alice的账户余额是否大于 10 万; -2. 把Alice的账户扣除 10 万变成 90 万,然后发送一笔转账消息给Bob的账户; -3. Bob的账户接受到转账消息,将Bob的账户余额加 10 万。 - -我们可以发现,无论是Alice还是Bob,都具有一个余额作为状态,即当前余额是记录在某个地方的,只需要读出来即可,这种设计我们叫做账户余额模型。 - -**如果以上三个步骤是在一个中心化系统中,甚至在同一个数据库中,那将非常简单,会直接退化成一个事务**,我们见到的银行账户、信用卡系统、证券交易系统、各种电商类应用,理财类应用基本都是一个中心化系统中的,最多也就是跨表跨数据库。 - -如果以上的步骤中,Alice和Bob的账户分属两个不同的系统,例如从 A 银行到 B 银行,就需要经过人民银行支付系统,即可信任的中心化第三方来做中介。 - -你可能发现了,在跨行转账的这种情况下,是没有办法做事务的,所以 **1 和 3 是不同步的,如果 3 操作失败,还需要从 2 倒退到 1 的状态,这个情况叫做冲正交易**。 - -**`普通账户模型具有自定义数据类型的优点,但是却需要自己设计事务机制,就是上述所说的冲正交易`**。 - ->其实不管是普通账户模型还是UTXO模型,都需要保证其各节点数据一致性,只不过UTXO常用于数字货币,通过区块链共识机制来保证一致性。而普通账户模型不一定用于区块链,此时就要自行设计分布式事务,而若用于区块链中,依然是依靠共识机制来保证一致性。所以个人认为两者差距并非通过分布式事务或共识机制来体现,主要还是体现在数据模型上。 - -## UTXO 模型 -UTXO 全称是:“Unspent Transaction Output”,这指的是:未花费的交易输出。这里面三个单词分别表示 “未花费的”“交易”“输出”,接下来我来详细讲解一下 UTXO 的含义。 - -**UTXO 的核心设计思路是无状态,它记录的是交易事件,而不记录最终状态**,也就是说只记录变更事件,用户需要根据历史记录自行计算余额。 - -有点像MySQL 中的 Binlog,主从模式的情况下,按照 Binlog 来更新数据,Redis 的 AOF 模式备份模式也是如此,UTXO 也是类似的思路。 - -下面我们按照按照普通账户中的例子来重新讲解一遍。 - -如果要记录交易本身,那么我们可以构造一笔交易,这笔交易中Alice转账 10 万给Bob的同时,90 万转给自己。 - -如下所示: -``` - Alice 100 万 --> Bob 10 万   -        --> Alice 90 万 -``` -这里其实有三条子记录,左边一条,右边两条,左边叫做输入,右边叫做输出。 - -输入和输出组成了交易,输入和输入需要满足一些约束条件: - -1. 任意一个交易必须至少一个输入、一个输出; -2. 输入必须全部移动,不能只使用部分,所以才产生了第二个输出指向Alice自己; -3. 输入金额 = 输出金额之和 + 交易手续费,这里必须是等式。 -对于Alice来说,首先构造交易的输入输出,满足上述条件,然后广播到全网,接收方自行判断交易是否属于自己。这里满足约束条件构成的交易模型,也就是Alice记录的三条转账事件就是 UTXO 模型。 - -## 账户余额模型与 UTXO 的比较 -我们可以归纳出 UTXO 与普通账户模型的一些区别。 - -1. 存储空间,**UTXO 占用空间比账户模型高**,因为账户模型只记录最终状态; -2. 易用性,UTXO 比较难处理,账户模型简单容易理解。例如 UTXO 在使用上,还需要配合高效的 UTXO 组装算法,这个算法要求尽可能降低输入输出的个数,还要让“零钱“归整,算法的复杂度相比账户余额无疑要高。 -3. 安全性,UTXO 比账户模型要高,**UTXO本身具备 ACID 的记账机制,每个操作都是原子的,而账户模型需要自行处理**,例如重放攻击; - -**普通账户模型具有较高的自由度,可以让智能合约有更好的发挥空间**,并且它避免了 UTXO 的复杂组装逻辑,精度控制上也更为得心应手。 - -UTXO 似乎天然是为数字货币设计的,具有 **较高频次跨账户转移** 场景都使用 UTXO 会比较好,考虑到智能合约的普适性,UTXO 与智能合约并不能很好地兼容,但是这也对开发者的自身水平提出了更高的要求。 - -## 区块链中的 UTXO 模型 -我们借用比特币开发者文档中 UTXO 模型的图示,来看看 UTXO 实际的构造形式。 - -![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/16.1.png) - -上图中,所有的交易都可以找到前向交易,例如 TX5 的前向交易是 TX2,TX2 中的 Output1 作为 TX5 中的 Input0。 - -意思就是 TX2 中的付款人使用了 Output1 中指向的比特币转移给 TX5 中的收款人,接着 TX5 中的人又把收到的比特币转移给了 TX6 中的收款人,成为了 TX6 中 Output0。 - -我们也可以发现,TX6 中的收款人还没有产生 TX7 交易,也就是说 Output0 还没有被花费, - -这时候我们终于得到了 UTXO 的真正语义:Unspent Transaction Output,未花费的交易输出。 - -我们这时候可以发现 UTXO 也同样能表示余额,不过是重演计算的方式,它用不同的方式表达了余额,**我们把一个地址上所有的 UTXO 全部找出来,就是这个地址总的余额了**。 - -我们还可以发现,**无论是 TX5 还是 TX2,都已经成为历史交易,它们都忠实客观地记录了两笔交易,这两笔交易代表的是事件**,而不是余额状态转移,这是我们看到的最直观的区别。 - -我们再来看看一个真实的交易例子。 - -![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/16.2.png) - -这是区块链上一笔真实交易的例子,它记录了一笔 450ETP 的转账记录。 - -左边是输入,右边是两笔输出,其中第二个输出是给自己的账户,这和我们Alice转账给Bob的例子是一样的。 - -下图是交易解码为 JSON 格式的样子,可以看到 Previous_output 是放到 Inputs 数组里的,意思就是前向输出作为本次的输入。 - -```python -{ -"hash" : "89e80e14db07c4904a57e2c1efb689bccbbf43942103c1a92166d5c0f27ea3d2", -"height" : 1093399, -"inputs" : -[ - { - "address" : "MLWtmjwCtmK44FMwJMSfAkHaEvnnb2N6HX", - "previous_output" : - { - "hash" : "770a72f35d3e3a78bd468949bad649f03b241cf7e2a84cc2d6fdabacdcc47f06", - "index" : 0 - }, - "script" : "[ 304402202b21d7a79276985dc99777b70fd5095796dad58f35e29a019d2cb6cca5df481802205ffab088a6047f5b6382ba02a0eed4e78ab7950fe264d3774e8b0b357a7593d101 ] [ 03ea3462dc01e7b5569e89737211887035f8f1e99e1fe4332181d83daccaa6d917 ]", - "sequence" : 4294967295 - } -], -"lock_time" : "0", -"outputs" : -[ - { - "address" : "MGz9yjLLn4AqyraRjSpiP2GmTWKnT3yfiL", - "attachment" : - { - "type" : "etp" - }, - "index" : 0, - "locked_height_range" : 0, - "script" : "dup hash160 [ 63ab0013d183f2592e4b46a358df01e88a09c0b8 ] equalverify checksig", - "value" : 45000000000 - }, - { - "address" : "MLWtmjwCtmK44FMwJMSfAkHaEvnnb2N6HX", - "attachment" : - { - "type" : "etp" - }, - "index" : 1, - "locked_height_range" : 0, - "script" : "dup hash160 [ 8a63941b392771c40f1c15e4374808f6bb464cba ] equalverify checksig", - "value" : 118082150283 - } -], -"version" : "2" -} -``` - -我们再看看比特币上的例子: - -![](https://github.com/yjjnls/blockchain-tutorial-cn/blob/master/img/16.3.png) - -这一笔比特币交易包含 6 个输入,几十个输出,交易一共 3.5kb,交易的输入输出会影响交易大小,比特币的交易费是根据[字节收费](https://zhuanlan.zhihu.com/p/38479785)的,交易尺寸越大越贵,而交易尺寸主要和输入输出的个数有关,也就是说,**算法上并不规定输入输出的个数,而只有区块尺寸限制**。 - -在比特币中将小于 100kb 的交易称为标准交易,超过 100kb 的称为非标准交易。它的前向 input 以及生成一个 out 约占用 161~250 bytes 。**所以在比特币中,大约的 inputs/ouputs 的最大数目限制为 100KB/161B ~= 600 个**。 - -## UTXO 的特性及缺点 - -从计算的角度来说,**UTXO 具有非常好的并行支付能力,也就是说如果没有尺寸限制,一笔交易可以包含任意笔输入输出,同时也没有次序要求**,在一笔交易中哪一个 UTXO 在前,哪个在后面不影响最终结果。 - -从存储的角度来说,UTXO 具有较好的可裁剪特性,可裁剪性指的是 UTXO 类型的交易,如果从最老的那一笔 UTXO 开始截断数据库,那么之前的数据可以删除掉了。 - -如果想进一步压缩数据尺寸,可以在任意位置截断,记录 UTXO 对应的交易哈希即可,然后从其他节点获取并校验 UTXO,这也是 SPV 轻钱包工作的基础之一。 - -以太坊中并没有使用比特币的这种 UTXO 设计,这与以太坊的宗旨有关,以太坊的目标是构建通用计算,而比特币是数字货币,需求不同导致设计的不同。 - -V 神指出了 UTXO 的缺陷,一共有三类。 - -1. 可表达的状态少 -UTXO 只能是已花费或者未花费状态,这就没有给需要任何其它内部状态的多阶段合约或者脚本留出生存空间,这也意味着 UTXO 只能用于建立简单的、一次性的合约,UTXO 更像是一种二进制控制位。 - -2. 区块链盲点(Blockchain-blindness) -UTXO 的脚本只能看不到自己这条历史轨迹,**无法看到区块链的数据的全貌**,这导致了 **功能性扩展受到了限制**,我们在花费比特币的过程中需要小心翼翼的组合 UTXO,这也导致了系统状态逻辑复杂,**不适合设计成智能合约的基础结构(Fabric和Ethereum都是普通账户模型)**。 - -3. 价值盲点(Value-blindness) -UTXO 脚本不能提供非常精细的金额控制,基于账户模型的余额在花费过程中,可以任意的按值存取,它仅取决于程序能表示的最小精度。 - -**而 UTXO 要求必须全部移动,如果要满足一个目标值金额,对组合 UTXO 算法的要求会比较高,采用许多有不同面值的 UTXO,一方面要求尽可能地精确,另一方面又要求输入输出的数量尽可能的小。** - -UTXO 是比特币上的原生设计,在区块链以前是没有这种逻辑数据结构,UTXO 的出现给了人们看待数据转移的不同视角,但 **UTXO 不是所有区块链所必需的,公链开发过程中的是否选用 UTXO 模型可以根据业务场景进行判断**。 \ No newline at end of file diff --git a/Basic/application.md b/Basic/application.md deleted file mode 100644 index 45bf0d4..0000000 --- a/Basic/application.md +++ /dev/null @@ -1,146 +0,0 @@ -# 区块链不能做什么 -但凡提到区块链的文章,都会强调“去中心化”、“不可篡改”等特(xue)性(tou)来吸人眼球。仿佛用区块链就能在不久的将来构建一个理想的乌托邦,但是拥有这么多特性的区块链到底怎么来构建这个乌托邦,却没人能说得清楚。毕竟现在的行业还处于初期探索,大部分注意力都集中在技术层面,技术尚未成熟大家就想要抢占市场,难免会不愿意承认“看不到方向”。若非要回答这个问题,总有人能说出一些看似合理的应用场景,但大多数经不起推敲,更没有验证。诚然,落地这事不能一味地钻研技术,应用场景、发展方向、经营管理模式等都是需要考虑的方向。这里就目前区块链的一些应用场景展开一些讨论,并非说区块链不能在这些领域有所应用,更多地是指出一些很多人不愿意提但又绕不开的问题。 - -![](https://github.com/yjjnls/awesome-blockchain/blob/master/Basic/img/blockchain-jesus.png) - -首先我们需要弄清楚一个容易被混淆的问题:[基于区块链开发的应用真的能“去中心化”、“防篡改”如此这般安全么?](https://www.zhihu.com/question/270698341) - -比特币、以太坊等数字货币为什么能实现所谓的“去中心化”与“不可篡改”呢?其实这里可以从区块链的本质来看, **`区块链的本质和基础是分布式账本以及确保所有结点都彼此充分一致的安全机制。`** 也就是人人都持有账本,才能防止个别人的篡改,这就是去中心化。 **所以可说去中心化、不可篡改、安全等特性都是关联在一起的,一个特性不能满足,其余特性也就被攻破了。** 那么是不是大家都持有账本,就是真的防止篡改了呢?显然不是,以比特币为例,其能做到防篡改是因为 - -1. 挖矿的算力和实体电力成本挂钩,即使是51%攻击,也会无利可图,这是利用经济学上的博弈来牵制作弊篡改行为; -2. 比特币系统(注意这里不是指比特币)由无数的结点构成,这些结点大部分都是矿机,而且是24小时不停地工作。 - -我们来详细分析一下,第一点是因为中本聪设计比特币时采用了POW共识,并且制订了一系列的奇葩规定,使得挖矿难度大,成本高,这点可以归结为区块链的设计,要提高作弊成本,使得作弊无利可图。而第二点看似无关紧要,其实是比较关键的,试想一下,如果大家有账本,但是都不去记账,比如某一时刻大部分矿机坏了,这时全网算力就会大大下降,那么作弊所需要的算力成本也就大大下降,超过了博弈平衡,那么作弊行为变得有利可图,也就无法收场了。再举个简单的例子,比如你根据网上的教程开发了一个区块链应用,调试的时候其实就你本地一个结点,结果中途发现了bug或者崩溃了,那么就得改了重来,这其实就是一种篡改,因为在你本地篡改的成本很低。所以第二点要保证有大量在线结点,且要设计合理的 **共识机制**进行经济制约。 - -简单地说区块链防篡改,那肯定是不对的,要依据其具体设计与实现来判断。而且分叉作为一种链上治理的手段,和篡改行为在本质上是相同的,只是执行方不一样,就像孔乙己说: - ->读书人的事能叫偷吗? - -我们再来看看区块链的去中心化,**区块链能提供信任机制的本质是将数据内容的所有权分散给用户,且在一定程度上不能篡改(这也是基于数据权利分散的特性)。** 这通常适用于小型团体或者不同机构之间来建立信任机制,如果由单一有背书的大公司来提供服务,未必需要区块链。这些数据可以是金融数据或者社交数据或者其他。 - -而数据分散的形式其实有两种,第一种是比特币和以太坊为代表的区块链1.0和2.0技术,所有用户都可以无门槛地加入结点,结点用户分布于全球每个角落,权利分散到每一个用户手中,从这一点上来看,这确实是名副其实的“全面去中心化”。但是其缺点也显而易见,那就是全球的用户都达成共识,所需要的时间非常长,作为公链来说,TPS非常低,无法进行商业应用。 - -| | BTC | ETH | EOS | MIOTA | BTS | Cardano | -| :------------ | --: | :--: | -------- | -------- | ---- | ------- | -| 共识机制 | POW | POW | DPOS+BFT | MCMC+POW | DPOS | POS | -| 每秒事务处理量/TPS | 5~7 | 7~14 | 1M (理论上) | 1000+ | 3300 | unknown | -| 总供应量/Millions | 21 | 可定增 | 1000 | 2780 | 3600 | 45000 | - - -第二种是区块链3.0技术(比如EOS)和联盟链,EOS在全球有21个超级结点,只有这21个超级结点进行记账。与比特币和以太坊的结点数量相比,21个结点达成共识所需的时间可以做到非常短,链的TPS也可以做到很高。联盟链用于不同企业和机构之间的账本同步,结点数量也是有限的,所以TPS才能提高。区块链3.0和联盟链的模型是一致的,只不过一个是公链,一个是联盟链,另外共识协议和细节处理也不同。他们的特点都是链的结点数量有限,而且要达到一定的门槛才能成为结点,所以这种“去中心化”又被称为 **“弱中心化”**。 - -同时,我们再进一步看,EOS在全球有21个超级结点,这和一个大公司在全球部署21个结点进行数据同步,从模型上来看没有多大区别,只是EOS的结点属于不同的结构。所以从这一点来看,**目前能够进行商业应用的区块链技术更多地是基于传统分布式技术的一些升级,`并不具有革命性`;而真正理想化的“全面去中心化”、点对点交易的理念,很难很难实现**(比特币其实也是中心化的,被五大矿池操纵,普通用户虽然可以参与,但是基本不起作用)。 - -同样,区块链作为一种数据库,去中心化一定是比中心化低效的,这也导致 **要达到同样的商用效果,区块链比传统分布式技术的成本更高**。只有在去中心化所带来的价值提升远大于效率的牺牲和成本的增加,才是有意义的,但在不用区块链也能很好运作的一些领域,加上区块链是否真的有必要,值得思考! - -明白了上面这些,再来看具体领域的应用,分析起来路就会更加清晰。 - -## 公示公证 - -> 区块链世界目前还是现实世界的「平行宇宙」版本。 - -区块链可以保证链上数据的真实、透明、不可篡改,这里的“真实”指的是数据是原始的,没有被篡改过;但是如何保证上链数据在现实生活中的真实合法性呢? - -比如现在流行的食品溯源,如何保证一只鸡是纯天然散养的?如何保证没有喂养激素?如果最后购买的是宰杀后的鸡,那宰杀过程的卫生如何保障? - -现有做法是将喂养记录上链,那如何保证记录的真实性呢?还有运输、宰杀过程如何生成有效的数据上链呢? -如果换做是蔬菜呢?每只鸡都可以有记录,难道每棵菜也有自己的编号? -再比如一些加工食品,包装上都会有检验合格的信息,区块链可以保证每一步的加工检验数据上链后不会被篡改,但无法保证上链的这些记录都是真的。 - -这些数据的共同特点是数据源都在线下,而无法保证上链数据真实有效的根本原因在于 **参与者的信息化程度过低**。 - -假设我们可以实时监控供应链上所有的货物处理过程,甚至账款的流转,那不管用不用区块链,都可以由某个第三方,如银行对供应商进行授信。但如果没有,那 **首先要解决的问题是信息化**,比如食品供应链的每个环节用互联网工具去记录,保证真实数据,还要打通整个行业的信息系统。这才是需要解决的核心问题,解决了核心问题,区块链才能真正地发挥作用。 - -## 通证经济 - -Token(通证)作为一种权益,代表资本市场的运作方式。Token可以分为基础设施型、金融型、商业垂直生态型等,其本质都代表了债券和股权。但不管是债权还是股权都有 **一个“不破产”的先决条件,就是发行经济主体的“有用”,而不是稀缺性(如总量限定,资本管制)决定了 Token 的价值,是有了价值才稀缺**。 - -比如在国外非常流行的REITs,它与房产挂钩,且有大型机构和企业做背书,这保障了其稳定与流通性。而且REITs也没有用区块链实现,大多数ICO项目只是希望首次发行Token获得融资来维持项目运转或者套现跑路。 - -另一方面,除了金融型Token,其余类型的Token都会与实物资产对应,那么就只有在数字版和实物版之间有明确的联系时,智能合约才能发挥作用。比如说房子,在房子的数字版所有权发生变化时,房子实物版的所有权也必须跟着改变。我们需要让数字世界“了解”实物世界,这被称为“神谕问题(Oracle problem)”。 -在 A 把房子交给 B 时,智能合约需要知道他是真的把房子交给 B 了。实现方式很多,但本质上都是一样的,都需要在实物世界中找一个可信的第三方验证这些事件。 - -比如说,这个房子在以太坊中被表示成了一个不可替代的代币Token。A 可以在一个原子交换中用房子跟 B 换一些以太币。那么问题来了,B 需要相信那个代币真的代表那个房子。这需要靠某种神谕来保证,将代表房子的代币交给他后,真的可以表示那个房子在法律上是属于他的。 - -进一步说,**即便政府机构承认那个代币真的可以代表那个房子,如果这个代币被偷了怎么办?房子属于那个偷代币的贼吗?如果代币丢了怎么办?房子就没法再卖了吗?还能再重新发一个代币给这个房子吗?如果可以,由谁来发呢?** - -按照全面去中心化的思想,你资产的唯一凭证就是你的秘钥,秘钥丢失意味着资产归零,而且不可篡改,不可逆;但这显然是大部分人无法接受的,任何试图挽回损失的措施都是与去中心化背道而驰的,这是十分矛盾的行为,可以参考以太坊的硬分叉。 - -不管是水果、汽车还是房子,要将数字资产与实物资产连接起来都是个难题,至少在去中心化的上下文中特别不好实现。**实物资产是由你所在的司法系统监管的,除了你创建的智能合约之外,他们还相信别的一些东西。也就是说智能合约中的所有权不一定能代表实物世界中的所有权,它跟普通合约一样存在信任问题。需要信任第三方的智能合约失去了自己最大的优势,不再是去信任化的合约。** - -即便是电子书、医疗记录或电影这样的数字资产,也会遇到同样的问题。对这些数字资产的“权益”最终都是由某个权威机构决定的,而我们需要信任他们的神谕。 - -就此而言,神谕只是简化版的法官。所以实际上你得到并不是只有机器执行的合约和简化的约束,而是掺杂了主观认识和人为判断风险的代码,而且这些代码要实现所有可能的情况,很复杂。换句话说,为了让合约变得“智能”,编写代码的复杂性急剧升高,**但你还是没办法摆脱对某个人的信任。** - - -## 版权保护 - -版权保护是区块链被吹捧的另一个应用领域,模式通常为: - -> 利用区块链做一个自有版权的平台(内容可以是音乐、文字、视频等),原创者可以直接发布其作品,用户支付相应的费用浏览内容,所支付的费用直接转给原创者,不经过中心化的平台;而且利用区块链技术可以溯源防伪…… - -与上面提到的公示公证不同的是,通过在线平台发布的作品直接完成了作品 **信息化**,并保证了作品的 **有效性**(信息源在线上而非线下)。所以通过区块链,可以保障每个作品都是有效且无法被篡改的,但是要实现作品的版权保护,又会有一些新的问题。 - -这种模式解决的痛点为: -1\. 用区块链不可篡改的特性进行溯源防伪 -2\. 原创者与用户点对点对接,跳过中心化平台的抽成,保障原创者的权益 - -出发点很好,但是我们细细分析还是有些问题的。版权其实分为署名权和分发权(信息网络传播权)两方面,而这两方面正好对应上述两点。 - -### 署名权 - -对于第一点署名权,区块链的不可篡改性确实可以用来保护版权 - -> 当一个作品在区块链平台上发布时,它就有了区块链上的标识(这个标识就是身份识别信息,包含加密密钥、原文件地址等、付费信息、使用次数、作者等等信息),这个标识不会因被拷贝而丢失(反而会记录拷贝这个行为);当这个拷贝文件被放置在另外一个平台时,一旦文件被使用,有以下两个情况:1、这个平台不支持区块链,所以无法识别该文件(就是无法播放)。在这个过程中,该文件会自动寻找原区块链平台,向使用者发出询问,输入这个产品的个人密钥,是否要付费使用该文件(或向这个平台授权等)。但该平台有可能屏蔽这个过程,也就是,该平台会认为这是一个病毒文件,根本就不让打开;2、如果这个平台支持区块链,这个标识就会走识别能不能授权播放(也就是需不需要付费使用)的流程。 - -但这里有个问题,比如对于文字资料,某个用户直接将文章的文字内容拷贝下来,或者记录下来(不是拷贝文件而是直接拷贝内容),然后用普通文稿的形式发布到另一个普通的平台上。对于这种行为,除非通过用户举报,否则是无法发现的。当然这些例子有些极端,但也说明区块链的版权保护也不是万能的;而且用现有技术也能重复内容比对,防拷贝等。也就是说 **区块链能做到的,现有技术也能做到,现有技术做不到的,区块链也做不到** ,并且如前面所说,用区块链来做版权保护,其实成本更高。 - -### 分发权 - -在大部分人眼中,版权=署名权,反而忽视了问题更大的分发权。举个例子,现在流行的短视频平台抖音,抖音上面的用户发的视频都有自己ID的水印,这是抹不掉的,相当于署名权完全在原创用户手中,但是你在抖音上面看到什么样的短视频,这完全由其推荐算法决定。这也是现在流行的信息流,这个地方就很有趣了,你相当于把作品的分发权交给了平台,那么这样做有什么后果呢?或者说平台可以怎么利用你的分发权来赚钱呢? - -1. 流量入口的转移 - 以往用户看到一个好的作品需要去关注原创者(比如某个原创歌手),然后从该歌手那里才能获得后续的更新,这时候流量入口在原创者那里,有了流量就有收益,而且流量也涉及到原创者的发展方向。但现在用户只需要打开APP,就可以看到关注歌手的更新、动态资讯等,用户要只需要浏览平台APP给你推荐的内容就可以了。 - - 这个时候用户由依赖原创者转变为依赖平台,流量入口转移到了平台那里,收益自然也转移了。原创者虽然也有分成,但是大头利润被平台赚取,且原创者的发展受制于平台,平台算法推荐的少,该原创者的关注度自然就下降了。 - - 而且单个原创者的作品有限,但是平台聚合了大量的原创者,利用他们作品的分发权,可以产生更大的流量,让你沉迷其中。想想你每天沉迷的是头条,是抖音还是里面的某几个大V? - -2. 流量干扰 - 因为机器推荐算法是单向的,当你习惯了沉迷在每日的推送中时,你会默认接受其中的内容,除非引起强烈不适。而**流量干扰,则是平台可以在推荐给你的信息流中夹杂一些其他东西**,比如广告,比如一些低质量的内容,或者一些花钱上位的内容。只要推送主体还是你喜欢的,对于这些流量干扰,你是不会介意的。而这些流量干扰有一个fashion的名字叫做信息流广告,经常出现在各大公司的财报中,目前是很多公司的主要利润和利润增长来源。 - - 由于流量入口的转移,原本可以附加于原创者的增值内容也都转移到了平台上,留给原创者的只是少得可怜的分成。 - -再举个例子,实体书店也会有推荐、促销活动,那和依赖推荐算法的平台有什么区别? - -**区别在于实体书店你除了能看到推荐书籍,其余书都能看到;但是推荐算法是把它认为你喜欢的书硬塞给你,请你一条一条浏览,其余的呢?没有其余的!你“只能”看他推荐给你的,算法甚至可以将每本书里的内容单独抽出来,然后整合在一起推荐给你,这样就更进一步模糊作品的归属,将用户的注意力转移到平台的推荐内容。** - -从上面的分析我们可以看到,现有技术可以保障署名权,但是无法保障涉及利益的分发权,而且区块链也做不到。 - -或许你可以说用区块链做一个没有推荐算法的版权保护平台不就行了?可那不是回到了各平台早期没有推荐算法比较落后的模式了? - -你又可以说用智能合约做一个透明的纯粹的推荐算法,但这个平台怎么盈利呢?一个行业只有有持续的利润才能不断地发展,而马克思说过: - -> 为了100%的利润,资本就敢践踏一切人间法律。 - -版权保护的痛点在于发展模式和经营模式,这些其实和技术没有太大关系。 - -### 升华 - -我们进一步分析,平台通过信息流广告霸占了巨额利润么?剥削了原创者么? - -分析过程可能会颠覆某些人的三观,在此省略,我们只看结论: - -短期来看,确实是这样。但是从长期来看, - -> 各行各业都有平均利润率,利润太高会导致竞争对手加入,并摊薄利润。 - -由于竞争,信息流所带来的暴利不会持续太久,竞争的结果是把利润返回给了消费用户。 - -> 消费者剩余,是属于消费者的。 - -结局是低质量原创者剥削了高质量原创者。 - -Ref -1. [恶的土壤,结不出善的头条](https://mp.weixin.qq.com/s/UX0J7uN08-ASuVnijKSijQ) -2. [携程提价,该不该谴责](https://mp.weixin.qq.com/s/32FdhFbkJ-hEtGIec8UdkA) \ No newline at end of file diff --git a/Basic/crypto.md b/Basic/crypto.md deleted file mode 100644 index 7f51526..0000000 --- a/Basic/crypto.md +++ /dev/null @@ -1,302 +0,0 @@ -# 数字加密相关知识 - -- [数字加密相关知识](#数字加密相关知识) - - [非对称加密](#非对称加密) - - [椭圆曲线加密](#椭圆曲线加密) - - [公钥与私钥](#公钥与私钥) - - [数字签名](#数字签名) - - [数字证书](#数字证书) - - [Merkle Tree](#merkle-tree) - - [数字签名扩展](#数字签名扩展) - - [数字签名算法](#数字签名算法) - - [RSA数字签名算法](#rsa数字签名算法) - - [DSA数字签名算法](#dsa数字签名算法) - - [ECDSA椭圆曲线数字签名算法](#ecdsa椭圆曲线数字签名算法) - - [盲签名](#盲签名) - - [多重签名](#多重签名) - - [群签名](#群签名) - - [环签名](#环签名) - - [环签名满足的性质](#环签名满足的性质) - - [环签名实现](#环签名实现) - - [环签名和群签名的比较](#环签名和群签名的比较) - - [算法实现](#算法实现) -- [Reference](#reference) - -## 非对称加密 - -`对称加密`指加密和解密使用`相同密钥`的加密算法。它要求发送方和接收方在安全通信之前,商定一个密钥,泄漏密钥就意味着任何人都可以对他们发送或接收的消息解密。 - -每对用户每次使用对称加密算法时,都需要使用其他人不知道的惟一钥匙,这会使得发收信双方所拥有的钥匙数量呈几何级数增长,密钥管理成为用户的负担。同时,对称加密算法能够提供加密和认证却缺乏了签名功能,使得使用范围有所缩小。 - -具体算法:DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法。 - -而`非对称加密`指加解密密钥不相关,典型如RSA、EIGamal、椭圆曲线算法。 - -### 椭圆曲线加密 - -公开密钥算法总是要基于一个数学上的难题。比如RSA 依据的是:给定两个素数p、q 很容易相乘得到n,而对n进行因式分解却相对困难。那椭圆曲线上有什么难题呢? - -![Trapdoor function](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Trapdoor_permutation.svg/300px-Trapdoor_permutation.svg.png) - -考虑如下等式: - - K=kG [其中 K,G为Ep(a,b)上的点,k为小于n(n是点G的阶)的整数] - -`不难发现,给定k和G,根据加法法则,计算K很容易;但给定K和G,求k就相对困难了。` -这就是椭圆曲线加密算法采用的难题,我们把点G称为基点(base point)。 - -现在我们描述一个利用椭圆曲线进行加密通信的过程: - -1. 用户A选定一条椭圆曲线Ep(a,b),并取椭圆曲线上一点,作为基点G。 -2. 用户A选择一个私有密钥k,并生成`公开密钥K=kG`。 -3. 用户A将Ep(a,b)和点K,G传给用户B。 -4. 用户B接到信息后 ,将待传输的明文编码到Ep(a,b)上一点M(编码方法很多,这里不作讨论),并产生一个随机整数r -5. 用户B计算点C1=M+rK;C2=rG。 -6. 用户B将C1、C2传给用户A。 -7. 用户A接到信息后,计算C1-kC2,结果就是点M。因为 - C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M - 再对点M进行解码就可以得到明文。 - -在这个加密通信中,如果有一个偷窥者H,他只能看到Ep(a,b)、K、G、C1、C2,`而通过K、G 求k 或通过C2、G求r 都是相对困难的`。因此,H无法得到A、B间传送的明文信息。 - -## 公钥与私钥 - -公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。 -通过公钥是无法(或极其困难)推算出私钥的。**注意这里公钥与私钥都可以用来加密,只是私钥是自己保存,而公钥是公开的。** - -比如A发信息给B,就用B的公钥加密信息,然后发给B,B用自己的私钥解密,就可以看到信息内容。 - -## 数字签名 - -A把要加密的内容用hash函数生成摘要digest,再用自己的私钥对digest加密,生成数字签名signature。连同加密的内容一起发给B。 - -B收到后,对**摘要digest(用A的公钥解密)**和**内容(用自己的私钥解密)** 都解密,再对内容使用相同hash,看得到的digest是否相同,相同,说明发送的内容没有被修改。 - -> 但是如果这里用B的公钥来加密摘要digest,B收到后用自己的私钥解密,这不是也可以验证内容是否被篡改么? - -如果仅仅从防篡改的角度来讲确实可以,但是这样无法验证这些内容是谁发来的!**所以用A的私钥来加密摘要digest,相当于A用自己的私钥给这个摘要digest进行签名,B收到后用A的公钥对digest进行解密还能验证这是不是A发来的内容**。 - -但是这里有个`潜在的问题`。 - -> **如果B存储的A的公钥被C替换成了C的公钥,那么C就可以冒充A和B进行通信,而B却完全不知道。** - -## 数字证书 - -证书中心用自己的私钥对`A的公钥和一些相关信息`一起加密,形成`数字证书`。 -A在发送内容的同时,在数字签名后再附上数字证书。 -B收到后,先用CA的公钥解密数字证书,得到A真正的公钥,再用A的公钥来验证签名是否是A的签名。 - -B可以每次都到CA的网站上(或者什么别的官方途径)获得CA的公钥。 - -这么做的目的是为了验证: -1. 确认该信息确实是A所发; -2. 确认A发出的信息是完整的。 - -- **公钥防泄漏,私钥防篡改、防假冒** - _ B收到后,只有用B自己的私钥才能解密内容,别人是无法解密的。`防泄漏` - _ 再用上述数字证书来验证数字签名是否来自A,发送内容有没有被篡改。`防篡改` - 数字证书一般挂靠在可信任的机构,无法篡改和伪造。 - -## Merkle Tree - -默克尔树,又叫哈希树,由**一个**root节点,**一组**中间节点和**一组**叶节点组成。 -叶节点包含存储数据或者其哈希值,中间节点和root节点都是其孩子的hash值。 - -![markle tree](https://github.com/yjjnls/Notes/blob/master/block%20chain/Basic/img/markle%20tree.jpg) - -应用: -1. 快速比较数据,两个默克尔树的根节点相同,那么其所代表的数据必然相同 -2. 快速定位修改,比如上面D1数据被修改,可通过root->N4->N1,快速定位到发生改变的D1 -3. 零知识证明,比如要证明某个数据中包含D0,那就构造一个默克尔树,公开root、N4、N1、N0,D0拥有者可以检测到D0存在,但不知道其他内容。(D0拥有者可以看到hash值,但看不到完整的数据内容)(比如用户可以查找自己的money是否在交易所的总备用金中,而不必知道其余用户的money信息;或者p2p下载中,文件切片成小块,下载一个分支后就可以验证该分支的数据是否正确,定位错误数据块重新下载或者继续下载下一个分支数据。) - -## 数字签名扩展 - -### 数字签名算法 - -常见的数字签名算法主要有RSA、DSA、ECDSA三种。 - -#### RSA数字签名算法 - -RSA是目前计算机密码学中最经典算法,也是目前为止使用最广泛的数字签名算法,RSA数字签名算法的密钥实现与RSA的加密算法是一样的,算法的名称都叫RSA。密钥的产生和转换都是一样的,包括在售的所有SSL数字证书、代码签名证书、文档签名以及邮件签名大多都采用RSA算法进行加密。 - -RSA数字签名算法主要包括MD和SHA两种算法,例如我们熟知的MD5和SHA-256即是这两种算法中的一类。 - -#### DSA数字签名算法 - -DSA全称Digital Signature Algorithm,DSA只是一种算法,和RSA不同之处在于它不能用作加密和解密,也不能进行密钥交换,只用于签名,所以它比RSA要快很多,其安全性与RSA相比差不多。DSA的一个重要特点是两个素数公开,这样,当使用别人的p和q时,即使不知道私钥,你也能确认它们是否是随机产生的,还是作了手脚。RSA算法却做不到。 - -DSA的整个签名算法流程如下: - -1. 发送方使用SHA-1和SHA-2编码将发送内容加密产生数字摘要; -2. 发送方用自己的专用密钥对摘要进行再次加密得到数字签名; -3. 发送方将原文和加密后的摘要传给接收方; -4. 接收方使用发送方提供的密钥对进行解密 ,同时对收到的内容用SHA-1/SHA-2编码加密产生同样的摘要; -5. 接收方再将解密后的摘要和4步骤中加密产生的摘要进行比对,如果两者一至,则说明传输过程的信息没有被破坏和篡改,否则传输信息则不安全。 - -#### ECDSA椭圆曲线数字签名算法 - -ECDSA是用于数字签名,是ECC与DSA的结合,整个签名过程与DSA类似,**所不一样的是签名中采取的算法为ECC**,最后签名出来的值也是分为r,s。而ECC(全称Elliptic Curves Cryptography)是一种椭圆曲线密码编码学。 - -ECDH每次用一个固定的DH key,导致不能向前保密(forward secrecy),所以一般都是用ECDHE(ephemeral)或其他版本的ECDH算法。ECDH则是基于ECC的DH(Diffie-Hellman)密钥交换算法。 - -ECC与RSA 相比,有以下的优点: - -1. 相同密钥长度下,安全性能更高,如160位ECC已经与1024位RSA、DSA有相同的安全强度。 -2. 计算量小,处理速度快,在私钥的处理速度上(解密和签名),ECC远 比RSA、DSA快得多。 -3. 存储空间占用小 ECC的密钥尺寸和系统参数与RSA、DSA相比要小得多, 所以占用的存储空间小得多。 -4. 带宽要求低使得ECC具有广泛得应用前景。 - -### 盲签名 - -前文说到了数字签名,但如果A想要B来对消息签名,但是又不想让B知道该消息的内容,该如何做呢?这里就需要用到盲签名了。 - -盲签名具有以下特点: -1. 签名者对其所签署的消息是不可见的,即签名者不知道他所签署消息的具体内容。 -2. 签名消息不可追踪,即当签名消息被公布后,签名者无法知道这是他哪次的签署的。 - -传统RSA加解密的结局方案为: - -1. 加密: ![](http://upload-images.jianshu.io/upload_images/11336404-be53f4cdd8eff0f1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -2. 解密: ![](http://upload-images.jianshu.io/upload_images/11336404-0c87f6ebf1f4b7b8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -而盲签名则按照如下步骤来进行: -1. 接收者首先将待签数据进行盲变换,把变换后的盲数据发给签名者。 -![](http://upload-images.jianshu.io/upload_images/11336404-c22f3f330c202a4d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -这里一般用对称加密,所以: -![](http://upload-images.jianshu.io/upload_images/11336404-e2f7a82497d99ebf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -2. 经签名者签名后再发给接收者。 - ![](http://upload-images.jianshu.io/upload_images/11336404-e858b0db3ba78057.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -3. 接收者对签名再作去盲变换,得出的便是签名者对原数据的盲签名。 - ![](http://upload-images.jianshu.io/upload_images/11336404-717c9c59aaf9e53a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - ![](http://upload-images.jianshu.io/upload_images/11336404-074b0968d84944d8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -流程如下: -![](http://upload-images.jianshu.io/upload_images/11336404-1c5466a4f060888b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -在这个过程中,B无法得知MSG是什么,B也不知道自己什么时候对MSG做了签名,即若B进行了多次签名,当公布出某一具体的MSG-signature时,B并不知道这个签名是自己在那一次进行签署的。 - -背景: -一般的签名,签名者对自己发出的签名,必须是记得的,比如,在何时何地对谁发的,他自己可以记下来。但是,如果把签名看作是电子现金的话,就涉及到一个匿名性的问题用实际钞票的时候,钞票上有没有写你的名字?当然没有。那我也不希望,银行通过追踪自己发出签名,来获得用户的消费情况。于是就设计出盲签名。 - -### 多重签名 - -多重签名技术(multisig)就是多个用户同时对一个数字资产进行签名才有效。 - -如果一个地址只能由一个私钥签名和支付,表现形式就是1/1;而多重签名的表现形式是m/n,也就是说一共n个私钥可以给一个账户签名,而当m个地址签名时,就可以支付一笔交易。所以,m一定是小于等于n的。 - -### 群签名 - -在一个群签名方案中,一个群体中的任意一个成员可以以匿名的方式代表整个群体对消息进行签名。与其他数字签名一样,群签名是可以公开验证的,而且可以只用单个群公钥来验证。 - -流程: -1. 初始化:群管理者建立群资源,生成对应的群公钥(Group Public Key)和群私钥(Group Private Key)群公钥对整个系统中的所有用户公开,比如群成员、验证者等。 -2. 成员加入:在用户加入群的时候,群管理者颁发群证书(Group Certificate)给群成员。 -3. 签名:群成员利用获得的群证书签署文件,生成群签名. -4. 验证:同时验证者利用**群公钥**仅可以验证所得群签名的正确性,但不能确定群中的正式签署者。 -5. 打开:**群管理者利用群私钥可以对群用户生成的群签名进行追踪,并暴露签署者身份**。 - -### 环签名 - -是一种简化的群签名,只有环成员没有管理者,不需要环成员间的合作。环签名方案中签名者首先选定一个临时的签名者集合,集合中包括签名者。然后签名者利用自己的私钥和签名集合中其他人的公钥就可以独立的产生签名,而无需他人的帮助。签名者集合中的成员可能并不知道自己被包含在其中。 - -#### 环签名满足的性质 - -1. 无条件匿名性: 攻击者无法确定签名是由环中哪个成员生成,即使在获得环成员私钥的情况下,概率也不超过1/n。 -2. 正确性: 签名必需能被所有其他人验证。 -3. 不可伪造性: 环中其他成员不能伪造真实签名者签名,外部攻击者即使在获得某个有效环签名的基础上,也不能为消息m伪造一个签名。 - -#### 环签名实现 - -1. 密钥生成: 为环中每个成员产生一个密钥对(公钥PKi,私钥SKi)。 -2. 签名: 签名者用自己的私钥和任意n个环成员(包括自己)的公钥为消息m生成签名a。 -3. 签名验证: 验证者根据环签名和消息m,验证签名是否为环中成员所签,如果有效就接收,否则丢弃。 - -#### 环签名和群签名的比较 - -1. 匿名性:都是一种个体代表群体签名的体制,验证者能验证签名为群体中某个成员所签,但并不能知道为哪个成员,以达到签名者匿名的作用。 -2. 可追踪性:**群签名中,群管理员的存在保证了签名的可追踪性**。群管理员可以撤销签名,揭露真正的签名者。环签名本身无法揭示签名者,除非签名者本身想暴露或者在签名中添加额外的信息。提出了一个可验证的环签名方案,方案中真实签名者希望验证者知道自己的身份,此时真实签名者可以通过透露自己掌握的秘密信息来证实自己的身份。 -3. 管理系统:群签名由群管理员管理,环签名不需要管理,签名者只有选择一个可能的签名者集合,获得其公钥,然后公布这个集合即可,所有成员平等。 - -#### 算法实现 - -```python -import os, hashlib, random, Crypto.PublicKey.RSA - -class ring: - def __init__(self, k, L=1024): - self.k = k - self.l = L - self.n = len(k) - self.q = 1 << (L - 1) - - def sign(self, m, z): - self.permut(m) - s = [None] * self.n - u = random.randint(0, self.q) - c = v = self.E(u) - for i in (range(z+1, self.n) + range(z)): - s[i] = random.randint(0, self.q) - e = self.g(s[i], self.k[i].e, self.k[i].n) - v = self.E(v^e) - if (i+1) % self.n == 0: - c = v - s[z] = self.g(v^u, self.k[z].d, self.k[z].n) - return [c] + s - - def verify(self, m, X): - self.permut(m) - def _f(i): - return self.g(X[i+1], self.k[i].e, self.k[i].n) - y = map(_f, range(len(X)-1)) - def _g(x, i): - return self.E(x^y[i]) - r = reduce(_g, range(self.n), X[0]) - return r == X[0] - - def permut(self, m): - self.p = int(hashlib.sha1('%s' % m).hexdigest(),16) - - def E(self, x): - msg = '%s%s' % (x, self.p) - return int(hashlib.sha1(msg).hexdigest(), 16) - - def g(self, x, e, n): - q, r = divmod(x, n) - if ((q + 1) * n) <= ((1 << self.l) - 1): - rslt = q * n + pow(r, e, n) - else: - rslt = x - return rslt -``` - -签名并验证两个由4个用户组成的环签名消息: - -```python -size = 4 -msg1, msg2 = 'hello', 'world!' - -def _rn(_): - return Crypto.PublicKey.RSA.generate(1024, os.urandom) - -key = map(_rn, range(size)) -r = ring(key) -for i in range(size): - s1 = r.sign(msg1, i) - s2 = r.sign(msg2, i) - assert r.verify(msg1, s1) and r.verify(msg2, s2) and not r.verify(msg1, s2) -``` - -# Reference - -1. [数字签名是什么?](http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html) -2. [Digital Signatures](http://learnmeabitcoin.com/guide/digital_signatures) -3. [比特币背后的密码学原理](https://www.jianshu.com/p/225ff9439132) -4. [《区块链原理设计与应用》第5章:密码学与安全技术](https://github.com/yjjnls/books/blob/master/block%20chain/%E5%8C%BA%E5%9D%97%E9%93%BE%E5%8E%9F%E7%90%86%E3%80%81%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%BA%94%E7%94%A8.pdf) -5. [Secure Hash Algorithms](https://en.wikipedia.org/wiki/Secure_Hash_Algorithms) -6. [Digital Signature Algorithm](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm) -7. [用实例给新手讲解RSA加密算法](https://www.cnblogs.com/jiftle/p/7903762.html) -8. [用Go语言实现环签名的签名和验证(一)](https://blog.csdn.net/qq_38200023/article/details/79902049) -9. [用Go语言实现环签名的签名和验证(二)](https://blog.csdn.net/qq_38200023/article/details/79912530) diff --git a/Basic/merkle_tree_in_blockchain.md b/Basic/merkle_tree_in_blockchain.md deleted file mode 100644 index eb725e4..0000000 --- a/Basic/merkle_tree_in_blockchain.md +++ /dev/null @@ -1,264 +0,0 @@ -# Merkle tree in blockchain -- [Merkle tree in blockchain](#merkle-tree-in-blockchain) - - [区块链节点中的数据都存在哪里](#区块链节点中的数据都存在哪里) - - [比特币中的区块结构是怎样的](#比特币中的区块结构是怎样的) - - [什么是 Merkle Tree 和 Merkle Proof](#什么是-merkle-tree-和-merkle-proof) - - [以太坊中的 merkle tree](#以太坊中的-merkle-tree) - - [Merkle Patricia tree](#merkle-patricia-tree) - - [更深入的 Merkle Patricia tree](#更深入的-merkle-patricia-tree) - - [Transaction trie](#transaction-trie) - - [State Trie](#state-trie) - - [Storage trie](#storage-trie) - - -## 区块链节点中的数据都存在哪里 - -在持久化方面,区块链数据可以直接存储在一个扁平的文件中,也可以存储在简单的数据库系统中,比特币和以太坊都区块链数据存储在 google的 LevelDb中。 - -## 比特币中的区块结构是怎样的 - -![](http://upload-images.jianshu.io/upload_images/11336404-713157d70bdf8b6a?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -Version: 用于区分软件版本号Previous Block Hash:是指向前一个区块头的 hash。在比特币中,区块头的 hash一般都是临时算出,并没有包含在本区块头或者区块中,但在持久化的时候可以作为索引存储以提高性能 - -Nonce、Difficulty Target和 Timestamp : 用在 pow共识算法中。 - -Merkle Root: 是区块中所有交易的指纹,merkle tree的树根。交易在区块链节点之间传播,所有节点都按相同的算法(merkle tree)将交易组合起来,如此可以判断交易是否完全一致,此外也用于轻量钱包中快速验证一个交易是否存在于一个区块中。 - -## 什么是 Merkle Tree 和 Merkle Proof - -![](http://upload-images.jianshu.io/upload_images/11336404-e83df55bc68ea283?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -如上图,merkle Tree是一颗平衡树,树根也就是 Merkle Root存在区块头中。树的构建过程是递归地的计算 Hash的过程,如:先是 Hash交易 a得到 Ha,Hash交易 b得到 Hb,再 Hash前两个 Hash(也就是 Ha和 Hb)得到 Hab,其他节点也是同理递归,最终得到 Merkle Root。 - -Merkle tree在区块链中有两个作用: - -1. 仅仅看 merkle root就可以知道区块中的所有交易是不是一样的 - -2. 对于轻量节点来说(不存储所有的交易信息,只同步区块头)提供了快速验证一个交易是否存在交易中的方法。 - -merkle proof从某处出发向上遍历,算出 merkle Root的所需要经过的路径节点。在上图的例子中,如果轻量钱包要验证 Txb(红色方框)是否已经包含在区块中,可以向全量节点请求 merkle Proof,用于证明 Txb的存在,过程为: - -1. 全量节点只要返回黄色部分的节点信息(Ha与 Hcd) - -2. 轻量节点执行计算 Hash(Txb)=Hb à Hash(Ha + Hb)=Hab à Hash(Hab + Hcd)=Habcd,计算出来的 merkleRoot(也就是 Habcd)跟已知区块头中的 merkleRoot比较,如果一样则认为交易确实已经入块。 - -在上图的区块中,仅仅存在少量的区块。如果区块所包含的交易很多,merkle proof仅仅需要带 log2(N)个节点,此时 merkle proof的优势就会变得非常明显。 - -## 以太坊中的 merkle tree - -在比特币中,系统底层不维护每个账户的余额,只有 UTXO(Unspent Transaction Outputs)。账户之间的转账通过交易完成,确切地说,比特币用户将 UTXO作为交易的输入,可以花掉一个或者多个 UTXO。 - -一个 UTXO像一张现金纸币,要么不使用,要么全部使用,而不能只花一部分。举个例子来说,一个用户有一个价值 1比特币的 UTXO,如果他想转账 0.5给某人,那他可以创建一个交易,以这个价值 1比特币的 UTXO为输入,另外产生 0.5比特币的 OTXO作为这个交易的输出(找零给自己)。 - -比特币这个公开底层系统本身不单独维护每个账户的余额,不过比特币钱包可以记录每个用户所拥有的 UTXO,这样计算出用户的余额。 - -以太坊相比比特币,额外引入了账号状态数据,比如 nonce、余额 balance和合约数据,这些是区块链的关键数据,具有以下特性: - -随着交易的入块需要不断高效地更新,所有的这些数据在不同节点之间能够高效地验证是一致的,状态数据不断更新的过程中,历史版本的数据数据需要保留。 - -系统中的每个节点执行完相同区块和交易后,那么这些节点中对应的所有账户数据都是一样的,账户列表相同,账户对应的余额等数据也相同。总的来说,这些账户数据就像状态机的状态,每个节点执行相同区块后,达到的状态应该是完全一致的。但是,这个状态并不是直接写到区块里面,因为这些数据都是可以由区块和交易重新产生的,如果写到区块里面会增加区块的大小,加重区块同步的负担。 - -![](http://upload-images.jianshu.io/upload_images/11336404-080b75ab1e7c9261?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -如上所示,区块头中保存了三个 merkle tree的 root: - -tansaction root: 跟比特币中的 Merkle Root作用相同,相当于区块中交易的指纹,用于快速验   证交易是否相同以及证明某个交易的存在。 - -state root: 这颗树是账户状态(余额和 nonce等)存放的地方,除此之外,还保存着 storage root,也就是合约数据保存的地方。receipts root:区块中合约相关的交易输出的事件。 - -## Merkle Patricia tree - -在 Transaction Root中,用类似比特币的二进制 merkle tree是能够解决问题的,因为它更适用于处理队列数据,一旦建好就不再修改。但是对于 state tree,情况就复杂多了,本质上来说,状态数据更像一个 map,包含着账号和账号状态的映射关系。除此之外,state tree还需要经常更新,经常插入或者删除,这样重新计算 Root的性能就显得尤其重要。 - -Trie是一种字典树,用于存储文本字符,并利用了单词之间共享前缀的特点,所以也叫做前缀树。Trie树在有些时候是比较浪费空间的,如下所示,即使这颗树只有两个词,如果这两个词很长,那么这颗树的节点也会变得非常多,无论是对于存储还是对于 cpu来说都是不可接受的。如下所示: - -![](http://upload-images.jianshu.io/upload_images/11336404-d1e60c3f5bab8680?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -相比 Trie树,Patricia Trie将那些公共的的路径压缩以节省空间和提高效率,如下所示: - -![](http://upload-images.jianshu.io/upload_images/11336404-e70174b908356ec8?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -以太坊中的 Merkle Patricia trie,顾名思义,它是 Patricia trie和 Merkle Tree的结合,即具有 merkle tree的特性,也具有 Patricia Trie的特征: - -1.密码学安全,每个节点都都是按 hash引用,hash用来在 LevelDb中找对应的存储数据; - -2.像 Patricia trie树一样,这些可以根据 Path来找对应的节点以找到 value; - -3.引入了多种节点类型: - -a.空节点 (比如说当一颗树刚刚创建为空的时候) - -b.叶子节点,最普通的 [key, value] - -c.扩展节点,跟叶子节点类似,不过值变成了指向别的节点的 hash,[key, hash] - -d.分支节点,是一个长度为 17的列表,前 16元素为可能的十六进制字符,最后一个元素为 value(如果这是 path的终点的话) - -举个例子: - -![](http://upload-images.jianshu.io/upload_images/11336404-18ee531a20343340?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -在上图中的 trie包含了 4对 key value,需要注意的是,key是按照 16进制来显示的,也就是 a7占用一个字节,11占用一个字节等等 - -1.第一层的 Node是扩展节点,4个 Key都有公有的前缀 a7,next node指向一个分支节点 - -2.第二层是一个分支节点,由于 key转换成了十六进制,每个 branch最多有 16个分支。下标也作为 path的一部分用于 path查找。比如说下标为 1的元素中指向最左边的叶子节点(key-end为 1355),到叶子节点就完成了 key搜索:扩展节点中 a7 + 分支节点下标 1 + 叶子节点 1355 = a711355 - -3.叶子节点和扩展节点的区分。正如上面提到的,叶子节点和扩展节点都是两个字段的节点,也就是 [key,value],存储中没有专门字段用来标识类型。为了区分这两种节点类型并节省空间,在 key中加入了 4bits(1 nibble)的 flags的前缀,用 flags的倒数第二低的位指示是叶子节点还是扩展节点。此外,加入了 4bits之后,key的长度还有可能不是偶数个 nibble(存储中只能按字节存储),为此,如果 key是奇数个 nibble,在 flags nibble之后再添加一个空的 nibble,并且用 flags的最低位表示是否有添加,详见上图左下角。 - -## 更深入的 Merkle Patricia tree - -更详细的字段关系如下图所示: - -![](http://upload-images.jianshu.io/upload_images/11336404-eddd4da7b467b169?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -下面将通过代码片段的形式,逐一验证各个 trie的结构(前提条件是先在本地搭建起以太坊私有链)。 - -### Transaction trie -如下所示,在本地环境发送交易并使之入块,查看区块的交易列表,TransactionsRoot和 RawTransaction: - -``` -> eth.getBlock(49).transactions -["0xdf648e4ce9bed9d3b0b35d969056ac496207692f96bd13327807e920e97a1b2f"] -> eth.getBlock(49).transactionsRoot -"0x1a65885367afcc561411fe68ed870e4952b11477ad5314de1ae8f26d48a03724" ->eth.getRawTransaction("0xdf648e4ce9bed9d3b0b35d969056ac496207692f96bd13327807e920e97a1b2f") -"0xf86505850430e2340083015f90947b04e3fe46e1cd9939bf572307fdc076478b5252018042a0e9893deacc678345ea700e714b84ce31ffe4a50267c324436fab2c48906871ada03704497c029452a1b19b1f4876e958ec7e873600408d89a8bf46e53c6e5f921e" -``` - -在 trie包中写单测函数,key为交易在区块中的 index,RLP编码,value为签名过的原始交易 RawTransaction: - -``` -func TestMyTrieCalculateTxTree(t *testing.T) { -    var trie Trie -    keybuf := new(bytes.Buffer) -    rlp.Encode(keybuf, uint(0)) -    valueBytes, _ := -        hexutil.Decode("0xf86505850430e2340083015f90947b04e3fe46e1cd9939bf572307fdc076478b5252018042a0e9893deacc678345ea700e714b84ce31ffe4a50267c324436fab2c48906871ada03704497c029452a1b19b1f4876e958ec7e873600408d89a8bf46e53c6e5f921e") -   trie.Update(keybuf.Bytes(), valueBytes) -    t.Logf("Got Root:%s", trie.Hash().String()) -} -``` - -运行输出得到的 Hash,也即 transactionsRoot为: - -``` -0x1a65885367afcc561411fe68ed870e4952b11477ad5314de1ae8f26d48a03724,跟 eth.getBlock(49).transactionsRoot得到的是一致的。 -$ go test -v -run TestMyTrieCalculateTxTree -=== RUN  TestMyTrieCalculateTxTree ---- PASS: TestMyTrieCalculateTxTree (0.00s) -    my_trie_test.go:18: Got Root:0x1a65885367afcc561411fe68ed870e4952b11477ad5314de1ae8f26d48a03724 -PASS -ok     github.com/ethereum/go-ethereum/trie    0.036s -``` - -### State Trie - -获取最新的区块的 stateRoot,以及打印出账号 0x08e5f4cc4d1b04c450d00693c95ae58825f6a307的余额 - -``` -> eth.getBlock(eth.blockNumber).stateRoot -"0xccc450ac770b0a644b81a8c0729733cf06d19f177e04fe664e1562dc3a620d60" -> eth.getBalance("0x08e5f4cc4d1b04c450d00693c95ae58825f6a307") -2.3229575729235784806170618e+25 -``` - -在 state包中写单测函数,state trie的数据以 trie节点 hash为 key存在 leveldb中,所以整个 state trie的入口 key就是 stateRoot。state tree中存储数据的 path为 account的 hash,value为 RLP编码过的结构体数据。为了简单起见和节省篇幅,这里省去了错误检查。 - -``` -func TestMyTrieCalculateStateTree(t *testing.T) { -    ldb, _ := ethdb.NewLDBDatabase("/Users/peace/ethereum/geth/chaindata", 0, 0) -    tr, _ := trie.New(common.HexToHash("0xccc450ac770b0a644b81a8c0729733cf06d19f177e04fe664e1562dc3a620d60"), -        trie.NewDatabase(ldb)) - -    accBytes, _ := hexutil.Decode("0x08e5f4cc4d1b04c450d00693c95ae58825f6a307") -    keyBytes := crypto.Keccak256Hash(accBytes).Bytes() -    valueBytes, _ := tr.TryGet(keyBytes) - -    var acc Account -    rlp.DecodeBytes(valueBytes, &acc) -    t.Logf("balance:%d", acc.Balance) -} -``` - -运行输出得到 0x08e5f4cc4d1b04c450d00693c95ae58825f6a307的余额,跟 eth.getBalance接口得到的结果是一致的。 - -``` -peaces-MacBook-Air:state peace$ go test -v -run TestMyTrieCalculateStateTree -=== RUN  TestMyTrieCalculateStateTree ---- PASS: TestMyTrieCalculateStateTree (0.01s) -    my_state_test.go:25: balance:23229575729235784806170618 -PASS -ok     github.com/ethereum/go-ethereum/core/state  0.051s -``` - -### Storage trie - -如下合约,为了简单起见,合约中省去了构造函数等不相关的内容,部署后地址为: - -``` -0x9ea9b9eeac924fd784b064dabf174a55113c4064。 -pragma solidity ^0.4.0; -contract testStorage { -   uint storeduint = 2018; -   string storedstring = 'Onething, OneWorld!'; -} -``` - -获取到当前最新块的 stateRoot为 0x86bce3794034cddb3126ec488d38cb3ee840eeff4e64c3afe0ec5a5ca8b5f6ed。 - -```sh -> eth.getBlock(eth.blockNumber).stateRoot -"0x86bce3794034cddb3126ec488d38cb3ee840eeff4e64c3afe0ec5a5ca8b5f6ed" -``` - -在 state包中写单测函数,首先获以 0x86bce3794034cddb3126ec488d38cb3ee840eeff4e64c3afe0ec5a5ca8b5f6ed创建 trie,取获取合约账号 0x9ea9b9eeac924fd784b064dabf174a55113c4064的 storageRoot,之后再以这个 storageRoot创建 trie。在取合约内部数据时,key为 hash过的 32字节 index,value为 RLP编码过的值。 - -``` -func TestMyTrieGetStorageData(t *testing.T) { -    ldb, _ := ethdb.NewLDBDatabase("/Users/peace/ethereum/geth/chaindata", 0, 0) -    statTr, _ := -        trie.New(common.HexToHash("0x86bce3794034cddb3126ec488d38cb3ee840eeff4e64c3afe0ec5a5ca8b5f6ed"), -            trie.NewDatabase(ldb)) - -    accBytes, _ := hexutil.Decode("0x9ea9b9eeac924fd784b064dabf174a55113c4064") -    accKeyBytes := crypto.Keccak256Hash(accBytes).Bytes() -    accValueBytes, _ := statTr.TryGet(accKeyBytes) - -    var acc Account -    rlp.DecodeBytes(accValueBytes, &acc) -    t.Logf("storageRoot:%s", acc.Root.String()) - -    storageTr, _ := trie.New(common.HexToHash(acc.Root.String()), -        trie.NewDatabase(ldb)) -    index0KeyBytes, _ := hexutil.Decode("0x0000000000000000000000000000000000000000000000000000000000000000") -    index0ValuesBytes, _ := storageTr.TryGet(crypto.Keccak256Hash(index0KeyBytes).Bytes()) -    var storedUint uint -    rlp.DecodeBytes(index0ValuesBytes, &storedUint) -    t.Logf("storedUint: %d", storedUint) - -    index1KeyBytes, _ := hexutil.Decode("0x0000000000000000000000000000000000000000000000000000000000000001") -    index1ValuesBytes, _ := storageTr.TryGet(crypto.Keccak256Hash(index1KeyBytes).Bytes()) -    t.Logf("raw bytes: %s", hexutil.Encode(index1ValuesBytes)) -    var storedString string -    rlp.DecodeBytes(index1ValuesBytes, &storedString) -    t.Logf("storedString: %s", storedString) -} -``` - -运行输出以下数据 storedUint为 2018,跟合约里的数据是一致的。值得注意的是 storedString的数据后面多了一个十六进制的 26(十进制为 38),是字符串长度 (19)的两倍,更多的细节请参见 http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage。 - -同时,更复杂的数据结构如变长数组、map等规则会更加复杂,同时这里也忽略了一些字段打包存储等细节,但是都围绕着 storageTrie,基本原理没有改变。 - -``` -go test -v -run TestMyTrieGetStorageData -=== RUN  TestMyTrieGetStorageData ---- PASS: TestMyTrieGetStorageData (0.01s) -    my_state_test.go:41: storageRoot:0x3fa426aa67fff5c38788fe04e4f9815652d0b259a44efed794c309577ddc2057 -    my_state_test.go:49: storedUint: 2018 -    my_state_test.go:53: raw bytes: 0xa04f6e657468696e672c204f6e65576f726c642100000000000000000000000026 -    my_state_test.go:56: storedString: Onething, OneWorld!& -PASS -ok     github.com/ethereum/go-ethereum/core/state  0.047s -``` diff --git a/README.md b/README.md index e074203..784a9db 100644 --- a/README.md +++ b/README.md @@ -134,22 +134,27 @@ The "classic" Satoshi-blockchain is like a git repo with a single master branch --> - **Encryption knowledge** - * [Basic concepts](./Basic/crypto.md#%E6%95%B0%E5%AD%97%E5%8A%A0%E5%AF%86%E7%9B%B8%E5%85%B3%E7%9F%A5%E8%AF%86) - Asymmetric encryption, Digital signature, Certificate - * [Digital signature extension](./Basic/crypto.md#%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E6%89%A9%E5%B1%95) - Multi-signature, Blind signature, Group signature, Ring signature - * [Merkle tree](./Basic/crypto.md#merkle-tree) - * [Merkle tree in blockchain](./Basic/merkle_tree_in_blockchain.md) + * [Basic concepts](https://www.jianshu.com/p/a044b303f7d5) - Asymmetric encryption, Digital signature, Certificate + * [Digital signature extension](https://www.jianshu.com/p/410e77ec23fa) - Multi-signature, Blind signature, Group signature, Ring signature + * [Merkle tree](https://www.jianshu.com/p/a044b303f7d5) + * [Merkle DAG](http://www.sohu.com/a/247540268_100222281) * [**CryptoNote v2.0**](https://cryptonote.org/whitepaper.pdf) - Untraceable Transactions and Egalitarian Proof-of-work - **Consensus** + * [Proof of Work](https://www.jianshu.com/p/3462f2ed74d7) + * [Proof of Stake](https://www.jianshu.com/p/2fd3bce523b0) * [Proof of Stake FAQs](https://github.com/ethereum/wiki/wiki/Proof-of-Stake-FAQs) / [Chinese version](https://ethfans.org/posts/Proof-of-Stake-FAQ-new-2018-3-15) + * [Delegated Proof of Stake](https://www.jianshu.com/p/ccc3fff7a60d) + * [Practical Byzantine Fault Tolerance](https://www.jianshu.com/p/e991c1385f9f) - **Account and transaction model** + * [UTXO model](https://www.jianshu.com/p/2f4e75dbc2e4)