您正在查看: Surou 发布的文章

解决TortoiseGit的pull操作将Unix换行符自动转成dos换行符——$'\r': command not found

一些同学想用Windows + sub ubuntu 编译eos,在windows下使用TortoiseGit pull 后,导致sh换行符被默认转换成dos '\r',运行出错。

解决方法

对于git bash用户使用命令:

git config --global core.autocrlf false

为了保证文件的换行符是以安全的方法,避免windows与unix的换行符混用的情况,最好也加上这么一句

git config --global core.safecrlf true

对于使用TortoiseGit的同学来说可以“快捷菜单”->“Git",将autocrlf去掉勾选

PULSE - 针对EOS的开源Chrome扩展程序

PULSE - 针对EOS的Chrome扩展程序。

Pulse是EOS区块链的开源浏览器扩展。它可以轻松与自定义dApp集成,并支持各种EOS网络上的多个帐户。Pulse可用于处理所有基于EOS的令牌。人们可以从全球的钱包和交易所发送,接收或放弃EOS及其定制代币。人们可以轻松地为Pulse源代码做出贡献,甚至可以在其上创建多个扩展。开放式存储库甚至允许开发人员创建类似的DApp,如Pulse。它具有专用模式,如果启用该模式,则有助于保护连接的dApp中的敏感信息。

官网主页

https://eospulse.io/

chrome商店地址

https://chrome.google.com/webstore/detail/eos-pulse/jlnbnjlakmkkhcmjbloceddnmclmfieo

代码地址

github: https://github.com/KoinEx/pulse

备注

账户安全性自己评估,可查看开源代码,此处只做代码层级的学习推荐

易懂的比特币工作机理详解

从做局角度思考比特币,以及比特别机理的其他
应用可能

前言

原始论文:
中文版:《比特币:一种点对点的电子现金系统》
http://wenku.baidu.com/view/f26c8d916bec0975f465e236.html
英文版:《Bitcoin: A Peer-to-Peer Electronic Cash System》
http://wenku.baidu.com/view/2e3f91bb1a37f111f1855b50.html
网络上介绍比特币的文章。
https://en.bitcoin.it/wiki/Main_Page 很详细。
http://blog.codingnow.com/2011/05/bitcoin.html
http://zhiqiang.org/blog/it/technical-document-of-bitcoin.html
http://www.showmuch.com/a/20110530/233347.html
http://ivarptr.blogspot.com/2011/05/bitcoin_31.html
http://www.8btc.com
以下做详细介绍。介绍方法为,尽可能简化概念,用日常可对应的概念对照比特币技术概念。不
会很精确,但保证概念正确

一,比特币运行机理概述

本章介绍比特币世界的运行机理。比特币体系是由账簿(block),交易单,钱(bitcoin),和
交易(transactions)等几个概念组成。

1 账簿(BLOCK),账簿链,与交易单

1)账簿

比特币体系,不存在单独货币。只存在账簿(BLOCK)与交易单。每个人有自己的账户 ID。
每一次交易,获得多少钱,花多少钱,全以交易单形式记录在一个全世界唯一的账簿上。账簿
上会记录很多交易单。相当于银行交易底单。只要账簿上收支都记录清楚,这个世界不需要单独
货币也可以做交易(等于大家都刷卡交易)。
如图:

账簿有很多本。每本账簿只记录比特币全世界 10 分钟内的交易信息。每 10 分钟一个新的
账簿被产生(制造/创建)出来。所以账簿随时间流逝会不断增多。每个人每做一笔交易,某个
账簿上就会记录这笔交易单。这个记录过程是比特币软件通过网络自动完成的(黑客很难伪造)。
目前比特币世界存活了 4 年多,就有大概 24 万多本账簿了。(4 年365 天24*60 分钟/10 分钟 =
21 万本账簿)

2)账簿链

账簿本全局唯一。因为这个世界发生过的交易肯定是全局唯一的。记账记录也会是唯一的。
所有账簿本是被一根链子串起来的(双向链表---账簿链)。每个账簿里,都记录着前一个账簿
和后一个账簿的索引。知道任何一个账簿,都能顺藤摸瓜向前或者向后找到所有账簿。这也决定
了账簿记录交易的前后关系。这等于是银行的对账底单。后面会说交易的前后关系对比特币体系
是最核心最重要的事情。
如图:

比特币世界(以下简称“世界”),只有一套唯一的账簿(由于被串起来,也就是账簿链)。
在每个用户 PC 上都保存一个备份。等于每个用户都保存一份这个世界从诞生起到当下所有交易
的记录备份(全球所有用户的所有交易)。记录备份较大,比特币软件会有优化手段,这里不细
说。
每个用户都备份所有交易记录,这就是比特币没有银行的机理,因为不需要一个中心来储
存所有交易,每个人都必须要储存所有的。

3)交易单

交易单记录一笔交易的具体信息。比如付款人账号 ID,收款人账号 ID,付款金额,付款人
签字,收款人公钥等。
世界中,每隔差不多 10 分钟,就会有一个新的账簿本制造出来,添加到世界里(加到账簿
链尾部)。有了新的账簿,这十分钟里发生的交易单,就都写到这个新产生的账簿中。查看账
簿可以去这里:http://blockexplorer.com/ 输入账簿(BLOCK)的序号,从 0 到 24 万,就可
以看每个账簿里记录的交易单。 可以看到序号靠前的账簿(BLOCK)里都没有交易单。说明账簿
产生前 10 分钟里没有交易(那时候大家还根本不知道这么个玩意儿)。
世界中,先临时记录下 10 分钟里的交易。然后用这些交易单,创建一个新账簿。把这些记
录都放进去。之后这本账簿就封存作为底单。只提供查询用。每个用户账户有多少钱,都要从这
些底单中推算出来。

2 钱(比特币/bitcoin)的由来---账簿创建(挖矿)

1)钱的由来

有账簿,可以记录交易。但是世界中总要有钱可花。初始时可以花的钱怎么来的?
世界中每个用户都可以创建账簿。谁创建账簿,就给谁钱!
一个账簿被创建后,这个账簿里初始就有钱。钱归创建者所有。所以每个账簿第一条交易
单总是“世界给予创建者张三 50 元”。注意,比特币世界里没有独立的钱,只有交易单。钱都
是通过交易单体现的。钱的产生都是从每个账簿第一条交易单来的。
账簿里初始有多少钱,根据世界中已经创建账簿的数量来定。世界刚开始,创建一个账簿
有 50 元。产生到第 21 万个账簿之后,每个账簿里初始降低为 25 元。以此类推,第 42 万个账簿
创建后,账簿里初始只有 12.5 元。 但随着账簿创建数量增多,账簿里初始钱减少。最后,比特
币世界中只会有 2100 万元钱。钱初始全部是创建账簿的人所有。账簿可以不断创建下去,但是
每个新账簿里初始的钱几乎为 0 了。(可花的钱数量恒定,很像通货紧缩的世界)。
这些创建账簿的人有钱之后,可以和其他愿意兑换的人兑换,做交易。比如买 PIZZA,或者
和愿意用美元/人民币购买比特币的人换现金。

2)账簿创建/制造(挖矿)

