mirror of
https://github.com/yjjnls/awesome-blockchain.git
synced 2025-04-05 20:35:40 -04:00
Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f33bdb2f8 | ||
![]() |
9d5fc0606f | ||
![]() |
0c67ca2a0d | ||
![]() |
c95ae536ca | ||
![]() |
0ce939be81 | ||
![]() |
6642a9b749 | ||
![]() |
a1e1afe971 | ||
![]() |
570538d543 | ||
![]() |
942e6566ba | ||
![]() |
719e361a5b | ||
![]() |
0b6fc584d6 | ||
![]() |
b92815e511 | ||
![]() |
8242e40650 | ||
![]() |
2512dcf00b | ||
![]() |
12daa2501c | ||
![]() |
ff4b382477 | ||
![]() |
33ded574a0 | ||
![]() |
12d82bd094 | ||
![]() |
12bcc623c0 | ||
![]() |
e58fdcac58 | ||
![]() |
527b266967 | ||
![]() |
1bf9c2987d | ||
![]() |
f8a5780203 | ||
![]() |
c444d784b5 | ||
![]() |
c86b1220d2 | ||
![]() |
ef658a0e20 | ||
![]() |
b730de7466 | ||
![]() |
2c3663bbd9 | ||
![]() |
516bac0a7c | ||
![]() |
8324e10e95 | ||
![]() |
d556b8d3aa | ||
![]() |
906500a7a2 | ||
![]() |
1f76886b11 | ||
![]() |
21c286ec8d | ||
![]() |
8eab3365f0 | ||
![]() |
1f32ce129c | ||
![]() |
32318327e1 | ||
![]() |
f0292d5873 | ||
![]() |
3e147b0608 | ||
![]() |
15bae86551 | ||
![]() |
fc7545ccbd | ||
![]() |
70b2da836d | ||
![]() |
688a23e0e7 | ||
![]() |
704906424d | ||
![]() |
280ed6c5e8 | ||
![]() |
9d1f8df29d | ||
![]() |
65cf1e08cf | ||
![]() |
68785c417a | ||
![]() |
cfc5c5afb4 | ||
![]() |
52c6b21e0b | ||
![]() |
57062dbc57 | ||
![]() |
5237c87234 | ||
![]() |
3be0a1b460 | ||
![]() |
396c01eac0 | ||
![]() |
b6068d1cbd | ||
![]() |
fb1f96f740 | ||
![]() |
561721a01f | ||
![]() |
c7170b1579 | ||
![]() |
4cb0be2152 | ||
![]() |
2a7d72917e | ||
![]() |
2ed7a43df7 | ||
![]() |
ad7ee427fb | ||
![]() |
1c1ef730b4 | ||
![]() |
ee693b8544 | ||
![]() |
ec407c3c39 | ||
![]() |
534a17379c | ||
![]() |
a0f7ec7b0b | ||
![]() |
e7ae1245d0 | ||
![]() |
da92f231b3 | ||
![]() |
9ab39e1683 | ||
![]() |
8eda55ccbf | ||
![]() |
b2c4b4b9ae | ||
![]() |
abf56dee0a | ||
![]() |
8f2e1f50dd | ||
![]() |
51fdccafe4 | ||
![]() |
d7120b7b87 | ||
![]() |
e93887165b | ||
![]() |
b417813ed9 | ||
![]() |
4022253d90 | ||
![]() |
5b8b8d34ed | ||
![]() |
de2f88982b | ||
![]() |
bf7fe6f0e8 | ||
![]() |
ea72090b94 | ||
![]() |
4ca29118f3 | ||
![]() |
bac9a96c46 | ||
![]() |
5aab3a43cd | ||
![]() |
98f7331920 | ||
![]() |
17b38d9e71 | ||
![]() |
28e878ad8a | ||
![]() |
51008f5ea1 | ||
![]() |
b0f7b3307f | ||
![]() |
e602064a31 | ||
![]() |
4d964cba8f | ||
![]() |
805e6728ad | ||
![]() |
3cf616a1f3 | ||
![]() |
759114227f | ||
![]() |
33af66d19c |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*mod
|
||||
*sumdb
|
170
Basic/account.md
170
Basic/account.md
@ -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 实际的构造形式。
|
||||
|
||||

|
||||
|
||||
上图中,所有的交易都可以找到前向交易,例如 TX5 的前向交易是 TX2,TX2 中的 Output1 作为 TX5 中的 Input0。
|
||||
|
||||
意思就是 TX2 中的付款人使用了 Output1 中指向的比特币转移给 TX5 中的收款人,接着 TX5 中的人又把收到的比特币转移给了 TX6 中的收款人,成为了 TX6 中 Output0。
|
||||
|
||||
我们也可以发现,TX6 中的收款人还没有产生 TX7 交易,也就是说 Output0 还没有被花费,
|
||||
|
||||
这时候我们终于得到了 UTXO 的真正语义:Unspent Transaction Output,未花费的交易输出。
|
||||
|
||||
我们这时候可以发现 UTXO 也同样能表示余额,不过是重演计算的方式,它用不同的方式表达了余额,**我们把一个地址上所有的 UTXO 全部找出来,就是这个地址总的余额了**。
|
||||
|
||||
我们还可以发现,**无论是 TX5 还是 TX2,都已经成为历史交易,它们都忠实客观地记录了两笔交易,这两笔交易代表的是事件**,而不是余额状态转移,这是我们看到的最直观的区别。
|
||||
|
||||
我们再来看看一个真实的交易例子。
|
||||
|
||||

