update basic concept link

This commit is contained in:
yjjnls 2019-05-10 16:08:16 +08:00
parent 5b8b8d34ed
commit 4022253d90
5 changed files with 9 additions and 886 deletions

View File

@ -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 的前向交易是 TX2TX2 中的 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 模型可以根据业务场景进行判断**

View File

@ -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)

View File

@ -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为小于nn是点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)和点KG传给用户B。
4. 用户B接到信息后 将待传输的明文编码到Ep(a,b)上一点M编码方法很多这里不作讨论并产生一个随机整数r
5. 用户B计算点C1=M+rKC2=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的公钥加密信息然后发给BB用自己的私钥解密就可以看到信息内容。
## 数字签名
A把要加密的内容用hash函数生成摘要digest再用自己的私钥对digest加密生成数字签名signature。连同加密的内容一起发给B。
B收到后对**摘要digest用A的公钥解密**和**内容(用自己的私钥解密)** 都解密再对内容使用相同hash看得到的digest是否相同相同说明发送的内容没有被修改。
> 但是如果这里用B的公钥来加密摘要digestB收到后用自己的私钥解密这不是也可以验证内容是否被篡改么
如果仅仅从防篡改的角度来讲确实可以,但是这样无法验证这些内容是谁发来的!**所以用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、N0D0拥有者可以检测到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 AlgorithmDSA只是一种算法和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所以一般都是用ECDHEephemeral或其他版本的ECDH算法。ECDH则是基于ECC的DHDiffie-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)

View File

@ -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得到 HaHash交易 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
在比特币中,系统底层不维护每个账户的余额,只有 UTXOUnspent 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.叶子节点和扩展节点的区分。正如上面提到的,叶子节点和扩展节点都是两个字段的节点,也就是 [keyvalue],存储中没有专门字段用来标识类型。为了区分这两种节点类型并节省空间,在 key中加入了 4bits1 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为交易在区块中的 indexRLP编码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的 hashvalue为 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字节 indexvalue为 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
```

View File

@ -134,22 +134,27 @@ The "classic" Satoshi-blockchain is like a git repo with a single master branch
--> -->
- **Encryption knowledge** - **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 * [Basic concepts](https://www.jianshu.com/p/a044b303f7d5) - 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 * [Digital signature extension](https://www.jianshu.com/p/410e77ec23fa) - Multi-signature, Blind signature, Group signature, Ring signature
* [Merkle tree](./Basic/crypto.md#merkle-tree) * [Merkle tree](https://www.jianshu.com/p/a044b303f7d5)
* [Merkle tree in blockchain](./Basic/merkle_tree_in_blockchain.md) <!-- * [Merkle tree in blockchain](./Basic/merkle_tree_in_blockchain.md) -->
* [Merkle DAG](http://www.sohu.com/a/247540268_100222281) * [Merkle DAG](http://www.sohu.com/a/247540268_100222281)
* [**CryptoNote v2.0**](https://cryptonote.org/whitepaper.pdf) - Untraceable Transactions and Egalitarian Proof-of-work * [**CryptoNote v2.0**](https://cryptonote.org/whitepaper.pdf) - Untraceable Transactions and Egalitarian Proof-of-work
<!-- <!--
### Consensus ### Consensus
--> -->
- **Consensus** - **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) * [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 ### Account and transaction model
--> -->
- **Account and transaction model** - **Account and transaction model**
* [UTXO model](https://www.jianshu.com/p/2f4e75dbc2e4)
<!-- <!--
### Exchange ### Exchange
--> -->