世界先记录 10 分钟里发生的所有交易单。接着用这些交易单创建一个账簿。账簿里记录了
这些交易单。
账簿创建后,成为记录全世界 10 分钟里发生交易的永久记录本。账簿属于这个世界。不属
于创建者。但是里面的钱归创建者。
账簿制造很难。不能随意创建。极其消耗电脑时间。大家先理解为那是一个艰难的计算过
程,和扔几亿个骰子差不多。要等到几亿个骰子的数字加起来刚刚符合要求。如果网络上一个用
户扔出的骰子符合要求,还要和其他正在扔骰子的用户比较。看看谁扔的筛子更多。选出扔筛子
最多的人制造的账簿。这要买很多 CPU/显卡来计算,要花电费。是个苦差事。创建一个账簿后,
账簿里面的钱,是奖励这些创建账簿的人。也是这个世界钱的由来。账簿极难伪造,所以钱也很
难造假。比现实世界造伪币难多了。
世界中,整个世界账簿每 10 分钟产生一个的速度不会改变。不管有多少试图创建账簿的用
户在同时努力,每 10 分钟只会有一个新账簿被创建出来。这是算法决定的。算法具体的后面讲。
创建出一个新的账簿,这个用户就发了一笔小财。每 10 分钟会有一个幸运儿。
现实世界中,人们需要买采矿机,挖掘矿石卖出得到钱。比特币世界里,人们需要买来强
力 PC 和显卡/ASIC 等,来创建账簿以得到钱。所以创建账簿这件事被形象地叫做“挖矿”。只
不过不用去发现金矿位置。在比特币互联网数字世界里,2040 年前,只要有强力矿机,就能挖
出矿(创建账簿),随即获得钱(不用卖矿)。
总得来讲,账簿是算出来的,钱也是凭空产生的。之所以有人愿意花现实世界的钱去购买,
一定是有某种原因的。后面再讲。归根结底是必须有人愿意。比如类似炒股票,抄黄金。低价
买高价卖。股票是纸,黄金是用处很小的金属。之所以有人愿意买,是因为卖得出去,能换成
现实世界的物品或者货币。
提一句,挖矿到最后,账簿本身就没有比特币产出了。但是由于交易频繁,交易过程中可
能因为交易数量和交易单分支过大,会产生一些交易费。这个交易费会直接给予交易单所在账簿
的制造人。这么做是为了鼓励在 2100 万比特币挖完之后,依然有动力继续制造账簿。没有账簿
就没有比特币交易体系。账簿必须不断制造下去。否则比特币体系就完蛋了。(没钱谁还挖矿 ,
没矿这世界就坍塌,这真的不是个坑么?)

3 交易过程(TRANSACTION)

用一个例子说明比特币世界交易过程
比如,2012 年某一天,张三要给李四 10 元(单位是比特币)。用来够买李四的披萨饼。
张三要做的事,是用比特币软件向全世界宣布,“我张三给李四 10 元了”。 于是比特币世界会
生成一个交易单,向 P2P 全网广播,大致内容(不一定精确,但说明个意思):

之后此交易单记录在世界第 180000 本账簿上。 网络上每个用户都保存了这个交易记录。这就
是交易全部过程。这个交易单世界每个用户可以查询。至于张三的名字,是以一串数字代替。这
样虽然可以查询交易单,但是不知道是现实世界中谁做的交易。每个用户在比特世界只是一串数
字。用于大家匿名交易。
张三李四各用比特币软件查询自己的账户时,由于第 180000 本账簿上有一笔交易单,李四
向全网求证这个交易是否属实,如果属实,李四头上就多了 10 元。张三的账户同理,会少了 10
元。

账户余额

注意,交易单里缺少一个信息,就是张三账户有多少钱。世界中也没有银行储存张三账户
有多少钱。那么如何辨别每个人账户有多少钱,是否能够支出那么多数额? 下面讲解比特币世
界如何统计一个用户的账户余额。
统计张三余额,就是统计张三的钱从哪里来,有多少。那只有 2 个可能。1,张三创建账簿
(挖矿)里初始给的钱;2,别人给张三的。从“上一个交易单 ID”可以很容易追溯张三户头的
钱都从哪些交易里来。
比如我账户有 200 元。来源分别是:1,创建账簿获得 50;2,张三给我 100 元;3,李四给
我 50 元。 张三的 100 元是创建 2 个账簿获得。李四 50 元是王二和周七 2 人各给了李四 25. 王
二和周七的钱也有来源,比如都由刘八分别给王二和周七各 25 元。刘八的 50 元是创建账簿所得。
逐步追溯回去,每个人的钱来源都能统计清楚。那就得到了我账户的 200 元数目。
交易单关系如图(箭头是钱的走向,按箭头的反方向从下到上,是追溯的过程):

最终大家钱的来源都会追溯到账簿创建时给予创建者的钱。毕竟钱只有“创建账簿”这么
一个来源。钱在交易过程中被不断分割为不同数量,给向不同账户。钱在比特币世界中,所有的
交易走向历史全部清清楚楚。每个人账户的钱也是靠交易单中的“资金来源”追溯出来。
我如果给出 200 元。我的交易单中就要注明这 200 元的 3 个资金来源(交易单 ID):1)我
创建账簿(挖矿)获得 50 元的交易单。2)张三给我 100 元的交易单 ID。3)李四给我 50 元的
交易单 ID。
如图:

二,安全的保障

比特币体系最大的贡献和优点,是很好的解决了交易安全问题。 现实生活中,金子有假的,
钞票有假的,支票有假的,信用卡有假的。但比特币交易单没假的。
银行和基金虽然没假的,但是银行利用国家暴力机器,强行逼迫被统治的劳动人民使用它
发行的统一货币。从而达到彻底控制人民劳动所得,任意利用金融工具获得巨额利益。恣意增发
货币导致通货膨胀,货币贬值,物价增高,人民劳动成果化为流水,房价高到无法负担,或者房
市崩盘。人民居无定所。这都是由于某些金融机构,某些国家,某些集团,利用货币中央管理的
特点,进行的正确或者错误的控制与操作导致。所以在安全交易的基础上,去中心化这个想法天
生有一些令人神往的特点(是不是真的靠谱后面细说)。交易安全也是摒弃中央银行管理的基础。
没有安全,一切无从谈起。
下面简单讲述一下比特币体系如何保障用户交易安全的机制。比特币安全机制涉及到非对
称加密算法和数字签名,sha256 散列算法(hash),Hashcash 的工作量证明机制等。详细理解
机理,需要仔细阅读这些技术文献。为了做到通俗易懂,本文把这些算法全部映射到现实中日常
生活用到的概念。不保证精确,但保证概念正确。
比特币世界中,张三提交一笔交易,给李四付款 25 元。类似张三签一张支票给李四。要保
证安全,需要确认几点: 1,交易确实是张三提交的;2,张三有 25 元;3,张三签署的支票这
25 元过去没有支付过给别人。以下逐一介绍。

1,sha256(散列/hash)含义

首先我们需要一个编码方法。给任意不同的数据(字符串,字母数字组合排列等,比如账
簿内容,或者交易单内容),标记一个全世界唯一的标记(一个整数)。相同的数据,给予相同的
标记。不同的数据(字母数字,以及排列顺序等),一定给予不同的标记。
这个编码方法有个特点。对一个数据编码很容易。但是只有编码,无法反推出编码的数据。
这个特点被广泛用于比特币机制。
sha256 散列的含义:可以理解为 sha256 是一个函数。任何一串数据,送入这个函数,都会
得到一个整数。这个整数大小范围在 0 到 2^256 次方。也就是 256bit 长度的整数。相同的数据
送入此函数,会得到相同的结果。数据不同,就会得到不同的结果。等于用一个 256bit 的数,
给任意数据做编码。每个数据都有自己特定的编码。256bit 长度的这个整数非常巨大。比地球
上所有沙子的颗粒还多。所以只要送入这个函数的数据稍有不同(字母数字,以及排列顺序等),
这个数据就会得到自己独一无二的 256bit 整数编码。

2,是否交易真实 --- 数字签名

1)交易单签字

由于没有银行做保证中介,张三和李四是直接交易。现实生活中 2 个从未相识的人做交易,
会极其不靠谱。因为没有实体货币,张三声称付款给李四后,完全可以不认帐。即便现实中,张
三也可以用假币付款然后跑路。
比特币采取的方法是:让付款方用户在交易单上根据交易单内容来签字(数字签名)。并
且让收款方有办法验证这个签字的真实性(不用法院和笔迹对比专家)。
如图所示,这是 3 个交易单,代表用户 0 给用户 1 支付钱款“交易单 1”,用户 1 给用户 2
支付“交易单 2”,用户 2 给用户 3 支付的“交易单 3”:

这个签名靠“非对称加密算法”,保证付款方签的字,没人能作假,并且付款方自己必须
认帐。

2)数字签名大致描述

有必要简单介绍数字签名。
一个只有签名者自己知道的密码,作为密钥。签名者用这个密钥可以对任意数据加密。得
到一个数字签名。 同时这个签字者对全世界公开一个“公钥”,意思即为公开的钥匙。其他人
可以方便快速的用这个“公钥”解密签字,查看签字的解密后内容。如果有证据表明:解密签字
后的内容,与加密者加密的内容相符,就能证明这个内容确实是加密者加密的。比如加密者用私
钥加密了一个字符串,写着自己名字的签名。大家用公钥解开一看,便知道这个签名一定是加密
者干的。
最重要的是,比特币交易单这个签名和交易内容严格相关。一个人,用同样的私钥(印章/
手印)签署不同内容的交易单,签出来的字也会不同 。这一点是计算机算法比按手印更优越的
地方。所以一旦用户对一个交易单签字了,且被其他人验证,就有两样事情他无法抵赖:1,付
款方签字付款了;2,付款方的资金来源(包括金额)

3)对交易单的签名和验证过程

签名:

1 付款人 A 首先制作一张交易单 T2。T2 要包括收款人 B 的“公钥”(一组数字)。
2 得到 A 资金来源的上一张交易单 T1 的数据。T1 代表 A 要支付这笔钱的来源,它必然是之
前某个时刻由某人通过 T1 支付给 A 的(或者挖矿所得)。
3 利用 T1 的数据和 B 的公钥联合起来,求出一个 hash 数值 x。
4 A 用自己密钥对 x 进行加密。得到一个 A 的签名 s。
5 把这个 A 的签名 s 附加到交易单 T2。发给收款人 B。

验证:

B 为了验证这个签名。需要做以下事情:
1,得到付款人 A 的公钥,这是解密 A 签名 s 的钥匙。 付款人的公钥,会在此张交易单的
上一张交易单 T1(资金来源交易单)里。 因为 A 要付款的这笔钱,一定是之前某个时刻,由某
个人 X,通过交易单 T1 发给 A 的。 于是付款人 A 的公钥也会在上面。
2,收款人 B 解密 T2 上 A 的签名 s,得到 A 加密前的内容,是一个整数 x。
3,收款人 B 把 T1 交易单数据与 B 的公钥联合起来, 取 hash 值 y
4,检验 x 是否等于 y。如果等于,那么说明 T2 交易单有效。一定是 A 发出的,且资金来源
A 无法抵赖。因为资金来源是 T1 交易单中的支付内容数据(包括金额),已经被关联计算到 hash
值 y 中。A 既然对 y 加密 成为数字签名 s,而这个加密过程只有 A 自己能做,故而证明交易单
T2 是 A 签署支付的。
网络上没有人能签出一样的数字签名。而且如果这个世界上存在一个被你签名的交易
单,说明你确实签过这个交易单,这个事实无从抵赖。类似现实生活的指纹画押。这都是算法保
证的,不用怀疑。类似方法各大银行包括美国军方也在使用。
有交易单,上面有你的签字。证明你确实付款了,而且这个签字和交易金额直接相关。金
额也无法抵赖。于是你账户的钱就会少。耍赖反悔是没用的。相当于卡已经刷了,你再也没有能
力拿回你的钱。
你不能制作一个别人向你付款的交易单。因为只有付款的人才有能力签字。你没有对方的
密钥(印章/手印等),无法签出一张别人向你付款的交易单。
另外如上图交易单关系图所示,每个一笔钱的走向是由一系列交易单链串起来的。交易单
签名的时候,签出来的名字,不仅和一张交易单内容相关,而且还和这笔钱之前相关所有交易单
的内容都相关(签字的形成,和前一个交易单的散列值相关)。这一个签字下去,就固化了一笔
钱上所有交易单的内容,金额与之前的签字(签字也是交易单内容的一部分)。任何人可以很容
易的验证和这笔钱相关交易单链中的所有签字。这意味着:要伪造或修改一个交易单,需要把一
笔钱交易单链条中,被修改交易单后面所有交易单全部修改一遍。而交易单储存在全世界统一的
账簿中。账簿又被所有用户备份。所以比特币世界,没有任何人能够修改交易单链中间的单据 。

3,是否有足够的钱支付

前面的“交易单关系图”,说明了如何根据交易单内容的“资金来源”一项,最终回溯出
一个账户的余额是否足够支付交易单款项。一个用户是否可以伪造自己的账户余额?并且签署一
个交易单,支付出本来不存在的钱?
首先,伪造挖矿是极难的。后面细说。
其次,伪造某一个交易单。让交易单的“资金来源”指向一个金额很大的大款账户交易单。
这需要伪造那个大款账户的签名。因为“资金来源”下面跟着是资金来源付款方的签名。这个签
名必须和交易单内容对应上。每个用户私有签名别人无法模仿,这是算法保证的,所以做不到 。
然后,篡改交易单,让交易单的“资金去向”是自己的小号,然后付款金额改得很大。根
据交易单关系图,金额修改得再大也没用。因为软件要从这个账户交易单一直追溯到挖矿记录 。
你篡改的交易单金额,无法正常回溯,会识做非法支付。即便你篡改了自己的软件。当别人用正
常软件回溯交易单(注意交易单是全世界统一的)时,依然会露馅。

4,是否重复支付 --- 建立全局唯一交易记录

还有一种对交易单篡改的情况,没有列出。那就是复制交易单。修改其中一个交易单的收
款人。从一个“资金来源”,重复向 2 个人付款。这就是“重复支付”。
举例:
A 挖矿所得 50 比特币后。A 给 B 支付 50 比特币换取 PIZZA。提交一个交易单“AB”。然后
复制此交易单变为“AC”,改为 A 给 C 支付 50 比特币换取 50 美元。A 把“AB”和“AC”2 个交
易单同时提交出去。
由于网络传输速度不同,B 先接到了付款交易单 AB。快递了 PIZZA。C 先接到了交易单“AC”,
支付给 A 美元。由于交易单签名是合法的。只是由于 B 和 C 所处网络地点不同,无法全面了解 A
发出交易单的全部情况和顺序。导致 A 用 50 元的余额账户支付了 100 元。 所以这种由于没有中
央服务器来决定交易时序的情况,就必须得到解决。
在银行的体系下,于比特币的付款类似在支票上签字。当我签字两张支票给 2 个收款人后,
2 个收款人会去银行兑现。银行的交易记录中,2 人的兑现必然是分前后次序的。一个人用支票
从我账户取钱时,另外一个人绝对不可能同时从我账户取钱(双重支付)。假如我账户只有 50
元。我签署了两张 50 元的支票。2 个收款人同时在 2 个银行网点兑现。银行的交易记录必然有
一笔兑现交易在前,取走 50 元后,我账户余额为 0.第二个收款人无法再从我账户提取现金。假
如银行允许同时支付,2 个用户同时兑现支票,他们俩人都会发现我账户有 50 元。于是同时提
现......比特币系统大家都是平等地位,没有银行之说,谁也说不清我是否为一笔钱签署了两张
支票。即便需要把 2 张支票的交易向全网广播,由于网速问题,整个网络也无法判断哪笔支付在
前。
禁止重复支付,是比特币技术要解决的核心问题。它的核心,就是在 P2P 网络体系下,创
建一套全世界统一且唯一的有前后次序的交易记录。以保证一笔钱的交易没有重复支付的情况。
下一章专门讲解

三,P2P 中建立全世界统一交易记录的解决方案

1,统一交易记录的意义

有一套全世界唯一的交易记录,在一笔钱被支付时,就能够查出之前这笔钱有没有进行过
支付。类似刷卡交易,卡上的钱一定是之前没有支付出去的。现实中,这是银行的统一交易单系
统保证的。钱被按照次序依次支付。比特币的支付都是靠电子交易单,如果没有统一的交易记录
和交易次序,交易单很容易复制后同时发送(修改支付对象)出去。等于同一笔钱支付了 2 次。
这个非法行为在网速不同的 P2P 网络上很难判别。