|
||||
|
||||
这是区块链上一笔真实交易的例子,它记录了一笔 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"
|
||||
}
|
||||
```
|
||||
|
||||
我们再看看比特币上的例子:
|
||||
|
||||

|
||||
|
||||
这一笔比特币交易包含 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 模型可以根据业务场景进行判断**。
|
@ -1,146 +0,0 @@
|
||||
# 区块链不能做什么
|
||||
但凡提到区块链的文章,都会强调“去中心化”、“不可篡改”等特(xue)性(tou)来吸人眼球。仿佛用区块链就能在不久的将来构建一个理想的乌托邦,但是拥有这么多特性的区块链到底怎么来构建这个乌托邦,却没人能说得清楚。毕竟现在的行业还处于初期探索,大部分注意力都集中在技术层面,技术尚未成熟大家就想要抢占市场,难免会不愿意承认“看不到方向”。若非要回答这个问题,总有人能说出一些看似合理的应用场景,但大多数经不起推敲,更没有验证。诚然,落地这事不能一味地钻研技术,应用场景、发展方向、经营管理模式等都是需要考虑的方向。这里就目前区块链的一些应用场景展开一些讨论,并非说区块链不能在这些领域有所应用,更多地是指出一些很多人不愿意提但又绕不开的问题。
|
||||
|
||||

|
||||
|
||||
首先我们需要弄清楚一个容易被混淆的问题:[基于区块链开发的应用真的能“去中心化”、“防篡改”如此这般安全么?](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)
|
302
Basic/crypto.md
302
Basic/crypto.md
@ -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进行因式分解却相对困难。那椭圆曲线上有什么难题呢?
|
||||
|
||||

|
||||
|
||||
考虑如下等式:
|
||||
|
||||
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值。
|
||||
|
||||

|
||||
|
||||
应用:
|
||||
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. 加密: 
|
||||
|
||||
2. 解密: 
|
||||
|
||||
而盲签名则按照如下步骤来进行:
|
||||
1. 接收者首先将待签数据进行盲变换,把变换后的盲数据发给签名者。
|
||||

|
||||
这里一般用对称加密,所以:
|
||||

|
||||
|
||||
2. 经签名者签名后再发给接收者。
|
||||

|
||||
|
||||
3. 接收者对签名再作去盲变换,得出的便是签名者对原数据的盲签名。
|
||||

|
||||

|
||||
|
||||
流程如下:
|
||||

|
||||
|
||||
在这个过程中,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)
|
@ -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中。
|
||||
|
||||
## 比特币中的区块结构是怎样的
|
||||
|
||||

|
||||
|
||||
Version: 用于区分软件版本号Previous Block Hash:是指向前一个区块头的 hash。在比特币中,区块头的 hash一般都是临时算出,并没有包含在本区块头或者区块中,但在持久化的时候可以作为索引存储以提高性能
|
||||
|
||||
Nonce、Difficulty Target和 Timestamp : 用在 pow共识算法中。
|
||||
|
||||
Merkle Root: 是区块中所有交易的指纹,merkle tree的树根。交易在区块链节点之间传播,所有节点都按相同的算法(merkle tree)将交易组合起来,如此可以判断交易是否完全一致,此外也用于轻量钱包中快速验证一个交易是否存在于一个区块中。
|
||||
|
||||
## 什么是 Merkle Tree 和 Merkle Proof
|
||||
|
||||

|
||||
|
||||
如上图,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和合约数据,这些是区块链的关键数据,具有以下特性:
|
||||
|
||||
随着交易的入块需要不断高效地更新,所有的这些数据在不同节点之间能够高效地验证是一致的,状态数据不断更新的过程中,历史版本的数据数据需要保留。
|
||||
|
||||
系统中的每个节点执行完相同区块和交易后,那么这些节点中对应的所有账户数据都是一样的,账户列表相同,账户对应的余额等数据也相同。总的来说,这些账户数据就像状态机的状态,每个节点执行相同区块后,达到的状态应该是完全一致的。但是,这个状态并不是直接写到区块里面,因为这些数据都是可以由区块和交易重新产生的,如果写到区块里面会增加区块的大小,加重区块同步的负担。
|
||||
|
||||

|
||||
|
||||
如上所示,区块头中保存了三个 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来说都是不可接受的。如下所示:
|
||||
|
||||

|
||||
|
||||
相比 Trie树,Patricia Trie将那些公共的的路径压缩以节省空间和提高效率,如下所示:
|
||||
|
||||

|
||||
|
||||
以太坊中的 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的终点的话)
|
||||
|
||||
举个例子:
|
||||
|
||||

|
||||
|
||||
在上图中的 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
|
||||
|
||||
更详细的字段关系如下图所示:
|
||||
|
||||

|
||||
|
||||
下面将通过代码片段的形式,逐一验证各个 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
|
||||
```
|
@ -62,8 +62,8 @@
|
||||
- [BlockTrail](https://www.blocktrail.com/BTC)
|
||||
- [Bitcoin Transaction Explorer](https://github.com/JornC/bitcoin-transaction-explorer)
|
||||
- [Blockexplorer.com](https://blockexplorer.com)
|
||||
- [Smartbit](https://www.smartbit.com.au)
|
||||
- [Bitkit](https://bitkit.live) - Real time transaction updates
|
||||
- [Blockonomics](https://www.blockonomics.co)
|
||||
|
||||
|
||||
## Libraries
|
||||
|
||||
|
10
CONTRIBUTING.md
Normal file
10
CONTRIBUTING.md
Normal file
@ -0,0 +1,10 @@
|
||||
Contributions welcome!
|
||||
|
||||
1. Fork it (https://github.com/yjjnls/awesome-blockchain/fork)
|
||||
2. Clone it (git clone https://github.com/yjjnls/awesome-blockchain)
|
||||
3. Create your feature branch (git checkout -b your_branch_name)
|
||||
4. Commit your changes (git commit -m 'Description of a commit')
|
||||
5. Push to the branch (git push origin your_branch_name)
|
||||
6. Create a new Pull Request
|
||||
|
||||
If you found this resource helpful, give it a 🌟 otherwise contribute to it and give it a ⭐️.
|
@ -25,6 +25,7 @@
|
||||
- [PocketEOS-IOS](https://github.com/OracleChain/PocketEOS-IOS) - Open source wallet for IOS.
|
||||
- [PocketEOS-Android](https://github.com/OracleChain/PocketEOS-Android) - Open source wallet for Android.
|
||||
- [EOS REACH Android](https://github.com/memtrip/eosreach) - Open source wallet for Android.
|
||||
- [Exodus](https://www.exodus.io/eos-wallet) - Popular multicurrency wallet with in-app exchange for desktop and mobile.
|
||||
|
||||
## Language Support
|
||||
|
||||
@ -68,4 +69,4 @@ https://developers.eos.io/
|
||||
|
||||
- [MonsterEOS](https://github.com/leordev/monstereos) - Tamagotchi game, integrates `eosio.token` with account balances, randomization etc.
|
||||
- [DecenTwitter](https://github.com/kesar/decentwitter) - Decent(ralized) Twitter - ZERO!!! Ram Cost
|
||||
- [Everipedia](https://github.com/EveripediaNetwork/Everipedia) - IPFS integration, tokenization and governance
|
||||
- [Everipedia](https://github.com/EveripediaNetwork/Everipedia) - IPFS integration, tokenization and governance
|
||||
|
@ -8,6 +8,8 @@
|
||||
## Wallets
|
||||
- [Official GUI]()
|
||||
- [Mymonero.com](https://mymonero.com) - Web wallet of fluffypony, core dev of Monero
|
||||
- [Exodus](https://www.exodus.io/monero-wallet/) - Popular multicurrency wallet with in-app exchange for desktop and mobile
|
||||
- [Monerujo](https://www.monerujo.io/) - Android wallet with ability to pay Bitcoin addresses using XMR.to
|
||||
- *Warning: fluffypony (core dev of Monero) is suspicious ([Link](https://monero.stackexchange.com/questions/897/does-monero-have-any-mobile-wallets-available) of those 2 wallets. Do your own research.*
|
||||
- [Android Wallet By Freewallet](https://play.google.com/store/apps/details?id=xmr.org.freewallet.app&hl=en)
|
||||
- [IOS Wallet By Freewallet](https://itunes.apple.com/us/app/monero-wallet-by-freewallet/id1126426159?mt=8)
|
||||
@ -52,4 +54,4 @@
|
||||
- [Bytecoin](https://bytecoin.org/) - The first cryptonote implementation
|
||||
- [FantomCoin](http://fantomcoin.org) - FantomCoin, allows merge mining with Monero
|
||||
- [Aeon](https://github.com/aeonix/aeon) - Aeon coin, a fork of Monero
|
||||
- [Digital Note](http://digitalnote.org)
|
||||
- [Digital Note](http://digitalnote.org)
|
||||
|
377
README.md
377
README.md
@ -6,39 +6,41 @@
|
||||
|
||||
The blockchain is an incorruptible digital ledger of economic transactions that can be programmed to record not just financial transactions but virtually everything of value (by [Don Tapscott](https://www.linkedin.com/pulse/whats-next-generation-internet-surprise-its-all-don-tapscott)).
|
||||
|
||||
<font color=#0099ff size=3>**This is not a simple collection of Internet resources, but verified and organized data ensuring it's really suitable for your learning process and useful for your development and application.**</font>
|
||||
<font color=#0099ff size=3>**This is not a simple collection of Internet resources, but verified and organized data ensuring it's really suitable for your learning process and useful for your development and application.**</font>
|
||||
|
||||
## Contents
|
||||
<details><summary>Click to expand</summary>
|
||||
|
||||
- [Awesome Blockchain](#awesome-blockchain)
|
||||
- [Contents](#contents)
|
||||
- [Frequently Asked Questions (F.A.Q.s) & Answers](#frequently-asked-questions-faqs--answers)
|
||||
- [Basic Introduction](#basic-introduction)
|
||||
- [Encryption knowledge](#encryption-knowledge)
|
||||
- [Consensus](#consensus)
|
||||
- [Account and transaction model](#account-and-transaction-model)
|
||||
- [Exchange](#exchange)
|
||||
- [Applications](#applications)
|
||||
- [Governance](#governance)
|
||||
- [Digital currency ranking](#digital-currency-ranking)
|
||||
- [Development Tutorial](#development-tutorial)
|
||||
- [BitCoin](#bitcoin)
|
||||
- [Ethereum](#ethereum)
|
||||
- [Consortium Blockchain](#consortium-blockchain)
|
||||
- [Fabric](#fabric)
|
||||
- [Hyperledger](#hyperledger)
|
||||
- [XuperChain](#xuperchain)
|
||||
- [FISCO-BCOS](#fisco-bcos)
|
||||
- [Releated Tools](#releated-tools)
|
||||
- [Solidity](#solidity)
|
||||
- [truffle](#truffle)
|
||||
- [web3.js](#web3js)
|
||||
- [Implementation of Blockchain](#implementation-of-blockchain)
|
||||
- [Projects and Applications](#projects-and-applications)
|
||||
- [Quorum](#quorum)
|
||||
- [Monero](#monero)
|
||||
- [IOTA](#iota)
|
||||
- [EOS](#eos)
|
||||
- [IFPS](#ifps)
|
||||
- [IPFS](#ipfs)
|
||||
- [Filecoin](#filecoin)
|
||||
- [BigchainDB](#bigchaindb)
|
||||
- [BitShares](#bitshares)
|
||||
- [ArcBlock](#arcblock)
|
||||
- [Further Extension](#further-extension)
|
||||
- [Papers](#papers)
|
||||
- [Books](#books)
|
||||
- [Applications](#applications-1)
|
||||
- [Applications](#applications)
|
||||
- [Identity Applications](#identity-applications)
|
||||
- [Public Blockchain Identity](#public-blockchain-identity)
|
||||
- [Blockchain as a collateral](#blockchain-as-a-collateral)
|
||||
@ -47,61 +49,57 @@ The blockchain is an incorruptible digital ledger of economic transactions that
|
||||
- [Internet of Things Applications](#internet-of-things-applications)
|
||||
- [Energy Applications](#energy-applications)
|
||||
- [Media and Journalism](#media-and-journalism)
|
||||
- [DeFi (Decentralised Finance)](#defi-decentralised-finance)
|
||||
- [Roadmaps](#roadmaps)
|
||||
- [Contribute](#contribute)
|
||||
|
||||
</details>
|
||||
|
||||
## Frequently Asked Questions (F.A.Q.s) & Answers
|
||||
|
||||
**Q: What's a Blockchain?**
|
||||
|
||||
A: A blockchain is a distributed database with a list (that is, chain) of records (that is, blocks) linked and secured by
|
||||
digital fingerprints (that is, crypto hashes).
|
||||
Example from [`blockchain.rb`](https://github.com/openblockchains/awesome-blockchains/blob/master/blockchain.rb/blockchain.rb):
|
||||
Example from [`genesis_block.json`](https://github.com/yjjnls/awesome-blockchain/tree/master/src/js/genesis_block.json):
|
||||
|
||||
```ruby
|
||||
[#<Block:0x1eed2a0
|
||||
@timestamp = 1637-09-15 20:52:38,
|
||||
@data = "Genesis",
|
||||
@previous_hash = "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
@hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b">,
|
||||
#<Block:0x1eec9a0
|
||||
@timestamp = 1637-09-15 21:02:38,
|
||||
@data = "Transaction Data...",
|
||||
@previous_hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b",
|
||||
@hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743">,
|
||||
#<Block:0x1eec838
|
||||
@timestamp = 1637-09-15 21:12:38,
|
||||
@data = "Transaction Data......",
|
||||
@previous_hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743",
|
||||
@hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4">,
|
||||
...
|
||||
```js
|
||||
{
|
||||
"version": 0,
|
||||
"height": 1,
|
||||
"previous_hash": null,
|
||||
"timestamp": 1550049140488,
|
||||
"merkle_hash": null,
|
||||
"generator_publickey": "18941c80a77f2150107cdde99486ba672b5279ddd469eeefed308540fbd46983",
|
||||
"hash": "d611edb9fd86ee234cdc08d9bf382330d6ccc721cd5e59cf2a01b0a2a8decfff",
|
||||
"block_signature": "603b61b14348fb7eb087fe3267e28abacadf3932f0e33958fb016ab60f825e3124bfe6c7198d38f8c91b0a3b1f928919190680e44fbe7289a4202039ffbb2109",
|
||||
"consensus_data": {},
|
||||
"transactions": []
|
||||
}
|
||||
```
|
||||
|
||||

|
||||
|
||||
**Q: What's a Hash? What's a (One-Way) Crypto(graphic) Hash Digest Checksum**?
|
||||
|
||||
A: A hash e.g. `eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743`
|
||||
A: A hash e.g. `d611edb9fd86ee234cdc08d9bf382330d6ccc721cd5e59cf2a01b0a2a8decfff`
|
||||
is a small digest checksum calculated
|
||||
with a one-way crypto(graphic) hash digest checksum function
|
||||
e.g. SHA256 (Secure Hash Algorithm 256 Bits)
|
||||
from the data. Example from [`blockchain.rb`](blockchain.rb/blockchain.rb):
|
||||
from the data. Example from [`crypto.js`](https://github.com/yjjnls/awesome-blockchain/blob/master/src/js/crypto.js):
|
||||
|
||||
```ruby
|
||||
def calc_hash
|
||||
sha = Digest::SHA256.new
|
||||
sha.update( @timestamp.to_s + @previous_hash + @data )
|
||||
sha.hexdigest ## returns "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743"
|
||||
end
|
||||
```js
|
||||
function calc_hash(data) {
|
||||
return crypto.createHash('sha256').update(data).digest('hex');
|
||||
}
|
||||
```
|
||||
|
||||
A blockchain uses
|
||||
|
||||
- the block timestamp (e.g. `1637-09-15 20:52:38`) and
|
||||
- the hash from the previous block (e.g. `edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b`) and finally
|
||||
- the block header (e.g. `Version`, `TimeStamp`, `Previous Hash...` )and
|
||||
- the block data (e.g. `Transaction Data...`)
|
||||
|
||||
to calculate the new hash digest checksum, that is, the hash
|
||||
e.g. `be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4`.
|
||||
to calculate the new hash digest checksum.
|
||||
|
||||
**Q: What's a Merkle Tree?**
|
||||
|
||||
@ -135,33 +133,38 @@ 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 tree in blockchain](./Basic/merkle_tree_in_blockchain.md) -->
|
||||
* [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
|
||||
-->
|
||||
- **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
|
||||
-->
|
||||
- **Account and transaction model**
|
||||
<!--
|
||||
* [UTXO model](https://www.jianshu.com/p/2f4e75dbc2e4)
|
||||
<!--
|
||||
### Exchange
|
||||
-->
|
||||
- **Exchange**
|
||||
<!--
|
||||
<!--
|
||||
### Applications
|
||||
-->
|
||||
- **Applications**
|
||||
* [Do You Need a Blockchain?](https://spectrum.ieee.org/computing/networks/do-you-need-a-blockchain)
|
||||
* [What can't blockchain do?](https://www.jianshu.com/p/70f6a29a6296)
|
||||
* [More](./Extension/application.md)
|
||||
* [More](./Extension/application.md)
|
||||
<!--
|
||||
### Governance
|
||||
-->
|
||||
@ -182,19 +185,20 @@ The "classic" Satoshi-blockchain is like a git repo with a single master branch
|
||||
|
||||
[<img src="https://bitcoin.org/img/icons/logotop.svg" align="right" width="120">](https://bitcoincore.org)
|
||||
|
||||
**Bitcoin** is an experimental digital currency that enables instant payments to anyone, anywhere in the world. Bitcoin uses **peer-to-peer** technology to **operate with no central authority**: managing transactions and issuing money are carried out collectively by the network.
|
||||
**Bitcoin** is an experimental digital currency that enables instant payments to anyone, anywhere in the world. Bitcoin uses **peer-to-peer** technology to **operate with no central authority**: managing transactions and issuing money are carried out collectively by the network.
|
||||
|
||||
- [BitCoin white paper: A Peer-to-Peer Electronic Cash System](https://bitcoin.org/bitcoin.pdf) / [Chinese version](BitCoin/white%20paper.md)
|
||||
- [BitCoin white paper: A Peer-to-Peer Electronic Cash System](https://bitcoin.org/bitcoin.pdf) / [Chinese version](BitCoin/white%20paper.md) / [Annotated BitCoin white paper](https://fermatslibrary.com/s/bitcoin)
|
||||
- [Mastering BitCoin](https://github.com/bitcoinbook/bitcoinbook) / [Chinese version](http://book.8btc.com/books/6/masterbitcoin2cn/_book/) / [pdf download](http://book.8btc.com/master_bitcoin?export=pdf)
|
||||
- [Bitcoin Improvement Proposals (BIPs)](https://github.com/bitcoin/bips/)
|
||||
|
||||
+ [But how does bitcoin actually work?](https://www.youtube.com/watch?v=bBC-nXj3Ng4)
|
||||
+ [Mining visualization](http://www.yogh.io/#mine:last)
|
||||
+ [Wallets](./BitCoin/awesome.md#wallets-api)
|
||||
+ [Explorers](./BitCoin/awesome.md#blockchain-explorers)
|
||||
+ [Libraries](./BitCoin/awesome.md#libraries) - C++, JavaScript, PHP, Ruby, Python, Java, .Net
|
||||
+ [Web services](./BitCoin/awesome.md#blockchain-api-and-web-services)
|
||||
+ [Full nodes](./BitCoin/awesome.md#full-nodes)
|
||||
+ [More](./BitCoin/awesome.md)
|
||||
+ [More](./BitCoin/awesome.md)
|
||||
|
||||
### [Ethereum](https://github.com/ethereum)
|
||||
|
||||
@ -205,19 +209,55 @@ The "classic" Satoshi-blockchain is like a git repo with a single master branch
|
||||
These apps run on a custom built **blockchain, an enormously powerful shared global infrastructure that can move value around and represent the ownership of property.**
|
||||
|
||||
|
||||
- [Ethereum white paper](https://github.com/ethereum/wiki/wiki/White-Paper) / [Chinese version](./Ethereum/white%20paper.md)
|
||||
- [Ethereum white paper](https://github.com/ethereum/wiki/wiki/White-Paper) / [Chinese version](./Ethereum/white%20paper.md) / [Annotated Ethereum white paper](https://fermatslibrary.com/s/ethereum-a-next-generation-smart-contract-and-decentralized-application-platform)
|
||||
- [Mastering Ethereum](https://github.com/ethereumbook/ethereumbook) / [Chinese version](https://github.com/inoutcode/ethereum_book)
|
||||
- [Ethereum Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf) / [Chinese version](https://github.com/yuange1024/ethereum_yellowpaper)
|
||||
- [Ethereum wiki](https://github.com/ethereum/wiki/wiki)
|
||||
- [Ethereum Design Rationale](https://github.com/ethereum/wiki/wiki/Design-Rationale) / [Chinese version](https://ethfans.org/posts/510)
|
||||
- [Ethereum problems](https://github.com/ethereum/wiki/wiki/Problems)
|
||||
- [Sharding roadmap](https://github.com/ethereum/wiki/wiki/Sharding-roadmap)
|
||||
- [**Ethereum flavored WebAssembly (ewasm)**](https://github.com/ewasm)
|
||||
- [ÐΞVp2p Wire Protocol](https://github.com/ethereum/wiki/wiki/%C3%90%CE%9EVp2p-Wire-Protocol)
|
||||
- [EVM-Awesome-List](https://github.com/ethereum/wiki/wiki/Ethereum-Virtual-Machine-(EVM)-Awesome-List)
|
||||
- [Patricia Tree](https://github.com/ethereum/wiki/wiki/Patricia-Tree)
|
||||
- Consensus
|
||||
- [Ethash](https://github.com/ethereum/wiki/wiki/Ethash)
|
||||
- [Ethash-DAG](https://github.com/ethereum/wiki/wiki/Ethash-DAG)
|
||||
- [Ethash Specification](https://github.com/ethereum/wiki/wiki/Ethash)
|
||||
- [Mining Ethash DAG](https://github.com/ethereum/wiki/wiki/Mining#ethash-dag)
|
||||
- [Dagger-Hashimoto Algorithm](https://github.com/ethereum/wiki/blob/master/Dagger-Hashimoto.md)
|
||||
- [DAG Explanation and Images](https://ethereum.stackexchange.com/questions/1993/what-actually-is-a-dag)
|
||||
- [Ethash in Ethereum Yellowpaper](https://ethereum.github.io/yellowpaper/paper.pdf#appendix.J)
|
||||
- [Ethash C API Example Usage](https://github.com/ethereum/wiki/wiki/Ethash-C-API)
|
||||
- [Accounts, Transactions, Gas, and Block Gas Limits in Ethereum](https://hudsonjameson.com/2017-06-27-accounts-transactions-gas-ethereum/)
|
||||
- [Ethereum Improvement Proposals](https://eips.ethereum.org/)
|
||||
- [Important EIPs and ERCs](https://github.com/ethereumbook/ethereumbook/blob/develop/appdx-standards-eip-erc.asciidoc#table-of-most-important-eips-and-ercs) / [EIP list](https://github.com/ethereum/EIPs)
|
||||
- Security
|
||||
- [Ethereum Smart Contract Security Best Practices](https://consensys.github.io/smart-contract-best-practices/) / [Chinese version](https://github.com/ConsenSys/smart-contract-best-practices/blob/master/README-zh.md)
|
||||
- [Onward with Ethereum Smart Contract Security](https://blog.zeppelin.solutions/onward-with-ethereum-smart-contract-security-97a827e47702)
|
||||
- [The Hitchhiker's Guide to Smart Contracts in Ethereum](https://blog.zeppelin.solutions/the-hitchhikers-guide-to-smart-contracts-in-ethereum-848f08001f05)
|
||||
- [**OpenZeppelin**](https://docs.openzeppelin.com/openzeppelin/)
|
||||
- [**openzeppelin contracts**](https://github.com/OpenZeppelin/openzeppelin-contracts) / [doc](https://docs.openzeppelin.com/contracts/2.x/)
|
||||
- [openzepplin sdk](https://github.com/OpenZeppelin/openzeppelin-sdk)
|
||||
- Token
|
||||
- [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) / [impl](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20)
|
||||
- [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md) / [impl](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC721)
|
||||
|
||||
+ Utils
|
||||
+ [Ethereum Blockchain Explorer](https://etherscan.io/)
|
||||
+ [Eth Gas Station](https://ethgasstation.info/)
|
||||
+ [Eth Network Status](https://ethstats.net/)
|
||||
|
||||
|
||||
+ [Ethereum Blockchain Explorer](https://etherscan.io/)
|
||||
+ [Eth Gas Station](https://ethgasstation.info/)
|
||||
+ [Eth Network Status](https://ethstats.net/)
|
||||
|
||||
- [**EEA** - Enterprise Ethereum: Private Blockchain For Enterprises](https://101blockchains.com/enterprise-ethereum/)
|
||||
- [What Is Enterprise Ethereum?](https://101blockchains.com/enterprise-ethereum/#1)
|
||||
- [What is The Enterprise Ethereum alliance?](https://101blockchains.com/enterprise-ethereum/#2)
|
||||
- [Benefits of Enterprise Ethereum](https://101blockchains.com/enterprise-ethereum/#3)
|
||||
- [Architecture Stack of the Enterprise Ethereum Blockchain](https://101blockchains.com/enterprise-ethereum/#4)
|
||||
- [What Are The Possible Enterprise Ethereum Use Cases?](https://101blockchains.com/enterprise-ethereum/#5)
|
||||
- [Ethereum Blockchain as a Service Providers](https://101blockchains.com/enterprise-ethereum/#6)
|
||||
- [Real-World Companies Using Enterprise Ethereum](https://101blockchains.com/enterprise-ethereum/#7)
|
||||
- [Final Words](https://101blockchains.com/enterprise-ethereum/#8)
|
||||
|
||||
### Consortium Blockchain
|
||||
* **Theory**
|
||||
@ -234,22 +274,161 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
- [How to Set Up a Private Ethereum Blockchain in 20 Minutes](https://arctouch.com/blog/how-to-set-up-ethereum-blockchain/)
|
||||
|
||||
|
||||
#### Fabric
|
||||
#### Hyperledger
|
||||
|
||||
[<img src="https://www.hyperledger.org/wp-content/uploads/2018/03/Hyperledger_Fabric_Logo_Color.png" align="right" width="120">](https://www.hyperledger.org/projects/fabric)
|
||||
|
||||
- [Hyperledger Org](https://wiki.hyperledger.org/)
|
||||
- Fabric
|
||||
- [Fabric Org](https://wiki.hyperledger.org/display/Fabric)
|
||||
- [Fabric Design Documents](https://wiki.hyperledger.org/display/fabric/Design+Documents)
|
||||
- [Fabric Wiki](https://hyperledger-fabric.readthedocs.io/en/latest/)
|
||||
- 1.4 [En](https://hyperledger-fabric.readthedocs.io/en/release-1.4/) / [Zn](https://hyperledger-fabric.readthedocs.io/zh_CN/release-1.4/) / [Release](https://hyperledger-fabric.readthedocs.io/_/downloads/en/release-1.4/pdf/)
|
||||
- 2.2 [En](https://hyperledger-fabric.readthedocs.io/en/release-2.2/) / [Zn](https://hyperledger-fabric.readthedocs.io/zh_CN/release-2.2/)
|
||||
- [Fabric Source Code Analyse](https://yeasy.gitbook.io/hyperledger_code_fabric/overview)
|
||||
- [A Kafka-based Ordering Service for Fabric](https://docs.google.com/document/d/19JihmW-8blTzN99lAubOfseLUZqdrB6sBR0HsRgCAnY/edit)
|
||||
|
||||
- Explorer
|
||||
- [Explorer Proposal](https://docs.google.com/document/d/1GuVNHZ5Jqq-gTVKflnZ1YiJfEoozvugqenC6QEQFQj4/edit)
|
||||
- [Explorer doc](https://blockchain-explorer.readthedocs.io/en/master/architecture/index.html)
|
||||
|
||||
- [IBM OpenTech Hyperledger Fabric 1.4 LTS Course](https://space.bilibili.com/102734951/channel/detail?cid=69148)
|
||||
- [edx: Introduction to Hyperledger Blockchain Technologies Free Course](https://www.edx.org/course/introduction-to-hyperledger-blockchain-technologie)
|
||||
|
||||
|
||||
#### [XuperChain](https://github.com/xuperchain/xuperchain)
|
||||
[<img src="https://avatars3.githubusercontent.com/u/43258643?s=200&v=4" align="right" width="80">](https://xchain.baidu.com/)
|
||||
|
||||
**XuperChain**, the first open source project of XuperChain Lab, introduces a highly flexible blockchain architecture with great transaction performance.
|
||||
|
||||
**XuperChain** is the underlying solution for union networks with following highlight features:
|
||||
|
||||
**High Performance**
|
||||
* Creative XuperModel technology makes contract execution and verification run parallelly.
|
||||
* [TDPoS](https://xuperchain.readthedocs.io/zh/latest/design_documents/xpos.html) ensures quick consensus in a large scale network.
|
||||
* WASM VM using AOT technology.
|
||||
|
||||
**Solid Security**
|
||||
* Contract account protected by multiple private keys ensures assets safety.
|
||||
* [Flexible authorization system](https://xuperchain.readthedocs.io/zh/latest/design_documents/permission_model.html) supports weight threshold, AK sets and could be easily extended.
|
||||
|
||||
**High Scalability**
|
||||
* Robust [P2P](https://xuperchain.readthedocs.io/zh/latest/design_documents/p2p.html) network supports a large scale network with thousands of nodes.
|
||||
* Branch management on ledger makes automatic convergence consistency and supports global deployment.
|
||||
|
||||
**Multi-Language Support**: Support pluggable multi-language contract VM using [XuperBridge](https://xuperchain.readthedocs.io/zh/latest/design_documents/XuperBridge.html) technology.
|
||||
|
||||
**Flexibility**: Modular and pluggable design provides high flexibility for users to build their blockchain solutions for various business scenarios.
|
||||
|
||||
- [Baidu Blockchain Engine](https://cloud.baidu.com/product/bbe.html)
|
||||
- [Homepage](https://xchain.baidu.com/)
|
||||
- [Doc](https://xuperchain.readthedocs.io/zh/latest/index.html)
|
||||
- [Wiki](https://github.com/xuperchain/xuperchain/wiki) / [English version](https://github.com/xuperchain/xuperchain/wiki/Wiki-in-English)
|
||||
|
||||
+ [Getting start](https://github.com/xuperchain/xuperchain/wiki/3.-Getting-Started)
|
||||
+ [Account operation](https://xuperchain.readthedocs.io/zh/latest/advanced_usage/contract_accounts.html)
|
||||
+ [Multiple nodes deployment](https://xuperchain.readthedocs.io/zh/latest/advanced_usage/multi-nodes.html)
|
||||
+ [Wasm contract](https://xuperchain.readthedocs.io/zh/latest/advanced_usage/create_contracts.html)
|
||||
+ [Proposal](https://xuperchain.readthedocs.io/zh/latest/advanced_usage/initiate_proposals.html)
|
||||
+ [Parallel chain](https://xuperchain.readthedocs.io/zh/latest/advanced_usage/parallel_chain.html)
|
||||
+ SDK
|
||||
+ [Go SDK](https://github.com/xuperchain/xuper-java-sdk)
|
||||
+ [Javascript SDK](https://github.com/xuperchain/xuper-sdk-js)
|
||||
+ [Java SDK](https://github.com/xuperchain/xuper-python-sdk)
|
||||
+ [Python SDK](https://github.com/xuperchain/xuper-python-sdk)
|
||||
+ [Detailed FAQs](https://xuperchain.readthedocs.io/zh/latest/FAQs.html)
|
||||
+ [Comparation with Fabric and Ethereum](https://github.com/xuperchain/xuperchain/wiki/%E9%99%84-%E8%AF%84%E6%B5%8B%E6%95%B0%E6%8D%AE%E5%AF%B9%E6%AF%94)
|
||||
|
||||
#### [FISCO-BCOS](https://github.com/FISCO-BCOS/Wiki)
|
||||
|
||||
## Releated Tools
|
||||
|
||||
### Solidity
|
||||
- [doc](https://solidity.readthedocs.io/en/develop/index.html) / [Chinese version](https://solidity-cn.readthedocs.io/zh/develop/)
|
||||
|
||||
### truffle
|
||||
- [BlockChain KickStarter From Scratch](https://prasannabrabourame.medium.com/blockchain-kickstarter-from-scratch-9a3906596cd0)
|
||||
|
||||
### web3.js
|
||||
- [doc](https://web3js.readthedocs.io/en/1.0/) / [Chinese version](http://web3.tryblockchain.org/Web3.js-api-refrence.html)
|
||||
|
||||
## Implementation of Blockchain
|
||||
- [**ATS**: _Functional Blockchain_](https://beta.observablehq.com/@galletti94/functional-blockchain)
|
||||
- [**C#**: _Programming The Blockchain in C#_](https://programmingblockchain.gitbooks.io/programmingblockchain/)
|
||||
- [**Crystal**: _Write your own blockchain and PoW algorithm using Crystal_](https://medium.com/@bradford_hamilton/write-your-own-blockchain-and-pow-algorithm-using-crystal-d53d5d9d0c52)
|
||||
- [**C++**: _Blockchain from Scratch_](https://github.com/openblockchains/awesome-blockchains/tree/master/blockchain.cpp)
|
||||
- [**Go: _Building Blockchain in Go_**](https://github.com/Jeiwan/blockchain_go) / [Chinese version 1](https://github.com/liuchengxu/blockchain-tutorial/blob/master/content/part-1/basic-prototype.md) / [Chinese version 2](https://zhangli1.gitbooks.io/dummies-for-blockchain/content/)
|
||||
- [_Part 1: Basic Prototype_](https://jeiwan.net/posts/building-blockchain-in-go-part-1/)
|
||||
- [_Part 2: Proof-of-Work_](https://jeiwan.net/posts/building-blockchain-in-go-part-2/)
|
||||
- [_Part 3: Persistence and CLI_](https://jeiwan.net/posts/building-blockchain-in-go-part-3/)
|
||||
- [_Part 4: Transactions 1_](https://jeiwan.net/posts/building-blockchain-in-go-part-4/)
|
||||
- [_Part 5: Addresses_](https://jeiwan.net/posts/building-blockchain-in-go-part-5/)
|
||||
- [_Part 6: Transactions 2_](https://jeiwan.net/posts/building-blockchain-in-go-part-6/)
|
||||
- [_Part 7: Network_](https://jeiwan.net/posts/building-blockchain-in-go-part-7/)
|
||||
- [**Go**: _Building A Simple Blockchain with Go_](https://www.codementor.io/codehakase/building-a-simple-blockchain-with-go-k7crur06v)
|
||||
- [**Go**: _Code your own blockchain in less than 200 lines of Go_](https://medium.com/@mycoralhealth/code-your-own-blockchain-in-less-than-200-lines-of-go-e296282bcffc)
|
||||
- [**Go**: _Code your own blockchain mining algorithm in Go_](https://medium.com/@mycoralhealth/code-your-own-blockchain-mining-algorithm-in-go-82c6a71aba1f)
|
||||
- [**Go**: _GoCoin - A full Bitcoin solution written in Go language (golang)_](https://github.com/piotrnar/gocoin)
|
||||
- [**Go**: _GoChain - A basic implementation of blockchain in go_](https://github.com/crisadamo/gochain)
|
||||
- [**Go**: _Having fun implementing a blockchain using Golang_](https://github.com/izqui/blockchain)
|
||||
- [**Go**: _NaiveChain - A naive and simple implementation of blockchains_](https://github.com/kofj/naivechain)
|
||||
- [**Java**: _Creating Your First Blockchain with Java_](https://medium.com/programmers-blockchain/create-simple-blockchain-java-tutorial-from-scratch-6eeed3cb03fa)
|
||||
- [**Java**: _Write a blockchain with java_](https://www.jianshu.com/p/afd8c465c91a)
|
||||
- [**JavaScript**: _A cryptocurrency implementation in less than 1500 lines of code_](https://github.com/conradoqg/naivecoin)
|
||||
- [**JavaScript**: _A web-based demonstration of blockchain concepts_](https://github.com/anders94/blockchain-demo/)
|
||||
- [**JavaScript**: _Build your own Blockchain in JavaScript_](https://github.com/nambrot/blockchain-in-js)
|
||||
- [**JavaScript**: _Code for Blockchain Demo_](https://github.com/seanjameshan/blockchain)
|
||||
- [**JavaScript**: _Creating a blockchain with JavaScript_](https://github.com/SavjeeTutorials/SavjeeCoin)
|
||||
- [**JavaScript**: _How To Launch Your Own Production-Ready Cryptocurrency_](https://hackernoon.com/how-to-launch-your-own-production-ready-cryptocurrency-ab97cb773371)
|
||||
- [**JavaScript**: _Learn & Build a JavaScript Blockchain_](https://medium.com/digital-alchemy-holdings/learn-build-a-javascript-blockchain-part-1-ca61c285821e)
|
||||
- [**JavaScript**: _Node.js Blockchain Imlementation: BrewChain: Chain+WebSockets+HTTP Server_](http://www.darrenbeck.co.uk/blockchain/nodejs/nodejscrypto/)
|
||||
- [**JavaScript**: _Writing a tiny blockchain in JavaScript_](https://www.savjee.be/2017/07/Writing-tiny-blockchain-in-JavaScript/)
|
||||
- [_Part 1: Implementing a basic blockchain_](https://www.savjee.be/2017/07/Writing-tiny-blockchain-in-JavaScript/)
|
||||
- [_Part 2: Implementing proof-of-work_](https://www.savjee.be/2017/09/Implementing-proof-of-work-javascript-blockchain/)
|
||||
- [_Part 3: Transactions & mining rewards_](https://www.savjee.be/2018/02/Transactions-and-mining-rewards/)
|
||||
- [_Part 4: Signing transactions_](https://www.savjee.be/2018/10/Signing-transactions-blockchain-javascript/)
|
||||
- [**Kotlin**: _Let’s implement a cryptocurrency in Kotlin_](https://medium.com/@vasilyf/lets-implement-a-cryptocurrency-in-kotlin-part-1-blockchain-8704069f8580)
|
||||
- [**Python**: _A Practical Introduction to Blockchain with Python_](http://adilmoujahid.com/posts/2018/03/intro-blockchain-bitcoin-python/)
|
||||
- [**Python**: _Build your own blockchain: a Python tutorial_](http://ecomunsing.com/build-your-own-blockchain)
|
||||
- [**Python**: _Learn Blockchains by Building One_](https://hackernoon.com/learn-blockchains-by-building-one-117428612f46)
|
||||
- [**Python**: _Let’s Build the Tiniest Blockchain_](https://medium.com/crypto-currently/lets-build-the-tiniest-blockchain-e70965a248b)
|
||||
- [**Python: _write-your-own-blockchain_**](https://bigishdata.com/2017/10/17/write-your-own-blockchain-part-1-creating-storing-syncing-displaying-mining-and-proving-work/)
|
||||
- [_Part 1 — Creating, Storing, Syncing, Displaying, Mining, and Proving Work_](https://bigishdata.com/2017/10/17/write-your-own-blockchain-part-1-creating-storing-syncing-displaying-mining-and-proving-work/)
|
||||
- [_Part 2 — Syncing Chains From Different Nodes_](https://bigishdata.com/2017/10/27/build-your-own-blockchain-part-2-syncing-chains-from-different-nodes/)
|
||||
- [_Part 3 — Nodes that Mine_](https://bigishdata.com/2017/11/02/build-your-own-blockchain-part-3-writing-nodes-that-mine/)
|
||||
- [_Part 4.1 — Bitcoin Proof of Work Difficulty Explained_](https://bigishdata.com/2017/11/13/how-to-build-a-blockchain-part-4-1-bitcoin-proof-of-work-difficulty-explained/)
|
||||
- [_Part 4.2 — Ethereum Proof of Work Difficulty Explained_](https://bigishdata.com/2017/11/21/how-to-build-your-own-blockchain-part-4-2-ethereum-proof-of-work-difficulty-explained/)
|
||||
- [**Ruby**: _lets-build-a-blockchain_](https://github.com/Haseeb-Qureshi/lets-build-a-blockchain)
|
||||
- [**Ruby**: _Programming Blockchains Step-by-Step (Manuscripts Book Edition)_](https://github.com/yukimotopress/programming-blockchains-step-by-step)
|
||||
- [**Scala**: _How to build a simple actor-based blockchain_](https://medium.freecodecamp.org/how-to-build-a-simple-actor-based-blockchain-aac1e996c177)
|
||||
- [**TypeScript**: _Naivecoin: a tutorial for building a cryptocurrency_](https://lhartikk.github.io/)
|
||||
- [_Minimal working blockchain_](https://lhartikk.github.io/jekyll/update/2017/07/14/chapter1.html)
|
||||
- [_Proof of Work_](https://lhartikk.github.io/jekyll/update/2017/07/13/chapter2.html)
|
||||
- [_Transactions_](https://lhartikk.github.io/jekyll/update/2017/07/12/chapter3.html)
|
||||
- [_Wallet_](https://lhartikk.github.io/jekyll/update/2017/07/11/chapter4.html)
|
||||
- [_Transaction relaying_](https://lhartikk.github.io/jekyll/update/2017/07/10/chapter5.html)
|
||||
- [_Wallet UI and blockchain explorer_](https://lhartikk.github.io/jekyll/update/2017/07/09/chapter6.html)
|
||||
- [**TypeScript**: _NaivecoinStake: a tutorial for building a cryptocurrency with the Proof of Stake consensus_](https://naivecoinstake.learn.uno/)
|
||||
- [Explore Blockchain OSS, libraries, packages, source code, cloud functions and APIs](https://kandi.openweaver.com/explore/blockchain)
|
||||
|
||||
---
|
||||
## Projects and Applications
|
||||
[<img src="https://raw.githubusercontent.com/jpmorganchase/quorum/master/logo.png" align="right" width="80">](https://github.com/jpmorganchase/quorum)
|
||||
### Quorum
|
||||
|
||||
**Quorum** is an Ethereum-based distributed ledger protocol with transaction/contract privacy and new consensus mechanisms.
|
||||
|
||||
**Quorum** is a fork of [go-ethereum](https://github.com/ethereum/go-ethereum) and is updated in line with go-ethereum releases.
|
||||
|
||||
Key enhancements over go-ethereum:
|
||||
|
||||
* **Privacy** - Quorum supports private transactions and private contracts through public/private state separation, and utilises peer-to-peer encrypted message exchanges (see [Constellation](https://github.com/jpmorganchase/constellation) and [Tessera](https://github.com/jpmorganchase/tessera)) for directed transfer of private data to network participants
|
||||
* **Alternative** Consensus Mechanisms - with no need for POW/POS in a permissioned network, Quorum instead offers multiple consensus mechanisms that are more appropriate for consortium chains:
|
||||
* **Raft-based Consensus** - a consensus model for faster blocktimes, transaction finality, and on-demand block creation
|
||||
* **Istanbul BFT** - a PBFT-inspired consensus algorithm with transaction finality, by AMIS.
|
||||
* **Peer Permissioning** - node/peer permissioning using smart contracts, ensuring only known parties can join the network
|
||||
* **Higher Performance** - Quorum offers significantly higher performance than public geth
|
||||
|
||||
|
||||
[<img src="https://avatars3.githubusercontent.com/u/7450663?s=460&v=4" align="right" width="80">](https://github.com/monero-project/monero)
|
||||
### Monero
|
||||
@ -286,7 +465,7 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
**IOTA** is the missing puzzle piece for **the Machine Economy** to fully emerge and reach its desired potential. We envision IOTA to be the public, permissionless backbone for the Internet of Things that enables true interoperability between all devices.
|
||||
|
||||
- [IOTA](https://iota.org) - Next Generation Blockchain
|
||||
- [Whitepaper](https://iota.org/IOTA_Whitepaper.pdf) - The Tangle
|
||||
- [Whitepaper](https://iota.org/IOTA_Whitepaper.pdf) - The Tangle / [Chinese version](http://www.iotachina.com/wp-content/uploads/2016/11/2016112902003453.pdf)
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/IOTA_(Distributed_Ledger_Technology))
|
||||
- [A Primer on IOTA](https://blog.iota.org/a-primer-on-iota-with-presentation-e0a6eb2cc621) - A Primer on IOTA (with Presentation)
|
||||
- [IOTA China](http://iotachina.com/) - IOTA China 首页
|
||||
@ -319,14 +498,14 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
|
||||
|
||||
[<img src="https://avatars2.githubusercontent.com/u/10536621?s=200&v=4" align="right" width="80">](https://github.com/ipfs)
|
||||
### IFPS
|
||||
### IPFS
|
||||
**IPFS** ([the InterPlanetary File System](https://github.com/ipfs/faq/issues/76)) is a new hypermedia distribution protocol, addressed by content and identities. IPFS enables the creation of completely distributed applications. It aims to make the web faster, safer, and more open.
|
||||
|
||||
**IPFS** is a distributed file system that seeks to connect all computing devices with the same system of files. In some ways, this is similar to the original aims of the Web, but IPFS is actually more similar to a single bittorrent swarm exchanging git objects. You can read more about its origins in the paper [IPFS - Content Addressed, Versioned, P2P File System](https://github.com/ipfs/ipfs/blob/master/papers/ipfs-cap2pfs/ipfs-p2p-file-system.pdf?raw=true).
|
||||
|
||||
**IPFS** is becoming a new major subsystem of the internet. If built right, it could complement or replace HTTP. It could complement or replace even more. It sounds crazy. It _is_ crazy.
|
||||
|
||||
- [Papers](papers) - Academic papers on IPFS
|
||||
- [White Paper](https://github.com/ipfs/papers/raw/master/ipfs-cap2pfs/ipfs-p2p-file-system.pdf) - Academic papers on IPFS / [Chinese version](https://gguoss.github.io/2017/05/28/ipfs/)
|
||||
- [Specs](https://github.com/ipfs/specs) - Specifications on the IPFS protocol
|
||||
- [Notes](https://github.com/ipfs/notes) - Various relevant notes and discussions (that do not fit elsewhere)
|
||||
[<img src="https://camo.githubusercontent.com/651f7045071c78042fec7f5b9f015e12589af6d5/68747470733a2f2f697066732e696f2f697066732f516d514a363850464d4464417367435a76413155567a7a6e3138617356636637485676434467706a695343417365" align="right" width="200">](https://github.com/ipfs)
|
||||
@ -338,9 +517,28 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
+ [**Roadmap**](https://github.com/ipfs/roadmap)
|
||||
+ [**More resouces**](./Extension/ipfs.md)
|
||||
|
||||
#### [Filecoin](https://filecoin.io/)
|
||||
- [White paper](https://filecoin.io/filecoin.pdf) / [Chinese version](http://chainx.org/paper/index/index/id/13.html)
|
||||
|
||||
#### [Polybase](https://polybase.xyz)
|
||||
- [White paper](https://framerusercontent.com/modules/assets/GRv4t0d6jQOJbIO7ZOFgonnXqM~f7GLGr1YpwfK85uVr8su7Mxe_3b6VkIZW94sRev8jj4.pdf) / [Docs](https://github.com/polybase/docs)
|
||||
|
||||
#### [BigchainDB](https://www.bigchaindb.com/)
|
||||
- [White paper](https://www.bigchaindb.com/whitepaper) / [Chinese version](http://blog.csdn.net/fengqing79/article/details/70154076)
|
||||
|
||||
### BitShares
|
||||
- [White paper]() / [Chinese version](https://www.8btc.com/article/3369)
|
||||
|
||||
### ArcBlock
|
||||
- [Blockchain Developer Platform](https://www.arcblock.io) / [White Paper](https://www.arcblock.io/en/whitepaper/latest)
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/petrosDemetrakopoulos/ethairballoons/master/logo_official.png" align="right" width="100">](https://github.com/petrosDemetrakopoulos/ethairballoons)
|
||||
### [EthAir Balloons](https://github.com/petrosDemetrakopoulos/ethairballoons)
|
||||
- A strictly typed ORM library for Ethereum blockchain. It allows developers to use Ethereum blockchain as a persistent storage in an organized and model-oriented way without writing custom complex Smart contracts.
|
||||
|
||||
---
|
||||
## Further Extension
|
||||
### [Papers](https://github.com/decrypto-org/blockchain-papers)
|
||||
|
||||
### Books
|
||||
|
||||
@ -466,12 +664,12 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
Best design and security practice_
|
||||
|
||||
|
||||
- [**Best of Bitcoin Maximalist - Scammers, Morons, Clowns, Shills & BagHODLers - Inside The New New Crypto Ponzi Economics**](https://github.com/bitsblocks/bitcoin-maximalist), 2018 - FREE
|
||||
- [**Best of Bitcoin Maximalist - Scammers, Morons, Clowns, Shills & BagHODLers - Inside The New New Crypto Ponzi Economics**](https://github.com/bitsblocks/bitcoin-maximalist), 2018 - FREE
|
||||
|
||||
- [**Crypto Facts - Decentralize Payments - Efficient, Low Cost, Fair, Clean - True or False?**](https://github.com/bitsblocks/crypto-facts), 2018 - FREE
|
||||
- [**Crypto Facts - Decentralize Payments - Efficient, Low Cost, Fair, Clean - True or False?**](https://github.com/bitsblocks/crypto-facts), 2018 - FREE
|
||||
|
||||
- [**IslandCoin White Paper - A Pen and Paper Cash System - How to Run a Blockchain on a Deserted Island**](https://github.com/bitsblocks/islandcoin-whitepaper)
|
||||
by Tal Kol --
|
||||
by Tal Kol --
|
||||
_Motivation ++
|
||||
Consensus ++
|
||||
Transaction and Block Specification -
|
||||
@ -480,12 +678,43 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
Genesis block ++
|
||||
References_
|
||||
|
||||
- [**Blockchain in Action**](https://www.manning.com/books/blockchain-in-action) by Bina Ramamurthy, early access --
|
||||
_Learn how blockchain differs from other distributed systems ++
|
||||
Smart contract development with Ethereum and the Solidity language ++
|
||||
Web UI for decentralized apps ++
|
||||
Identity, privacy and security techniques ++
|
||||
On-chain and off-chain data storage_
|
||||
|
||||
- [**Permissioned Blockchains in Action**](https://www.manning.com/books/permissioned-blockchains-in-action) by Mansoor Ahmed-Rengers & Marta Piekarska-Geater, early access --
|
||||
_A guide to creating innovative applications using blockchain technology ++
|
||||
Writing smart contracts and distributed applications using Solidity ++
|
||||
Configuring DLT networks ++
|
||||
Designing blockchain solutions for specific use cases ++
|
||||
Identity management in permissioned blockchains networks_
|
||||
|
||||
- [**Programming Hyperledger Fabric**](https://www.amazon.com/dp/0578802228) by Siddharth Jain, --
|
||||
_A guide to developing blockchain applications for enterprise use cases ++
|
||||
Where Fabric fits in to the blockchain landscape ++
|
||||
The ins and outs of deploying real-world applications ++
|
||||
Developing smart contracts and client applications in Node ++
|
||||
Debugging and troubleshooting ++
|
||||
Securing production applications_
|
||||
|
||||
- [**Self-Sovereign Identity**](https://www.manning.com/books/self-sovereign-identity) by Alex Preukschat and Drummond Reed, --
|
||||
_In Self-Sovereign Identity: Decentralized digital identity and verifiable credentials++
|
||||
you’ll learn how SSI empowers us to receive digitally-signed credentials++
|
||||
store them in private wallets++
|
||||
and securely prove our online identities._
|
||||
|
||||
|
||||
|
||||
### Applications
|
||||
|
||||
#### Identity Applications
|
||||
|
||||
##### Public Blockchain Identity
|
||||
|
||||
- [Awesome Name Services](https://github.com/scio-labs/awesome-name-services/) – Awesome list curating all decentralized domain name services (DNS).
|
||||
- [Blockstack](https://blockstack.org) - Platform for decentralized, server-less apps where users control their data. Identity included.
|
||||
- [Evernym](http://www.evernym.com) - Self-Sovereign identity built on top of open source permissioned blockchain.
|
||||
- [Jolocom](https://jolocom.com) - Self-sovereing identity wallet.
|
||||
@ -537,12 +766,30 @@ These apps run on a custom built **blockchain, an enormously powerful shared glo
|
||||
|
||||
- [Steem](https://steem.io) - Decentralized social network which incentivises content creation and curation.
|
||||
- [PopChest](https://popchest.com) - Incentivized distributed video platform.
|
||||
- [Civil](https://joincivil.com) - Decentralized newsmaking platform.
|
||||
- [Civil](https://joincivil.com) - Decentralized newsmaking platform.
|
||||
|
||||
#### DeFi (Decentralised Finance)
|
||||
|
||||
- [Uniswap](https://uniswap.org) - Decentralized exchange powered by the Automated Market Maker model (AMM).
|
||||
- [Compound](https://compound.finance) - Decentralized lending and borrowing.
|
||||
- [1inch Exchange](https://1inch.exchange) - Get the best rates among multiple DEXes.
|
||||
- [Synthetix](https://synthetix.io/) - Protocol for synthetic assets.
|
||||
|
||||
+ Tools
|
||||
+ [Defi Dashboard](https://debank.com/): portfolio tracker, project lists, rankings, etc.
|
||||
+ [Zapper](https://zapper.fi/): dashboard for viewing and managing your DeFi investments.
|
||||
+ [Furucombo](https://furucombo.app/): easily create flashloans without writing a single line of code.
|
||||
+ [Covalent](https://www.covalenthq.com/): an unified API bringing visibility to billions of blockchain data points.
|
||||
|
||||
### Roadmaps
|
||||
|
||||
- [**Blockchain Developer Roadmap**](https://roadmap.sh/blockchain) -- Roadmap to become a Blockchain Developer.
|
||||
|
||||
---
|
||||
|
||||
## Contribute
|
||||
|
||||
Contributions welcome!
|
||||
Contributions welcome!
|
||||
|
||||
1. Fork it (<https://github.com/yjjnls/awesome-blockchain/fork>)
|
||||
2. Clone it (`git clone https://github.com/yjjnls/awesome-blockchain`)
|
||||
|
4
src/.gitignore
vendored
4
src/.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
package-lock.json
|
||||
node_modules
|
||||
node_modules
|
||||
build
|
||||
.DS_Store
|
12
src/ConfidentialTx/README.md
Normal file
12
src/ConfidentialTx/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Confidential Transaction
|
||||
This package is a Zero Knowledge Proof implementation in Golang, based on [zkproofs](https://github.com/ing-bank/zkproofs).
|
||||
|
||||
It contains bulletproofs and perdersen commitment, which could be used for confidential transaction.
|
||||
|
||||
To test the local version, you could run the cmd:
|
||||
|
||||
```shell
|
||||
go run main.go
|
||||
```
|
||||
|
||||
And the package could be embadded in geth, it will support the feature later and give details.
|
122
src/ConfidentialTx/byteconversion/conversion.go
Normal file
122
src/ConfidentialTx/byteconversion/conversion.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package byteconversion
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the big.Int based on the passed byte-array assuming the byte-array contains
|
||||
* the two's-complement representation of this big.Int. The byte array will be in big-endian byte-order:
|
||||
* the most significant byte is in the zeroth element. The array will contain the minimum number of bytes
|
||||
* required to represent this BigInteger, including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)).
|
||||
*/
|
||||
func FromByteArray(bytesIn [] byte) (*big.Int, error) {
|
||||
|
||||
const MINUS_ONE = -1
|
||||
|
||||
if len(bytesIn) == 0 {
|
||||
err := errors.New("Cannot convert empty array to big.Int.")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
highestByte := bytesIn[0]
|
||||
isNegative := (highestByte & 128) !=0
|
||||
var convertedBytes []byte
|
||||
|
||||
if isNegative {
|
||||
|
||||
tmpInt := new(big.Int).SetBytes(bytesIn)
|
||||
tmpInt = tmpInt.Sub(tmpInt, big.NewInt(1))
|
||||
tmpBytes := tmpInt.Bytes()
|
||||
|
||||
if tmpBytes[0] == 255 {
|
||||
convertedBytes = FlipBytes(tmpBytes)[1:]
|
||||
} else {
|
||||
convertedBytes = tmpBytes
|
||||
copy(convertedBytes, FlipBytes(tmpBytes))
|
||||
}
|
||||
tmp := new(big.Int).SetBytes(convertedBytes)
|
||||
return tmp.Mul(tmp, big.NewInt(MINUS_ONE)), nil
|
||||
} else {
|
||||
// if positive leave unchanged (additional 0-bytes will be ignored)
|
||||
return new(big.Int).SetBytes(bytesIn), nil
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array containing the two's-complement representation of this big.Int.
|
||||
* The byte array will be in big-endian byte-order: the most significant byte is in the
|
||||
* zeroth element. The array will contain the minimum number of bytes required to represent
|
||||
* this BigInteger, including at least one sign bit, which is (ceil((this.bitLength() + 1)/8)).
|
||||
*/
|
||||
func ToByteArray(in *big.Int) []byte {
|
||||
|
||||
isNegative := in.Cmp(new(big.Int).SetInt64(0)) < 0
|
||||
|
||||
bytes := in.Bytes()
|
||||
length := len(bytes)
|
||||
|
||||
if length == 0 {
|
||||
return []byte { 0 };
|
||||
}
|
||||
|
||||
highestByte := bytes[0]
|
||||
var convertedBytes []byte
|
||||
|
||||
if !isNegative {
|
||||
if (highestByte & 128) !=0 {
|
||||
|
||||
convertedBytes = make([]byte, length + 1)
|
||||
convertedBytes[0] = 0
|
||||
copy(convertedBytes[1:], bytes)
|
||||
return convertedBytes
|
||||
} else {
|
||||
return bytes
|
||||
}
|
||||
} else {
|
||||
if (highestByte & 128) !=0 {
|
||||
|
||||
convertedBytes = make([]byte, length + 1)
|
||||
convertedBytes[0] = 255
|
||||
copy(convertedBytes[1:], FlipBytes(bytes))
|
||||
} else {
|
||||
convertedBytes = FlipBytes(bytes)
|
||||
}
|
||||
|
||||
convertedInt := new(big.Int).SetBytes(convertedBytes)
|
||||
convertedInt.Add(convertedInt, big.NewInt(1))
|
||||
return convertedInt.Bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips all bytes in each of the array's elements.
|
||||
* Returns the flipped elements.
|
||||
*/
|
||||
func FlipBytes(bytesIn [] byte) []byte {
|
||||
|
||||
length := len(bytesIn)
|
||||
flippedBytes := make([]byte, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
flippedBytes[i] = bytesIn[i] ^ 255;
|
||||
}
|
||||
return flippedBytes
|
||||
}
|
186
src/ConfidentialTx/byteconversion/conversion_test.go
Normal file
186
src/ConfidentialTx/byteconversion/conversion_test.go
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package byteconversion
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"math/big"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
func TestFromBytes127(t *testing.T) {
|
||||
|
||||
bytesIn := []byte{127}
|
||||
actualInt, err := FromByteArray(bytesIn)
|
||||
expectedInt := big.NewInt(127)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error.")
|
||||
}
|
||||
|
||||
if actualInt.Cmp(expectedInt) != 0 {
|
||||
t.Errorf("Assert failure: incorrect value: ", actualInt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromBytesNegLarge(t *testing.T) {
|
||||
|
||||
bytesIn := []byte{228, 20, 131, 38, 208, 100, 246, 105, 110, 11, 247, 198,
|
||||
54, 252, 188, 185, 163, 179, 13, 6, 144, 164, 44, 232, 184,
|
||||
135, 147, 140, 88, 87, 191, 46, 22, 23, 252, 216, 72, 25,
|
||||
5, 124, 29, 81, 56, 242, 199, 0, 68, 132, 102, 246, 34,
|
||||
203, 122, 8, 7, 44, 237, 1, 181, 36}
|
||||
|
||||
actualInt, err := FromByteArray(bytesIn)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error.")
|
||||
}
|
||||
|
||||
expectedInt := new(big.Int)
|
||||
s := "-34046416216309720507914088123716811131285777464224028073691261328351974060"
|
||||
s += "2415131562281745277658128038564338505868351008385659638777486107364060"
|
||||
expectedInt.SetString(s, 10)
|
||||
|
||||
if (actualInt.Cmp(expectedInt) != 0) {
|
||||
t.Errorf("Assert failure: incorrect value: ", actualInt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesNeg1(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{255}
|
||||
actualBytes := ToByteArray(big.NewInt(-1))
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array: ", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesZero(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{0}
|
||||
actualBytes := ToByteArray(big.NewInt(0))
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array: ", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesPos127(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{127}
|
||||
actualBytes := ToByteArray(big.NewInt(127))
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array: ", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func TestToBytesPos128(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{0, 128}
|
||||
actualBytes := ToByteArray(big.NewInt(128))
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array: ", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesNegLarge(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{228, 20, 131, 38, 208, 100, 246, 105, 110, 11, 247, 198,
|
||||
54, 252, 188, 185, 163, 179, 13, 6, 144, 164, 44, 232, 184,
|
||||
135, 147, 140, 88, 87, 191, 46, 22, 23, 252, 216, 72, 25,
|
||||
5, 124, 29, 81, 56, 242, 199, 0, 68, 132, 102, 246, 34,
|
||||
203, 122, 8, 7, 44, 237, 1, 181, 36}
|
||||
|
||||
c := new(big.Int)
|
||||
s := "-34046416216309720507914088123716811131285777464224028073691261328351974060"
|
||||
s += "2415131562281745277658128038564338505868351008385659638777486107364060"
|
||||
|
||||
c.SetString(s, 10)
|
||||
actualBytes := ToByteArray(c)
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array:", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesPosLarge(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{25, 165, 147, 207, 93, 105, 151, 6, 97, 172, 17, 104, 255,
|
||||
15, 255, 106, 246, 80, 123, 85, 231, 140, 213, 98, 148, 126,
|
||||
243, 177, 33, 73, 55, 165, 160, 240, 79, 198, 55, 253, 14,
|
||||
55, 32, 177, 71, 135, 142, 229, 100, 102, 171, 66, 115, 74,
|
||||
137, 135, 74, 20, 127, 132, 155, 6, 126, 202, 57, 173, 72,
|
||||
172, 226, 124, 34, 57, 156, 232, 3, 188, 209, 157, 156, 145,
|
||||
127, 253, 208, 219, 188, 171, 157, 155, 121, 59, 97, 242,
|
||||
121, 187, 52, 222, 168, 83, 10, 89, 57, 83, 109, 245, 248,
|
||||
143, 16, 106, 224, 68, 176, 175, 132, 118, 253, 177, 20, 77,
|
||||
123, 204, 224, 204, 16, 203, 207, 33, 129, 105, 196, 100,
|
||||
140, 179, 5, 167, 73, 146, 98, 210, 60, 247, 253, 95, 19,
|
||||
210, 189, 122, 157, 89, 42, 65, 26, 4, 123, 86, 255, 118,
|
||||
188, 109, 65, 90, 164, 231, 37, 144, 52, 20, 123, 16, 24,
|
||||
18, 139, 147, 149, 145, 241, 82, 242, 163, 254, 236, 26,
|
||||
205, 162, 208, 161, 145, 227, 15, 105, 61, 208, 29, 103, 4,
|
||||
218, 177, 143, 148, 155, 160, 183, 116, 93, 232, 140, 47,
|
||||
48, 61, 167, 130, 135, 160, 67, 69, 13, 156, 78, 212, 45,
|
||||
205, 139, 232, 173, 241, 235, 67, 201, 117, 187, 231, 40,
|
||||
246, 57, 235, 157, 45, 229, 218, 104, 4, 175, 202, 30, 9,
|
||||
118, 237, 41, 227, 44, 60, 33, 29, 66, 125, 181, 117, 249,
|
||||
209, 154, 13, 92, 216, 249, 18, 150, 214, 108, 211, 214,
|
||||
59, 34, 52, 63, 150, 67, 215, 35, 217, 152, 26, 173, 129,
|
||||
242, 101, 184, 80, 194, 17, 176, 100, 24, 211, 54, 2, 159,
|
||||
254, 31, 137, 86, 185, 234, 126, 108, 147, 241, 239, 80,
|
||||
34, 146, 2, 184, 200, 238, 38, 34, 65, 208, 117, 124, 76,
|
||||
120, 41, 18, 66, 174, 207, 163, 163, 58, 70, 37, 1, 214,
|
||||
52, 211, 66, 17, 226, 55, 205, 83, 147, 237, 76, 46, 224,
|
||||
93, 80, 200, 233, 101, 137, 19, 229, 70, 0, 89, 160, 174,
|
||||
33, 120, 9, 151, 214, 187, 219, 171, 155, 233, 110, 236,
|
||||
165, 232, 19, 233, 129, 194, 89, 196, 79, 183, 137, 113,
|
||||
31, 170, 107, 65, 45, 20, 29, 80, 177, 233, 186, 201, 208,
|
||||
153, 137, 123, 18, 161, 116, 55, 163, 29, 81, 160, 9, 86,
|
||||
70, 168, 226, 79, 145, 40, 242, 140, 145, 65, 129, 140,
|
||||
161, 173, 95, 240, 240, 179, 43, 56, 108, 86, 222, 221,
|
||||
121, 246, 154, 181, 96, 231, 173, 252, 87, 40, 46, 46, 226,
|
||||
162, 158, 70, 148, 48, 147, 235, 118, 139, 25, 232, 67, 233,
|
||||
118, 68, 181, 196, 195, 39, 193, 65, 141, 97, 163, 204, 186,
|
||||
57, 124, 96, 89, 123, 30, 120, 210, 196, 89, 114, 43, 35, 39,
|
||||
201, 210, 81, 93, 14, 53, 184, 55, 77, 239, 40, 15, 174, 50,
|
||||
175, 84, 57, 222, 239, 120, 197, 20, 84, 23, 192, 248, 65, 32}
|
||||
|
||||
c := new(big.Int)
|
||||
c.SetString("104629761028213599805480925925790425959567049134816086059352755190570739745518666205937735343498567571920655134925379760277594103917074168324328020614304601458655903777672976689775490077688338107235672544860350622774919412889869264967814894367526323662860621314829844532302923598163101691161809563689257119439517772642328497948311622477782095358615210758790515715457810324166800501005841394919183757519983518882638580018812501425864890227616096353048530301822817514776417452329353267096156269934135291712469590753885382455774960306786815780130886957965944086429085979982216274733117683220681240533762036024585652327095570558251184159582343561742487484870623990453783273002077229085268928493917134578545501339771123831252046790356292192853434440371050718893013786884617886032764594150932952606679129922556451438850853950795322470594816924482063623508100143990257521028260138831741855201480669520432545573066469798604777686564518950203058887546628141972672996503838284845932369286957158959203830688307302165544372812778767992596780236523180938001711578036548650984477439658901737951530184210064832275649697658679190072031726344089085533172632374570201218311185378464524281671726449877984821159571188587422006373681926621549415758315808", 10)
|
||||
actualBytes := ToByteArray(c)
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array:", actualBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToBytesPos39568(t *testing.T) {
|
||||
|
||||
expectedBytes := []byte{0, 154, 144}
|
||||
actualBytes := ToByteArray(big.NewInt(39568))
|
||||
|
||||
if !reflect.DeepEqual(expectedBytes, actualBytes) {
|
||||
t.Errorf("Assert failure: incorrect byte-array:", actualBytes)
|
||||
}
|
||||
}
|
68
src/ConfidentialTx/byteconversion/parse.go
Normal file
68
src/ConfidentialTx/byteconversion/parse.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package byteconversion
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidBigInteger = errors.New("invalid ASCII for big integer")
|
||||
)
|
||||
|
||||
// Decodes a byte array (ASCII encoding of comma-separated integers) into an array of big integers
|
||||
func ParseInput(in []byte) ([]*big.Int, error) {
|
||||
|
||||
prevIndex := 0
|
||||
var output[] *big.Int
|
||||
|
||||
for index, element := range in {
|
||||
|
||||
if element == 44 {
|
||||
newInt, err := ConvertToBigInt(in[prevIndex:index])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output = append(output, newInt)
|
||||
prevIndex = index + 1
|
||||
}
|
||||
}
|
||||
|
||||
newInt, err := ConvertToBigInt(in[prevIndex:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(output, newInt), nil
|
||||
}
|
||||
|
||||
// Decodes a byte array (ASCII encoding of a signed integer) into a big integer
|
||||
func ConvertToBigInt(in []byte) (*big.Int, error) {
|
||||
|
||||
// Validate
|
||||
for index, element := range in {
|
||||
if !((element >= 48 && element <= 57) || (index == 0 && element == 45)) {
|
||||
return nil, errInvalidBigInteger
|
||||
}
|
||||
}
|
||||
|
||||
s := string(in)
|
||||
i := new(big.Int)
|
||||
i.SetString(s, 10)
|
||||
return i, nil
|
||||
}
|
316
src/ConfidentialTx/byteconversion/parse_test.go
Normal file
316
src/ConfidentialTx/byteconversion/parse_test.go
Normal file
File diff suppressed because one or more lines are too long
70
src/ConfidentialTx/main.go
Normal file
70
src/ConfidentialTx/main.go
Normal file
@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"./zkproofs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// value
|
||||
x := new(big.Int).SetInt64(30)
|
||||
y := new(big.Int).SetInt64(20)
|
||||
z := new(big.Int).SetInt64(10)
|
||||
// get blind factor, pedersen commit and zkproof
|
||||
blindFactorX, tX, hX, pX, proofX, _ := zkproofs.GetZkrp().GenerateProof(x)
|
||||
|
||||
proofDataX, _ := zkproofs.DumpProof(tX, hX, pX, &proofX)
|
||||
|
||||
verifier, proof, _ := zkproofs.LoadProof(proofDataX)
|
||||
|
||||
var ok bool
|
||||
ok, err := verifier.Verify(*proof)
|
||||
if !ok {
|
||||
fmt.Println("proofX failed!!!")
|
||||
fmt.Println(ok)
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println("proofX verified >0.")
|
||||
}
|
||||
|
||||
blindFactorY, tY, hY, pY, proofY, _ := zkproofs.GetZkrp().GenerateProof(y)
|
||||
|
||||
proofDataY, _ := zkproofs.DumpProof(tY, hY, pY, &proofY)
|
||||
|
||||
verifier, proof, _ = zkproofs.LoadProof(proofDataY)
|
||||
ok, _ = verifier.Verify(proofY)
|
||||
if !ok {
|
||||
fmt.Println("proofY failed!!!")
|
||||
} else {
|
||||
fmt.Println("proofY verified >0.")
|
||||
}
|
||||
|
||||
blindFactorZ, tZ, hZ, pZ, proofZ, _ := zkproofs.GetZkrp().GenerateProof(z)
|
||||
|
||||
proofDataZ, _ := zkproofs.DumpProof(tZ, hZ, pZ, &proofZ)
|
||||
|
||||
verifier, proof, _ = zkproofs.LoadProof(proofDataZ)
|
||||
ok, _ = verifier.Verify(proofZ)
|
||||
if !ok {
|
||||
fmt.Println("proofZ failed!!!")
|
||||
} else {
|
||||
fmt.Println("proofZ verified >0.")
|
||||
}
|
||||
|
||||
// 佩德森承诺检查输入之和与输出之和是否相等
|
||||
blindOut := new(big.Int).Add(blindFactorY, blindFactorZ)
|
||||
blindDiff := new(big.Int).Sub(blindFactorX, blindOut)
|
||||
|
||||
check := zkproofs.VerifyPedersenCommitment([]*zkproofs.PedersenCommitment{proofX.V}, []*zkproofs.PedersenCommitment{proofY.V, proofZ.V}, blindDiff)
|
||||
fmt.Println("pedersen verify result:", check)
|
||||
|
||||
}
|
||||
|
||||
// 最好能抽象出一个独立的 zkrp.Bp 出来,然后
|
||||
// 主要有两个 challenge值 y 和 z 应该是由 verifier 那边根据A 和 S 生成的,这边把这个交互过程省略了,所以最后应该把
|
||||
// zkrp.Zkip.Hh = hprime
|
||||
// zkrp.Zkip.Cc = tprime
|
||||
// 这两个值也放在 proof 中发过去,有个问题:G 和 H 用户是否应该知道?
|
103
src/ConfidentialTx/main_test.go
Normal file
103
src/ConfidentialTx/main_test.go
Normal file
@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"./zkproofs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCTx(t *testing.T) {
|
||||
// value
|
||||
x := new(big.Int).SetInt64(30)
|
||||
y := new(big.Int).SetInt64(20)
|
||||
z := new(big.Int).SetInt64(10)
|
||||
|
||||
blindFactorX, tX, hX, pX, proofX, _ := zkproofs.GetZkrp().GenerateProof(x)
|
||||
var ok bool
|
||||
verifier := zkproofs.GetVerifier(tX, hX, pX)
|
||||
ok, _ = verifier.Verify(proofX)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorY, tY, hY, pY, proofY, _ := zkproofs.GetZkrp().GenerateProof(y)
|
||||
verifier = zkproofs.GetVerifier(tY, hY, pY)
|
||||
ok, _ = verifier.Verify(proofY)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorZ, tZ, hZ, pZ, proofZ, _ := zkproofs.GetZkrp().GenerateProof(z)
|
||||
verifier = zkproofs.GetVerifier(tZ, hZ, pZ)
|
||||
ok, _ = verifier.Verify(proofZ)
|
||||
assert.True(t, ok)
|
||||
|
||||
// 佩德森承诺检查输入之和与输出之和是否相等
|
||||
blindOut := new(big.Int).Add(blindFactorY, blindFactorZ)
|
||||
blindDiff := new(big.Int).Sub(blindFactorX, blindOut)
|
||||
|
||||
check := zkproofs.VerifyPedersenCommitment([]*zkproofs.PedersenCommitment{proofX.V}, []*zkproofs.PedersenCommitment{proofY.V, proofZ.V}, blindDiff)
|
||||
fmt.Println("pedersen verify result:", check)
|
||||
assert.True(t, check)
|
||||
}
|
||||
|
||||
func TestCTx2(t *testing.T) {
|
||||
// value
|
||||
x := new(big.Int).SetInt64(30)
|
||||
y := new(big.Int).SetInt64(40)
|
||||
z := new(big.Int).SetInt64(10)
|
||||
|
||||
blindFactorX, tX, hX, pX, proofX, _ := zkproofs.GetZkrp().GenerateProof(x)
|
||||
var ok bool
|
||||
verifier := zkproofs.GetVerifier(tX, hX, pX)
|
||||
ok, _ = verifier.Verify(proofX)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorY, tY, hY, pY, proofY, _ := zkproofs.GetZkrp().GenerateProof(y)
|
||||
verifier = zkproofs.GetVerifier(tY, hY, pY)
|
||||
ok, _ = verifier.Verify(proofY)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorZ, tZ, hZ, pZ, proofZ, _ := zkproofs.GetZkrp().GenerateProof(z)
|
||||
verifier = zkproofs.GetVerifier(tZ, hZ, pZ)
|
||||
ok, _ = verifier.Verify(proofZ)
|
||||
assert.True(t, ok)
|
||||
|
||||
// 佩德森承诺检查输入之和与输出之和是否相等
|
||||
blindOut := new(big.Int).Add(blindFactorY, blindFactorZ)
|
||||
blindDiff := new(big.Int).Sub(blindFactorX, blindOut)
|
||||
|
||||
check := zkproofs.VerifyPedersenCommitment([]*zkproofs.PedersenCommitment{proofX.V}, []*zkproofs.PedersenCommitment{proofY.V, proofZ.V}, blindDiff)
|
||||
fmt.Println("pedersen verify result:", check)
|
||||
assert.False(t, check)
|
||||
}
|
||||
|
||||
func TestCTx3(t *testing.T) {
|
||||
// value
|
||||
x := new(big.Int).SetInt64(30)
|
||||
y := new(big.Int).SetInt64(40)
|
||||
z := new(big.Int).SetInt64(-10)
|
||||
|
||||
blindFactorX, tX, hX, pX, proofX, _ := zkproofs.GetZkrp().GenerateProof(x)
|
||||
var ok bool
|
||||
verifier := zkproofs.GetVerifier(tX, hX, pX)
|
||||
ok, _ = verifier.Verify(proofX)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorY, tY, hY, pY, proofY, _ := zkproofs.GetZkrp().GenerateProof(y)
|
||||
verifier = zkproofs.GetVerifier(tY, hY, pY)
|
||||
ok, _ = verifier.Verify(proofY)
|
||||
assert.True(t, ok)
|
||||
|
||||
blindFactorZ, tZ, hZ, pZ, proofZ, _ := zkproofs.GetZkrp().GenerateProof(z)
|
||||
verifier = zkproofs.GetVerifier(tZ, hZ, pZ)
|
||||
ok, _ = verifier.Verify(proofZ)
|
||||
assert.False(t, ok)
|
||||
|
||||
// 佩德森承诺检查输入之和与输出之和是否相等
|
||||
blindOut := new(big.Int).Add(blindFactorY, blindFactorZ)
|
||||
blindDiff := new(big.Int).Sub(blindFactorX, blindOut)
|
||||
|
||||
check := zkproofs.VerifyPedersenCommitment([]*zkproofs.PedersenCommitment{proofX.V}, []*zkproofs.PedersenCommitment{proofY.V, proofZ.V}, blindDiff)
|
||||
fmt.Println("pedersen verify result:", check)
|
||||
assert.True(t, check)
|
||||
}
|
111
src/ConfidentialTx/zkproofs/bb.go
Normal file
111
src/ConfidentialTx/zkproofs/bb.go
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
/*
|
||||
This file contains the implementation of the BB signature scheme proposed in the paper:
|
||||
Short signatures without random oracle
|
||||
Boneh and Boyen
|
||||
Eurocrypt 2004
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
// "../crypto/bn256"
|
||||
bn256 "github.com/ethereum/go-ethereum/crypto/bn256/google"
|
||||
)
|
||||
|
||||
type keypair struct {
|
||||
pubk *bn256.G1
|
||||
privk *big.Int
|
||||
}
|
||||
|
||||
/*
|
||||
keygen is responsible for the key generation.
|
||||
*/
|
||||
func keygen() (keypair, error) {
|
||||
var (
|
||||
kp keypair
|
||||
e error
|
||||
res error
|
||||
)
|
||||
kp.privk, e = rand.Int(rand.Reader, bn256.Order)
|
||||
if e != nil {
|
||||
return kp, e
|
||||
}
|
||||
kp.pubk = new(bn256.G1).ScalarBaseMult(kp.privk)
|
||||
_, res = new(bn256.G2).Unmarshal(kp.pubk.Marshal())
|
||||
if res != nil {
|
||||
return kp, errors.New("Could not compute scalar multiplication.")
|
||||
}
|
||||
return kp, e
|
||||
}
|
||||
|
||||
/*
|
||||
sign receives as input a message and a private key and outputs a digital signature.
|
||||
*/
|
||||
func sign(m *big.Int, privk *big.Int) (*bn256.G2, error) {
|
||||
var (
|
||||
res error
|
||||
signature *bn256.G2
|
||||
)
|
||||
inv := ModInverse(Mod(Add(m, privk), bn256.Order), bn256.Order)
|
||||
signature = new(bn256.G2).ScalarBaseMult(inv)
|
||||
_, res = new(bn256.G2).Unmarshal(signature.Marshal())
|
||||
if res != nil {
|
||||
return signature, nil
|
||||
} else {
|
||||
return nil, errors.New("Error while computing signature.")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
verify receives as input the digital signature, the message and the public key. It outputs
|
||||
true if and only if the signature is valid.
|
||||
*/
|
||||
func verify(signature *bn256.G2, m *big.Int, pubk *bn256.G1) (bool, error) {
|
||||
// e(y.g^m, sig) = e(g1,g2)
|
||||
var (
|
||||
gm *bn256.G1
|
||||
res bool
|
||||
e error
|
||||
)
|
||||
// g^m
|
||||
gm = new(bn256.G1).ScalarBaseMult(m)
|
||||
_, e = new(bn256.G2).Unmarshal(gm.Marshal())
|
||||
// y.g^m
|
||||
gm = gm.Add(gm, pubk)
|
||||
// e(y.g^m, sig)
|
||||
p1 := bn256.Pair(gm, signature)
|
||||
// e(g1,g2)
|
||||
g1 := new(bn256.G1).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||
g2 := new(bn256.G2).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||
p2 := bn256.Pair(g1, g2)
|
||||
// p1 == p2?
|
||||
// p2 = p2.Neg(p2)
|
||||
// p1 = p1.Add(p1, p2)
|
||||
// res = p1.IsOne()
|
||||
res = bytes.Equal(p1.Marshal(), p2.Marshal())
|
||||
if e != nil {
|
||||
return res, nil
|
||||
}
|
||||
return false, errors.New("Error while computing signature.")
|
||||
}
|
33
src/ConfidentialTx/zkproofs/bb_test.go
Normal file
33
src/ConfidentialTx/zkproofs/bb_test.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
func TestKeyGen(t *testing.T) {
|
||||
kp, _ := keygen()
|
||||
signature, _ := sign(big.NewInt(42), kp.privk)
|
||||
res, _ := verify(signature, big.NewInt(42), kp.pubk)
|
||||
if res != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", res)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
75
src/ConfidentialTx/zkproofs/bn.go
Normal file
75
src/ConfidentialTx/zkproofs/bn.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"math/big"
|
||||
|
||||
"../byteconversion"
|
||||
)
|
||||
|
||||
var k1 = new(big.Int).SetBit(big.NewInt(0), 160, 1) // 2^160, security parameter that should match prover
|
||||
|
||||
func CalculateHash(b1 *big.Int, b2 *big.Int) (*big.Int, error) {
|
||||
|
||||
digest := sha256.New()
|
||||
digest.Write(byteconversion.ToByteArray(b1))
|
||||
if b2 != nil {
|
||||
digest.Write(byteconversion.ToByteArray(b2))
|
||||
}
|
||||
output := digest.Sum(nil)
|
||||
tmp := output[0:len(output)]
|
||||
return byteconversion.FromByteArray(tmp)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns base**exponent mod |modulo| also works for negative exponent (contrary to big.Int.Exp)
|
||||
*/
|
||||
func ModPow(base *big.Int, exponent *big.Int, modulo *big.Int) *big.Int {
|
||||
|
||||
var returnValue *big.Int
|
||||
|
||||
if exponent.Cmp(big.NewInt(0)) >= 0 {
|
||||
returnValue = new(big.Int).Exp(base, exponent, modulo)
|
||||
} else {
|
||||
// Exp doesn't support negative exponent so instead:
|
||||
// use positive exponent than take inverse (modulo)..
|
||||
returnValue = ModInverse(new(big.Int).Exp(base, new(big.Int).Abs(exponent), modulo), modulo)
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
func Add(x *big.Int, y *big.Int) *big.Int {
|
||||
return new(big.Int).Add(x, y)
|
||||
}
|
||||
|
||||
func Sub(x *big.Int, y *big.Int) *big.Int {
|
||||
return new(big.Int).Sub(x, y)
|
||||
}
|
||||
|
||||
func Mod(base *big.Int, modulo *big.Int) *big.Int {
|
||||
return new(big.Int).Mod(base, modulo)
|
||||
}
|
||||
|
||||
func Multiply(factor1 *big.Int, factor2 *big.Int) *big.Int {
|
||||
return new(big.Int).Mul(factor1, factor2)
|
||||
}
|
||||
|
||||
func ModInverse(base *big.Int, modulo *big.Int) *big.Int {
|
||||
return new(big.Int).ModInverse(base, modulo)
|
||||
}
|
129
src/ConfidentialTx/zkproofs/bn_test.go
Normal file
129
src/ConfidentialTx/zkproofs/bn_test.go
Normal file
@ -0,0 +1,129 @@
|
||||
// Copyright 2017 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
|
||||
func TestCalculateHash(t * testing.T) {
|
||||
|
||||
a := GetBigInt("20905485153255974750600830283139712767405035066172127447413526262122898097752829902691919420016794244099612526431387099905077116995490485444167190551980224865082320241670546533063409921082864323224863076823319894932240914571396941354556281385023649535909639921239646795929610627460276589386330363348840105387073757406261480377763345436612442076323102518362946991582624513737241437269968051355243751819094759669539075841991633425362795570590507959822047022497500292880734028347273355847985904992235033659931679254742902977502890883426551960403450937665750386501228142099266824028488862959626463948822181376617128628357")
|
||||
b := GetBigInt("5711912074763938920844020768820827016918638588776093786691324830937965710562669998102969607754216881533101753509522661181935679768137553251696427895001308210043958162362474454915118307661021406997989560047755201343617470288619030784987198511772840498354380632474664457429003510207310347179884080000294301502325103527312780599913053243627156705417875172756769585807691558680079741149166677442267851492473670184071199725213912264373214980177804010561543807969309223405291240876888702197126709861726023144260487044339708816278182396486957437256069194438047922679665536060592545457448379589893428429445378466414731324407")
|
||||
|
||||
expectedResult := GetBigInt("-19913561841364303941087968013056854925409568225408501509608065500928998362191")
|
||||
actualResult, _ := CalculateHash(a, b)
|
||||
actualResult2, _ := CalculateHash(a, b)
|
||||
|
||||
if expectedResult.Cmp(actualResult) != 0 {
|
||||
t.Errorf("Assert failure: hashed is: %s", actualResult)
|
||||
}
|
||||
if expectedResult.Cmp(actualResult2) != 0 {
|
||||
t.Errorf("Assert failure: hashed 2 is: %s", actualResult2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModPow1(t *testing.T) {
|
||||
|
||||
base := big.NewInt(10)
|
||||
exponent := big.NewInt(3)
|
||||
modulo := big.NewInt(7)
|
||||
|
||||
result := ModPow(base, exponent, modulo)
|
||||
|
||||
if result.Cmp(big.NewInt(6)) != 0 {
|
||||
t.Errorf("Assert failure: expected 6, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModPow2(t *testing.T) {
|
||||
|
||||
base := big.NewInt(30)
|
||||
exponent := big.NewInt(2)
|
||||
modulo := big.NewInt(7)
|
||||
|
||||
var result = ModPow(base, exponent, modulo)
|
||||
|
||||
if result.Cmp(big.NewInt(4)) != 0 {
|
||||
t.Errorf("Assert failure: expected 4, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModPowNegativeExp1(t *testing.T) {
|
||||
|
||||
result := ModPow(big.NewInt(16), big.NewInt(-1), big.NewInt(7))
|
||||
|
||||
if result.Cmp(big.NewInt(4)) != 0 {
|
||||
t.Errorf("Assert failure: expected 4, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModPowNegativeExp2(t *testing.T) {
|
||||
|
||||
result := ModPow(big.NewInt(34), big.NewInt(-2), big.NewInt(9))
|
||||
|
||||
if result.Cmp(big.NewInt(7)) != 0 {
|
||||
t.Errorf("Assert failure: expected 7, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModInverse1(t *testing.T) {
|
||||
|
||||
base := big.NewInt(5)
|
||||
modulo := big.NewInt(1)
|
||||
|
||||
var result = ModInverse(base, modulo)
|
||||
|
||||
if result.Cmp(big.NewInt(0)) != 0 {
|
||||
t.Errorf("Assert failure: expected 0, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModInverse2(t *testing.T) {
|
||||
|
||||
base := big.NewInt(3)
|
||||
modulo := big.NewInt(7)
|
||||
|
||||
var result = ModInverse(base, modulo)
|
||||
|
||||
if result.Cmp(big.NewInt(5)) != 0 {
|
||||
t.Errorf("Assert failure: expected 5, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiply(t *testing.T) {
|
||||
|
||||
factor1 := big.NewInt(3)
|
||||
factor2 := big.NewInt(7)
|
||||
|
||||
var result = Multiply(factor1, factor2)
|
||||
if result.Cmp(big.NewInt(21)) != 0 {
|
||||
t.Errorf("Assert failure: expected 21, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMod(t *testing.T) {
|
||||
|
||||
result := Mod(big.NewInt(16), big.NewInt(7))
|
||||
|
||||
if result.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("Assert failure: expected 2, actual: %s", result)
|
||||
}
|
||||
}
|
||||
|
1358
src/ConfidentialTx/zkproofs/bulletproofs.go
Normal file
1358
src/ConfidentialTx/zkproofs/bulletproofs.go
Normal file
File diff suppressed because it is too large
Load Diff
355
src/ConfidentialTx/zkproofs/bulletproofs_test.go
Normal file
355
src/ConfidentialTx/zkproofs/bulletproofs_test.go
Normal file
@ -0,0 +1,355 @@
|
||||
// Copyright 2018 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ing-bank/zkproofs/go-ethereum/crypto/bn256"
|
||||
)
|
||||
|
||||
/*
|
||||
Test method VectorCopy, which simply copies the first input argument to size n vector.
|
||||
*/
|
||||
func TestVectorCopy(t *testing.T) {
|
||||
var (
|
||||
result []*big.Int
|
||||
)
|
||||
result, _ = VectorCopy(new(big.Int).SetInt64(1), 3)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(1)) == 0)
|
||||
ok = ok && (result[1].Cmp(GetBigInt("1")) == 0)
|
||||
ok = ok && (result[2].Cmp(GetBigInt("1")) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test method VectorConvertToBig.
|
||||
*/
|
||||
func TestVectorConvertToBig(t *testing.T) {
|
||||
var (
|
||||
result []*big.Int
|
||||
a []int64
|
||||
)
|
||||
a = make([]int64, 3)
|
||||
a[0] = 3
|
||||
a[1] = 4
|
||||
a[2] = 5
|
||||
result, _ = VectorConvertToBig(a, 3)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(3)) == 0)
|
||||
ok = ok && (result[1].Cmp(GetBigInt("4")) == 0)
|
||||
ok = ok && (result[2].Cmp(GetBigInt("5")) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Scalar Product returns the inner product between 2 vectors.
|
||||
*/
|
||||
func TestScalarProduct(t *testing.T) {
|
||||
var (
|
||||
a, b []*big.Int
|
||||
)
|
||||
a = make([]*big.Int, 3)
|
||||
b = make([]*big.Int, 3)
|
||||
a[0] = new(big.Int).SetInt64(7)
|
||||
a[1] = new(big.Int).SetInt64(7)
|
||||
a[2] = new(big.Int).SetInt64(7)
|
||||
b[0] = new(big.Int).SetInt64(3)
|
||||
b[1] = new(big.Int).SetInt64(3)
|
||||
b[2] = new(big.Int).SetInt64(3)
|
||||
result, _ := ScalarProduct(a, b)
|
||||
ok := (result.Cmp(new(big.Int).SetInt64(63)) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Vector addition.
|
||||
*/
|
||||
func TestVectorAdd(t *testing.T) {
|
||||
var (
|
||||
a, b []*big.Int
|
||||
)
|
||||
a = make([]*big.Int, 3)
|
||||
b = make([]*big.Int, 3)
|
||||
a[0] = new(big.Int).SetInt64(7)
|
||||
a[1] = new(big.Int).SetInt64(8)
|
||||
a[2] = new(big.Int).SetInt64(9)
|
||||
b[0] = new(big.Int).SetInt64(3)
|
||||
b[1] = new(big.Int).SetInt64(30)
|
||||
b[2] = new(big.Int).SetInt64(40)
|
||||
result, _ := VectorAdd(a, b)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(10)) == 0)
|
||||
ok = ok && (result[1].Cmp(GetBigInt("38")) == 0)
|
||||
ok = ok && (result[2].Cmp(GetBigInt("49")) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Vector subtraction.
|
||||
*/
|
||||
func TestVectorSub(t *testing.T) {
|
||||
var (
|
||||
a, b []*big.Int
|
||||
)
|
||||
a = make([]*big.Int, 3)
|
||||
b = make([]*big.Int, 3)
|
||||
a[0] = new(big.Int).SetInt64(7)
|
||||
a[1] = new(big.Int).SetInt64(8)
|
||||
a[2] = new(big.Int).SetInt64(9)
|
||||
b[0] = new(big.Int).SetInt64(3)
|
||||
b[1] = new(big.Int).SetInt64(30)
|
||||
b[2] = new(big.Int).SetInt64(40)
|
||||
result, _ := VectorSub(a, b)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(4)) == 0)
|
||||
ok = ok && (result[1].Cmp(GetBigInt("115792089237316195423570985008687907852837564279074904382605163141518161494315")) == 0)
|
||||
ok = ok && (result[2].Cmp(GetBigInt("115792089237316195423570985008687907852837564279074904382605163141518161494306")) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Vector componentwise multiplication.
|
||||
*/
|
||||
func TestVectorMul(t *testing.T) {
|
||||
var (
|
||||
a, b []*big.Int
|
||||
)
|
||||
a = make([]*big.Int, 3)
|
||||
b = make([]*big.Int, 3)
|
||||
a[0] = new(big.Int).SetInt64(7)
|
||||
a[1] = new(big.Int).SetInt64(8)
|
||||
a[2] = new(big.Int).SetInt64(9)
|
||||
b[0] = new(big.Int).SetInt64(3)
|
||||
b[1] = new(big.Int).SetInt64(30)
|
||||
b[2] = new(big.Int).SetInt64(40)
|
||||
result, _ := VectorMul(a, b)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(21)) == 0)
|
||||
ok = ok && (result[1].Cmp(new(big.Int).SetInt64(240)) == 0)
|
||||
ok = ok && (result[2].Cmp(new(big.Int).SetInt64(360)) == 0)
|
||||
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test method PowerOf, which must return a vector containing a growing sequence of
|
||||
powers of 2.
|
||||
*/
|
||||
func TestPowerOf(t *testing.T) {
|
||||
result, _ := PowerOf(new(big.Int).SetInt64(3), 3)
|
||||
ok := (result[0].Cmp(new(big.Int).SetInt64(1)) == 0)
|
||||
ok = ok && (result[1].Cmp(new(big.Int).SetInt64(3)) == 0)
|
||||
ok = ok && (result[2].Cmp(new(big.Int).SetInt64(9)) == 0)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test Inner Product argument.
|
||||
*/
|
||||
func TestInnerProduct(t *testing.T) {
|
||||
var (
|
||||
zkrp Bp
|
||||
zkip bip
|
||||
a []*big.Int
|
||||
b []*big.Int
|
||||
)
|
||||
// TODO:
|
||||
// Review if it is the best way, since we maybe could use the
|
||||
// inner product independently of the range proof.
|
||||
zkrp.Setup(0, 16)
|
||||
a = make([]*big.Int, zkrp.N)
|
||||
a[0] = new(big.Int).SetInt64(2)
|
||||
a[1] = new(big.Int).SetInt64(-1)
|
||||
a[2] = new(big.Int).SetInt64(10)
|
||||
a[3] = new(big.Int).SetInt64(6)
|
||||
b = make([]*big.Int, zkrp.N)
|
||||
b[0] = new(big.Int).SetInt64(1)
|
||||
b[1] = new(big.Int).SetInt64(2)
|
||||
b[2] = new(big.Int).SetInt64(10)
|
||||
b[3] = new(big.Int).SetInt64(7)
|
||||
c := new(big.Int).SetInt64(142)
|
||||
commit, _ := CommitInnerProduct(zkrp.Gg, zkrp.Hh, a, b)
|
||||
zkip.Setup(zkrp.H, zkrp.Gg, zkrp.Hh, c)
|
||||
proof, _ := zkip.Prove(a, b, commit)
|
||||
ok, _ := zkip.Verify(proof)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test the FALSE case of ZK Range Proof scheme using Bulletproofs.
|
||||
*/
|
||||
func TestFalseBulletproofsZKRP(t *testing.T) {
|
||||
var (
|
||||
zkrp Bp
|
||||
)
|
||||
startTime := time.Now()
|
||||
zkrp.Setup(0, 4294967296) // ITS BEING USED TO COMPUTE N
|
||||
setupTime := time.Now()
|
||||
fmt.Println("Setup time:")
|
||||
fmt.Println(setupTime.Sub(startTime))
|
||||
|
||||
x := new(big.Int).SetInt64(4294967296)
|
||||
proof, _ := zkrp.Prove(x)
|
||||
proofTime := time.Now()
|
||||
fmt.Println("Proof time:")
|
||||
fmt.Println(proofTime.Sub(setupTime))
|
||||
|
||||
ok, _ := zkrp.Verify(proof)
|
||||
verifyTime := time.Now()
|
||||
fmt.Println("Verify time:")
|
||||
fmt.Println(verifyTime.Sub(proofTime))
|
||||
|
||||
fmt.Println("Range Proofs invalid test result:")
|
||||
fmt.Println(ok)
|
||||
if ok != false {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test the TRUE case of ZK Range Proof scheme using Bulletproofs.
|
||||
*/
|
||||
func TestTrueBulletproofsZKRP(t *testing.T) {
|
||||
var (
|
||||
zkrp Bp
|
||||
)
|
||||
startTime := time.Now()
|
||||
zkrp.Setup(0, 4294967296) // ITS BEING USED TO COMPUTE N
|
||||
setupTime := time.Now()
|
||||
fmt.Println("Setup time:")
|
||||
fmt.Println(setupTime.Sub(startTime))
|
||||
|
||||
x := new(big.Int).SetInt64(65535)
|
||||
proof, _ := zkrp.Prove(x)
|
||||
proofTime := time.Now()
|
||||
fmt.Println("Proof time:")
|
||||
fmt.Println(proofTime.Sub(setupTime))
|
||||
|
||||
ok, _ := zkrp.Verify(proof)
|
||||
verifyTime := time.Now()
|
||||
fmt.Println("Verify time:")
|
||||
fmt.Println(verifyTime.Sub(proofTime))
|
||||
|
||||
fmt.Println("Range Proofs result:")
|
||||
fmt.Println(ok)
|
||||
if ok != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBulletproofs(b *testing.B) {
|
||||
var (
|
||||
zkrp Bp
|
||||
proof proofBP
|
||||
ok bool
|
||||
)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
zkrp.Setup(0, 4294967296) // ITS BEING USED TO COMPUTE N
|
||||
x := new(big.Int).SetInt64(4294967295)
|
||||
proof, _ = zkrp.Prove(x)
|
||||
ok, _ = zkrp.Verify(proof)
|
||||
if ok != true {
|
||||
b.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
var (
|
||||
a *big.Int
|
||||
A *bn256.G1
|
||||
)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
a, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
A = new(bn256.G1).ScalarBaseMult(a)
|
||||
}
|
||||
fmt.Println("A:")
|
||||
fmt.Println(A)
|
||||
}
|
||||
|
||||
func TestHashBP(t *testing.T) {
|
||||
agx, _ := new(big.Int).SetString("110720467414728166769654679803728202169916280248550137472490865118702779748947", 10)
|
||||
agy, _ := new(big.Int).SetString("103949684536896233354287911519259186718323435572971865592336813380571928560949", 10)
|
||||
sgx, _ := new(big.Int).SetString("78662919066140655151560869958157053125629409725243565127658074141532489435921", 10)
|
||||
sgy, _ := new(big.Int).SetString("114946280626097680211499478702679495377587739951564115086530426937068100343655", 10)
|
||||
pointa := &p256{X: agx, Y: agy}
|
||||
points := &p256{X: sgx, Y: sgy}
|
||||
result1, result2, _ := HashBP(pointa, points)
|
||||
res1, _ := new(big.Int).SetString("103823382860325249552741530200099120077084118788867728791742258217664299339569", 10)
|
||||
res2, _ := new(big.Int).SetString("8192372577089859289404358830067912230280991346287696886048261417244724213964", 10)
|
||||
ok1 := (result1.Cmp(res1) != 0)
|
||||
ok2 := (result2.Cmp(res2) != 0)
|
||||
ok := ok1 && ok2
|
||||
if ok {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashBPGx(t *testing.T) {
|
||||
gx, _ := new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
|
||||
gy, _ := new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
|
||||
point := &p256{X: gx, Y: gy}
|
||||
result1, result2, _ := HashBP(point, point)
|
||||
res1, _ := new(big.Int).SetString("11897424191990306464486192136408618361228444529783223689021929580052970909263", 10)
|
||||
res2, _ := new(big.Int).SetString("22166487799255634251145870394406518059682307840904574298117500050508046799269", 10)
|
||||
ok1 := (result1.Cmp(res1) != 0)
|
||||
ok2 := (result2.Cmp(res2) != 0)
|
||||
ok := ok1 && ok2
|
||||
if ok {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInv(t *testing.T) {
|
||||
y, _ := new(big.Int).SetString("103823382860325249552741530200099120077084118788867728791742258217664299339569", 10)
|
||||
yinv := ModInverse(y, ORDER)
|
||||
res, _ := new(big.Int).SetString("38397371868935917445400134055424677162505875368971619911110421656148020877351", 10)
|
||||
ok := (yinv.Cmp(res) != 0)
|
||||
if ok {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHPrime(t *testing.T) {
|
||||
var zkrp *Bp
|
||||
var proof *proofBP
|
||||
zkrp, _ = LoadParamFromDisk("setup.dat")
|
||||
proof, _ = LoadProofFromDisk("proof.dat")
|
||||
ok, _ := zkrp.Verify(*proof)
|
||||
if !ok {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", ok)
|
||||
}
|
||||
}
|
431
src/ConfidentialTx/zkproofs/ccs08.go
Normal file
431
src/ConfidentialTx/zkproofs/ccs08.go
Normal file
@ -0,0 +1,431 @@
|
||||
// Copyright 2018 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
This file contains the implementation of the ZKRP scheme proposed in the paper:
|
||||
Efficient Protocols for Set Membership and Range Proofs
|
||||
Jan Camenisch, Rafik Chaabouni, abhi shelat
|
||||
Asiacrypt 2008
|
||||
*/
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
bn256 "github.com/ethereum/go-ethereum/crypto/bn256/google"
|
||||
// "../crypto/bn256"
|
||||
)
|
||||
|
||||
/*
|
||||
paramsSet contains elements generated by the verifier, which are necessary for the prover.
|
||||
This must be computed in a trusted setup.
|
||||
*/
|
||||
type paramsSet struct {
|
||||
signatures map[int64]*bn256.G2
|
||||
H *bn256.G2
|
||||
// TODO:must protect the private key
|
||||
kp keypair
|
||||
// u determines the amount of signatures we need in the public params.
|
||||
// Each signature can be compressed to just 1 field element of 256 bits.
|
||||
// Then the parameters have minimum size equal to 256*u bits.
|
||||
// l determines how many pairings we need to compute, then in order to improve
|
||||
// verifier`s performance we want to minize it.
|
||||
// Namely, we have 2*l pairings for the prover and 3*l for the verifier.
|
||||
}
|
||||
|
||||
/*
|
||||
paramsUL contains elements generated by the verifier, which are necessary for the prover.
|
||||
This must be computed in a trusted setup.
|
||||
*/
|
||||
type paramsUL struct {
|
||||
signatures map[string]*bn256.G2
|
||||
H *bn256.G2
|
||||
// TODO:must protect the private key
|
||||
kp keypair
|
||||
// u determines the amount of signatures we need in the public params.
|
||||
// Each signature can be compressed to just 1 field element of 256 bits.
|
||||
// Then the parameters have minimum size equal to 256*u bits.
|
||||
// l determines how many pairings we need to compute, then in order to improve
|
||||
// verifier`s performance we want to minize it.
|
||||
// Namely, we have 2*l pairings for the prover and 3*l for the verifier.
|
||||
u, l int64
|
||||
}
|
||||
|
||||
/*
|
||||
proofSet contains the necessary elements for the ZK Set Membership proof.
|
||||
*/
|
||||
type proofSet struct {
|
||||
V *bn256.G2
|
||||
D, C *bn256.G2
|
||||
a *bn256.GT
|
||||
s, t, zsig, zv *big.Int
|
||||
c, m, zr *big.Int
|
||||
}
|
||||
|
||||
/*
|
||||
proofUL contains the necessary elements for the ZK proof.
|
||||
*/
|
||||
type proofUL struct {
|
||||
V []*bn256.G2
|
||||
D, C *bn256.G2
|
||||
a []*bn256.GT
|
||||
s, t, zsig, zv []*big.Int
|
||||
c, m, zr *big.Int
|
||||
}
|
||||
|
||||
/*
|
||||
SetupSet generates the signature for the elements in the set.
|
||||
*/
|
||||
func SetupSet(s []int64) (paramsSet, error) {
|
||||
var (
|
||||
i int
|
||||
p paramsSet
|
||||
)
|
||||
p.kp, _ = keygen()
|
||||
|
||||
p.signatures = make(map[int64]*bn256.G2)
|
||||
for i = 0; i < len(s); i++ {
|
||||
sig_i, _ := sign(new(big.Int).SetInt64(int64(s[i])), p.kp.privk)
|
||||
p.signatures[s[i]] = sig_i
|
||||
}
|
||||
//TODO: protect the 'master' key
|
||||
h := GetBigInt("18560948149108576432482904553159745978835170526553990798435819795989606410925")
|
||||
p.H = new(bn256.G2).ScalarBaseMult(h)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
/*
|
||||
SetupUL generates the signature for the interval [0,u^l).
|
||||
The value of u should be roughly b/log(b), but we can choose smaller values in
|
||||
order to get smaller parameters, at the cost of having worse performance.
|
||||
*/
|
||||
func SetupUL(u, l int64) (paramsUL, error) {
|
||||
var (
|
||||
i int64
|
||||
p paramsUL
|
||||
)
|
||||
p.kp, _ = keygen()
|
||||
|
||||
p.signatures = make(map[string]*bn256.G2)
|
||||
for i = 0; i < u; i++ {
|
||||
sig_i, _ := sign(new(big.Int).SetInt64(i), p.kp.privk)
|
||||
p.signatures[strconv.FormatInt(i, 10)] = sig_i
|
||||
}
|
||||
//TODO: protect the 'master' key
|
||||
h := GetBigInt("18560948149108576432482904553159745978835170526553990798435819795989606410925")
|
||||
p.H = new(bn256.G2).ScalarBaseMult(h)
|
||||
p.u = u
|
||||
p.l = l
|
||||
return p, nil
|
||||
}
|
||||
|
||||
/*
|
||||
ProveSet method is used to produce the ZK Set Membership proof.
|
||||
*/
|
||||
func ProveSet(x int64, r *big.Int, p paramsSet) (proofSet, error) {
|
||||
var (
|
||||
v *big.Int
|
||||
proof_out proofSet
|
||||
)
|
||||
|
||||
// Initialize variables
|
||||
proof_out.D = new(bn256.G2)
|
||||
// proof_out.D.SetInfinity()
|
||||
_, _, epz, _ := proof_out.D.CurvePoints()
|
||||
epz.SetZero()
|
||||
proof_out.m, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
|
||||
D := new(bn256.G2)
|
||||
v, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
A, ok := p.signatures[x]
|
||||
if ok {
|
||||
// D = g^s.H^m
|
||||
D = new(bn256.G2).ScalarMult(p.H, proof_out.m)
|
||||
proof_out.s, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
aux := new(bn256.G2).ScalarBaseMult(proof_out.s)
|
||||
D.Add(D, aux)
|
||||
|
||||
proof_out.V = new(bn256.G2).ScalarMult(A, v)
|
||||
proof_out.t, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
proof_out.a = bn256.Pair(G1, proof_out.V)
|
||||
proof_out.a.ScalarMult(proof_out.a, proof_out.s)
|
||||
proof_out.a.Neg(proof_out.a)
|
||||
proof_out.a.Add(proof_out.a, new(bn256.GT).ScalarMult(E, proof_out.t))
|
||||
} else {
|
||||
return proof_out, errors.New("Could not generate proof. Element does not belong to the interval.")
|
||||
}
|
||||
proof_out.D.Add(proof_out.D, D)
|
||||
|
||||
// Consider passing C as input,
|
||||
// so that it is possible to delegate the commitment computation to an external party.
|
||||
proof_out.C, _ = Commit(new(big.Int).SetInt64(x), r, p.H)
|
||||
// Fiat-Shamir heuristic
|
||||
proof_out.c, _ = HashSet(proof_out.a, proof_out.D)
|
||||
proof_out.c = Mod(proof_out.c, bn256.Order)
|
||||
|
||||
proof_out.zr = Sub(proof_out.m, Multiply(r, proof_out.c))
|
||||
proof_out.zr = Mod(proof_out.zr, bn256.Order)
|
||||
proof_out.zsig = Sub(proof_out.s, Multiply(new(big.Int).SetInt64(x), proof_out.c))
|
||||
proof_out.zsig = Mod(proof_out.zsig, bn256.Order)
|
||||
proof_out.zv = Sub(proof_out.t, Multiply(v, proof_out.c))
|
||||
proof_out.zv = Mod(proof_out.zv, bn256.Order)
|
||||
return proof_out, nil
|
||||
}
|
||||
|
||||
/*
|
||||
ProveUL method is used to produce the ZKRP proof that secret x belongs to the interval [0,U^L].
|
||||
*/
|
||||
func ProveUL(x, r *big.Int, p paramsUL) (proofUL, error) {
|
||||
var (
|
||||
i int64
|
||||
v []*big.Int
|
||||
proof_out proofUL
|
||||
)
|
||||
decx, _ := Decompose(x, p.u, p.l)
|
||||
|
||||
// Initialize variables
|
||||
v = make([]*big.Int, p.l, p.l)
|
||||
proof_out.V = make([]*bn256.G2, p.l, p.l)
|
||||
proof_out.a = make([]*bn256.GT, p.l, p.l)
|
||||
proof_out.s = make([]*big.Int, p.l, p.l)
|
||||
proof_out.t = make([]*big.Int, p.l, p.l)
|
||||
proof_out.zsig = make([]*big.Int, p.l, p.l)
|
||||
proof_out.zv = make([]*big.Int, p.l, p.l)
|
||||
proof_out.D = new(bn256.G2)
|
||||
// proof_out.D.SetInfinity()
|
||||
_, _, epz, _ := proof_out.D.CurvePoints()
|
||||
epz.SetZero()
|
||||
proof_out.m, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
|
||||
// D = H^m
|
||||
D := new(bn256.G2).ScalarMult(p.H, proof_out.m)
|
||||
for i = 0; i < p.l; i++ {
|
||||
v[i], _ = rand.Int(rand.Reader, bn256.Order)
|
||||
A, ok := p.signatures[strconv.FormatInt(decx[i], 10)]
|
||||
if ok {
|
||||
proof_out.V[i] = new(bn256.G2).ScalarMult(A, v[i])
|
||||
proof_out.s[i], _ = rand.Int(rand.Reader, bn256.Order)
|
||||
proof_out.t[i], _ = rand.Int(rand.Reader, bn256.Order)
|
||||
proof_out.a[i] = bn256.Pair(G1, proof_out.V[i])
|
||||
proof_out.a[i].ScalarMult(proof_out.a[i], proof_out.s[i])
|
||||
proof_out.a[i].Neg(proof_out.a[i])
|
||||
proof_out.a[i].Add(proof_out.a[i], new(bn256.GT).ScalarMult(E, proof_out.t[i]))
|
||||
|
||||
ui := new(big.Int).Exp(new(big.Int).SetInt64(p.u), new(big.Int).SetInt64(i), nil)
|
||||
muisi := new(big.Int).Mul(proof_out.s[i], ui)
|
||||
muisi = Mod(muisi, bn256.Order)
|
||||
aux := new(bn256.G2).ScalarBaseMult(muisi)
|
||||
D.Add(D, aux)
|
||||
} else {
|
||||
return proof_out, errors.New("Could not generate proof. Element does not belong to the interval.")
|
||||
}
|
||||
}
|
||||
proof_out.D.Add(proof_out.D, D)
|
||||
|
||||
// Consider passing C as input,
|
||||
// so that it is possible to delegate the commitment computation to an external party.
|
||||
proof_out.C, _ = Commit(x, r, p.H)
|
||||
// Fiat-Shamir heuristic
|
||||
proof_out.c, _ = Hash(proof_out.a, proof_out.D)
|
||||
proof_out.c = Mod(proof_out.c, bn256.Order)
|
||||
|
||||
proof_out.zr = Sub(proof_out.m, Multiply(r, proof_out.c))
|
||||
proof_out.zr = Mod(proof_out.zr, bn256.Order)
|
||||
for i = 0; i < p.l; i++ {
|
||||
proof_out.zsig[i] = Sub(proof_out.s[i], Multiply(new(big.Int).SetInt64(decx[i]), proof_out.c))
|
||||
proof_out.zsig[i] = Mod(proof_out.zsig[i], bn256.Order)
|
||||
proof_out.zv[i] = Sub(proof_out.t[i], Multiply(v[i], proof_out.c))
|
||||
proof_out.zv[i] = Mod(proof_out.zv[i], bn256.Order)
|
||||
}
|
||||
return proof_out, nil
|
||||
}
|
||||
|
||||
/*
|
||||
VerifySet is used to validate the ZK Set Membership proof. It returns true iff the proof is valid.
|
||||
*/
|
||||
func VerifySet(proof_out *proofSet, p *paramsSet) (bool, error) {
|
||||
var (
|
||||
D *bn256.G2
|
||||
r1, r2 bool
|
||||
p1, p2 *bn256.GT
|
||||
)
|
||||
// D == C^c.h^ zr.g^zsig ?
|
||||
D = new(bn256.G2).ScalarMult(proof_out.C, proof_out.c)
|
||||
D.Add(D, new(bn256.G2).ScalarMult(p.H, proof_out.zr))
|
||||
aux := new(bn256.G2).ScalarBaseMult(proof_out.zsig)
|
||||
D.Add(D, aux)
|
||||
|
||||
DBytes := D.Marshal()
|
||||
pDBytes := proof_out.D.Marshal()
|
||||
r1 = bytes.Equal(DBytes, pDBytes)
|
||||
|
||||
r2 = true
|
||||
// a == [e(V,y)^c].[e(V,g)^-zsig].[e(g,g)^zv]
|
||||
p1 = bn256.Pair(p.kp.pubk, proof_out.V)
|
||||
p1.ScalarMult(p1, proof_out.c)
|
||||
p2 = bn256.Pair(G1, proof_out.V)
|
||||
p2.ScalarMult(p2, proof_out.zsig)
|
||||
p2.Neg(p2)
|
||||
p1.Add(p1, p2)
|
||||
p1.Add(p1, new(bn256.GT).ScalarMult(E, proof_out.zv))
|
||||
|
||||
pBytes := p1.Marshal()
|
||||
aBytes := proof_out.a.Marshal()
|
||||
r2 = r2 && bytes.Equal(pBytes, aBytes)
|
||||
return r1 && r2, nil
|
||||
}
|
||||
|
||||
/*
|
||||
VerifyUL is used to validate the ZKRP proof. It returns true iff the proof is valid.
|
||||
*/
|
||||
func VerifyUL(proof_out *proofUL, p *paramsUL) (bool, error) {
|
||||
var (
|
||||
i int64
|
||||
D *bn256.G2
|
||||
r1, r2 bool
|
||||
p1, p2 *bn256.GT
|
||||
)
|
||||
// D == C^c.h^ zr.g^zsig ?
|
||||
D = new(bn256.G2).ScalarMult(proof_out.C, proof_out.c)
|
||||
D.Add(D, new(bn256.G2).ScalarMult(p.H, proof_out.zr))
|
||||
for i = 0; i < p.l; i++ {
|
||||
ui := new(big.Int).Exp(new(big.Int).SetInt64(p.u), new(big.Int).SetInt64(i), nil)
|
||||
muizsigi := new(big.Int).Mul(proof_out.zsig[i], ui)
|
||||
muizsigi = Mod(muizsigi, bn256.Order)
|
||||
aux := new(bn256.G2).ScalarBaseMult(muizsigi)
|
||||
D.Add(D, aux)
|
||||
}
|
||||
|
||||
DBytes := D.Marshal()
|
||||
pDBytes := proof_out.D.Marshal()
|
||||
r1 = bytes.Equal(DBytes, pDBytes)
|
||||
|
||||
r2 = true
|
||||
for i = 0; i < p.l; i++ {
|
||||
// a == [e(V,y)^c].[e(V,g)^-zsig].[e(g,g)^zv]
|
||||
p1 = bn256.Pair(p.kp.pubk, proof_out.V[i])
|
||||
p1.ScalarMult(p1, proof_out.c)
|
||||
p2 = bn256.Pair(G1, proof_out.V[i])
|
||||
p2.ScalarMult(p2, proof_out.zsig[i])
|
||||
p2.Neg(p2)
|
||||
p1.Add(p1, p2)
|
||||
p1.Add(p1, new(bn256.GT).ScalarMult(E, proof_out.zv[i]))
|
||||
|
||||
pBytes := p1.Marshal()
|
||||
aBytes := proof_out.a[i].Marshal()
|
||||
r2 = r2 && bytes.Equal(pBytes, aBytes)
|
||||
}
|
||||
return r1 && r2, nil
|
||||
}
|
||||
|
||||
/*
|
||||
proof contains the necessary elements for the ZK proof.
|
||||
*/
|
||||
type proof struct {
|
||||
p1, p2 proofUL
|
||||
}
|
||||
|
||||
/*
|
||||
params contains elements generated by the verifier, which are necessary for the prover.
|
||||
This must be computed in a trusted setup.
|
||||
*/
|
||||
type params struct {
|
||||
p *paramsUL
|
||||
a, b int64
|
||||
}
|
||||
|
||||
type ccs08 struct {
|
||||
p *params
|
||||
x, r *big.Int
|
||||
proof_out proof
|
||||
pubk *bn256.G1
|
||||
}
|
||||
|
||||
/*
|
||||
Setup receives integers a and b, and configures the parameters for the rangeproof scheme.
|
||||
*/
|
||||
func (zkrp *ccs08) Setup(a, b int64) error {
|
||||
// Compute optimal values for u and l
|
||||
var (
|
||||
u, l int64
|
||||
logb float64
|
||||
p *params
|
||||
)
|
||||
if a > b {
|
||||
zkrp.p = nil
|
||||
return errors.New("a must be less than or equal to b")
|
||||
}
|
||||
p = new(params)
|
||||
logb = math.Log(float64(b))
|
||||
if logb != 0 {
|
||||
// TODO: understand how to find optimal parameters
|
||||
//u = b / int64(logb)
|
||||
u = 57
|
||||
if u != 0 {
|
||||
l = 0
|
||||
for i := b; i > 0; i = i / u {
|
||||
l = l + 1
|
||||
}
|
||||
params_out, e := SetupUL(u, l)
|
||||
p.p = ¶ms_out
|
||||
p.a = a
|
||||
p.b = b
|
||||
zkrp.p = p
|
||||
return e
|
||||
} else {
|
||||
zkrp.p = nil
|
||||
return errors.New("u is zero")
|
||||
}
|
||||
} else {
|
||||
zkrp.p = nil
|
||||
return errors.New("log(b) is zero")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Prove method is responsible for generating the zero knowledge proof.
|
||||
*/
|
||||
func (zkrp *ccs08) Prove() error {
|
||||
ul := new(big.Int).Exp(new(big.Int).SetInt64(zkrp.p.p.u), new(big.Int).SetInt64(zkrp.p.p.l), nil)
|
||||
|
||||
// x - b + ul
|
||||
xb := new(big.Int).Sub(zkrp.x, new(big.Int).SetInt64(zkrp.p.b))
|
||||
xb.Add(xb, ul)
|
||||
first, _ := ProveUL(xb, zkrp.r, *zkrp.p.p)
|
||||
|
||||
// x - a
|
||||
xa := new(big.Int).Sub(zkrp.x, new(big.Int).SetInt64(zkrp.p.a))
|
||||
second, _ := ProveUL(xa, zkrp.r, *zkrp.p.p)
|
||||
|
||||
zkrp.proof_out.p1 = first
|
||||
zkrp.proof_out.p2 = second
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Verify is responsible for validating the proof.
|
||||
*/
|
||||
func (zkrp *ccs08) Verify() (bool, error) {
|
||||
first, _ := VerifyUL(&zkrp.proof_out.p1, zkrp.p.p)
|
||||
second, _ := VerifyUL(&zkrp.proof_out.p2, zkrp.p.p)
|
||||
return first && second, nil
|
||||
}
|
190
src/ConfidentialTx/zkproofs/ccs08_test.go
Normal file
190
src/ConfidentialTx/zkproofs/ccs08_test.go
Normal file
@ -0,0 +1,190 @@
|
||||
// Copyright 2018 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"math/big"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/ing-bank/zkproofs/go-ethereum/crypto/bn256"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
Tests decomposion into bits.
|
||||
*/
|
||||
func TestDecompose(t *testing.T) {
|
||||
h := GetBigInt("925")
|
||||
decx, _ := Decompose(h, 10, 3)
|
||||
if decx[0] != 5 || decx[1] != 2 || decx[2] != 9 {
|
||||
t.Errorf("Assert failure: expected true, actual: %d", decx)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Inversion on G1 group.
|
||||
*/
|
||||
func TestNegScalarBaseMulG1(t *testing.T) {
|
||||
b, _ := rand.Int(rand.Reader, bn256.Order)
|
||||
pb := new(bn256.G1).ScalarBaseMult(b)
|
||||
mb := Sub(new(big.Int).SetInt64(0), b)
|
||||
mpb := new(bn256.G1).ScalarBaseMult(mb)
|
||||
a := new(bn256.G1).Add(pb, mpb)
|
||||
aBytes := a.Marshal()
|
||||
fmt.Println(aBytes)
|
||||
fmt.Println(a)
|
||||
for i := 0; i < len(aBytes)-1; i++ {
|
||||
if aBytes[i] != 0 {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", aBytes[i] == 0)
|
||||
}
|
||||
}
|
||||
if aBytes[len(aBytes)-1] != 1 {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", aBytes[len(aBytes)-1] == 1)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Inversion on G2 group.
|
||||
*/
|
||||
func TestNegScalarBaseMulG2(t *testing.T) {
|
||||
b, _ := rand.Int(rand.Reader, bn256.Order)
|
||||
pb := new(bn256.G2).ScalarBaseMult(b)
|
||||
mb := Sub(new(big.Int).SetInt64(0), b)
|
||||
mpb := new(bn256.G2).ScalarBaseMult(mb)
|
||||
a := new(bn256.G2).Add(pb, mpb)
|
||||
if a.IsZero() != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", a.IsZero())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests Inversion on GFp12 finite field.
|
||||
*/
|
||||
func TestInvertGFp12(t *testing.T) {
|
||||
b, _ := rand.Int(rand.Reader, bn256.Order)
|
||||
c, _ := rand.Int(rand.Reader, bn256.Order)
|
||||
|
||||
pb, _ := new(bn256.G1).Unmarshal(new(bn256.G1).ScalarBaseMult(b).Marshal())
|
||||
qc, _ := new(bn256.G2).Unmarshal(new(bn256.G2).ScalarBaseMult(c).Marshal())
|
||||
|
||||
k1 := bn256.Pair(pb, qc)
|
||||
k2 := new(bn256.GT).Invert(k1)
|
||||
k3 := new(bn256.GT).Add(k1, k2)
|
||||
if k3.IsOne() != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", k3.IsOne())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests the ZK Range Proof building block, where the interval is [0, U^L).
|
||||
*/
|
||||
func TestZKRP_UL(t *testing.T) {
|
||||
var (
|
||||
r *big.Int
|
||||
)
|
||||
p, _ := SetupUL(10, 5)
|
||||
r, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
proof_out, _ := ProveUL(new(big.Int).SetInt64(42176), r, p)
|
||||
result, _ := VerifyUL(&proof_out, &p)
|
||||
fmt.Println("ZKRP UL result: ")
|
||||
fmt.Println(result)
|
||||
if result != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", result)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests if the Setup algorithm is rejecting wrong input as expected.
|
||||
*/
|
||||
func TestZKRPSetupInput(t *testing.T) {
|
||||
var (
|
||||
zkrp ccs08
|
||||
)
|
||||
e := zkrp.Setup(1900, 1899)
|
||||
result := e.Error() != "a must be less than or equal to b"
|
||||
if result {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", result)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests the ZK Set Membership (CCS08) protocol.
|
||||
*/
|
||||
func TestZKSet(t *testing.T) {
|
||||
var (
|
||||
r *big.Int
|
||||
s []int64
|
||||
)
|
||||
s = make([]int64, 4)
|
||||
s[0] = 12
|
||||
s[1] = 42
|
||||
s[2] = 61
|
||||
s[3] = 71
|
||||
startTime := time.Now()
|
||||
p, _ := SetupSet(s)
|
||||
setupTime := time.Now()
|
||||
fmt.Println(" ############### Setup time:")
|
||||
fmt.Println(setupTime.Sub(startTime))
|
||||
r, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
proof_out, _ := ProveSet(12, r, p)
|
||||
proofTime := time.Now()
|
||||
fmt.Println("Proof time:")
|
||||
fmt.Println(proofTime.Sub(setupTime))
|
||||
result, _ := VerifySet(&proof_out, &p)
|
||||
verifyTime := time.Now()
|
||||
fmt.Println("Verify time:")
|
||||
fmt.Println(verifyTime.Sub(proofTime))
|
||||
fmt.Println("ZK Set Membership result: ")
|
||||
fmt.Println(result)
|
||||
if result != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", result)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests the entire ZK Range Proof (CCS08) protocol.
|
||||
*/
|
||||
func TestZKRP(t *testing.T) {
|
||||
var (
|
||||
result bool
|
||||
zkrp ccs08
|
||||
)
|
||||
startTime := time.Now()
|
||||
zkrp.Setup(347184000, 599644800)
|
||||
setupTime := time.Now()
|
||||
fmt.Println(" ############### Setup time:")
|
||||
fmt.Println(setupTime.Sub(startTime))
|
||||
zkrp.x = new(big.Int).SetInt64(419835123)
|
||||
zkrp.r, _ = rand.Int(rand.Reader, bn256.Order)
|
||||
e := zkrp.Prove()
|
||||
proofTime := time.Now()
|
||||
fmt.Println("Proof time:")
|
||||
fmt.Println(proofTime.Sub(setupTime))
|
||||
if e != nil {
|
||||
fmt.Println(e.Error())
|
||||
}
|
||||
result, _ = zkrp.Verify()
|
||||
verifyTime := time.Now()
|
||||
fmt.Println("Verify time:")
|
||||
fmt.Println(verifyTime.Sub(proofTime))
|
||||
fmt.Println("ZKRP result: ")
|
||||
fmt.Println(result)
|
||||
if result != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", result)
|
||||
}
|
||||
}
|
261
src/ConfidentialTx/zkproofs/p256.go
Normal file
261
src/ConfidentialTx/zkproofs/p256.go
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
Encapsulates secp256k1 elliptic curve.
|
||||
*/
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"../byteconversion"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
||||
// "../crypto/secp256k1"
|
||||
)
|
||||
|
||||
var (
|
||||
CURVE = secp256k1.S256()
|
||||
GX = CURVE.Gx
|
||||
GY = CURVE.Gy
|
||||
)
|
||||
|
||||
/*
|
||||
Elliptic Curve Point struct.
|
||||
*/
|
||||
type p256 struct {
|
||||
X, Y *big.Int
|
||||
}
|
||||
type PedersenCommitment = p256
|
||||
|
||||
/*
|
||||
IsZero returns true if and only if the elliptic curve point is the point at infinity.
|
||||
*/
|
||||
func (p *p256) IsZero() bool {
|
||||
c1 := (p.X == nil || p.Y == nil)
|
||||
if !c1 {
|
||||
z := new(big.Int).SetInt64(0)
|
||||
return p.X.Cmp(z) == 0 && p.Y.Cmp(z) == 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
Neg returns the inverse of the given elliptic curve point.
|
||||
*/
|
||||
func (p *p256) Neg(a *p256) *p256 {
|
||||
// (X, Y) -> (X, X + Y)
|
||||
if a.IsZero() {
|
||||
return p.SetInfinity()
|
||||
}
|
||||
one := new(big.Int).SetInt64(1)
|
||||
mone := new(big.Int).Sub(CURVE.N, one)
|
||||
p.ScalarMult(p, mone)
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
Input points must be distinct
|
||||
*/
|
||||
func (p *p256) Add(a, b *p256) *p256 {
|
||||
if a.IsZero() {
|
||||
p.X = b.X
|
||||
p.Y = b.Y
|
||||
return p
|
||||
} else if b.IsZero() {
|
||||
p.X = b.X
|
||||
p.Y = b.Y
|
||||
return p
|
||||
|
||||
}
|
||||
if a.X.Cmp(b.X) == 0 {
|
||||
p.X = new(big.Int)
|
||||
p.Y = new(big.Int)
|
||||
return p
|
||||
}
|
||||
resx, resy := CURVE.Add(a.X, a.Y, b.X, b.Y)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
Double returns 2*P, where P is the given elliptic curve point.
|
||||
*/
|
||||
func (p *p256) Double(a *p256) *p256 {
|
||||
if a.IsZero() {
|
||||
return p.SetInfinity()
|
||||
}
|
||||
resx, resy := CURVE.Double(a.X, a.Y)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
ScalarMul encapsulates the scalar Multiplication Algorithm from secp256k1.
|
||||
*/
|
||||
func (p *p256) ScalarMult(a *p256, n *big.Int) *p256 {
|
||||
if a.IsZero() {
|
||||
return p.SetInfinity()
|
||||
}
|
||||
cmp := n.Cmp(big.NewInt(0))
|
||||
if cmp == 0 {
|
||||
return p.SetInfinity()
|
||||
}
|
||||
n = Mod(n, CURVE.N)
|
||||
bn := n.Bytes()
|
||||
resx, resy := CURVE.ScalarMult(a.X, a.Y, bn)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
ScalarBaseMult returns the Scalar Multiplication by the base generator.
|
||||
*/
|
||||
func (p *p256) ScalarBaseMult(n *big.Int) *p256 {
|
||||
cmp := n.Cmp(big.NewInt(0))
|
||||
if cmp == 0 {
|
||||
return p.SetInfinity()
|
||||
}
|
||||
n = Mod(n, CURVE.N)
|
||||
bn := n.Bytes()
|
||||
resx, resy := CURVE.ScalarBaseMult(bn)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
Multiply actually is reponsible for the addition of elliptic curve points.
|
||||
The name here is to maintain compatibility with bn256 interface.
|
||||
This algorithm verifies if the given elliptic curve points are equal, in which case it
|
||||
returns the result of Double function, otherwise it returns the result of Add function.
|
||||
*/
|
||||
func (p *p256) Multiply(a, b *p256) *p256 {
|
||||
if a.IsZero() {
|
||||
p.X = b.X
|
||||
p.Y = b.Y
|
||||
return p
|
||||
} else if b.IsZero() {
|
||||
p.X = a.X
|
||||
p.Y = a.Y
|
||||
return p
|
||||
}
|
||||
if a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 {
|
||||
resx, resy := CURVE.Double(a.X, a.Y)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
if a.X.Cmp(b.X) == 0 {
|
||||
p.X = new(big.Int)
|
||||
p.Y = new(big.Int)
|
||||
return p
|
||||
}
|
||||
resx, resy := CURVE.Add(a.X, a.Y, b.X, b.Y)
|
||||
p.X = resx
|
||||
p.Y = resy
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
SetInfinity sets the given elliptic curve point to the point at infinity.
|
||||
*/
|
||||
func (p *p256) SetInfinity() *p256 {
|
||||
p.X = nil
|
||||
p.Y = nil
|
||||
return p
|
||||
}
|
||||
|
||||
/*
|
||||
String returns the readable representation of the given elliptic curve point, i.e.
|
||||
the tuple formed by X and Y coordinates.
|
||||
*/
|
||||
func (p *p256) String() string {
|
||||
return "p256(" + p.X.String() + "," + p.Y.String() + ")"
|
||||
}
|
||||
|
||||
/*
|
||||
MapToGroup is a hash function that returns a valid elliptic curve point given as
|
||||
input a string. It is also known as hash-to-point and is used to obtain a generator
|
||||
that has no discrete logarithm known relation, thus addressing the concept of
|
||||
NUMS (nothing up my sleeve).
|
||||
This implementation is based on the paper:
|
||||
Short signatures from the Weil pairing
|
||||
Boneh, Lynn and Shacham
|
||||
Journal of Cryptology, September 2004, Volume 17, Issue 4, pp 297–319
|
||||
*/
|
||||
func MapToGroup(m string) (*p256, error) {
|
||||
var (
|
||||
i int
|
||||
buffer bytes.Buffer
|
||||
)
|
||||
i = 0
|
||||
for i < 256 {
|
||||
buffer.Reset()
|
||||
buffer.WriteString(strconv.Itoa(i))
|
||||
buffer.WriteString(m)
|
||||
x, _ := HashToInt(buffer)
|
||||
x = Mod(x, CURVE.P)
|
||||
fx, _ := F(x)
|
||||
fx = Mod(fx, CURVE.P)
|
||||
y := fx.ModSqrt(fx, CURVE.P)
|
||||
if y != nil {
|
||||
p := &p256{X: x, Y: y}
|
||||
if p.IsOnCurve() && !p.IsZero() {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
return nil, errors.New("Failed to Hash-to-point.")
|
||||
}
|
||||
|
||||
/*
|
||||
F receives a big integer x as input and return x^3 + 7 mod ORDER.
|
||||
*/
|
||||
func F(x *big.Int) (*big.Int, error) {
|
||||
// Compute x^2
|
||||
x3p7 := Multiply(x, x)
|
||||
x3p7 = Mod(x3p7, CURVE.P)
|
||||
// Compute x^3
|
||||
x3p7 = Multiply(x3p7, x)
|
||||
x3p7 = Mod(x3p7, CURVE.P)
|
||||
// Compute X^3 + 7
|
||||
x3p7 = Add(x3p7, new(big.Int).SetInt64(7))
|
||||
x3p7 = Mod(x3p7, CURVE.P)
|
||||
return x3p7, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Hash is responsible for the computing a Zp element given the input string.
|
||||
*/
|
||||
func HashToInt(b bytes.Buffer) (*big.Int, error) {
|
||||
digest := sha256.New()
|
||||
digest.Write(b.Bytes())
|
||||
output := digest.Sum(nil)
|
||||
tmp := output[0:len(output)]
|
||||
return byteconversion.FromByteArray(tmp)
|
||||
}
|
||||
|
||||
/*
|
||||
IsOnCurve returns TRUE if and only if p has coordinates X and Y that satisfy the
|
||||
Elliptic Curve equation: y^2 = x^3 + 7.
|
||||
*/
|
||||
func (p *p256) IsOnCurve() bool {
|
||||
// y² = x³ + 7
|
||||
y2 := new(big.Int).Mul(p.Y, p.Y)
|
||||
y2.Mod(y2, CURVE.P)
|
||||
|
||||
x3 := new(big.Int).Mul(p.X, p.X)
|
||||
x3.Mul(x3, p.X)
|
||||
|
||||
x3.Add(x3, new(big.Int).SetInt64(7))
|
||||
x3.Mod(x3, CURVE.P)
|
||||
|
||||
return x3.Cmp(y2) == 0
|
||||
}
|
82
src/ConfidentialTx/zkproofs/p256_test.go
Normal file
82
src/ConfidentialTx/zkproofs/p256_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
"math/big"
|
||||
"github.com/ing-bank/zkproofs/go-ethereum/crypto/secp256k1"
|
||||
)
|
||||
|
||||
const TestCount = 1000
|
||||
|
||||
func TestIsZero(t *testing.T) {
|
||||
curve := secp256k1.S256()
|
||||
a := make([]byte, 32)
|
||||
a = curve.N.Bytes()
|
||||
Ax, Ay := curve.ScalarBaseMult(a)
|
||||
p1 := p256{X:Ax, Y:Ay}
|
||||
res := p1.IsZero()
|
||||
if res != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t * testing.T) {
|
||||
curve := secp256k1.S256()
|
||||
a1 := new(big.Int).SetInt64(71).Bytes()
|
||||
A1x, A1y := curve.ScalarBaseMult(a1)
|
||||
p1 := &p256{X:A1x, Y:A1y}
|
||||
a2 := new(big.Int).SetInt64(17).Bytes()
|
||||
A2x, A2y := curve.ScalarBaseMult(a2)
|
||||
p2 := &p256{X:A2x, Y:A2y}
|
||||
p3 := p1.Add(p1, p2)
|
||||
sa := new(big.Int).SetInt64(-88).Bytes()
|
||||
sAx, sAy := curve.ScalarBaseMult(sa)
|
||||
sp := &p256{X:sAx, Y:sAy}
|
||||
p4 := p3.Add(p3, sp)
|
||||
res := p4.IsZero()
|
||||
if res != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarMultp256(t *testing.T) {
|
||||
curve := secp256k1.S256()
|
||||
a1 := new(big.Int).SetInt64(71).Bytes()
|
||||
Ax, Ay := curve.ScalarBaseMult(a1)
|
||||
p1 := &p256{X:Ax, Y:Ay}
|
||||
pr := p1.ScalarMult(p1, curve.N)
|
||||
res := pr.IsZero()
|
||||
if res != true {
|
||||
t.Errorf("Assert failure: expected true, actual: %t", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarBaseMult(t *testing.T) {
|
||||
a1 := new(big.Int).SetInt64(71)
|
||||
p1 := new(p256).ScalarBaseMult(a1)
|
||||
res := p1.IsZero()
|
||||
if res != false {
|
||||
t.Errorf("Assert failure: expected false, actual: %t", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapToGroup(t *testing.T) {
|
||||
curve := secp256k1.S256()
|
||||
m := "Testing Hash-to-point function:"
|
||||
p, _ := MapToGroup(m)
|
||||
p.ScalarMult(p, curve.N)
|
||||
}
|
||||
|
||||
func BenchmarkScalarMultp256(b *testing.B) {
|
||||
a := make([]byte, 32)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rand.Read(a)
|
||||
_ = new(p256).ScalarBaseMult(new(big.Int).SetBytes(a))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
183
src/ConfidentialTx/zkproofs/util.go
Normal file
183
src/ConfidentialTx/zkproofs/util.go
Normal file
@ -0,0 +1,183 @@
|
||||
// Copyright 2018 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
|
||||
"../byteconversion"
|
||||
bn256 "github.com/ethereum/go-ethereum/crypto/bn256/google"
|
||||
// "../crypto/bn256"
|
||||
)
|
||||
|
||||
//Constants that are going to be used frequently, then we just need to compute them once.
|
||||
var (
|
||||
G1 = new(bn256.G1).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||
G2 = new(bn256.G2).ScalarBaseMult(new(big.Int).SetInt64(1))
|
||||
E = bn256.Pair(G1, G2)
|
||||
)
|
||||
|
||||
/*
|
||||
Decompose receives as input a bigint x and outputs an array of integers such that
|
||||
x = sum(xi.u^i), i.e. it returns the decomposition of x into base u.
|
||||
*/
|
||||
func Decompose(x *big.Int, u int64, l int64) ([]int64, error) {
|
||||
var (
|
||||
result []int64
|
||||
i int64
|
||||
)
|
||||
result = make([]int64, l, l)
|
||||
i = 0
|
||||
for i < l {
|
||||
result[i] = Mod(x, new(big.Int).SetInt64(u)).Int64()
|
||||
x = new(big.Int).Div(x, new(big.Int).SetInt64(u))
|
||||
i = i + 1
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Commit method corresponds to the Pedersen commitment scheme. Namely, given input
|
||||
message x, and randomness r, it outputs g^x.h^r.
|
||||
*/
|
||||
func Commit(x, r *big.Int, h *bn256.G2) (*bn256.G2, error) {
|
||||
var (
|
||||
C *bn256.G2
|
||||
)
|
||||
C = new(bn256.G2).ScalarBaseMult(x)
|
||||
C.Add(C, new(bn256.G2).ScalarMult(h, r))
|
||||
return C, nil
|
||||
}
|
||||
|
||||
/*
|
||||
CommitG1 method corresponds to the Pedersen commitment scheme. Namely, given input
|
||||
message x, and randomness r, it outputs g^x.h^r.
|
||||
*/
|
||||
func CommitG1(x, r *big.Int, h *p256) (*p256, error) {
|
||||
var (
|
||||
C *p256
|
||||
)
|
||||
C = new(p256).ScalarBaseMult(x)
|
||||
Hr := new(p256).ScalarMult(h, r)
|
||||
C.Add(C, Hr)
|
||||
return C, nil
|
||||
}
|
||||
|
||||
func Mult(a *p256, n *big.Int) *p256 {
|
||||
return new(p256).ScalarMult(a, n)
|
||||
}
|
||||
|
||||
/*
|
||||
HashSet is responsible for the computing a Zp element given elements from GT and G2.
|
||||
*/
|
||||
func HashSet(a *bn256.GT, D *bn256.G2) (*big.Int, error) {
|
||||
digest := sha256.New()
|
||||
digest.Write([]byte(a.String()))
|
||||
digest.Write([]byte(D.String()))
|
||||
output := digest.Sum(nil)
|
||||
tmp := output[0:len(output)]
|
||||
return byteconversion.FromByteArray(tmp)
|
||||
}
|
||||
|
||||
/*
|
||||
Hash is responsible for the computing a Zp element given elements from GT and G2.
|
||||
*/
|
||||
func Hash(a []*bn256.GT, D *bn256.G2) (*big.Int, error) {
|
||||
digest := sha256.New()
|
||||
for i := range a {
|
||||
digest.Write([]byte(a[i].String()))
|
||||
}
|
||||
digest.Write([]byte(D.String()))
|
||||
output := digest.Sum(nil)
|
||||
tmp := output[0:len(output)]
|
||||
return byteconversion.FromByteArray(tmp)
|
||||
}
|
||||
|
||||
/*
|
||||
Read big integer in base 10 from string.
|
||||
*/
|
||||
func GetBigInt(value string) *big.Int {
|
||||
i := new(big.Int)
|
||||
i.SetString(value, 10)
|
||||
return i
|
||||
}
|
||||
|
||||
/*
|
||||
Get common base
|
||||
*/
|
||||
func GetZkrp() *Bp {
|
||||
var zkrp Bp
|
||||
zkrp.Setup(0, 4294967296)
|
||||
return &zkrp
|
||||
}
|
||||
|
||||
/*
|
||||
Get zkrp verifier
|
||||
*/
|
||||
func GetVerifier(t *big.Int, h []*p256, p *p256) *Bp {
|
||||
zkrp := GetZkrp()
|
||||
zkrp.Zkip.Cc = t
|
||||
zkrp.Zkip.Hh = h
|
||||
zkrp.Zkip.P = p
|
||||
return zkrp
|
||||
}
|
||||
|
||||
/*
|
||||
Pedersen Commitment verification, check input_sum == output_sum ?
|
||||
*/
|
||||
func VerifyPedersenCommitment(input, output []*PedersenCommitment, blindDiff *big.Int) bool {
|
||||
inputCommitment := new(PedersenCommitment)
|
||||
outputCommitment := new(PedersenCommitment)
|
||||
for _, p := range input {
|
||||
inputCommitment = inputCommitment.Add(inputCommitment, p)
|
||||
}
|
||||
for _, p := range output {
|
||||
outputCommitment = outputCommitment.Add(outputCommitment, p)
|
||||
}
|
||||
// 计算佩德森承诺输入输出之差
|
||||
diffCommitment := inputCommitment.Add(inputCommitment, outputCommitment.Neg(outputCommitment))
|
||||
// 根据盲因子计算理论值
|
||||
H, _ := MapToGroup(SEEDH)
|
||||
checkCommitment := Mult(H, blindDiff)
|
||||
// 比较是否相等
|
||||
return checkCommitment.X.Cmp(diffCommitment.X) == 0 && checkCommitment.Y.Cmp(diffCommitment.Y) == 0
|
||||
|
||||
}
|
||||
|
||||
type ProofData struct {
|
||||
Proof *proofBP
|
||||
T *big.Int
|
||||
Hh []*p256
|
||||
P *p256
|
||||
}
|
||||
|
||||
func DumpProof(t *big.Int, h []*p256, p *p256, proof *proofBP) ([]byte, error) {
|
||||
zkrproof := &ProofData{proof, t, h, p}
|
||||
data, err := json.Marshal(zkrproof)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func LoadProof(data []byte) (*Bp, *proofBP, error) {
|
||||
var p ProofData
|
||||
err := json.Unmarshal(data, &p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return GetVerifier(p.T, p.Hh, p.P), p.Proof, nil
|
||||
}
|
18
src/ConfidentialTx/zkproofs/util_test.go
Normal file
18
src/ConfidentialTx/zkproofs/util_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2018 ING Bank N.V.
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package zkproofs
|
||||
|
23
src/ERC20/contracts/Migrations.sol
Normal file
23
src/ERC20/contracts/Migrations.sol
Normal file
@ -0,0 +1,23 @@
|
||||
pragma solidity >=0.4.21 <0.6.0;
|
||||
|
||||
contract Migrations {
|
||||
address public owner;
|
||||
uint public last_completed_migration;
|
||||
|
||||
constructor() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
modifier restricted() {
|
||||
if (msg.sender == owner) _;
|
||||
}
|
||||
|
||||
function setCompleted(uint completed) public restricted {
|
||||
last_completed_migration = completed;
|
||||
}
|
||||
|
||||
function upgrade(address new_address) public restricted {
|
||||
Migrations upgraded = Migrations(new_address);
|
||||
upgraded.setCompleted(last_completed_migration);
|
||||
}
|
||||
}
|
72
src/ERC20/contracts/erc20/ERC20.sol
Normal file
72
src/ERC20/contracts/erc20/ERC20.sol
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
Implements EIP20 token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
|
||||
.*/
|
||||
|
||||
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
import "./ERC20Interface.sol";
|
||||
|
||||
|
||||
contract ERC20 is ERC20Interface {
|
||||
|
||||
uint256 constant private MAX_UINT256 = 2**256 - 1;
|
||||
mapping (address => uint256) public balances;
|
||||
mapping (address => mapping (address => uint256)) public allowed;
|
||||
/*
|
||||
NOTE:
|
||||
The following variables are OPTIONAL vanities. One does not have to include them.
|
||||
They allow one to customise the token contract & in no way influences the core functionality.
|
||||
Some wallets/interfaces might not even bother to look at this information.
|
||||
*/
|
||||
string public name; //fancy name: eg Simon Bucks
|
||||
uint8 public decimals; //How many decimals to show.
|
||||
string public symbol; //An identifier: eg SBX
|
||||
|
||||
constructor(
|
||||
uint256 _initialAmount,
|
||||
string memory _tokenName,
|
||||
uint8 _decimalUnits,
|
||||
string memory _tokenSymbol
|
||||
) public {
|
||||
balances[msg.sender] = _initialAmount; // Give the creator all initial tokens
|
||||
totalSupply = _initialAmount; // Update total supply
|
||||
name = _tokenName; // Set the name for display purposes
|
||||
decimals = _decimalUnits; // Amount of decimals for display purposes
|
||||
symbol = _tokenSymbol; // Set the symbol for display purposes
|
||||
}
|
||||
|
||||
function transfer(address _to, uint256 _value) public returns (bool success) {
|
||||
require(balances[msg.sender] >= _value);
|
||||
balances[msg.sender] -= _value;
|
||||
balances[_to] += _value;
|
||||
emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
|
||||
uint256 allowance = allowed[_from][msg.sender];
|
||||
require(balances[_from] >= _value && allowance >= _value);
|
||||
balances[_to] += _value;
|
||||
balances[_from] -= _value;
|
||||
if (allowance < MAX_UINT256) {
|
||||
allowed[_from][msg.sender] -= _value;
|
||||
}
|
||||
emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
|
||||
return true;
|
||||
}
|
||||
|
||||
function balanceOf(address _owner) public view returns (uint256 balance) {
|
||||
return balances[_owner];
|
||||
}
|
||||
|
||||
function approve(address _spender, uint256 _value) public returns (bool success) {
|
||||
allowed[msg.sender][_spender] = _value;
|
||||
emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars
|
||||
return true;
|
||||
}
|
||||
|
||||
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
|
||||
return allowed[_owner][_spender];
|
||||
}
|
||||
}
|
50
src/ERC20/contracts/erc20/ERC20Interface.sol
Normal file
50
src/ERC20/contracts/erc20/ERC20Interface.sol
Normal file
@ -0,0 +1,50 @@
|
||||
// Abstract contract for the full ERC 20 Token standard
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
|
||||
pragma solidity ^0.5.0;
|
||||
|
||||
|
||||
contract ERC20Interface {
|
||||
/* This is a slight change to the ERC20 base standard.
|
||||
function totalSupply() constant returns (uint256 supply);
|
||||
is replaced with:
|
||||
uint256 public totalSupply;
|
||||
This automatically creates a getter function for the totalSupply.
|
||||
This is moved to the base contract since public getter functions are not
|
||||
currently recognised as an implementation of the matching abstract
|
||||
function by the compiler.
|
||||
*/
|
||||
/// total amount of tokens
|
||||
uint256 public totalSupply;
|
||||
|
||||
/// @param _owner The address from which the balance will be retrieved
|
||||
/// @return The balance
|
||||
function balanceOf(address _owner) public view returns (uint256 balance);
|
||||
|
||||
/// @notice send `_value` token to `_to` from `msg.sender`
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transfer(address _to, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
|
||||
/// @param _from The address of the sender
|
||||
/// @param _to The address of the recipient
|
||||
/// @param _value The amount of token to be transferred
|
||||
/// @return Whether the transfer was successful or not
|
||||
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @notice `msg.sender` approves `_spender` to spend `_value` tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @param _value The amount of tokens to be approved for transfer
|
||||
/// @return Whether the approval was successful or not
|
||||
function approve(address _spender, uint256 _value) public returns (bool success);
|
||||
|
||||
/// @param _owner The address of the account owning tokens
|
||||
/// @param _spender The address of the account able to transfer the tokens
|
||||
/// @return Amount of remaining tokens allowed to spent
|
||||
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
|
||||
|
||||
// solhint-disable-next-line no-simple-event-func-name
|
||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||
}
|
5
src/ERC20/migrations/1_initial_migration.js
Normal file
5
src/ERC20/migrations/1_initial_migration.js
Normal file
@ -0,0 +1,5 @@
|
||||
const Migrations = artifacts.require("Migrations");
|
||||
|
||||
module.exports = function (deployer) {
|
||||
deployer.deploy(Migrations);
|
||||
};
|
5
src/ERC20/migrations/2_deploy_tokens.js
Normal file
5
src/ERC20/migrations/2_deploy_tokens.js
Normal file
@ -0,0 +1,5 @@
|
||||
const ERC20 = artifacts.require("ERC20");
|
||||
|
||||
module.exports = (deployer) => {
|
||||
deployer.deploy(ERC20, 10000, 'Simon Bucks', 1, 'SBX');
|
||||
};
|
221
src/ERC20/test/erc20.js
Normal file
221
src/ERC20/test/erc20.js
Normal file
@ -0,0 +1,221 @@
|
||||
"use strict";
|
||||
|
||||
let assertRevert = async (promise) => {
|
||||
try {
|
||||
await promise;
|
||||
} catch (error) {
|
||||
const revertFound = error.message.search('revert') >= 0;
|
||||
assert(revertFound, `Expected "revert", got ${error} instead`);
|
||||
return;
|
||||
}
|
||||
assert.fail('Expected revert not received');
|
||||
};
|
||||
|
||||
const ERC20Abstraction = artifacts.require('ERC20');
|
||||
let HST;
|
||||
|
||||
contract('ERC20', (accounts) => {
|
||||
beforeEach(async () => {
|
||||
HST = await ERC20Abstraction.new(10000, 'Simon Bucks', 1, 'SBX', { from: accounts[0] });
|
||||
});
|
||||
|
||||
it('creation: should create an initial balance of 10000 for the creator', async () => {
|
||||
const balance = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance.toNumber(), 10000);
|
||||
});
|
||||
|
||||
it('creation: test correct setting of vanity information', async () => {
|
||||
const name = await HST.name.call();
|
||||
assert.strictEqual(name, 'Simon Bucks');
|
||||
|
||||
const decimals = await HST.decimals.call();
|
||||
assert.strictEqual(decimals.toNumber(), 1);
|
||||
|
||||
const symbol = await HST.symbol.call();
|
||||
assert.strictEqual(symbol, 'SBX');
|
||||
});
|
||||
|
||||
it.skip('creation: should succeed in creating over 2^256 - 1 (max) tokens', async () => {
|
||||
// 2^256 - 1
|
||||
const HST2 = await ERC20Abstraction.new('115792089237316195423570985008687907853269984665640564039457584007913129639935', 'Simon Bucks', 1, 'SBX', { from: accounts[0] });
|
||||
const totalSupply = await HST2.totalSupply();
|
||||
const match = totalSupply.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77');
|
||||
assert(match, 'result is not correct');
|
||||
});
|
||||
|
||||
// TRANSERS
|
||||
// normal transfers without approvals
|
||||
it('transfers: ether transfer should be reversed.', async () => {
|
||||
const balanceBefore = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balanceBefore.toNumber(), 10000);
|
||||
|
||||
await assertRevert(new Promise((resolve, reject) => {
|
||||
web3.eth.sendTransaction({ from: accounts[0], to: HST.address, value: web3.utils.toWei('10', 'Ether') }, (err, res) => {
|
||||
if (err) { reject(err); }
|
||||
resolve(res);
|
||||
});
|
||||
}));
|
||||
|
||||
const balanceAfter = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balanceAfter.toNumber(), 10000);
|
||||
});
|
||||
|
||||
it('transfers: should transfer 10000 to accounts[1] with accounts[0] having 10000', async () => {
|
||||
await HST.transfer(accounts[1], 10000, { from: accounts[0] });
|
||||
const balance = await HST.balanceOf.call(accounts[1]);
|
||||
assert.strictEqual(balance.toNumber(), 10000);
|
||||
});
|
||||
|
||||
it('transfers: should fail when trying to transfer 10001 to accounts[1] with accounts[0] having 10000', async () => {
|
||||
await assertRevert(HST.transfer.call(accounts[1], 10001, { from: accounts[0] }));
|
||||
});
|
||||
|
||||
it('transfers: should handle zero-transfers normally', async () => {
|
||||
assert(await HST.transfer.call(accounts[1], 0, { from: accounts[0] }), 'zero-transfer has failed');
|
||||
});
|
||||
|
||||
// NOTE: testing uint256 wrapping is impossible since you can't supply > 2^256 -1
|
||||
// todo: transfer max amounts
|
||||
|
||||
// APPROVALS
|
||||
it('approvals: msg.sender should approve 100 to accounts[1]', async () => {
|
||||
await HST.approve(accounts[1], 100, { from: accounts[0] });
|
||||
const allowance = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance.toNumber(), 100);
|
||||
});
|
||||
|
||||
// bit overkill. But is for testing a bug
|
||||
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 once.', async () => {
|
||||
const balance0 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance0.toNumber(), 10000);
|
||||
|
||||
await HST.approve(accounts[1], 100, { from: accounts[0] }); // 100
|
||||
const balance2 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
|
||||
|
||||
await HST.transferFrom.call(accounts[0], accounts[2], 20, { from: accounts[1] });
|
||||
await HST.allowance.call(accounts[0], accounts[1]);
|
||||
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] }); // -20
|
||||
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance01.toNumber(), 80); // =80
|
||||
|
||||
const balance22 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance22.toNumber(), 20);
|
||||
|
||||
const balance02 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance02.toNumber(), 9980);
|
||||
});
|
||||
|
||||
// should approve 100 of msg.sender & withdraw 50, twice. (should succeed)
|
||||
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 20 twice.', async () => {
|
||||
await HST.approve(accounts[1], 100, { from: accounts[0] });
|
||||
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance01.toNumber(), 100);
|
||||
|
||||
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
|
||||
const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance012.toNumber(), 80);
|
||||
|
||||
const balance2 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance2.toNumber(), 20);
|
||||
|
||||
const balance0 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance0.toNumber(), 9980);
|
||||
|
||||
// FIRST tx done.
|
||||
// onto next.
|
||||
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
|
||||
const allowance013 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance013.toNumber(), 60);
|
||||
|
||||
const balance22 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance22.toNumber(), 40);
|
||||
|
||||
const balance02 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance02.toNumber(), 9960);
|
||||
});
|
||||
|
||||
// should approve 100 of msg.sender & withdraw 50 & 60 (should fail).
|
||||
it('approvals: msg.sender approves accounts[1] of 100 & withdraws 50 & 60 (2nd tx should fail)', async () => {
|
||||
await HST.approve(accounts[1], 100, { from: accounts[0] });
|
||||
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance01.toNumber(), 100);
|
||||
|
||||
await HST.transferFrom(accounts[0], accounts[2], 50, { from: accounts[1] });
|
||||
const allowance012 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert.strictEqual(allowance012.toNumber(), 50);
|
||||
|
||||
const balance2 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance2.toNumber(), 50);
|
||||
|
||||
const balance0 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance0.toNumber(), 9950);
|
||||
|
||||
// FIRST tx done.
|
||||
// onto next.
|
||||
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
|
||||
});
|
||||
|
||||
it('approvals: attempt withdrawal from account with no allowance (should fail)', async () => {
|
||||
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 60, { from: accounts[1] }));
|
||||
});
|
||||
|
||||
it('approvals: allow accounts[1] 100 to withdraw from accounts[0]. Withdraw 60 and then approve 0 & attempt transfer.', async () => {
|
||||
await HST.approve(accounts[1], 100, { from: accounts[0] });
|
||||
await HST.transferFrom(accounts[0], accounts[2], 60, { from: accounts[1] });
|
||||
await HST.approve(accounts[1], 0, { from: accounts[0] });
|
||||
await assertRevert(HST.transferFrom.call(accounts[0], accounts[2], 10, { from: accounts[1] }));
|
||||
});
|
||||
|
||||
it.skip('approvals: approve max (2^256 - 1)', async () => {
|
||||
await HST.approve(accounts[1], '115792089237316195423570985008687907853269984665640564039457584007913129639935', { from: accounts[0] });
|
||||
const allowance = await HST.allowance(accounts[0], accounts[1]);
|
||||
assert(allowance.equals('1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77'));
|
||||
});
|
||||
|
||||
// should approve max of msg.sender & withdraw 20 without changing allowance (should succeed).
|
||||
it.skip('approvals: msg.sender approves accounts[1] of max (2^256 - 1) & withdraws 20', async () => {
|
||||
const balance0 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance0.toNumber(), 10000);
|
||||
|
||||
const max = '1.15792089237316195423570985008687907853269984665640564039457584007913129639935e+77';
|
||||
await HST.approve(accounts[1], max, { from: accounts[0] });
|
||||
const balance2 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance2.toNumber(), 0, 'balance2 not correct');
|
||||
|
||||
await HST.transferFrom(accounts[0], accounts[2], 20, { from: accounts[1] });
|
||||
const allowance01 = await HST.allowance.call(accounts[0], accounts[1]);
|
||||
assert(allowance01.equals(max));
|
||||
|
||||
const balance22 = await HST.balanceOf.call(accounts[2]);
|
||||
assert.strictEqual(balance22.toNumber(), 20);
|
||||
|
||||
const balance02 = await HST.balanceOf.call(accounts[0]);
|
||||
assert.strictEqual(balance02.toNumber(), 9980);
|
||||
});
|
||||
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
it('events: should fire Transfer event properly', async () => {
|
||||
const res = await HST.transfer(accounts[1], '2666', { from: accounts[0] });
|
||||
const transferLog = res.logs.find(element => element.event.match('Transfer'));
|
||||
assert.strictEqual(transferLog.args._from, accounts[0]);
|
||||
assert.strictEqual(transferLog.args._to, accounts[1]);
|
||||
assert.strictEqual(transferLog.args._value.toString(), '2666');
|
||||
});
|
||||
|
||||
it('events: should fire Transfer event normally on a zero transfer', async () => {
|
||||
const res = await HST.transfer(accounts[1], '0', { from: accounts[0] });
|
||||
const transferLog = res.logs.find(element => element.event.match('Transfer'));
|
||||
assert.strictEqual(transferLog.args._from, accounts[0]);
|
||||
assert.strictEqual(transferLog.args._to, accounts[1]);
|
||||
assert.strictEqual(transferLog.args._value.toString(), '0');
|
||||
});
|
||||
|
||||
it('events: should fire Approval event properly', async () => {
|
||||
const res = await HST.approve(accounts[1], '2666', { from: accounts[0] });
|
||||
const approvalLog = res.logs.find(element => element.event.match('Approval'));
|
||||
assert.strictEqual(approvalLog.args._owner, accounts[0]);
|
||||
assert.strictEqual(approvalLog.args._spender, accounts[1]);
|
||||
assert.strictEqual(approvalLog.args._value.toString(), '2666');
|
||||
});
|
||||
});
|
100
src/ERC20/truffle-config.js
Normal file
100
src/ERC20/truffle-config.js
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Use this file to configure your truffle project. It's seeded with some
|
||||
* common settings for different networks and features like migrations,
|
||||
* compilation and testing. Uncomment the ones you need or modify
|
||||
* them to suit your project as necessary.
|
||||
*
|
||||
* More information about configuration can be found at:
|
||||
*
|
||||
* truffleframework.com/docs/advanced/configuration
|
||||
*
|
||||
* To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider)
|
||||
* to sign your transactions before they're sent to a remote public node. Infura accounts
|
||||
* are available for free at: infura.io/register.
|
||||
*
|
||||
* You'll also need a mnemonic - the twelve word phrase the wallet uses to generate
|
||||
* public/private key pairs. If you're publishing your code to GitHub make sure you load this
|
||||
* phrase from a file you've .gitignored so it doesn't accidentally become public.
|
||||
*
|
||||
*/
|
||||
|
||||
// const HDWalletProvider = require('truffle-hdwallet-provider');
|
||||
// const infuraKey = "fj4jll3k.....";
|
||||
//
|
||||
// const fs = require('fs');
|
||||
// const mnemonic = fs.readFileSync(".secret").toString().trim();
|
||||
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
* Networks define how you connect to your ethereum client and let you set the
|
||||
* defaults web3 uses to send transactions. If you don't specify one truffle
|
||||
* will spin up a development blockchain for you on port 9545 when you
|
||||
* run `develop` or `test`. You can ask a truffle command to use a specific
|
||||
* network from the command line, e.g
|
||||
*
|
||||
* $ truffle test --network <network-name>
|
||||
*/
|
||||
|
||||
networks: {
|
||||
// Useful for testing. The `development` name is special - truffle uses it by default
|
||||
// if it's defined here and no other network is specified at the command line.
|
||||
// You should run a client (like ganache-cli, geth or parity) in a separate terminal
|
||||
// tab if you use this network and you must also set the `host`, `port` and `network_id`
|
||||
// options below to some value.
|
||||
//
|
||||
development: {
|
||||
host: "127.0.0.1", // Localhost (default: none)
|
||||
port: 8545, // Standard Ethereum port (default: none)
|
||||
network_id: "*", // Any network (default: none)
|
||||
},
|
||||
|
||||
// Another network with more advanced options...
|
||||
// advanced: {
|
||||
// port: 8777, // Custom port
|
||||
// network_id: 1342, // Custom network
|
||||
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
|
||||
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
|
||||
// from: <address>, // Account to send txs from (default: accounts[0])
|
||||
// websockets: true // Enable EventEmitter interface for web3 (default: false)
|
||||
// },
|
||||
|
||||
// Useful for deploying to a public network.
|
||||
// NB: It's important to wrap the provider as a function.
|
||||
// ropsten: {
|
||||
// provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),
|
||||
// network_id: 3, // Ropsten's id
|
||||
// gas: 5500000, // Ropsten has a lower block limit than mainnet
|
||||
// confirmations: 2, // # of confs to wait between deployments. (default: 0)
|
||||
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
|
||||
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
|
||||
// },
|
||||
|
||||
// Useful for private networks
|
||||
// private: {
|
||||
// provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
|
||||
// network_id: 2111, // This network is yours, in the cloud.
|
||||
// production: true // Treats this network as if it was a public net. (default: false)
|
||||
// }
|
||||
},
|
||||
|
||||
// Set default mocha options here, use special reporters etc.
|
||||
mocha: {
|
||||
// timeout: 100000
|
||||
},
|
||||
|
||||
// Configure your compilers
|
||||
compilers: {
|
||||
solc: {
|
||||
// version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version)
|
||||
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
|
||||
// settings: { // See the solidity docs for advice about optimization and evmVersion
|
||||
// optimizer: {
|
||||
// enabled: false,
|
||||
// runs: 200
|
||||
// },
|
||||
// evmVersion: "byzantium"
|
||||
// }
|
||||
}
|
||||
}
|
||||
};
|
20
src/basic_chain/js/Readme.md
Normal file
20
src/basic_chain/js/Readme.md
Normal file
@ -0,0 +1,20 @@
|
||||
# BlockChain for Node.js
|
||||
|
||||
Basic implementation for blockchain in Node.js
|
||||
|
||||
- [x] Block structure
|
||||
- [x] Blockchain structure
|
||||
- [x] Consensus
|
||||
- [x] PoW
|
||||
- [x] PoS
|
||||
- [x] DPoS
|
||||
- [x] Pbft
|
||||
- [x] DPoS + Pbft
|
||||
- [x] Network
|
||||
- [x] Persistence
|
||||
- [x] Transaction UTXO
|
||||
- [x] Sync
|
||||
|
||||
Tests on Ubuntu and MAC.
|
||||
|
||||
|
@ -5,13 +5,18 @@ class Account {
|
||||
this.keypair_ = keypair;
|
||||
this.id_ = id;
|
||||
this.amount_ = 0;
|
||||
if (!this.keypair_) { }
|
||||
if (!this.id_) { }
|
||||
if (!this.keypair_) {
|
||||
// load from file
|
||||
}
|
||||
if (!this.id_) {
|
||||
// load from file
|
||||
}
|
||||
}
|
||||
|
||||
get_id() { return this.id_; }
|
||||
get_key() { return this.keypair_; }
|
||||
get_amount() { return this.amount_; }
|
||||
set_amount(amount) {this.amount_ = amount;}
|
||||
}
|
||||
|
||||
module.exports = Account;
|
@ -1,14 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var crypto = require("crypto");
|
||||
var ed = require("ed25519");
|
||||
var Crypto = require("./crypto");
|
||||
|
||||
|
||||
class Block extends EventEmitter {
|
||||
constructor(data, consensus) {
|
||||
super();
|
||||
// body
|
||||
this.transcations_ = data ? data.transactions : [];
|
||||
this.transactions_ = data ? data.transactions : [];
|
||||
// header
|
||||
this.version_ = 0;
|
||||
this.height_ = data ? data.previous_block.height + 1 : -1;
|
||||
@ -37,7 +37,7 @@ class Block extends EventEmitter {
|
||||
get_timestamp() { return this.timestamp_; }
|
||||
get_signature() { return this.block_signature_; }
|
||||
get_publickey() { return this.generator_publickey_; }
|
||||
get_transcations() { return this.transcations_; }
|
||||
get_transactions() { return this.transactions_; }
|
||||
get_consensus_data() { return this.consensus_data_; }
|
||||
set_consensus_data(data) { this.consensus_data_ = data; }
|
||||
toObject() {
|
||||
@ -51,7 +51,7 @@ class Block extends EventEmitter {
|
||||
"hash": this.hash_,
|
||||
"block_signature": this.block_signature_,
|
||||
"consensus_data": this.consensus_data_,
|
||||
"transcations": this.transcations_
|
||||
"transactions": this.transactions_
|
||||
};
|
||||
return block;
|
||||
}
|
||||
@ -65,23 +65,20 @@ class Block extends EventEmitter {
|
||||
this.hash_ = data.hash;
|
||||
this.block_signature_ = data.block_signature;
|
||||
this.consensus_data_ = data.consensus_data;
|
||||
this.transcations_ = data.transactions;
|
||||
this.transactions_ = data.transactions;
|
||||
}
|
||||
|
||||
calc_hash(data) {
|
||||
return crypto.createHash('sha256').update(data).digest('hex');
|
||||
}
|
||||
calc_merkle_hash() {
|
||||
// calc merkle root hash according to the transcations in the block
|
||||
// calc merkle root hash according to the transactions in the block
|
||||
var hashes = [];
|
||||
for (var i = 0; i < this.transcations_.length; ++i) {
|
||||
hashes.push(this.calc_hash(this.transcations_.toString('utf-8')));
|
||||
for (var i = 0; i < this.transactions_.length; ++i) {
|
||||
hashes.push(Crypto.calc_hash(this.transactions_.toString('utf-8')));
|
||||
}
|
||||
while (hashes.length > 1) {
|
||||
var tmp = [];
|
||||
for (var i = 0; i < hashes.length / 2; ++i) {
|
||||
for (i = 0; i < hashes.length / 2; ++i) {
|
||||
let data = hashes[i * 2] + hashes[i * 2 + 1];
|
||||
tmp.push(this.calc_hash(data));
|
||||
tmp.push(Crypto.calc_hash(data));
|
||||
}
|
||||
if (hashes.length % 2 === 1) {
|
||||
tmp.push(hashes[hashes.length - 1]);
|
||||
@ -93,8 +90,8 @@ class Block extends EventEmitter {
|
||||
|
||||
prepare_data() {
|
||||
let tx = "";
|
||||
for (var i = 0; i < this.transcations_.length; ++i) {
|
||||
tx += this.transcations_[i].toString('utf-8');
|
||||
for (var i = 0; i < this.transactions_.length; ++i) {
|
||||
tx += this.transactions_[i].toString('utf-8');
|
||||
}
|
||||
let data = this.version_.toString()
|
||||
+ this.height_.toString()
|
||||
@ -109,11 +106,11 @@ class Block extends EventEmitter {
|
||||
}
|
||||
// calc the hash of the block
|
||||
calc_block_hash() {
|
||||
return this.calc_hash(this.prepare_data());
|
||||
return Crypto.calc_hash(this.prepare_data());
|
||||
}
|
||||
sign(keypair) {
|
||||
var hash = this.calc_block_hash();
|
||||
return ed.Sign(Buffer.from(hash, 'utf-8'), keypair).toString('hex');
|
||||
return Crypto.sign(keypair, hash);
|
||||
}
|
||||
make_proof(consensus, keypair) {
|
||||
let self = this;
|
||||
@ -128,13 +125,7 @@ class Block extends EventEmitter {
|
||||
|
||||
static verify_signature(block) {
|
||||
var hash = block.hash;
|
||||
var res = ed.Verify(Buffer.from(hash, 'utf8'), Buffer.from(block.block_signature, 'hex'), Buffer.from(block.generator_publickey, 'hex'));
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
static get_address_by_publickey(publicKey) {
|
||||
|
||||
return Crypto.verify_signature(hash, block.block_signature, block.generator_publickey);
|
||||
}
|
||||
|
||||
}
|
573
src/basic_chain/js/blockchain.js
Normal file
573
src/basic_chain/js/blockchain.js
Normal file
@ -0,0 +1,573 @@
|
||||
'use strict';
|
||||
|
||||
var Block = require("./block");
|
||||
const genesis_block = require("./genesis_block.json");
|
||||
var Node = require("./network");
|
||||
var Account = require("./account");
|
||||
var Transaction = require("./transaction").Transaction;
|
||||
var TxInput = require("./transaction").TxInput;
|
||||
var TxOutput = require("./transaction").TxOutput;
|
||||
var Msg = require("./message");
|
||||
var MessageType = require("./message").type;
|
||||
var Promise = require("bluebird");
|
||||
var level = require("level");
|
||||
var Crypto = require("./crypto");
|
||||
|
||||
var Pbft = require("./consensus/pbft");
|
||||
let pbft = false;
|
||||
class BlockChain {
|
||||
constructor(Consensus, keypair, id, is_bad = false) {
|
||||
// todo
|
||||
this.pending_block_ = {};
|
||||
this.tx_pool = {};
|
||||
// this.chain_ = [];
|
||||
|
||||
this.is_bad_ = is_bad;
|
||||
this.pbft_ = new Pbft(this);
|
||||
|
||||
// ///////////////////////////////////////
|
||||
this.genesis_block_ = genesis_block;
|
||||
|
||||
|
||||
this.account_ = new Account(keypair, id);
|
||||
this.consensus_ = new Consensus(this);
|
||||
this.node_ = null;
|
||||
}
|
||||
async start() {
|
||||
this.db_ = level(`/tmp/data_${this.get_account_id()}`);
|
||||
try {
|
||||
// load blocks
|
||||
let last = await this.db_.get("last_block");
|
||||
this.last_block_ = JSON.parse(last);
|
||||
console.log(`node: ${this.get_account_id()} last block: ${this.last_block_.height}`);
|
||||
} catch (err) {
|
||||
// empty chain
|
||||
this.last_block_ = genesis_block;
|
||||
this.save_last_block();
|
||||
console.log(`node: ${this.get_account_id()} empty`);
|
||||
}
|
||||
|
||||
this.node_ = new Node(this.get_account_id());
|
||||
this.node_.on("message", this.on_data.bind(this));
|
||||
this.node_.start();
|
||||
// start loop
|
||||
var self = this;
|
||||
setTimeout(function next_loop() {
|
||||
self.loop(function () {
|
||||
setTimeout(next_loop, 1000);
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
loop(cb) {
|
||||
let self = this;
|
||||
if (this.consensus_.prepared()) {
|
||||
if (!self.is_bad_) {
|
||||
this.generate_block(this.get_account_keypair(), () => {
|
||||
// broadcast block
|
||||
let block = self.get_last_block();
|
||||
console.log(`node: ${self.get_account_id()} generate block! block height: ${block.height} hash: ${block.hash}`);
|
||||
});
|
||||
} else {
|
||||
self.fork();
|
||||
}
|
||||
}
|
||||
cb();
|
||||
}
|
||||
|
||||
async save_block(block) {
|
||||
if (!block)
|
||||
block = this.last_block_;
|
||||
// query from db via hash
|
||||
// if not exist, write into db, else do nothing
|
||||
if (this.pending_block_[block.hash]) {
|
||||
delete this.pending_block_[block.hash];
|
||||
}
|
||||
await this.db_.put(block.hash, JSON.stringify(block));
|
||||
await this.db_.put("last_block", JSON.stringify(block));
|
||||
// console.log(`save block: ${block.hash} to db`);
|
||||
|
||||
// tx
|
||||
if (!block.transactions) {
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < block.transactions.length; ++i) {
|
||||
let tx = block.transactions[i];
|
||||
if (this.tx_pool[tx.id]) {
|
||||
delete this.tx_pool[tx.id];
|
||||
// console.log(`node ${this.get_account_id()} delete tx ${tx.id}`);
|
||||
}
|
||||
await this.db_.put(tx.id, JSON.stringify(tx));
|
||||
}
|
||||
}
|
||||
async save_last_block() {
|
||||
await this.save_block();
|
||||
}
|
||||
|
||||
generate_block(keypair, cb) {
|
||||
// load transactions
|
||||
var tx = [this.create_coinbase()];
|
||||
var i = 0;
|
||||
for (let key in this.tx_pool) {
|
||||
if (i == 10)
|
||||
break;
|
||||
tx.push(this.tx_pool[key]);
|
||||
i++;
|
||||
console.log(`node ${this.get_account_id()} load tx ${key}`);
|
||||
}
|
||||
// create block
|
||||
let block = new Block({
|
||||
"keypair": keypair,
|
||||
"previous_block": this.last_block_,
|
||||
"transactions": tx
|
||||
}, this.consensus_);
|
||||
// make proof of the block/mine
|
||||
let self = this;
|
||||
block.on('block completed', (data) => {
|
||||
if (data.previous_hash == self.last_block_.hash &&
|
||||
data.height == self.last_block_.height + 1) {
|
||||
// console.log("block completed");
|
||||
self.commit_block(data);
|
||||
|
||||
self.broadcast(Msg.block(data));
|
||||
|
||||
if (cb) cb();
|
||||
} else {
|
||||
// [fork]
|
||||
self.process_fork(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
commit_block(block_data) {
|
||||
if (pbft && !this.is_bad_) {
|
||||
var block = new Block();
|
||||
block.set_data(block_data);
|
||||
let self = this;
|
||||
block.on('consensus completed', (data) => {
|
||||
self.last_block_ = data;
|
||||
self.save_last_block();
|
||||
});
|
||||
this.pbft_.make_consensus(block);
|
||||
|
||||
} else {
|
||||
this.last_block_ = block_data;
|
||||
this.save_last_block();
|
||||
}
|
||||
}
|
||||
get_height() {
|
||||
return this.last_block_.height;
|
||||
}
|
||||
async get_from_db(hash) {
|
||||
// query block with hash value
|
||||
try {
|
||||
let block_data = await this.db_.get(hash);
|
||||
let block = JSON.parse(block_data);
|
||||
return block;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async iterator_back(cb, hash) {
|
||||
if (!hash) {
|
||||
return;
|
||||
}
|
||||
let block = await this.get_from_db(hash);
|
||||
let res = cb(block);
|
||||
if (res)
|
||||
await this.iterator_back(cb, block.previous_hash);
|
||||
}
|
||||
async iterator_forward(cb, hash) {
|
||||
if (!hash) {
|
||||
return;
|
||||
}
|
||||
let block = await this.get_from_db(hash);
|
||||
await this.iterator_forward(cb, block.previous_hash);
|
||||
cb(block);
|
||||
}
|
||||
get_last_block() {
|
||||
return this.last_block_;
|
||||
}
|
||||
get_genesis_block() {
|
||||
return this.generate_block_;
|
||||
}
|
||||
get_amount() {
|
||||
// get the amount of the account
|
||||
return this.account_.get_amount();
|
||||
}
|
||||
get_account_id() {
|
||||
// get the node id
|
||||
return this.account_.get_id();
|
||||
}
|
||||
get_account_keypair() {
|
||||
return this.account_.get_key();
|
||||
}
|
||||
get_public_key() {
|
||||
return this.get_account_keypair().publicKey.toString('hex');
|
||||
}
|
||||
send_msg(socket, data) {
|
||||
this.node_.send(socket, data);
|
||||
}
|
||||
broadcast(data) {
|
||||
this.node_.broadcast(data);
|
||||
}
|
||||
list_peers() {
|
||||
return this.node_.list_peers();
|
||||
}
|
||||
sync() {
|
||||
let peers = this.list_peers();
|
||||
let index = Math.floor(Math.random() * peers.length);
|
||||
let id = peers[index];
|
||||
this.send_msg(parseInt(id), Msg.sync({ "id": this.get_account_id() }));
|
||||
}
|
||||
async verify_transaction(tx) {
|
||||
let input_amount = 0;
|
||||
for (var i = 0; i < tx.input.length; ++i) {
|
||||
let input = tx.input[i];
|
||||
// coinbase
|
||||
if (input.id == null) {
|
||||
// check milestone
|
||||
if (tx.output[0].amount == 50) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let vout = null;
|
||||
if (this.tx_pool[input.id]) {
|
||||
vout = this.tx.tx_pool[input.id];
|
||||
} else {
|
||||
vout = await this.get_from_db(input.id);
|
||||
}
|
||||
if (!vout) {
|
||||
// invalid vout
|
||||
return false;
|
||||
}
|
||||
vout = vout.output[input.index];
|
||||
let res = Crypto.verify_signature(JSON.stringify(vout), input.ScriptSig, vout.ScriptPubKey);
|
||||
if (!res) {
|
||||
return false;
|
||||
}
|
||||
input_amount += vout.amount;
|
||||
}
|
||||
let output_amount = 0;
|
||||
for (i = 0; i < tx.output.length; ++i) {
|
||||
output_amount += tx.output[i].amount;
|
||||
}
|
||||
if (input_amount < output_amount) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// verify the block is valid
|
||||
async verify(block) {
|
||||
// verify the block signature
|
||||
if (!Block.verify_signature(block))
|
||||
return false;
|
||||
// verify consensus
|
||||
if (!this.consensus_.verify(block)) {
|
||||
// [fork] slot
|
||||
this.save_block(block);
|
||||
return false;
|
||||
}
|
||||
// verify transactions
|
||||
let tx = block.transactions;
|
||||
if (tx) {
|
||||
for (var i = 0; i < tx.length; ++i) {
|
||||
try {
|
||||
if (await this.db_.get(tx[i].id)) {
|
||||
// [fork] transaction exists
|
||||
return false;
|
||||
}
|
||||
} catch (err) {
|
||||
// nothing
|
||||
}
|
||||
if (!await this.verify_transaction(tx[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
process_fork(block) {
|
||||
if (block.previous_hash != this.last_block_.hash &&
|
||||
block.height == this.last_block_.height + 1) {
|
||||
// [fork] right height and different previous block
|
||||
this.save_block(block);
|
||||
|
||||
} else if (block.previous_hash == this.last_block_.hash &&
|
||||
block.height == this.last_block_.height &&
|
||||
block.hash != this.last_block_.hash) {
|
||||
// [fork] same height and same previous block, but different block id
|
||||
this.save_block(block);
|
||||
}
|
||||
}
|
||||
async on_data(msg) {
|
||||
switch (msg.type) {
|
||||
case MessageType.Block:
|
||||
{
|
||||
let block = msg.data;
|
||||
// console.log(`node: ${this.get_account_id()} receive block: height ${block.height}`);
|
||||
// check if exist
|
||||
let query = await this.get_from_db(block.hash);
|
||||
if (this.pending_block_[block.hash] || query) {
|
||||
// console.log("block already exists");
|
||||
return;
|
||||
}
|
||||
// verify
|
||||
if (!await this.verify(block)) {
|
||||
// console.log("verify failed");
|
||||
return;
|
||||
}
|
||||
|
||||
this.pending_block_[block.hash] = block;
|
||||
|
||||
// add to chain
|
||||
if (block.previous_hash == this.last_block_.hash &&
|
||||
block.height == this.last_block_.height + 1) {
|
||||
// console.log("on block data");
|
||||
this.commit_block(block);
|
||||
// console.log("----------add block");
|
||||
} else {
|
||||
// [fork]
|
||||
this.process_fork(block);
|
||||
}
|
||||
// broadcast
|
||||
this.broadcast(msg);
|
||||
}
|
||||
break;
|
||||
case MessageType.Transaction:
|
||||
{
|
||||
// check if exist(pending or in chain) verify, store(into pending) and broadcast
|
||||
let tx = msg.data;
|
||||
if (this.tx_pool[tx.id]) {
|
||||
// already exists
|
||||
return;
|
||||
}
|
||||
this.tx_pool[tx.id] = tx;
|
||||
// verify transaction
|
||||
let res = await this.verify_transaction(tx);
|
||||
if (!res) {
|
||||
delete this.tx_pool[tx.id];
|
||||
} else {
|
||||
// console.log(`node ${this.get_account_id()} store tx ${tx.id}`);
|
||||
}
|
||||
|
||||
// broadcast
|
||||
this.broadcast(msg);
|
||||
}
|
||||
break;
|
||||
case MessageType.Sync:
|
||||
{
|
||||
console.log(`${this.get_account_id()} receive sync info`);
|
||||
let data = msg.data;
|
||||
let id = data.id;
|
||||
if (data.hash) {
|
||||
let block = await this.get_from_db(data.hash);
|
||||
this.send_msg(id, Msg.sync_block({ "id": this.get_account_id(), "block": block }));
|
||||
console.log(`---> ${this.get_account_id()} send sync block: ${block.height}`);
|
||||
|
||||
} else {
|
||||
this.send_msg(id, Msg.sync_block({ "id": this.get_account_id(), "last_block": this.last_block_ }));
|
||||
console.log(`---> ${this.get_account_id()} send sync last block: ${this.last_block_.height}`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MessageType.SyncBlock:
|
||||
{
|
||||
let data = msg.data;
|
||||
let id = data.id;
|
||||
let block = null;
|
||||
if (data.hasOwnProperty("last_block")) {
|
||||
block = data.last_block;
|
||||
this.last_block_ = block;
|
||||
console.log(`++++ ${this.get_account_id()} change last block: ${block.height}`);
|
||||
} else {
|
||||
block = data.block;
|
||||
}
|
||||
console.log(`<--- ${this.get_account_id()} receive sync block: ${block.height}\n`);
|
||||
|
||||
this.save_block(block);
|
||||
let hash = block.previous_hash;
|
||||
let res = null;
|
||||
if (hash) {
|
||||
res = await this.get_from_db(hash);
|
||||
}
|
||||
if (!res) {
|
||||
console.log(`---> ${this.get_account_id()} continue sync hash: ${hash}`);
|
||||
this.send_msg(id, Msg.sync({ "id": this.get_account_id(), "hash": hash }));
|
||||
} else {
|
||||
console.log(`==== ${this.get_account_id()} complete syning!`);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (pbft && !this.is_bad_) {
|
||||
this.pbft_.processMessage(msg);
|
||||
} else {
|
||||
console.log("unkown msg");
|
||||
console.log(msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// print() {
|
||||
// // todo chain_
|
||||
// let output = '';
|
||||
// for (var i = 0; i < this.chain_.length; ++i) {
|
||||
// let height = this.chain_[i].height;
|
||||
// let hash = this.chain_[i].hash.substr(0, 6);
|
||||
// let generator_id = this.chain_[i].consensus_data.generator_id;
|
||||
// if (generator_id == undefined) generator_id = null;
|
||||
// output += `(${height}:${hash}:${generator_id}) -> `;
|
||||
// }
|
||||
// console.log(`node: ${this.get_account_id()} ${output}`);
|
||||
// }
|
||||
// async fork() {
|
||||
// console.log('----------fork----------');
|
||||
// // load transactions
|
||||
// var tx1 = [{
|
||||
// amount: 1000,
|
||||
// recipient: 'bob',
|
||||
// sender: 'alice'
|
||||
// }];
|
||||
// // create block
|
||||
// let block1 = new Block({
|
||||
// "keypair": this.get_account_keypair(),
|
||||
// "previous_block": this.last_block_,
|
||||
// "transactions": tx1
|
||||
// }, this.consensus_);
|
||||
// // make proof of the block/mine
|
||||
// let self = this;
|
||||
// let block_data1 = await new Promise((resolve, reject) => {
|
||||
// block1.on('block completed', (data) => {
|
||||
// if (data.height == self.last_block_.height + 1) {
|
||||
// resolve(data);
|
||||
// } else {
|
||||
// reject('block1 failed');
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// // load transactions
|
||||
// var tx2 = [{
|
||||
// amount: 1000,
|
||||
// recipient: 'cracker',
|
||||
// sender: 'alice'
|
||||
// }];
|
||||
// // create block
|
||||
// let block2 = new Block({
|
||||
// "keypair": this.get_account_keypair(),
|
||||
// "previous_block": this.last_block_,
|
||||
// "transactions": tx2
|
||||
// }, this.consensus_);
|
||||
// let block_data2 = await new Promise((resolve, reject) => {
|
||||
// block2.on('block completed', (data) => {
|
||||
// if (data.height == self.last_block_.height + 1) {
|
||||
// resolve(data);
|
||||
// } else {
|
||||
// reject('block2 failed');
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
// var i = 0;
|
||||
// for (var id in this.node_.peers_) {
|
||||
// let socket = this.node_.peers_[id];
|
||||
// if (i % 2 == 0) {
|
||||
// var msg1 = Msg.block(block_data1);
|
||||
// this.node_.send(socket, msg1);
|
||||
// } else {
|
||||
// var msg2 = Msg.block(block_data2);
|
||||
// this.node_.send(socket, msg2);
|
||||
// }
|
||||
// i++;
|
||||
// }
|
||||
// console.log("fork");
|
||||
// this.commit_block(block_data1);
|
||||
// }
|
||||
create_coinbase() {
|
||||
let input = new TxInput(null, -1, `${new Date()} node: ${this.get_account_id()} coinbase tx`);
|
||||
let output = new TxOutput(50, this.get_public_key());
|
||||
let tx = new Transaction([input], [output]);
|
||||
return tx;
|
||||
}
|
||||
|
||||
async get_utxo(cb) {
|
||||
let publicKey = this.get_public_key();
|
||||
let spentTXOs = {};
|
||||
await this.iterator_back((block) => {
|
||||
let txs = block.transactions;
|
||||
// tx
|
||||
for (var i = 0; i < txs.length; ++i) {
|
||||
let tx = txs[i];
|
||||
let transaction_id = tx.id;
|
||||
// output
|
||||
for (var j = 0; j < tx.output.length; ++j) {
|
||||
let output = tx.output[j];
|
||||
// owns
|
||||
if (output.ScriptPubKey == publicKey) {
|
||||
// not spent
|
||||
if (spentTXOs.hasOwnProperty(transaction_id) &&
|
||||
spentTXOs[transaction_id].hasOwnProperty(j)) {
|
||||
continue;
|
||||
} else {
|
||||
if (!cb(transaction_id, j, output)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// input
|
||||
for (j = 0; j < tx.input.length; ++j) {
|
||||
let input = tx.input[j];
|
||||
// not coinbase
|
||||
if (input.id != null && input.index != -1) {
|
||||
if (!spentTXOs[input.id]) {
|
||||
spentTXOs[input.id] = [];
|
||||
}
|
||||
spentTXOs[input.id].push(input.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
this.get_last_block().hash);
|
||||
}
|
||||
async get_balance() {
|
||||
let value = 0;
|
||||
await this.get_utxo((transaction_id, index, vout) => {
|
||||
value += vout.amount;
|
||||
return true;
|
||||
});
|
||||
return value;
|
||||
}
|
||||
async create_transaction(to, amount) {
|
||||
let value = 0;
|
||||
let input = [];
|
||||
let output = [];
|
||||
let self = this;
|
||||
let tx = null;
|
||||
await this.get_utxo((transaction_id, index, vout) => {
|
||||
value += vout.amount;
|
||||
let signature = Crypto.sign(self.get_account_keypair(), JSON.stringify(vout));
|
||||
input.push(new TxInput(transaction_id, index, signature));
|
||||
if (value >= amount) {
|
||||
output.push(new TxOutput(amount, to));
|
||||
if (value > amount)
|
||||
output.push(new TxOutput(value - amount, self.get_public_key()));
|
||||
tx = new Transaction(input, output);
|
||||
// stop
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (value < amount) {
|
||||
throw new Error("amount is not enough!");
|
||||
}
|
||||
if (tx == null) {
|
||||
throw new Error("create transaction failed!");
|
||||
}
|
||||
this.tx_pool[tx.id] = tx;
|
||||
this.broadcast(Msg.transaction(tx));
|
||||
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlockChain;
|
23
src/basic_chain/js/crypto.js
Normal file
23
src/basic_chain/js/crypto.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require("crypto");
|
||||
var ed = require("ed25519");
|
||||
|
||||
function calc_hash(data) {
|
||||
return crypto.createHash('sha256').update(data).digest('hex');
|
||||
}
|
||||
|
||||
function sign(keypair, data) {
|
||||
return ed.Sign(Buffer.from(data, 'utf-8'), keypair).toString('hex');
|
||||
}
|
||||
|
||||
function verify_signature(data, signature, publickey) {
|
||||
var res = ed.Verify(Buffer.from(data, 'utf-8'), Buffer.from(signature, 'hex'), Buffer.from(publickey, 'hex'));
|
||||
return res;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
calc_hash,
|
||||
sign,
|
||||
verify_signature
|
||||
};
|
@ -8,5 +8,5 @@
|
||||
"hash": "d611edb9fd86ee234cdc08d9bf382330d6ccc721cd5e59cf2a01b0a2a8decfff",
|
||||
"block_signature": "603b61b14348fb7eb087fe3267e28abacadf3932f0e33958fb016ab60f825e3124bfe6c7198d38f8c91b0a3b1f928919190680e44fbe7289a4202039ffbb2109",
|
||||
"consensus_data": {},
|
||||
"transcations": []
|
||||
"transactions": []
|
||||
}
|
@ -3,10 +3,12 @@
|
||||
var MessageType = {
|
||||
Connection: 0,
|
||||
Block: 1,
|
||||
Transcation: 2,
|
||||
Transaction: 2,
|
||||
PrePrepare: 3,
|
||||
Prepare: 4,
|
||||
Commit: 5
|
||||
Commit: 5,
|
||||
Sync: 6,
|
||||
SyncBlock: 7
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@ -15,5 +17,8 @@ module.exports = {
|
||||
block: (data) => { return { type: MessageType.Block, data: data }; },
|
||||
preprepare: (data) => { return { type: MessageType.PrePrepare, data: data }; },
|
||||
prepare: (data) => { return { type: MessageType.Prepare, data: data }; },
|
||||
commit: (data) => { return { type: MessageType.Commit, data: data }; }
|
||||
commit: (data) => { return { type: MessageType.Commit, data: data }; },
|
||||
transaction: (data) => { return { type: MessageType.Transaction, data: data }; },
|
||||
sync: (data) => { return { type: MessageType.Sync, data: data }; },
|
||||
sync_block: (data) => { return { type: MessageType.SyncBlock, data: data }; }
|
||||
};
|
@ -33,6 +33,11 @@ class Node extends EventEmitter {
|
||||
socket.on('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
socket.on('error', function (e) {
|
||||
resolve();
|
||||
});
|
||||
socket.setEncoding('utf8');
|
||||
socket.on('data', (data) => { self.on_data(data, socket); });
|
||||
});
|
||||
// console.log(`id: ${self.id_} connected to remote_id: ${remote_id}`);
|
||||
let data = Msg.connection(self.id_);
|
||||
@ -72,6 +77,9 @@ class Node extends EventEmitter {
|
||||
|
||||
}
|
||||
send(socket, data) {
|
||||
if (typeof socket === 'number') {
|
||||
socket = this.peers_[socket];
|
||||
}
|
||||
if (typeof data === 'object') {
|
||||
data = JSON.stringify(data);
|
||||
}
|
@ -9,7 +9,8 @@
|
||||
"ed25519": "0.0.4",
|
||||
"eslint": "^5.13.0",
|
||||
"eslint-plugin-html": "^5.0.0",
|
||||
"js-sha256": "^0.9.0"
|
||||
"js-sha256": "^0.9.0",
|
||||
"level": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
@ -18,12 +18,12 @@ let genesis = {
|
||||
"hash": null,
|
||||
"block_signature": null,
|
||||
"consensus_data": {},
|
||||
"transcations": []
|
||||
"transactions": []
|
||||
};
|
||||
|
||||
function prepare_data() {
|
||||
let tx = "";
|
||||
genesis.transcations.forEach(val => {
|
||||
genesis.transactions.forEach(val => {
|
||||
tx += val.toString('utf8');
|
||||
});
|
||||
let data = genesis.version.toString()
|
34
src/basic_chain/js/test/db.js
Normal file
34
src/basic_chain/js/test/db.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require('crypto');
|
||||
var ed = require('ed25519');
|
||||
var BlockChain = require("../blockchain");
|
||||
var Consensus = require("../consensus/dpos");
|
||||
|
||||
var password = 'I am tester!';
|
||||
|
||||
var hash = crypto.createHash('sha256').update(password).digest();
|
||||
var keypair = ed.MakeKeypair(hash);
|
||||
|
||||
let blockchains = [];
|
||||
for (var i = 0; i < 20; ++i) {
|
||||
let blockchain = new BlockChain(Consensus, keypair, i);
|
||||
blockchain.start();
|
||||
blockchains.push(blockchain);
|
||||
}
|
||||
|
||||
// setTimeout(() => {
|
||||
// for (var i = 0; i < 20; ++i) {
|
||||
// console.log(`${i} --> ${blockchains[i].list_peers()}`);
|
||||
// }
|
||||
// }, 3000);
|
||||
|
||||
setTimeout(async () => {
|
||||
console.log("=================");
|
||||
await blockchains[0].iterator_forward((block) => {
|
||||
console.log("-----------------");
|
||||
console.log(block.height);
|
||||
console.log(block.hash);
|
||||
return true;
|
||||
}, blockchains[0].get_last_block().hash);
|
||||
}, 5000);
|
41
src/basic_chain/js/test/sync.js
Normal file
41
src/basic_chain/js/test/sync.js
Normal file
@ -0,0 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require('crypto');
|
||||
var ed = require('ed25519');
|
||||
var BlockChain = require("../blockchain");
|
||||
var Consensus = require("../consensus/dpos");
|
||||
|
||||
|
||||
let blockchains = [];
|
||||
for (var i = 0; i < 20; ++i) {
|
||||
|
||||
var password = `I am tester ${i}!`;
|
||||
var hash = crypto.createHash('sha256').update(password).digest();
|
||||
var keypair = ed.MakeKeypair(hash);
|
||||
console.log(`node ${i} address: ${keypair.publicKey.toString('hex')}`);
|
||||
|
||||
let blockchain = new BlockChain(Consensus, keypair, i);
|
||||
blockchain.start();
|
||||
blockchains.push(blockchain);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
for (var i = 0; i < blockchains.length; ++i) {
|
||||
console.log(`${i} --> ${blockchains[i].list_peers()}`);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
blockchains[19].sync();
|
||||
}, 3000);
|
||||
|
||||
|
||||
// async function get_balance() {
|
||||
// let amount = await blockchains[0].get_balance();
|
||||
// console.log(`node 0 balance: ${amount}`);
|
||||
// amount = await blockchains[6].get_balance();
|
||||
// console.log(`node 6 balance: ${amount}`);
|
||||
// }
|
||||
|
||||
// setInterval(get_balance, 10000);
|
40
src/basic_chain/js/test/transaction.js
Normal file
40
src/basic_chain/js/test/transaction.js
Normal file
@ -0,0 +1,40 @@
|
||||
'use strict';
|
||||
|
||||
var crypto = require('crypto');
|
||||
var ed = require('ed25519');
|
||||
var BlockChain = require("../blockchain");
|
||||
var Consensus = require("../consensus/dpos");
|
||||
|
||||
|
||||
let blockchains = [];
|
||||
for (var i = 0; i < 20; ++i) {
|
||||
|
||||
var password = `I am tester ${i}!`;
|
||||
var hash = crypto.createHash('sha256').update(password).digest();
|
||||
var keypair = ed.MakeKeypair(hash);
|
||||
console.log(`node ${i} address: ${keypair.publicKey.toString('hex')}`);
|
||||
|
||||
let blockchain = new BlockChain(Consensus, keypair, i);
|
||||
blockchain.start();
|
||||
blockchains.push(blockchain);
|
||||
}
|
||||
|
||||
// setTimeout(() => {
|
||||
// for (var i = 0; i < 20; ++i) {
|
||||
// console.log(`${i} --> ${blockchains[i].list_peers()}`);
|
||||
// }
|
||||
// }, 3000);
|
||||
|
||||
setTimeout(() => {
|
||||
let address = blockchains[6].get_public_key();
|
||||
blockchains[0].create_transaction(address, 30);
|
||||
}, 3000);
|
||||
|
||||
async function get_balance() {
|
||||
let amount = await blockchains[0].get_balance();
|
||||
console.log(`node 0 balance: ${amount}`);
|
||||
amount = await blockchains[6].get_balance();
|
||||
console.log(`node 6 balance: ${amount}`);
|
||||
}
|
||||
|
||||
setInterval(get_balance, 10000);
|
64
src/basic_chain/js/transaction.js
Normal file
64
src/basic_chain/js/transaction.js
Normal file
@ -0,0 +1,64 @@
|
||||
'use strict';
|
||||
var Crypto = require("./crypto");
|
||||
|
||||
class TxOutput {
|
||||
constructor(amount, ScriptPubKey) {
|
||||
this.amount_ = amount;
|
||||
this.script_pubkey_ = ScriptPubKey;
|
||||
}
|
||||
toObject() {
|
||||
let output = {
|
||||
"amount": this.amount_,
|
||||
"ScriptPubKey": this.script_pubkey_
|
||||
};
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
class TxInput {
|
||||
constructor(id, index, ScriptSig) {
|
||||
this.id_ = id;
|
||||
this.index_ = index;
|
||||
this.script_sig_ = ScriptSig;
|
||||
}
|
||||
toObject() {
|
||||
let input = {
|
||||
"id": this.id_,
|
||||
"index": this.index_,
|
||||
"ScriptSig": this.script_sig_
|
||||
};
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
class Transaction {
|
||||
constructor(input, output) {
|
||||
this.input_ = [];
|
||||
for (i = 0; i < input.length; ++i) {
|
||||
this.input_.push(input[i].toObject());
|
||||
}
|
||||
this.output_ = [];
|
||||
for (var i = 0; i < output.length; ++i) {
|
||||
this.output_.push(output[i].toObject());
|
||||
}
|
||||
this.id_ = Crypto.calc_hash(JSON.stringify(this.input_) + JSON.stringify(this.output_));
|
||||
return this.toObject();
|
||||
}
|
||||
get_id() { return this.id_; }
|
||||
get_input() { return this.input_; }
|
||||
get_output() { return this.output_; }
|
||||
toObject() {
|
||||
let tx = {
|
||||
"id": this.id_,
|
||||
"input": this.input_,
|
||||
"output": this.output_
|
||||
};
|
||||
return tx;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
TxOutput,
|
||||
TxInput,
|
||||
Transaction
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
# BlockChain for Node.js
|
||||
|
||||
Basic implementation for blockchain in node.js, supporting pow, pos, pbft, dpos and pbft+dpos.
|
||||
|
||||
Tests passed on Ubuntu and MAC.
|
||||
|
||||
```
|
||||
npm install
|
||||
|
||||
node test/xxx.js
|
||||
```
|
@ -1,284 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var Block = require("./block");
|
||||
const genesis_block = require("./genesis_block.json");
|
||||
var Node = require("./network");
|
||||
var Account = require("./account");
|
||||
var Transcation = require("./transcation");
|
||||
var Msg = require("./message");
|
||||
var MessageType = require("./message").type;
|
||||
var Promise = require("bluebird");
|
||||
|
||||
var Pbft = require("./consensus/pbft");
|
||||
let pbft = true;
|
||||
class BlockChain {
|
||||
constructor(Consensus, keypair, id, is_bad = false) {
|
||||
// todo
|
||||
this.pending_block_ = {};
|
||||
this.chain_ = [];
|
||||
|
||||
this.is_bad_ = is_bad;
|
||||
this.pbft_ = new Pbft(this);
|
||||
|
||||
// ///////////////////////////////////////
|
||||
this.genesis_block_ = genesis_block;
|
||||
this.last_block_ = genesis_block;
|
||||
this.save_last_block();
|
||||
|
||||
this.account_ = new Account(keypair, id);
|
||||
this.consensus_ = new Consensus(this);
|
||||
this.node_ = null;
|
||||
}
|
||||
start() {
|
||||
this.node_ = new Node(this.get_account_id());
|
||||
this.node_.on("message", this.on_data.bind(this));
|
||||
this.node_.start();
|
||||
// start loop
|
||||
var self = this;
|
||||
setTimeout(function next_loop() {
|
||||
self.loop(function () {
|
||||
setTimeout(next_loop, 1000);
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
loop(cb) {
|
||||
let self = this;
|
||||
if (this.consensus_.prepared()) {
|
||||
if (!self.is_bad_) {
|
||||
this.generate_block(this.get_account_keypair(), () => {
|
||||
// broadcast block
|
||||
let block = self.get_last_block();
|
||||
console.log(`node: ${self.get_account_id()} generate block! block height: ${block.height} hash: ${block.hash}`);
|
||||
});
|
||||
} else {
|
||||
self.fork();
|
||||
}
|
||||
}
|
||||
cb();
|
||||
}
|
||||
|
||||
save_last_block() {
|
||||
// query from db via hash
|
||||
// if not exist, write into db, else do nothing
|
||||
// todo(tx is also need to store?)
|
||||
if (this.pending_block_[this.last_block_.hash]) {
|
||||
delete this.pending_block_[this.last_block_.hash];
|
||||
}
|
||||
this.chain_.push(this.last_block_);
|
||||
}
|
||||
generate_block(keypair, cb) {
|
||||
// load transcations
|
||||
var tx = [];
|
||||
// create block
|
||||
let block = new Block({
|
||||
"keypair": keypair,
|
||||
"previous_block": this.last_block_,
|
||||
"transactions": tx
|
||||
}, this.consensus_);
|
||||
// make proof of the block/mine
|
||||
let self = this;
|
||||
block.on('block completed', (data) => {
|
||||
if (data.height == self.last_block_.height + 1) {
|
||||
console.log("block completed");
|
||||
self.commit_block(data);
|
||||
|
||||
self.broadcast(Msg.block(data));
|
||||
|
||||
if (cb) cb();
|
||||
} else {
|
||||
// fork or store into tmp
|
||||
console.log('fork');
|
||||
// todo
|
||||
self.pending_block_[data.hash] = data;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
commit_block(block_data) {
|
||||
if (pbft && !this.is_bad_) {
|
||||
var block = new Block();
|
||||
block.set_data(block_data);
|
||||
let self = this;
|
||||
block.on('consensus completed', (data) => {
|
||||
self.last_block_ = data;
|
||||
self.save_last_block();
|
||||
});
|
||||
this.pbft_.make_consensus(block);
|
||||
|
||||
} else {
|
||||
this.last_block_ = block_data;
|
||||
this.save_last_block();
|
||||
}
|
||||
}
|
||||
get_height() {
|
||||
return this.last_block_.height;
|
||||
}
|
||||
get_block(hash) {
|
||||
// query block with hash value
|
||||
// todo
|
||||
for (var i = 0; i < this.chain_.length; ++i) {
|
||||
if (this.chain_[i] == hash) {
|
||||
return this.chain_[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
get_last_block() {
|
||||
return this.last_block_;
|
||||
}
|
||||
get_genesis_block() {
|
||||
return this.generate_block_;
|
||||
}
|
||||
get_amount() {
|
||||
// get the amount of the account
|
||||
return this.account_.get_amount();
|
||||
}
|
||||
get_account_id() {
|
||||
// get the node id
|
||||
return this.account_.get_id();
|
||||
}
|
||||
get_account_keypair() {
|
||||
return this.account_.get_key();
|
||||
}
|
||||
broadcast(data) {
|
||||
this.node_.broadcast(data);
|
||||
}
|
||||
list_peers() {
|
||||
return this.node_.list_peers();
|
||||
}
|
||||
// verify the block is valid
|
||||
verify(block) {
|
||||
// verify the block signature
|
||||
if (!Block.verify_signature(block))
|
||||
return false;
|
||||
// verify consensus
|
||||
if (!this.consensus_.verify(block))
|
||||
return false;
|
||||
// verify transcations
|
||||
let tx = block.transcations;
|
||||
for (var i = 0; i < tx.length; ++i) {
|
||||
// todo (check tx is exist and valid)
|
||||
if (!Transcation.verify(tx[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
on_data(msg) {
|
||||
switch (msg.type) {
|
||||
case MessageType.Block:
|
||||
{
|
||||
let block = msg.data;
|
||||
// console.log(`node: ${this.get_account_id()} receive block: height ${block.height}`);
|
||||
// check if exist
|
||||
if (this.pending_block_[block.hash] || this.get_block(block.hash))
|
||||
return;
|
||||
// verify
|
||||
if (!this.verify(block))
|
||||
return;
|
||||
|
||||
this.pending_block_[block.hash] = block;
|
||||
|
||||
// add to chain
|
||||
if (block.height == this.last_block_.height + 1) {
|
||||
// console.log("on block data");
|
||||
this.commit_block(block);
|
||||
// console.log("----------add block");
|
||||
} else {
|
||||
// fork or store into tmp
|
||||
// console.log('fork');
|
||||
// todo
|
||||
}
|
||||
// broadcast
|
||||
this.broadcast(msg);
|
||||
}
|
||||
break;
|
||||
case MessageType.Transcation:
|
||||
{
|
||||
// check if exist(pending or in chain) verify, store(into pending) and broadcast
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (pbft && !this.is_bad_) {
|
||||
this.pbft_.processMessage(msg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
print() {
|
||||
// todo chain_
|
||||
let output = '';
|
||||
for (var i = 0; i < this.chain_.length; ++i) {
|
||||
let height = this.chain_[i].height;
|
||||
let hash = this.chain_[i].hash.substr(0, 6);
|
||||
let generator_id = this.chain_[i].consensus_data.generator_id;
|
||||
if (generator_id == undefined) generator_id = null;
|
||||
output += `(${height}:${hash}:${generator_id}) -> `;
|
||||
}
|
||||
console.log(`node: ${this.get_account_id()} ${output}`);
|
||||
}
|
||||
async fork() {
|
||||
console.log('----------fork----------');
|
||||
// load transcations
|
||||
var tx1 = [{
|
||||
amount: 1000,
|
||||
recipient: 'bob',
|
||||
sender: 'alice'
|
||||
}];
|
||||
// create block
|
||||
let block1 = new Block({
|
||||
"keypair": this.get_account_keypair(),
|
||||
"previous_block": this.last_block_,
|
||||
"transactions": tx1
|
||||
}, this.consensus_);
|
||||
// make proof of the block/mine
|
||||
let self = this;
|
||||
let block_data1 = await new Promise((resolve, reject) => {
|
||||
block1.on('block completed', (data) => {
|
||||
if (data.height == self.last_block_.height + 1) {
|
||||
resolve(data);
|
||||
} else {
|
||||
reject('block1 failed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// load transcations
|
||||
var tx2 = [{
|
||||
amount: 1000,
|
||||
recipient: 'cracker',
|
||||
sender: 'alice'
|
||||
}];
|
||||
// create block
|
||||
let block2 = new Block({
|
||||
"keypair": this.get_account_keypair(),
|
||||
"previous_block": this.last_block_,
|
||||
"transactions": tx2
|
||||
}, this.consensus_);
|
||||
let block_data2 = await new Promise((resolve, reject) => {
|
||||
block2.on('block completed', (data) => {
|
||||
if (data.height == self.last_block_.height + 1) {
|
||||
resolve(data);
|
||||
} else {
|
||||
reject('block2 failed');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
for (var id in this.node_.peers_) {
|
||||
let socket = this.node_.peers_[id];
|
||||
if (i % 2 == 0) {
|
||||
var msg1 = Msg.block(block_data1);
|
||||
this.node_.send(socket, msg1);
|
||||
} else {
|
||||
var msg2 = Msg.block(block_data2);
|
||||
this.node_.send(socket, msg2);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
console.log("fork");
|
||||
this.commit_block(block_data1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BlockChain;
|
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
class Transcation {
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
static verify(tx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transcation;
|
Loading…
x
Reference in New Issue
Block a user