2,统一交易记录的次序---时间戳机制(TimeStamps)

统一交易记录的核心数据,除了每笔交易内容,还有每笔交易的前后次序。只要有了全世
界所有交易的前后次序。尤其是对于一笔钱支付的先后顺序。就能够防止一笔钱被支付两次。因
为一笔钱被支付过后,不可能再重复地从同样的支付人账户中支付出去。
我们知道比特币世界有一套全局统一并且唯一的账簿本集合。每本账簿记录10分钟的交易。
按照时间顺序,账簿依次排列。我们肯定不能用一段字符串标记时间,来判断账簿的前后关系 。
这种明文在没有中央节点服务器的体系下是行不通的。因为用户机器上的时间不可能都严格一
致。
我们要做的只是把账簿数据产生的前后顺序用某种算法固定和记录下来,并且可以验证。
决定账簿前后关系的机制,采用了时间戳机制(TimeStamps)。
这里时间戳的意思是对数据产生的先后次序进行标记。这个标记很难人为修改。很容易被
反复验证。在时间戳机制中,这个标记使用一个账簿数据的 hash。
如果我们使得每个账簿的内容,和前一个账簿内容相关,并且可以验证这一点。那么就可
以说明账簿的前后关系。因为如果没有前一个账簿的内容去生成后面的账簿,后一个账簿内容一
定是非法的。
举例说明:
1,把账簿 A 的全部内容当成数据,计算一个散列数值 a。
2,把这个散列数值 a 放入下一个账簿 B 里。
3,验证账簿 A 一定产生于账簿 B 前一个:找到账簿 B 里的散列数值,姑且叫 x。 计算账簿
A 内容的散列 a 。 假如 x = a ,那么结论成立。
4,继续使用账簿 B 的全部内容生成一个散列数值,放入 C,依次类推,形成账簿链条。
如图:

形成这样的链条不用依据用户机器上的时间。只根据数据产生实际的先后关系。这样的链
条一但形成,没有任何人能够修改账簿的前后关系。因为牵一发而动全身。要颠倒 2 个账簿的次
序,就要重新计算改动账簿之后的所有账簿内容。再次提示,这是 P2P 网络,所有用户都备份同
一份账簿链,修改所有用户机器上的备份数据是不可能的。哪怕修改 51%用户机器上的数据都不
可能。 而验证数据产生的次序,概念上是要靠全网用户表决的。

3,统一交易记录的产生

1)综述

银行系统使用了大量人力物力,联合暴力国家机器,来保障中央服务器的安全,也就是产
生和维护一份全球唯一的,反应交易事实和次序的交易单。
中央服务器带来的好处显而易见。互联网应用通常都基于 SERVER/CLIENT 模式。网站服务
器,网络银行服务器,游戏服务器,域名服务器等。但弊病是一旦服务器被攻击,占领,那么整
个服务体系全部沦陷崩溃。
P2P 网络没有中央节点。每个 PC 用户都是平等的。一套全世界统一的账簿,无法从一个中
央服务器中产生。这是 P2P 网络最大的特点。
作为 P2P 体系,没有中央节点是其最明显优势。但维护一套全局唯一且值得信任,不会被
攻击的数据,则没有简单易行的办法。而 P2P 节点都是平等的。全局统一交易记录只能由全部
P2P 的节点协作来产生。
假如这套交易记录能够被 P2P 上的邪恶节点进行伪造,改写。那就会导致比特币经济崩溃。
所以必须靠算法解决。不让任何人轻易修改记录。

2)解决办法 --- 全网节点协作产生

产生全局统一交易记录的解决的办法是:全体用户一起协作产生。
准确的说,是全网挖矿节点一起协作产生。
注意,“挖矿节点”,以后被简称为“节点”。
为了全体一起产生统一的交易记录和交易次序。需要每一笔交易都在全网广播。这样一来,
一笔交易就可以由全体用户都来验证是否通过。这等于是全体用户做了银行。
比如今天我在全网喊“我给张三 100 元”,全世界人都知道了。大家一起协作,把这个交易
记录到一个全局统一的交易记录账簿中。
到了明天我要抵赖不给张三钱,是不行的。同样,假如我给张三钱的时候如果同时喊“我
这笔钱同时再给李四”,或者过了一天我又喊“我昨天的那笔钱 100 元再给张三一次”, 也是不
行的。
原因是:我手里没有实际的钱币,钱都是写在交易单上,交易单由大家维护,全世界唯一,
备份保存在所有用户机器上,扣钱的时候,张三会问全世界人是不是有这么一笔交易存在。这个
时候,我要抵赖,只有通过强行修改这个世界上所有 P2P 节点上的交易单。显然这不可能。
也就是说,一个中央银行,变为了所有用户都维护一套中央银行数据。大家一起维护这个
数据。随时更新。一起决定某个交易是不是合法。
要集体维护一个统一的交易记录不是一件容易的事情。这可以解释为什么这个世界要每 10
分钟创建一个账簿。为何账簿创建(挖矿)如此困难。为何 10 分钟内的交易单要打包到一个账
簿。为何全局唯一的账簿要用一个链子串起来。
这些全都是因为要解决比特世界中交易的时序问题。以及防止这个机制被作弊者破坏。因
为比特币交易是基于算法安全,而不是基于信任,法律,集权,强迫,惩罚等手段。

3)P2P 诚实用户节点创建账簿(交易记录)

由于交易单是全网络广播。每个用户都可以得到所有交易单。其实,只要一个诚实的用户,
根据接收到交易单的次序,创建的交易单记录,就可以给大家作为全局统一的账簿。账簿作为对
交易单的永久封存。
P2P 上不同的诚实节点,也许某一时刻收到的交易单的顺序不一样。但这没有关系。如果
我有 100 块,先买 20 块的 PIZZA 还是先买 50 块的手表,都是花钱。只要我的钱足够,我的付
款交易就有效。如果我先花掉一些钱,账户变空,又收到一些钱,再花掉这些钱。这就对交易的
顺序记录有所要求。由于交易单都是可以回溯的,并且一笔钱相关的交易单也都串成一条链。一
个诚实节点只用等待所有交易单发送到自己机器上,进行记录便可。交易单本身就知道交易顺序
(每个交易单都会记录这笔支付款的资金来源交易单索引)。
所以,创建账簿的问题,其实归结为:如何判定一个制造账簿的用户是诚实的。这就需要
全网诚实节点协作了。
确认一个节点是诚实的之后,就采纳这个节点创建的账簿。作为全世界统一的账簿作为交
易记录。每个比特币用户都复制一份这个账簿的数据到自己机器上。

4,诚实P2P挖矿节点判定

1)hashcash 的工作量证明

hashcash 机制大致描述如下(http://www.hashcash.org):
如果希望判定一个人提供的的信息是本着正常使用,具备一定价值的。那么我们倾向认为
提供这个信息的人,愿意为此付出一定工作量来证明他的诚实。假如有一种机制,能够容易的证
明提供信息的人为此付出了一定工作量,那么此信息是可以接受,并被认为合理的。
比如,我收邮件的时候,做了一个规定:“把邮件内容数据,加入一个随机数,求一个 sha256
散列数值。这个散列值一共 256bit 。前 20bit 必须都为 0”. 这样,要给我发信的人,就必须反复尝试一个随机数,以保证邮件内容数据加上这个随机
数,能够产生 sha256 的结果------前 20bit 都是 0.
如何产生出指定要求的整数?完全靠运气和 CPU 运算时间。这就是一个工作量。工作本身
毫无意义。但是如果谁愿意付出这个工作量,就意味着他给我的邮件多半是有意义的。这就叫“工
作量证明”。也就是意味着这个人很有可能是诚实的。
这个机制被广泛用于防止垃圾邮件等。因为群发垃圾邮件的人,不可能有那么多时间去给
每个人算一个毫无意义的数字,浪费时间,降低发垃圾邮件的效率。

2)比特币的工作量证明.

比特币系统最棒的做法产生于此:把工作量证明与建立全局统一交易记录结合起来
一个 P2P 用户节点如果要试图创建一个被全网认可的新账簿(挖矿节点),要花很大力气做
一些毫无意义的运算(挖矿)。运算结果可以被所有人容易的证明他确实做了这些工作。得到结
果的同时,他就创建了一个数据块,这个数据块里面可以放入一些经过这个用户节点检验的交易
单。这些交易单有严格的顺序。由于他创建这个数据块花费了很大力气,所以多半他是诚实用
户,这个数据块也许值得信任。里面的交易单的真实性也就值得信任。于是这个数据块就有可能
作为账簿被全网接受。
这就是比特币最核心的工作机理:依靠“工作量证明”来创建存放交易单的账簿。
下面介绍比特币中如何进行“工作量证明”:
为了证明我是一个诚实的用户,我需要做一个数字游戏。 目的是猜出一个随机数。
游戏的规则是:
1,得到这个世界中已经创建好的账簿链中最后一个账簿,用这个账簿内容做数据,计算一
个 hash 值
2,不断接收这个世界被广播出来,且没有被放入账簿链的交易单。检验这些交易单(根据
交易单链信息和支付款项等信息),剔除掉不合理的(比如账户余额不足的支付)
3,猜一个幸运随机数 n(比如从 0 一直到 999999.... )
4,把 1-3 步骤得到的数据都组织起来成为一个数据 buffer,送入 sha256,得到一个 256bit
的散列值 x
5,检查 x 这个整数,前面若干 bit(比如 96bit)是否都是 0?如果是,这个 x 符合“工作
量证明难度”么? 如果符合,那么“工作量证明”游戏结束!
6,如果不是,从步骤 2 开始不断重复。假如这个时候收到了一个其他节点发来的新的账簿
数据块。 还没有猜出满足要求的随机数,需要重新开始游戏。
“工作量证明难度”:有一个本地难度标准。这个标准是一个浮点数,可以换算为一个 256bit
的整数。算出的 sha256 散列值 x 必须小于这个难度数字。
这种重复性的计算往往要重复上亿次,才会得到一个幸运随机数。
游戏如果及时结束,我们就得到了一个幸运随机数 n , 以及一组未放入世界统一账簿集里
的交易单! 这个随机数 n 代表了我为了证明我的诚实做出的努力。
到此为止,一个“工作量证明”结束。 我证明了我是个诚实的用户。
不过,一个用户最终被判定为诚实用户并且能够创建出账簿,还要靠另外一个 P2P 竞争机
制。我们在下面“账簿的创建”中讲解。

5,账簿的创建,以及重复支付检验

得到幸运数字并不等于能够成功创建账簿。最终判定需要得到全网节点的认可。

1)全网账簿创建速度控制 --- 10分钟一个

前面说过,得到幸运随机数后,计算的 sha256 值要经过难度检测。看计算出的散列值是否
小于某个“工作量证明难度值”。
比特币依靠这个难度数字,来进行全网账簿创建的速度调整。每过一段时间,节点都会检
测新账簿的创建是否符合 10 分钟一个。 如果不符合,则调整这个难度数字,使得下一个账簿
创建的时间或延长或缩短,让这个世界的账簿创建速度始终保持 10 分钟一个。
这个机制屏蔽了日益增快的硬件计算速度带来的影响。把摩尔定律闭之门外。

2)创建临时账簿,打包广播

当我得到了一个幸运随机数,意味着我只是获得了一个创建临时账簿的权利。我马上把以
下数据打包成一个账簿数据块:
1,sha256 算出的 x,作为这个数据块 ID!这个 ID 前面几十 bit 全部是 0.
2,做游戏时收集到的所有经过我检验的交易单,
3,幸运随机数 n,
4,世界账簿链中最后一个账簿的 hash 值
5,账簿需要的其他信息
如图:

账簿数据块被我在全网广播。意思是:“大家看,我算出了一个幸运随机数,大家把我创建
的账簿加入到世界统一账簿链中”

3)检测重复支付

当由节点创建出来的临时账簿在全网广播后,每个收到账簿的节点都要判断这个数据块中
的交易单都是合法的。并且,根据已经创建的世界账簿链中交易单,逐一查找,以判断这个临时
账簿内所有的交易单,在世界中已经发生的所有交易中从来没有出现过。这点很重要,判定是否
有重复支付,就在这个环节完成。
当所有节点检测一个临时账簿没有重复支出,交易单都合法后,节点会把收到的这个账簿,
临时挂接到本地备份的世界账簿链的最后。

4)账簿链分支判断,最终创建账簿

在一个节点进行工作量证明的同时,全网其他希望创建账簿的挖矿节点(要知道创建账簿
成功是会有钱的!),也在做同样的事情------不断接收网上广播的交易单,根据这些交易数据,计
算出幸运随机数。然后打包成临时账簿,广播出来。
全网到处广播着被新创建出来的临时账簿。有时候一个节点会先后收到连接在同一个链尾
部的多个新创建临时账簿。
这时候,一个节点在本地会把自己维护的本地账簿链建立分支。比如一个节点先后收到 2
个临时账簿块。它们两个同位于世界账簿链的最后一个账簿链表的后面。 于是在本地数据的账
簿链上,生成 2 个分支(两根链条)。之后此节点继续在先收到的账簿分支后面进行工作量证明
的工作------利用先收到的临时账簿,进行创建下一个账簿的工作。
每个节点收到新建临时账簿的时间不同,在本地建立的账簿链分支也会不同。
当节点收到最长的工作链账簿时,会抛弃掉比较短的分支。转为在最长的链条上工作。
注意,这里所谓“最长的工作量”,是指计算难度最大的工作链。 计算难度由一个节点本
地的难度系数控制。这个难度系数随着随机数计算速度而改变。每个账簿块里面会记录一个难度
系数。
举例如图:

绿色为世界第一块创建的账簿(创世纪账簿)。 黑色方块为这个世界最长的账簿链。也是
最终被全网所有用户确认为世界统一账簿。并在本地备份。 灰色的方块代表某个节点临时工作
的本地账簿链分支。当某个分支收到新账簿而延长后,另外一条短的分支会被抛弃。里面的交易
单会被拿出来检查,如果存在没有放入现存账簿的交易单,会继续用作新工作量证明的素材。
这条账簿链形成的步骤如下:
1,先后收到连接在同一个尾部的 2 个不同临时账簿,都以绿色上面的黑块作为前一个账簿。
打分支

2,右边账簿收到了后续账簿,抛弃左边分支

3,右边收到了连接在同一个尾部的 2 个不同临时账簿

4,先收到了链接在右支路的账簿块

5,又收到了 2 个链接在左边支路的账簿块,这时 2 个分支还是一样长,都无法抛弃

6,新支路上收到一块,彻底抛弃最右边的 2 个灰块

7,左路又收到一块

8,最终左路又收到一块,成为最长链,

9,假如左路继续收到账簿块链接,则右路彻底会被抛弃

临时账簿被不断创建,广播。当全网节点全部确认一个共同的难度总和最大的账簿链条,
并开始在这个链条之后开始工作量证明,这个账簿链中的账簿,就被最终确认为世界统一账簿链。
里面记录的交易单,就作为世界统一交易,被永久封存。
创建账簿的过程,也就是检测交易单正确性和顺序的过程,也是保存交易单的过程。同时
它还是创建比特币的过程。创建账簿的机制是比特币的核心。
由于全网节点一起在不停的计算,创建新账簿。所以账簿链增长最长的分支,代表了全网
用户做出的最大工作量证明(每一个黑快代表一个用户得到了一个工作量证明随机数)。凡是在这
个最长链中的账簿,就被全网所有用户认为是最诚实,最可信的。
换句话说,是全网络诚实的节点一起工作,进行工作量证明的工作。并且全网还要一起来
判别,哪个账簿工作在最长账簿链上,就代表了最诚实的账簿创建。

5)全网协作与竞争,每10分钟唯一的最诚实赢家

P2P 网络是个完全平等的网络。你可以挖矿创建账簿得到钱。我也可以。在同一时间,也
许全球网络中有无数个账簿被创建出来。但是比特币世界依靠前面账簿创建的难度机制,以及最
大工作量分支机制,限制了每 10 分钟只有一个账簿会被确认。因为账簿是全局唯一,每 10 分
钟的交易单和交易单次序当然只能放入唯一的账簿中被大家承认。这就保证了全网统一的交易记
录。
PC 电脑速度越来越快,计算那个幸运随机数的速度也越来越快。这个世界账簿的创建不能
受到越来越快电脑硬件的影响。于是引入工作量难度机制,电脑越快,难度系数会被调整得越高。
电脑计算出幸运随机数的速度就都慢下来了。
全网所有挖矿节点都在争相用“工作量证明“创建数据块。每十分钟只有一名幸运的用户
从竞争中脱颖而出。这种竞争保证了全网不断增加的账簿链中,难度最大的分支代表工作量最大
累计的证明。一个用户一秒钟内做了更多的 sha256 运算,说明他越诚恳,越诚实。于是他产生
的账簿的难度系数可能越高,就越有可能添加到最长的账簿链分支上。最长分支代表了最大工作
量证明,从而被全网节点认可。简单说,如果我算得快,在最长链上工作,增加这条链的难度总
和就大,就越有可能竞争过其他对手。把自己工作的链变成最长。但我尽管算得比别人快,但我
的账簿产生速度并不会很快。因为我计算难度大。所以还是很可能被其他节点抢先算出下一个账
簿块。从而加到最长链上。
由于大家创建账簿块的速度差不多,而我创建账簿的难度系数大,所以在竞争中我会比较
有优势。
这就是靠协作与竞争,判断出最诚实,最卖力气的一些节点。它们创建出的账簿被全网确
认账簿的几率就大。从而会得到更多的钱。
由于这些机制保证,所以伪造一个新账簿,从而为自己账号加钱,非常困难。因为你要作
弊,就需要算出一个合法的账簿 ID。这个 ID 要得到全网承认,必须在 10 分钟内所有竞争对手
中脱颖而出。想竞争过全网络的所有挖矿节点,你的计算能力必须要比他们加起来的计算量还要
大!

6,交易确认过程

到此为止,介绍完全部比特币工作机理。下面描述一个完整的交易确认过程,交易单的确
认,是靠账簿的不断创建和确认来进行。过程如下
1,新交易单广播到全 P2P 网络
2,挖矿节点收集所有的新有效交易单,放入一个新数据块
3,节点开始根据新数据块内容结合老数据块散列,计算一个符合要求的随机数,试图产生一个
新的账簿 -------比特币工作量证明
4,如果一个节点找到了幸运随机数,表明创建了一个账簿。马上广播到其他节点
5,其他节点开始验证这个新账簿的有效性,检测账簿内的交易单都是新的,之前没有重复支付
过。
6,如果其他节点验证此账簿有效(账簿链上账簿创建难度之和最大),则承认它。这个账簿算
正式挂接到全局账簿链后。然后开始在此账簿后面继续创建新账簿
一个交易单,要想被最终确认。需要首先被放入一个新成功创建的账簿。然后,再经历几
个(一般是 5 个)新账簿的创建后,这个交易单才被最终确认安全通过。表明支付成功。
因为往往世界账簿链被增加 6 个之后,账簿链被修改和作弊的可能性已经几乎降为 0 。这个时候确认交易成功是绝对可靠的。这么做背后的原理是,作弊者无法找到足以抗衡网络所
有诚实节点计算能力的计算机集群,去强力计算持续在世界账簿链上增加 6 个假账簿。
一旦一个交易单被所有节点确认通过,再想修改和取消,几乎是不可能的。这保证了比特
币体系的不可逆转性和不可更改性。

7,保证账簿合法性机制详解

1)P2P所有网络用户监督交易,保存全局统一交易记录备份

交易用投票进行,而全体用户的交易记录和客户端无法同时被篡改。保证数据安全性。
其次,P2P 网络不接受非法交易。比如我伪造一个新交易单,挖矿所得 5000 元。 这种交
易单,在 P2P 的其他网络用户客户端上,无法通过鉴定。也就无法支付我伪造的 5000 元。

2)时间戳保证交易顺序,无法修改账簿链

交易顺序的保障,制止了电子货币的重复支付。
利用时间戳机制,创建新账簿要包含一个”上个账簿数据散列值“,是为了把所有账簿创
建都联系起来。新的账簿创建总是依赖于老的账簿数据。这个机制保证了这个世界的账簿是一环
扣一环创建的。每一环的数据都决定了下一个账簿 ID 的产生。所以,即确定了账簿产生的顺序。
也保证没有人能够孤立地改变其中一个账簿内交易单的数据而不被检查出来。
因为如果改变了账簿链中的一个账簿内交易单的数据。这个账簿的散列值就一定会变化。
账簿散列值变化,一定会引起这个账簿之后创建的所有账簿的 ID 号发生变化!要改变这个作弊
账簿之后所有账簿的 ID,我们都知道,不可能。因为全网用户机器上都有一个账簿链备份。

3)sha256 保证创建合法账簿极难,检验账簿合法性极其容易

创建一个有效账簿异常困难,但检查一个账簿是否有效非常简单快速,把一个账簿的当初
创建时用到的数据送入 sha256 函数重新计算一次,必然得到此账簿的 ID。所以每次交易,每个
用户都可以使用全世界统一的账簿链,通过里面的交易单,追溯钱的来龙去脉,彻底搞清历史交
易清单。从而保证自己的交易安全。

4)非对称加密保证无法伪造别人支付给作弊者的交易单

无论我怎么修改交易单,或者新添加一张伪造交易单,我都无法伪造其他人的签名。也就
无法从别人的账户偷来钱。

5)工作量证明机制,保证数量占优的诚实节点产生的统一交易记录内容与次序真实

前面的机制,把伪造挖矿,伪造他人支付,伪造非法交易单,伪造交易次序,重复支付等
情况完全杜绝。
作弊的方式在比特比体系中,只有很少几种可能,比如改变刚刚发生的交易单,试图拿回
刚支付出去的钱。也就是抹掉类似向全世界喊的“我支付给张三 1000 元”这样的交易。
要这么作弊,就要试图创建假的账簿和交易单。附加在已有账簿链的最后。然后在假账簿
后面持续制造新的假账簿,以维持世界统一账簿链,不被诚实节点创建的账簿所冲掉(假账簿链
分支难度和变为短分支,被丢弃)。这需要作弊的节点制造的账簿能够被全网判定为最长分支上
的账簿。这需要比全网计算能力还要强的运算能力。或者掠夺诚实网络节点的一半以上成为作弊
节点。
下面详细解释对交易单和账簿的作弊是如何被避免的。
做个假账簿也需要链接到世界账簿链最后,形成连续的假账簿链。比如我在第 240000 个账
簿之后要产生一个假账簿,链接到已存在账簿链最后。假账簿里面删除掉我向世界喊的“我支付
给张三 1000 元”这个交易单。由于一个交易确定,需要在交易发生之后,世界上产生 6 个被全
网认可的新账簿。所以我需要连续生成 6 个假账簿,里面不包含“我支付张三 1000”的交易单。
要这么做,我必须生成 6 一个难度系数很高的工作量证明随机数。然后把假账簿打包广播
出去。其他诚实节点一定会产生含有“我支付张三 1000 元“交易单的账簿。我的 6 个假账簿必
须在全网挖矿节点竞争中连续胜出。这需要我的账簿制作的难度系数比所有诚实节点产生账簿的
难度系数之和都高。
之所以要比其他诚实节点制作账簿难度之和要高,是因为,我作弊生成假账簿的同时,全
网都在共同协力生成新账簿。一个账簿要被认可,需要从 10 分钟内全网竞争中产生。
考虑极端情况,可能有的节点,在收到我广播的假账簿之前,就已经把其他诚实节点的账
簿都串接到了账簿链上(由于网络延时,且诚实节点间没有竞争)。 这样,我的账簿制作难度必
须比这些诚实账簿制作难度之和要大。这样才能在假账簿链分支的长度上领先诚实节点创建的分
支。由于之后不能让任何一个含有“我支付张三 1000 元”的账簿出现在世界统一账簿链中。所
以我需要再制作 5 个假账簿。继续保持我创建账簿的难度系数,比全网全部的诚实节点创建账
簿的难度之和要大。
这需要比全网络所有节点总和还要大的计算能力。而代价只是我收回了刚刚 10 分钟内支付
的几笔交易。这完全不现实,也不划算。
如果有了比全网节点计算力还强大的电脑,我应该持续做诚实节点挖矿,这个收益会比作
弊要强。从动机上也避免了这种作弊发生。

四,从“做局”角度分析比特币

其实应该这么说,从“暴富”角度分析比特币更恰当。如果我想发财,懂软件,懂加密学,
懂金融。比特币应该是这个星球人类能够想出最聪明的发财办法之一。
也许全部的奥秘就在比特比挖矿的价值成指数递减。而挖矿速度其实不变。
待续。。。

五,比特币技术在游戏方面的应用

Bitmessage 已经是使用比特币技术的应用。这个纯 P2P 的聊天软件,可以在完全没有聊天
服务器的情况下,支持点对点或多人的聊天。并且聊天记录无法修改和删除(。。。。。。这。。。。。)。
网络游戏,迄今为止,全部是 SERVER/CLIENT 模式。由运营商来开辟机房,大堆大堆的
服务器,运行着游戏服务器软件。玩家使用客户端,进行多人在线游戏。当一个玩家使用道具时,
CLIENT 会向 SERVER 发送一个“我张三要用一个烟花道具!”。 于是 SERVER 开始去 DB 查询,
张三有烟花道具么?哦,有。那么告诉 CLIENT“张三可以放一个烟花”。 于是 CLIENT 在屏幕
上播放张三脑袋上冒几个粒子特效。同时 5 块钱从 SERVER 上张三的户头扣走。
这种模式如果换成比特币会怎么样。
首先,没有 SERVER
其次,每个 CLIENT 软件都维护一套全世界所有玩家所有道具的数据库
最后,张三说我要放烟花,于是每个 CLIENT 看看数据库里有烟花给张三放么?大家投票说
YES,然后张三 CLIENT 播放一些特效。同时一笔张三支付给游戏开发者户头的交易生成。交易
按照比特币的流程进行。同时每个 CLIENT 还在拼命计算账簿链......
这个前景确实不大美妙.... 待续...

EOS智能合约开发点滴记录-第二篇智能合约编写

前文 《EOS智能合约开发点滴记录-第一篇开发环境搭建》

开发合约前,我们先选择下将要用的编辑工具
我常用的有 clion 和vscode,电脑os为mac,不过其他系统差别不大,如果你习惯用于Windows,那建议选择 Windows Subsystem for Linux,不建议用其他三方封装的工具,以免更新不及时,或者不兼容,导致生产上出问题.

下面我们讲用以vscode 做演示.

配置智能合约项目

先用vscode打开eosio.cdt项目代码目录,按提示安装相应的扩展插件.一般只需要安装个 c/c++
等vscode加载索引完,会根据eosio.cdt项目下的cmakelists.txt 配置好开发环境及其include路径.
所以我一般习惯是直接将自己的智能合约项目直接clone 在 eosio.cdt/examples目录下,省的自己做相关依赖配置.

我们先假定一个智能合约项目,项目名为bcskill.game (项目名与合约名一致,省的后面弄混)
新建bcskill.game目录(目录名也要与合约名一致,方便后面部署),路径为eosio.cdt/examples/bcskill.game
目录结构如下

eosio.cdt/examples/bcskill.game
eosio.cdt/examples/bcskill.game/common //存放一些公用的源码文件
eosio.cdt/examples/bcskill.game/bcskill.game.hpp //智能合约头文件
eosio.cdt/examples/bcskill.game/bcskill.game.cpp // 智能合约源文件
eosio.cdt/examples/bcskill.game/README.md // 帮助文档

习惯的代码结构

bcskill.game.hpp

#pragma once
//一些常用的头文件依赖 根据实际所需添加
#include <eosiolib/eosio.hpp>
#include <eosiolib/time.hpp>
#include <eosiolib/transaction.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/crypto.h>

namespace bcskillgame // 为自己的项目创建单独的命名空间
{
// 一些用到的命名空间 根据实际需要添加
    using eosio::time_point;
    using eosio::microseconds;
    using eosio::name;
    using namespace std;
    // 定义下一些用到的宏,比如系统EOS代币符号
    #define EOS_SYMBOL symbol(symbol_code("EOS"),4)
    一些内部会用到的结构体
    struct play_hero_info{
        uint8_t hero_id;
        uint8_t hero_grade;
        uint8_t count;
    };
    ...
    // 定义所需的table表 
    struct [[eosio::table("system"), eosio::contract("bcskill.game")]] system{
        uint64_t id;
        bool upgrading; //升级或维护中,暂时停止合约内所有的业务
        time_point zero_time;
        auto primary_key() const { return id; }
    };
    // `eosio::table("system")`中的system为合约内table的名字,后面可以用 get table 这个名字查看链上此table数据
    //  eosio::contract("bcskill.game")]] 中的 "bcskill.game" 为合约的名字,也就是我们之前定好的,也就是说这个表属于"bcskill.game" 合约
    ...
    // 为上面定义好的table 创建实例化对象
    typedef eosio::multi_index<"system"_n, system> system_tables;
    // multi_index<"system" 中的 system 为table的表名, 第二个system 为表的定义名.system_tables为实例化后的对象名.
    //下面开始创建合约类
    class [[eosio::contract("bcskill.game")]] bcskill_contract : public eosio::contract // 从系统合约对象继承下
    {
        public:
            using eosio::contract::contract; // 引入父命名空间
            // 创建合约action接口 后面可以用push action调用
            ACTION upgrading(bool upgrading);
            ...

        private:
            // 添加一些私有的数据类型
            // 一些整数类型可以用 enum 枚举
            enum GAME_STATUS_TYPE{
                GAME_STATUS_PADDING = 1,
                GAME_STATUS_RUNNING,
                GAME_STATUS_FINISHED
            };
            // 如果是小数,可以用class
            class RACE_PWOER_TYPE{
                public:
                static constexpr auto RACE_PWOER_ORC_HUM_TA = 0.3;
                static constexpr auto RACE_PWOER_UD_VS_HUM_ORC_TA = 0.3;
                static constexpr auto RACE_PWOER_HUM_ORC_TA_VS_NE = 0.3;
                static constexpr auto RACE_PWOER_NE_VS_UD = 1.3;
            };
            // 合约内不建议使用小数操作,一些场景可以先统一增大倍数,转为整数
            class UPDATE_RATE_TYPE{
                public:
                static constexpr auto PROBABILITY_ACCURACY = 100;
                static constexpr auto UPDATE_RATE_1_SUCCESS = 0.7 * PROBABILITY_ACCURACY;
                ...
            };
            // 定义写私有的方法,合约内部使用,这里的方法不会被 push action调用到
            bool is_upgrading();
    };
}

bcskill.game.cpp

#include "bcskill.game.hpp" //引入头文件
// 引入一些其他的头文件,比如
#include "common/utils.hpp"
// 引入所需的命名空间
using namespace bcskillgame;
using namespace eosio;
// 为头文件中各个action添加实现方法
void bcskill_contract::upgrading(bool upgrading){
    require_auth( _self.value );
    system_tables system_table(_self, _self.value);
    auto itr = system_table.begin();
    if(itr == system_table.end()){
        system_table.emplace( _self, [&]( auto& s ) {
            s.id = system_table.available_primary_key();
            s.upgrading = upgrading;
        });
    }else{
        system_table.modify( itr, _self, [&]( auto& s ) {
            s.upgrading = upgrading;
        });
    }
}
...
// 一些私有方法实现
bool bcskill_contract::is_upgrading(){
    bool result = false;
    system_tables system_table(_self, _self.value);
    auto itr = system_table.begin();
    if(itr != system_table.end()){
        result = itr->upgrading;
    }
    return result;
}
....

// 为action 申明调用
extern "C" {
    [[noreturn]] void apply(uint64_t receiver, uint64_t code, uint64_t action) {
        if(code=="eosio.token"_n.value && action=="transfer"_n.value) {
            execute_action( name(receiver), name(code), &bcskill_contract::transfer);
        }
        else if(code==receiver){
            switch(action)
            {
                EOSIO_DISPATCH_HELPER( bcskill_contract, (upgrading) //只有添加后,action才能被外部 push action
                default:
                    eosio_assert(false, "it is not my action"); // 为安全,防止被恶意调用,影响合约响应
                    break;
            }
        }
        eosio_exit(0);
    }
};

此时合约的基本代码结构已完成

编译合约

进入代码目录执行

eosio-cpp -o bcskill.game.wasm bcskill.game.cpp --abigen

执行完毕后,会生成
bcskill.game.abi 和 bcskill.game.wasm
我们可以简单的理解为 abi 为(.h)头文件,wasm 为dll或so 库.
我们想执行某个账号下的合约时,先会获取这个合约的abi信息,也就是先获取合约内所有的action接口,然后根据所指定的接口在发起交易,执行合约内对应的逻辑.

合约部署

cleos -u https://api.eoslaomao.com  set contract bcskillsurou ../bcskill.game/ -p bcskillsurou

合约执行

cleos -u https://api.eoslaomao.com push action bcskillsurou upgrading '{"upgrading": 1}' -p bcskillsurou

查看链上table

cleos -u https://api.eoslaomao.com get table bcskillsurou bcskillsurou system

类似返回数据如下

{
  "rows": [{
      "id": 0,
      "upgrading": 1,
      "zero_time": "2019-03-30T16:00:00.000"
    }
  ],
  "more": false
}

建议

对于数据类型的选取,编写前,最好专门花时间确定下,避免不必要的RAM浪费或者后面数据溢出.
比如uint8_t 到 uint64_t的选取,如果小于255 就用 uint8_t节约内存
对于一些数据table的划分,最好能实现评估下,如果前端没有全局(_self)查找或排序要求,创建对应合适的scope下,降低find时的消耗.
尽量避免后面做数据迁移.迁移会存在一些人为的操作失误风险,以及期间可能需要暂停dapp,影响用户体验.

本文结束,全文演示了我目前习惯的合约目录及代码结构,以及基本的合约使用,后面有时间再补充,如果大家有什么更好的方法或者建议可以留言,一起学习~

EOS智能合约开发点滴记录-第一篇开发环境搭建

最近一直在做Dapp开发,一直忙的没时间更新博客,最近换工作空闲时间,更几片连续文章,总结下EOS合约开发的步骤,一些遇到的问题,以及一些实际场景,哪些数据适合上链的选取,以及怎样方便调用.方便一起的学习的同学参考,以及自己备忘.

我们先尽量简单直白的说下什么是EOS链,资源,智能合约,以及之间的关系.

  1. EOS链
    我们可以把EOS链想成一个由多台服务器组成的一个计算机云集群{无意突出此链的中心化}.每台服务器(节点)上都部署着一个EOS节点程序,各个节点彼此链接.这个计算机集群,根据投票排名前21名的节点,可以参与计算任务(出块),并且计算后会有相应的奖励(挖矿).

  2. 资源:RAM,CPU,NET
    我们把EOS链想成云服务器集群,我们购买的EOS账号就相当于在云服务的服务商那购买了个账号,然后给账号购买使用的资源:存储(RAM),算力(CPU)用于计算任务,网络(NET)用于任务与集群传输.我们使用EOS链,就相当于使用云服务器提供商提供的计算和存储服务.

  3. 智能合约
    我们可以把智能合约想成是一个执行脚本,每个合约可以部署到自己的EOS账号下(可以理解成每个账号会有个专门的存储索引,指引存放的合约,每个普通账号只能同时部署一个合约,后面的会把前面的覆盖掉),合约内可以写一些逻辑,以及数据的增删改查,也可以调用其余的合约.我们外部可以用过rpc方法访问EOS链(发起交易),指定运行哪个账号下的合约的某个接口(执行action).

也许举例还不够简单,或者不妥,等之后在完善.
下面我们开始主题

智能合约开发环境搭建

先介绍下智能合约的开发编译工具,在v1.3.0之前使用的是eosiocpp(直接包含在eos项目代码内,整体项目编译完或者安装完二进制包,直接可以使用).之后版本已经弃用(EOSIO 1.3.0 Release Notes),统一使用新版本编译工具 eosio.cdt,并放在单独的仓库管理(需要单独编译,或者下载二进制包安装).如果只开发合约的话,可以只安装eosio.cdt,无需再编译安装eos链项目.
新旧版本合约规则变化较大,目前网络上的文章一部分还停留在旧版本,不建议再参考语法.

安装eosio.cdt

编写此文时 eosio.cdt最新版本 1.6.1, EOSIO v1.7.0
eosio.cdt 可以使用源码编译安装,也可以直接使用官方编译好的二进制安装包

1. 源码编译安装

clone 源代码

git clone https://github.com/EOSIO/eosio.cdt

切换此时最新release分支

cd eosio.cdt
git checkout -b v1.6.1

编译源代码

git submodule update --init --recursive
./build.sh

编译完成之后会显示

然后执行安装

sudo ./install.sh

2.二进制包安装

EOSIO.CDT目前支持Mac OS X brew,Linux x86_64 Debian软件包和Linux x86_64 RPM软件包
如果之前已经用源码等方式安装过,需要先卸载,

Mac OS X Brew

安装

$ brew tap eosio/eosio.cdt
$ brew install eosio.cdt

卸载

$ brew remove eosio.cdt
Ubuntu Debian Package

安装

$ wget https://github.com/eosio/eosio.cdt/releases/download/v1.6.1/eosio.cdt_1.6.1-1_amd64.deb
$ sudo apt install ./eosio.cdt_1.6.1-1_amd64.deb

卸载

    $ sudo apt remove eosio.cdt
Fedora RPM Package

安装

    $ wget https://github.com/eosio/eosio.cdt/releases/download/v1.6.1/eosio.cdt-1.6.1-1.fedora-x86_64.rpm
    $ sudo yum install ./eosio.cdt-1.6.1-1.fedora-x86_64.rpm

卸载

    $ sudo yum remove eosio.cdt
Centos RPM Package

安装

    $ wget https://github.com/eosio/eosio.cdt/releases/download/v1.6.1/eosio.cdt-1.6.1-1.centos-x86_64.rpm
    $ sudo yum install ./eosio.cdt-1.6.1-1.centos-x86_64.rpm

卸载

    $ sudo yum remove eosio.cdt

建议

个人还是推荐使用Ubuntu 18.04/16.04系统,目前了解到的bp以及社区其他节点,大都在此系统上运行,相对测试较多,并且查找问题资料相对较多.

常见问题

  1. ubuntu安装完,执行eosio-cpp compile可能会报错误
    libstdc++.so.6 version glibcxx_3.4.21' not found

    解决方式如下

    sudo apt-get install software-properties-common
    sudo add-apt-repository ppa:ubuntu-toolchain-r/test
    sudo apt-get update
    sudo apt-get install libstdc++6

参考

  1. https://github.com/EOSIO/eosio.cdt
  2. https://blog.csdn.net/ITleaks/article/details/85841850

后文 《EOS智能合约开发点滴记录-第二篇智能合约编写》