数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
只读属性,返回当前节点持有的帐户列表。
同步调用: web3.eth.accounts
异步调用: web3.eth.getAccounts(callback(error, result){ ... })
返回值: Array - 节点持有的帐户列表。
示例: var accounts = web3.eth.accounts; console.log(accounts);
以太坊开发入门教程
区块链
2018-04-24 11:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链这么火,可是你很快就会发现,想要入门区块链开发,尤其是想要从零开始学习区块链编程,根本都找不到突破口!
那么,要掌握区块链开发技术,应该学习哪些知识,应该学习哪些编程语言,才能快速入门区块链开发? 如果你希望马上开始学习以太坊区块链应用开发,可以访问汇智网提供的出色的在线互动教程: 以太坊应用开发入门教程 以太坊去中心化电商应用开发实战
区块链是什么
区块链是什么?一句话,它是一种特殊的分布式数据库技术的实现。
首先,区块链的主要作用是储存信息。任何需要保存的信息,都可以写入区块链,也可以从里面读取,所以它是数据库。
其次,任何人都可以架设服务器,加入区块链网络,成为一个节点。区块链的世界里面,没有中心节点,每个节点都是平等的,都保存着整个数据库。你可以向任何一个节点,写入/读取数据,因为所有节点最后都会同步,保证区块链一致。
分布式数据库并非新发明,市场上早有此类产品。但是,区块链有一个革命性特点。
区块链没有管理员,它是彻底无中心的。其他的数据库都有管理员,但是区块链没有。如果有人想对区块链添加审核,也实现不了,因为它的设计目标就是防止出现居于中心地位的管理当局。
正是因为无法管理,区块链才能做到无法被控制。否则一旦大公司大集团控制了管理权,他们就会控制整个平台,其他使用者就都必须听命于他们了。
但是,没有了管理员,人人都可以往里面写入数据,怎么才能保证数据是可信的呢?被坏人改了怎么办?请接着往下读,这就是区块链奇妙的地方。
那么,什么是区块?
区块链由一个个区块(block)组成。区块很像数据库的记录,每次写入数据,就是创建一个区块。
每个区块包含两个部分。 区块头(Head):记录当前区块的特征值 区块体(Body):实际数据
区块头包含了当前区块的多项特征值。 生成时间 实际数据(即区块体)的哈希 上一个区块的哈希 ...
这里,你需要理解什么叫哈希(hash),这是理解区块链必需的。
什么是哈希?
所谓"哈希"就是计算机可以对任意内容,计算出一个长度相同的特征值。区块链的 哈希长度是256位,这就是说,不管原始内容是什么,最后都会计算出一个256位的二进制数字。而且可以保证,只要原始内容不同,对应的哈希一定是不同的。
举例来说,字符串123的哈希是a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0(十六进制),转成二进制就是256位,而且只有123能得到这个哈希。(理论上,其他字符串也有可能得到这个哈希,但是概率极低,可以近似认为不可能发生。)
因此,就有两个重要的推论。 推论1:每个区块的哈希都是不一样的,可以通过哈希标识区块。 推论2:如果区块的内容变了,它的哈希一定会改变。
为什么叫区块链?
区块与哈希是一一对应的,每个区块的哈希都是针对"区块头"(Head)计算的。也就是说,把区块头的各项特征值,按照顺序连接在一起,组成一个很长的字符串,再对这个字符串计算哈希。 Hash = SHA256( 区块头 )
上面就是区块哈希的计算公式,SHA256是区块链的哈希算法。注意,这个公式里面只包含区块头,不包含区块体,也就是说,哈希由区块头唯一决定,
前面说过,区块头包含很多内容,其中有当前区块体的哈希,还有上一个区块的哈希。这意味着,如果当前区块体的内容变了,或者上一个区块的哈希变了,一定会引起当前区块的哈希改变。
这一点对区块链有重大意义。如果有人修改了一个区块,该区块的哈希就变了。为了让后面的区块还能连到它(因为下一个区块包含上一个区块的哈希),该人必须依次修改后面所有的区块,否则被改掉的区块就脱离区块链了。由于后面要提到的原因,哈希的计算很耗时,短时间内修改多个区块几乎不可能发生,除非有人掌握了全网51%以上的计算能力。
正是通过这种联动机制,区块链保证了自身的可靠性,数据一旦写入,就无法被篡改。这就像历史一样,发生了就是发生了,从此再无法改变。
每个区块都连着上一个区块,这也是"区块链"这个名字的由来。
区块链适合的应用场景
我们都知道,在技术上不存在银弹,区块链也不是。
区块链作为无人管理的分布式数据库,从2009年开始已经运行了8年,没有出现大的问题。这证明它是可行的。
但是,为了保证数据的可靠性,区块链也有自己的代价。一是效率,数据写入区块链,最少要等待十分钟,所有节点都同步数据,则需要更多的时间;二是能耗,区块的生成需要矿工进行无数无意义的计算,这是非常耗费能源的。
因此,区块链有它自己的适用场景: 不存在所有成员都信任的管理当局 写入的数据不要求实时使用 挖矿的收益能够弥补本身的成本
如果无法满足上述的条件,那么传统的数据库是更好的解决方案。
区块链平台选择
如果你要学习区块链的开发,首先需要选择合适的区块链平台。目前区块链有1.0和2.0之说。
区块链 1.0
主要由数字货币和支付行为组成。特征包括: 以区块为单位的链状数据块结构; 共享账本; 非对称加密; 源代码开源
区块链1.0主要具备的是去中心化的数字货币和支付平台的功能 ; 目标是为了去中心化,典型代表就是比特币(Bitcoin)。区块链1.0对开发者并不是很友好。
区块链 2.0
主要特点是支持智能合约和去中心化应用开发。 智能合约:区块链系统中的应用,是已编码的可自动运行的业务逻辑,通常有自己的代币和专用开发语言; 去中心化应用:即DApp,包含用户界面 的应用,包括但不限于各种加密货币,如以太钱包;虚拟机,用于执行智能合约编译后 的代码,虚拟机是图灵完备的。智能合约开始在区块链上应用,用机器合约指令代替 人工操作,让一切变得更加透明,没人有人为操作,干扰。比如以太坊上的ICO,就大大降低了融资成本。
因此,区块链2.0是对开发者友好的区块链平台。以太坊是区块链2.0的典型代表。
区块链开发语言选择
开发语言的选择取决于你要做什么事情。
如果你要自己实现一个区块链平台,那选择什么开发语言都可以,比如:java、c/c++、python、nodejs、go...
如果你要对已有的区块链平台进行底层改造,那就看这个平台的主流开发语言是什么。例如以太坊的底层协议实现最流行的版本是采用go语言,那么你可以学习go。
如果你要基于已有的区块链进行应用开发,那么就要看这个平台的约束。例如以太坊上智能合约的主流开发语言是solidity,去中心化应用的开发语言是nodejs和html/javascript/css。 以太坊开发入门免费教程 可以让你对这些内容有一个初步了解。
因此,对于希望学习区块链开发技术的工程师而言,从以太坊应用开发入手是最佳的区块链开发入门方式,如果你已经有web开发基础,只需要先理解智能合约的概念和作用,然后学习solidity来开发以太坊区块链的智能合约,结合web前端,就可以快速开发出一个基于区块链的去中心化应用了!
区块链
2018-04-24 10:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
智能合约开发是以太坊的核心,学习过程主要是搭建以太坊开发环境和solidity语言的编程。本文不用任何以太坊Dapp框架,直接在ganache下开发智能合约。
让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序。
该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数。
我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部。此外,当你使用框架时,将对框架所做的繁重工作有更多的体会!
我们的目标是: 建立开发环境。 学习编写智能合约
1. 设置开发环境
我们使用一个模拟的内存区块链(ganache)代替真实的区块链在进行开发。在本教程的2章,我们将与真实的区块链交互。下面是安装ganache、web3js的步骤,然后在linux上启动一个测试链。在macOS上安装过程也是一样的。
你可以看到ganache-cli自动创建了10个测试账号,每个账号预分配了100(虚构的)ethers
如果需要更详细的开发环境安装教程,可以参考如下文章: windows以太坊开发环境搭建 linux/ubuntu以太坊开发环境搭建
2.简单的投票合约
我们将使用solidity编程语言来编写我们的合约。如果您熟悉面向对象编程,学习编写solidity合约应该是轻而易举的事。我们将编写一个合约对象,含有一个构造函数初始化候选人数组。合约对象有2个方法: 返回候选人获得的总票数 增加候选人的投票数。 注意:构造函数只被调用一次,当您部署合约到区块链。不像在网络世界里的每一个部署你的代码覆盖旧的代码,部署后的代码在区块链上是不变的。例如,如果你更新你的合约并且再次部署,旧合约仍然会在区块链上, 它所存储的数据不受影响,新的部署将创建一个新实例的合约。
下面是投票合约的代码: pragma solidity ^ 0.4 .18 ; // We have to specify what version of compiler this code will compile with contract Voting { /* mapping field below is equivalent to an associative array or hash. The key of the mapping is candidate name stored as type bytes32 and value is an unsigned integer to store the vote count */ mapping (bytes32 => uint8) public votesReceived; /* Solidity doesn't let you pass in an array of strings in the constructor (yet). We will use an array of bytes32 instead to store the list of candidates */ bytes32[] public candidateList; /* This is the constructor which will be called once when you deploy the contract to the blockchain. When we deploy the contract, we will pass an array of candidates who will be contesting in the election */ function Voting (bytes32[] candidateNames) public { candidateList = candidateNames; } // This function returns the total votes a candidate has received so far function totalVotesFor (bytes32 candidate) view public returns (uint8) { require (validCandidate(candidate)); return votesReceived[candidate]; } // This function increments the vote count for the specified candidate. This // is equivalent to casting a vote function voteForCandidate (bytes32 candidate) public { require (validCandidate(candidate)); votesReceived[candidate] += 1 ; } function validCandidate (bytes32 candidate) view public returns (bool) { for (uint i = 0 ; i < candidateList.length; i++) { if (candidateList[i] == candidate) { return true ; } } return false ; } }
复制上面的代码,在hello_world_voting目录下创建一个Voting.sol文件。现在让我们来编译代码并将其部署到ganache的区块链上.
为了编译solidity代码,我们需要安装名字为solc的npm模块 ~/hello_world_voting$ npm install solc
我们将在node控制台中使用这个库来编译我们的合约。在上一篇文章中我们提到,web3js是一个让我们可以通过rpc访问区块链的库。我们将使用该库来部署我们的应用程序并与之交互。
首先,在命令行中断运行 node 命令进入node控制台,初始化solc和文本对象。下面的所有代码片段都需要在node控制台中键入 ~/hello_world_voting$ node > Web3 = require ( 'web3' ) > web3 = new Web3( new Web3.providers.HttpProvider( "http://localhost:8545" ));
为了确保web3对象已经初始化、区块链能够访问,让我们试一下查询区块链上的所有账户。您应该看到如下的结果: > web3.eth.accounts [ '0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e' , '0x7d920be073e92a590dc47e4ccea2f28db3f218cc' , '0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9' , '0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6' , '0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a' , '0x622e557aad13c36459fac83240f25ae91882127c' , '0xbf8b1630d5640e272f33653e83092ce33d302fd2' , '0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b' , '0x175dae81345f36775db285d368f0b1d49f61b2f8' , '0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3' ]
从voting.sol加载代码,保存在一个字符串变量中,然后开始编译 > code = fs.readFileSync( 'Voting.sol' ).toString() > solc = require ( 'solc' ) > compiledCode = solc.compile(code)
当你的代码编译成功并打印了合约对象的内容(在node控制台中输出的内容),有2个字段很重要,需要理解它们: compiledCode.contracts[‘:Voting’].bytecode : Voting.sol源代码编译后得到的字节码。这是将被部署到blockchain的代码。 compiledCode.contracts[‘:Voting’].interface : 合约接口或模板(称为ABI)告诉用户合约含有哪些方法。您需要这些ABI的定义,因为将来你总是需要与合约交互的。更多ABI信息请参考 这里 。
如果希望能在线学习以太坊DApp的开发,分享一个教程:
http://xc.hubwiz.com/course/5a952991adb3847553d205d1


区块链
2018-04-24 09:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战开发入门 以太坊+IPFS去中心化电商DApp实战开发
返回链上指定地址的账户余额。
调用: web3.eth.getBalance(addressHexString [, defaultBlock] [, callback])
参数: addressHexString : String - 要查询余额的地址。 defaultBlock : Number|String -(可选)如果不设置此值,将使用 web3.eth.defaultBlock 设定的块,否则使用指定的块。 callback : Funciton - (可选)回调函数,用于支持异步的执行方式。 返回值: String - 一个包含给定地址的当前余额的BigNumber实例,单位为wei。
示例: var balance = web3.eth.getBalance("0x407d73d8a49eeb85d32cf465507dd71d507100c1"); console.log(balance); // instanceof BigNumber console.log(balance.toString(10)); // '1000000000000' console.log(balance.toNumber()); // 1000000000000
以太坊开发入门教程
区块链
2018-04-23 21:13:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
搜集整理的以太坊开发教程pdf电子书,放在百度云盘上了:
基本概念
区块链、以太坊的一些基本概念、核心思路及关键算法方面的教程。 精通比特币中文版: https://pan.baidu.com/s/1uWXKvKVxliTQW47xcbGvAg 区块链技术指南中文版: https://pan.baidu.com/s/1O-565-qDOPyR6LtnXQygRQ 以太坊白皮书中文版: https://pan.baidu.com/s/13T4yyjViGEWTbQZJ_fHbiw 以太坊官方文档中文版: https://pan.baidu.com/s/1Jg2t4Jsau7-nFFO-ckkm7w
底层开发
使用不同语言进行区块链的底层开发方面的开发教程,内容不涉及以太坊,但有助于理解以太坊或其他区块链的底层运作原理。 c#区块链编程中文版: https://pan.baidu.com/s/1Wz0aUsxl5wuR9ww_BnYdjw Node.js区块链开发: https://pan.baidu.com/s/1Mi5oBWCb2eSdzMqZBP7Zkg
参考手册
开发必备的以太坊开发工具、开发库的参考手册及使用教程。 web3.js API文档中文版: https://pan.baidu.com/s/1aOtte6zHUFEs5XHD0caWLA solidity官方文档中文版: https://pan.baidu.com/s/18YG0QDVjH9L2B02TnIqVvw truffle官方文档中文版: https://pan.baidu.com/s/1mTXw8g7zgCaJkX6QZiejVQ geth使用指南中文版: https://pan.baidu.com/s/1k8gJ1VihtXkOpCfPdB6Pdg Ganache CLI命令行参数详解: https://pan.baidu.com/s/1yEntGAbJUpERC4UXT-Ajhw
开发环境搭建
在自己的机器上搭建以太坊开发环境方面的教程。 linux/ubuntu以太坊开发环境搭建: https://pan.baidu.com/s/12xmAScSafm2cS6qPVoJrIQ windows以太坊开发环境搭建: https://pan.baidu.com/s/1uA28smpBF9vQB7IHAOx3FQ ubuntu以太坊私链开发环境搭建: https://pan.baidu.com/s/1vy1vRz2_XGC4vsJw01ejlg windows以太坊DApp私链开发环境搭建: https://pan.baidu.com/s/11aGSUNk8zHZq6Ho_kJP6ow ipfs安装和ipfs-api开发环境搭建: https://pan.baidu.com/s/1QjnqQhj_Az11iZSJUFDH-Q
合约部署
以太坊智能合约部署方面的教程。 使用truffle和infura部署以太坊合约: https://pan.baidu.com/s/1kSBFzHWN1hWWzCJlTlzDww solidity如何实现字符串拼接: https://pan.baidu.com/s/1X6GMY7RY8o81ItwRtdYkog
区块链
2018-04-23 20:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
智能合约开发是以太坊开发的核心,而代币合约的编写是智能合约开发的核心,用solidity开发一个代币合约的例子是学习以太坊开发智能合约时必须掌握的。
以太坊的应用被称为去中心化应用(DApp),DApp的开发主要包括两大部分: 智能合约的开发 用户界面的开发
在本文中,我们将介绍智能合约的开发语言solidity。
让我们先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。 contract SimpleStorage { uint storedData; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } }
在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。
uint storedData ; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。
跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。
这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。
编写代币合约
接下来的合约将实现一个形式最简单的加密货币。任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。 contract Coin { //关键字“public”使变量能从合约外部访问。 address public minter; mapping (address => uint) public balances; //事件让轻客户端能高效的对变化做出反应。 event Sent(address from, address to, uint amount); //这个构造函数的代码仅仅只在合约创建的时候被运行。 function Coin() { minter = msg.sender; } function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; Sent(msg.sender, receiver, amount); } }
这个合约引入了一些新的概念,让我们来逐个介绍。 address public minter;`
这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下: function minter() returns (address) { return minter; }
当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。
下一行代码创建了一个public的状态变量,但是其类型更加复杂: mapping (address => uint) public balances;
该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下: function balances(address _account) returns (uint balance) { return balances[_account]; }
我们可以很方便的通过这个函数查询某个特定账号的余额。 event Sent(address from, address to, uint value);
这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码: Coin.Sent().watch({}, '', function(error, result) { if (!error) { console.log("Coin transfer: " + result.args.amount + " coins were sent from " + result.args.from + " to " + result.args.to + "."); console.log("Balances now:\n" + "Sender: " + Coin.balances.call(result.args.from) + "Receiver: " + Coin.balances.call(result.args.to)); } }
注意在客户端中是如何调用自动生成的 balances 函数的。
这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。
最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。
分享两个教程和一些免费资料给读者:
一个适合区块链新手的以太坊DApp开发教程:
http://xc.hubwiz.com/course/5a952991adb3847553d205d1
一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台:
http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6
收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接:
1. web3.js API官方文档中文版: https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g
2. 以太坊官方文档中文版 : https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA
3. 以太坊白皮书中文版 : https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow
4. Solidity的官方文档中文版 : https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw
5. Truffle的官方文档中文版 : https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ
6. C#区块链编程指南 : https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA
7. 区块链技术指南 : https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg
8. 精通比特币中文版 : https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg
9. Node.js区块链开发 : https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg
10. geth使用指南文档中文版 : https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag
11. 以太坊DApp开发环境搭建-Ubuntu : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA
12. 以太坊DApp开发环境搭建-windows : https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA
13. 以太坊DApp开发私链搭建-Ubuntu : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g
14. 以太坊DApp开发私链搭建-windows : https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA
区块链
2018-04-23 20:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
智能合约开发是以太坊开发的核心,学习编写智能合约主要是搭建以太坊开发环境和solidity开发智能合约。不使用任何以太坊Dapp框架,直接使用solidity在ganache下开发智能合约。
让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序。
该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数。
我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部。此外,当你使用框架时,将对框架所做的繁重工作有更多的体会!
我们的目标是: 建立开发环境。 学习编写智能合约
1. 设置开发环境
我们使用一个模拟的内存区块链(ganache)代替真实的区块链在进行开发。在本教程的2章,我们将与真实的区块链交互。下面是安装ganache、web3js的步骤,然后在linux上启动一个测试链。在macOS上安装过程也是一样的。
你可以看到ganache-cli自动创建了10个测试账号,每个账号预分配了100(虚构的)ethers
如果需要更详细的开发环境安装教程,可以参考如下文章: windows以太坊开发环境搭建 linux/ubuntu以太坊开发环境搭建
2.简单的投票合约
我们将使用solidity编程语言来编写我们的合约。如果您熟悉面向对象编程,学习编写solidity合约应该是轻而易举的事。我们将编写一个合约对象,含有一个构造函数初始化候选人数组。合约对象有2个方法: 返回候选人获得的总票数 增加候选人的投票数。 注意:构造函数只被调用一次,当您部署合约到区块链。不像在网络世界里的每一个部署你的代码覆盖旧的代码,部署后的代码在区块链上是不变的。例如,如果你更新你的合约并且再次部署,旧合约仍然会在区块链上, 它所存储的数据不受影响,新的部署将创建一个新实例的合约。
下面是投票合约的代码: pragma solidity ^0.4.18; // We have to specify what version of compiler this code will compile with contract Voting { /* mapping field below is equivalent to an associative array or hash. The key of the mapping is candidate name stored as type bytes32 and value is an unsigned integer to store the vote count */ mapping (bytes32 => uint8) public votesReceived; /* Solidity doesn't let you pass in an array of strings in the constructor (yet). We will use an array of bytes32 instead to store the list of candidates */ bytes32[] public candidateList; /* This is the constructor which will be called once when you deploy the contract to the blockchain. When we deploy the contract, we will pass an array of candidates who will be contesting in the election */ function Voting(bytes32[] candidateNames) public { candidateList = candidateNames; } // This function returns the total votes a candidate has received so far function totalVotesFor(bytes32 candidate) view public returns (uint8) { require(validCandidate(candidate)); return votesReceived[candidate]; } // This function increments the vote count for the specified candidate. This // is equivalent to casting a vote function voteForCandidate(bytes32 candidate) public { require(validCandidate(candidate)); votesReceived[candidate] += 1; } function validCandidate(bytes32 candidate) view public returns (bool) { for(uint i = 0; i < candidateList.length; i++) { if (candidateList[i] == candidate) { return true; } } return false; } }
复制上面的代码,在hello_world_voting目录下创建一个Voting.sol文件。现在让我们来编译代码并将其部署到ganache的区块链上.
为了编译solidity代码,我们需要安装名字为solc的npm模块 ~/hello_world_voting$ npm install solc
我们将在node控制台中使用这个库来编译我们的合约。在上一篇文章中我们提到,web3js是一个让我们可以通过rpc访问区块链的库。我们将使用该库来部署我们的应用程序并与之交互。
首先,在命令行中断运行 node 命令进入node控制台,初始化solc和文本对象。下面的所有代码片段都需要在node控制台中键入 ~/hello_world_voting$ node > Web3 = require('web3') > web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
为了确保web3对象已经初始化、区块链能够访问,让我们试一下查询区块链上的所有账户。您应该看到如下的结果: > web3.eth.accounts ['0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e', '0x7d920be073e92a590dc47e4ccea2f28db3f218cc', '0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9', '0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6', '0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a', '0x622e557aad13c36459fac83240f25ae91882127c', '0xbf8b1630d5640e272f33653e83092ce33d302fd2', '0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b', '0x175dae81345f36775db285d368f0b1d49f61b2f8', '0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3']
从voting.sol加载代码,保存在一个字符串变量中,然后开始编译 > code = fs.readFileSync('Voting.sol').toString() > solc = require('solc') > compiledCode = solc.compile(code)
当你的代码编译成功并打印了合约对象的内容(在node控制台中输出的内容),有2个字段很重要,需要理解它们: compiledCode.contracts[‘:Voting’].bytecode : Voting.sol源代码编译后得到的字节码。这是将被部署到blockchain的代码。 compiledCode.contracts[‘:Voting’].interface : 合约接口或模板(称为ABI)告诉用户合约含有哪些方法。您需要这些ABI的定义,因为将来你总是需要与合约交互的。更多ABI信息请参考 这里 。
分享些教程和资料给读者:
一个适合区块链新手的以太坊DApp开发教程:
http://xc.hubwiz.com/course/5a952991adb3847553d205d1
一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台:
http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6
收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接:
1. web3.js API官方文档中文版: https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g
2. 以太坊官方文档中文版 : https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA
3. 以太坊白皮书中文版 : https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow
4. Solidity的官方文档中文版 : https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw
5. Truffle的官方文档中文版 : https://pan.baidu.com/s/1y6SVd7lSLUHK21YF5FzIUQ
6. C#区块链编程指南 : https://pan.baidu.com/s/1sJPLqp1eQqkG7jmxqwn3EA
7. 区块链技术指南 : https://pan.baidu.com/s/13cJxAa80I6iMCczA04CZhg
8. 精通比特币中文版 : https://pan.baidu.com/s/1lz6te3wcQuNJm28rFvBfxg
9. Node.js区块链开发 : https://pan.baidu.com/s/1Ldpn0DvJ5LgLqwix6eWgyg
10. geth使用指南文档中文版 : https://pan.baidu.com/s/1M0WxhmumF_fRqzt_cegnag
11. 以太坊DApp开发环境搭建-Ubuntu : https://pan.baidu.com/s/10qL4q-uKooMehv9X2R1qSA
12. 以太坊DApp开发环境搭建-windows : https://pan.baidu.com/s/1cyYkhIJIFuI2oyxM9Ut0eA
13. 以太坊DApp开发私链搭建-Ubuntu : https://pan.baidu.com/s/1aBOFZT2bCjD2o0EILBWs-g
14. 以太坊DApp开发私链搭建-windows : https://pan.baidu.com/s/10Y6F1cqUltZNN99aJv9kAA
区块链
2018-04-23 20:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动开发教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
在这个教程中,让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序。
该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数。
我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部。此外,当你使用框架时,将对框架所做的繁重工作有更多的体会!
这个开发教程的目的是: 建立开发环境。 学习编写智能合约
1. 设置开发环境
我们使用一个模拟的内存区块链(ganache)代替真实的区块链在进行开发。在本教程的2章,我们将与真实的区块链交互。下面是安装ganache、web3js的步骤,然后在linux上启动一个测试链。在macOS上安装过程也是一样的。
你可以看到ganache-cli自动创建了10个测试账号,每个账号预分配了100(虚构的)ethers
如果需要更详细的开发环境安装教程,可以参考如下文章: windows以太坊开发环境搭建 linux/ubuntu以太坊开发环境搭建
2.简单的投票合约
我们将使用solidity编程语言来编写我们的合约。如果您熟悉面向对象编程,学习编写solidity合约应该是轻而易举的事。我们将编写一个合约对象,含有一个构造函数初始化候选人数组。合约对象有2个方法: 返回候选人获得的总票数 增加候选人的投票数。 注意:构造函数只被调用一次,当您部署合约到区块链。不像在网络世界里的每一个部署你的代码覆盖旧的代码,部署后的代码在区块链上是不变的。例如,如果你更新你的合约并且再次部署,旧合约仍然会在区块链上, 它所存储的数据不受影响,新的部署将创建一个新实例的合约。
下面是投票合约的代码: pragma solidity ^0.4.18; // We have to specify what version of compiler this code will compile with contract Voting { /* mapping field below is equivalent to an associative array or hash. The key of the mapping is candidate name stored as type bytes32 and value is an unsigned integer to store the vote count */ mapping (bytes32 => uint8) public votesReceived; /* Solidity doesn't let you pass in an array of strings in the constructor (yet). We will use an array of bytes32 instead to store the list of candidates */ bytes32[] public candidateList; /* This is the constructor which will be called once when you deploy the contract to the blockchain. When we deploy the contract, we will pass an array of candidates who will be contesting in the election */ function Voting(bytes32[] candidateNames) public { candidateList = candidateNames; } // This function returns the total votes a candidate has received so far function totalVotesFor(bytes32 candidate) view public returns (uint8) { require(validCandidate(candidate)); return votesReceived[candidate]; } // This function increments the vote count for the specified candidate. This // is equivalent to casting a vote function voteForCandidate(bytes32 candidate) public { require(validCandidate(candidate)); votesReceived[candidate] += 1; } function validCandidate(bytes32 candidate) view public returns (bool) { for(uint i = 0; i < candidateList.length; i++) { if (candidateList[i] == candidate) { return true; } } return false; } }
复制上面的代码,在hello_world_voting目录下创建一个Voting.sol文件。现在让我们来编译代码并将其部署到ganache的区块链上.
为了编译solidity代码,我们需要安装名字为solc的npm模块 ~/hello_world_voting$ npm install solc
我们将在node控制台中使用这个库来编译我们的合约。在上一篇文章中我们提到,web3js是一个让我们可以通过rpc访问区块链的库。我们将使用该库来部署我们的应用程序并与之交互。
首先,在命令行中断运行 node 命令进入node控制台,初始化solc和文本对象。下面的所有代码片段都需要在node控制台中键入 ~/hello_world_voting$ node > Web3 = require('web3') > web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
为了确保web3对象已经初始化、区块链能够访问,让我们试一下查询区块链上的所有账户。您应该看到如下的结果: > web3.eth.accounts ['0x9c02f5c68e02390a3ab81f63341edc1ba5dbb39e', '0x7d920be073e92a590dc47e4ccea2f28db3f218cc', '0xf8a9c7c65c4d1c0c21b06c06ee5da80bd8f074a9', '0x9d8ee8c3d4f8b1e08803da274bdaff80c2204fc6', '0x26bb5d139aa7bdb1380af0e1e8f98147ef4c406a', '0x622e557aad13c36459fac83240f25ae91882127c', '0xbf8b1630d5640e272f33653e83092ce33d302fd2', '0xe37a3157cb3081ea7a96ba9f9e942c72cf7ad87b', '0x175dae81345f36775db285d368f0b1d49f61b2f8', '0xc26bda5f3370bdd46e7c84bdb909aead4d8f35f3']
从voting.sol加载代码,保存在一个字符串变量中,然后开始编译 > code = fs.readFileSync('Voting.sol').toString() > solc = require('solc') > compiledCode = solc.compile(code)
当你的代码编译成功并打印了合约对象的内容(在node控制台中输出的内容),有2个字段很重要,需要理解它们: compiledCode.contracts[‘:Voting’].bytecode : Voting.sol源代码编译后得到的字节码。这是将被部署到blockchain的代码。 compiledCode.contracts[‘:Voting’].interface : 合约接口或模板(称为ABI)告诉用户合约含有哪些方法。您需要这些ABI的定义,因为将来你总是需要与合约交互的。更多ABI信息请参考 这里 。
区块链
2018-04-23 20:30:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
一、安装DApp开发环境
1.1 安装Node.js
我们使用官方长期支持的8.10.0LTS版本,点击这个 链接 下载32位安装包,32位安装包即可用于32位系统,也可用于64位系统。 如果你确认你的系统是64位,也可以下载 64位包装包 。 下载后直接安装即可。安装完毕,打开一个控制台窗口,可以使用node了: C:
区块链
2018-04-23 19:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
一、安装前的准备
1.1 查看当前CPU架构
在终端中执行以下命令,确定是32位架构还是64位架构: ~$ uname –p x86_64
如果你看到输出x86_64,那么就是64位系统,否则是32位。
1.2下载工具
确保你安装了下载工具wget: ~$ wget –V GNU Wget 1.17.1 built on linux-gnu
如果还没有安装wget,使用apt-get来安装 ~$ sudo apt-get install wget
二、安装DApp开发环境
2.1 安装Node.js
首先根据你的linux是32位还是64位,分别下载不同的预编译版本,我们使用官方长期支持的8.10.0LTS版本: 64位: ~$ wget https://nodejs.org/dist/v8.10.0/node-v8.10.0-linux-x64.tar.gz
32位: ~$ wget https://nodejs.org/dist/v8.10.0/node-v8.10.0-linux-x86.tar.gz
然后解压到当前目录,以64位为例: ~$ tar zxvf node-v8.10.0-linux-x64.tar.gz
然后接下来修改.bashrc来设置相关的环境变量: ~$ echo "export NODE_HOME=$HOME/node-v8.10.0-linux-x64" >> .bashrc ~$ echo "export NODE_PATH=$NODE_HOME/lib/node_modules" >> .bashrc ~$ echo "export PATH=$NODE_HOME/bin:$PATH" >> .bashrc
最后重新载入.bashrc(或者重新登陆)来使node生效: ~$ source .bashrc
现在,你可以使用node了: ~$ node –v v8.10.0
2.2 安装节点仿真器
为了快速开发和测试以太坊DApp,我们通常使用以太坊节点仿真器来模拟区块链,最流行的节点仿真器就是Ganache,之前被称为TeseRPC。
在终端执行以下命令: ~$ npm install –g ganache-cli
安装完毕后,执行命令验证安装成功: ~$ ganache-cli Ganache CLI v6.0.3 (ganache-core: 2.0.2)
要了解ganache命令行的详细用法,可以查看 以太坊ganache CLI命令行参数详解
2.3 安装solidity编译器
solidity是开发以太坊智能合约的编程语言,不熟悉的话可以查看 以太坊solidity开发语言简介 。 ~$ npm install –g solc
安装完毕后,执行命令验证安装成功 ~$ solcjs –version 0.40.2+commit.3155dd80.Emscripten.clang
2.4安装web3 ~$ npm install –g web3@0.20.2
安装验证: ~$ node –p 'require("web3")' {[Function: Web3] providers:{…}}
2.5安装truffle框架
执行以下命令安装truffle开发框架: ~$ npm install –g truffle
验证安装: ~$ truffle version Truffle v4.1.3 (core 4.1.3)
2.6安装webpack
执行以下命令安装webpack: ~$ npm install –g webpack@3.11.0
验证安装 ~$ webpack –v 3.11.0
三、构建示例项目
3.1 新建DApp项目
执行以下命令创建项目目录并进入该目录: ~$ mkdir demo ~$ cd demo
然后用webpack模版初始化项目骨架结构: ~/demo$ truffle unbox webpack Downloading… Unpacking… Setting up… Unbox successful. Sweet!
3.2 安装项目依赖的NPM包
执行以下命令安装nmp包: ~/demo$ npm install
3.3 修改truffle配置
truffle.js中,修改port为8545,因为ganache-cli在8545端口监听: module.exports = { networks:{ development: { … port: 8545 … } } }
3.4 启动节点
执行以下命令启动节点仿真器,以便部署合约并执行交易: ~/demo$ ganache-cli
3.5 编译合约
执行以下命令编译项目合约: ~/demo$ truffle compile
3.6 部署合约:
执行以下命令来部署合约: ~/demo$ truffle migrate
3.7 启动DApp
执行以下命令来启动DApp: ~/demo$ npm run dev
在浏览器里访问 http://localhost:8080即可
如果你希望从别的机器也可以访问你的DApp应用,修改一下package.json: { scripts:{ "dev": "webpack-dev-server –-host 0.0.0.0" } }
免费资料 以太坊DApp开发环境搭建-Ubuntu平台 以太坊DApp开发环境搭建 - Windows ubuntu以太坊私有链搭建教程 windows以太坊私有链搭建教程 以太坊开发入门免费教程
原文链接
区块链
2018-04-23 19:27:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以太坊的应用被称为去中心化应用(DApp),DApp的开发主要包括两大部分: 智能合约的开发 用户界面的开发
在本文中,我们将介绍智能合约的开发语言solidity。 如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战
Solidity是一种语法类似JavaScript的高级语言。它被设计成以编译的方式生成以太坊虚拟机代码。在后续内容中你将会发现,使用它很容易创建用于投票、众筹、封闭拍卖、多重签名钱包等等的合约。
编写第一个合约
让我们先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。 contract SimpleStorage { uint storedData; function set(uint x) { storedData = x; } function get() constant returns (uint retVal) { return storedData; } }
在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。
uint storedData ; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。
跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。
这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如何增加一个存取限制,使得只有你才能修改这个数字。
编写代币合约
接下来的合约将实现一个形式最简单的加密货币。任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。 contract Coin { //关键字“public”使变量能从合约外部访问。 address public minter; mapping (address => uint) public balances; //事件让轻客户端能高效的对变化做出反应。 event Sent(address from, address to, uint amount); //这个构造函数的代码仅仅只在合约创建的时候被运行。 function Coin() { minter = msg.sender; } function mint(address receiver, uint amount) { if (msg.sender != minter) return; balances[receiver] += amount; } function send(address receiver, uint amount) { if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; Sent(msg.sender, receiver, amount); } }
这个合约引入了一些新的概念,让我们来逐个介绍。 address public minter;`
这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下: function minter() returns (address) { return minter; }
当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。
下一行代码创建了一个public的状态变量,但是其类型更加复杂: mapping (address => uint) public balances;
该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下: function balances(address _account) returns (uint balance) { return balances[_account]; }
我们可以很方便的通过这个函数查询某个特定账号的余额。 event Sent(address from, address to, uint value);
这行代码声明了一个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码: Coin.Sent().watch({}, '', function(error, result) { if (!error) { console.log("Coin transfer: " + result.args.amount + " coins were sent from " + result.args.from + " to " + result.args.to + "."); console.log("Balances now:\n" + "Sender: " + Coin.balances.call(result.args.from) + "Receiver: " + Coin.balances.call(result.args.to)); } }
注意在客户端中是如何调用自动生成的 balances 函数的。
这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。
最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。
免费电子书
如果你希望深入了解solidity的语言特性,可以下载我们制作的免费pdf电子书: Solidity官方文档中文版 。
区块链
2018-04-23 19:04:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
如果你不喜欢浪费时间在开发环境的搭建上,可以使用汇智网的在线教程: 以太坊DApp实战开发入门 去中心化电商DApp实战开发
一、安装前的准备
1.1 查看当前CPU架构
在终端中执行以下命令,确定是32位架构还是64位架构: ~$ uname –p x86_64
如果你看到输出x86_64,那么就是64位系统,否则是32位。
1.2下载工具
确保你安装了下载工具wget: ~$ wget –V GNU Wget 1.17.1 built on linux-gnu
如果还没有安装wget,使用apt-get来安装 ~$ sudo apt-get install wget

二、安装DApp开发环境
##2.1 安装Node.js 首先根据你的ubuntu是32位还是64位,分别下载不同的预编译版本,我们使用官方长期支持的8.10.0LTS版本: 64位: ~$ wget https://nodejs.org/dist/v8.10.0/node-v8.10.0-linux-x64.tar.gz 32位: ~$ wget https://nodejs.org/dist/v8.10.0/node-v8.10.0-linux-x86.tar.gz 然后解压到当前目录,以64位为例: ~$ tar zxvf node-v8.10.0-linux-x64.tar.gz
然后接下来修改.bashrc来设置相关的环境变量: ~$ echo "export NODE_HOME=$HOME/node-v8.10.0-linux-x64" >> .bashrc ~$ echo "export NODE_PATH=$NODE_HOME/lib/node_modules" >> .bashrc ~$ echo "export PATH=$NODE_HOME/bin:$PATH" >> .bashrc
最后重新载入.bashrc(或者重新登陆)来使node生效: ~$ source .bashrc
现在,你可以使用node了: ~$ node –v v8.10.0
##2.2 安装Geth 在终端执行以下命令: ~$ wget https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.3-329ac18e.tar.gz ~$ mv get-linux-amd64-1.8.3-329ac18e geth ~$ echo export PATH=$HOME/geth:$PATH >> .bashrc ~$ soure .bashrc
安装完毕后,执行命令验证安装成功: ~$ geth version Geth Version: 1.8.3-stable
2.3 安装solidity编译器 ~$ npm install –g solc
安装完毕后,执行命令验证安装成功 ~$ solcjs –version 0.40.2+commit.3155dd80.Emscripten.clang
2.4安装web3 ~$ npm install –g web3@0.20.2
安装验证: ~$ node –p 'require("web3")' {[Function: Web3] providers:{…}}
2.5安装truffle框架
执行以下命令安装truffle开发框架: ~$ npm install –g truffle
验证安装: ~$ truffle version Truffle v4.1.3 (core 4.1.3)
2.6安装webpack
执行以下命令安装webpack: ~$ npm install –g webpack@3.11.0
验证安装 ~$ webpack –v 3.11.0

三、运行私链节点
3.1创世块配置
创建一个节点目录node1,并在其中创建私链的创世块配置文件: ~$ mkdir node1 ~$ cd node1 ~/node1$ touch private.json
然后编辑内容如下: { "config": { "chainId": 7878, "homesteadBlock": 0, "eip155Block": 0, "eip158Block": 0 }, "difficulty": "200", "gasLimit": "2100000", "alloc": { "7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" }, "f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" } } } config.chainId:用来声明以太坊网络编号,选择一个大于10的数字即可。 difficulty:用来声明挖矿难度,越小的值难度越低,也就能更快速地出块。
3.2初始化私链节点
执行geth的init命令初始化私链节点: ~/node1$ geth --datadir ./data init private.json
这会在当前目录下创建data目录,用来保存区块数据及账户信息: ~/node1$ ls data private.json
可以上述命令写到一个脚本init.sh里,这样避免每次都输入那么多记不住的东西: ~/node1$ touch init.sh ~node1$ chmod +x init.sh
编辑内容如下: #!/bin/bash geth --datadir ./data init private.json
在部署下一个节点时,就可以直接执行这个脚本进行初始化了。例如,在另一台机器上: ~/node1$ ./init.sh
3.3启动私链节点
从指定的私链数据目录启动并设定一个不同的网络编号来启动节点: ~/node1$ geth --rpc --datadir ./data --networkid 7878 console
同样,你可以用一个脚本console.sh来简化启动节点时的输入: ~/node1$ touch console.sh ~/node1$ chmod +x console.sh
编辑内容如下: #!/bin/bash geth --rpc \ --rpcaddr 0.0.0.0 \ --rpccorsdomain "*" \ --datadir ./data \ --networkid 7878 \ console rpcaddr:用来声明节点RPC API的监听地址,设为0.0.0.0就可以从其他机器访问API了; rpccorsdomain:用于解决web3从浏览器中跨域调用的安全限制问题。 以后启动节点,只要直接执行这个脚本即可: ~/node1$ ./console.sh
3.4 账户管理
3.4.1 查看账户列表
在geth控制台,使用eth对象的accounts属性查看目前的账户列表: > eth.accounts []
因为我们还没有创建账户,所以这个列表还是空的。
3.4.2创建新账户
在geth控制台,使用personal对象的newAccount()方法创建一个新账户,参数为你自己选择的密码: > personal.newAccount('78787878') 0xd8bcf1324d566cbec5d3b67e6e14485b06a41d49
输出就是新创建的账户地址(公钥),你的输出不会和上面的示例相同。geth会保存到数据目录下的keystore文件中。密码要自己记住,以后还需要用到。
3.4.3查询账户余额
在geth控制台,使用personal对象的getBalance()方法获取指定账户的余额,参数为账户地址: > eth.getBalance(eth.accounts[0]) 0
或者直接输入账户地址: > eth.getBalance('0xd8bcf1324d566cbec5d3b67e6e14485b06a41d49') 0
新创建的账户,余额果然为0。
3.4.4挖矿
没钱的账户什么也干不了,需要挖矿来挣点钱。 在geth控制台执行miner对象的start()方法来启动挖矿: > miner.start(1)
等几分钟以后,检查账户余额: > eth.getBalance(eth.accounts[0]) 2.695e+21
钱不少了,2695ETH了,目前市值将近500万人民币了,哈。 执行miner对象的stop()方法停止挖矿: > miner.stop()
3.4.5解锁账户
在部署合约时需要一个解锁的账户。在geth控制台使用personal对象的unlockAccount()方法来解锁指定的账户,参数为账户地址和账户密码(在创建账户时指定的那个密码): > eth.unlockAccount(eth.accounts[0],'78787878') true

四、构建示例项目
4.1 新建DApp项目
执行以下命令创建项目目录并进入该目录: ~$ mkdir demo ~$ cd demo
然后用webpack模版初始化项目骨架结构: ~/demo$ truffle unbox webpack Downloading… Unpacking… Setting up… Unbox successful. Sweet!
4.2 安装项目依赖的NPM包
执行以下命令安装nmp包: ~/demo$ npm install
4.3 修改truffle配置
truffle.js中,修改port为8545,因为geth默认在8545端口监听: module.exports = { networks:{ development: { … port: 8545 … } } }
4.4 启动节点
在 另一个终端,执行以下命令启动节点软件,以便部署合约并执行交易: ~$ cd node1 ~/node1$ ./console.sh >
注意:为了在节点上部署合约,别忘了启动geth后先解锁账户: > personal.unlockAcount(eth.accounts[0],'78787878') true
4.5 编译合约
执行以下命令编译项目合约: ~/demo$ truffle compile
4.6 部署合约
执行以下命令来部署合约: ~/demo$ truffle migrate
如果你之前忘了在geth控制台解锁账户,会看到如下错误,参考前面说明进行解锁即可: Error: authentication needed: password or unlock
如果已经正确地解锁了账户,你会看到部署过程停止在如下状态: Replacing Migrations… … 0x3088762a5bc9…
这是因为truffle在等待部署交易提交,但是我们在私链中还没有启动挖矿。 现在切换回geth终端窗口,查看交易池的状态: > txpool.status { pending:1, queued:0 }
果然有一个挂起的交易!启动挖矿就是了: > miner.start(1)
稍等小会儿,再查看交易池的状态: > txpool.status { pending:0, queued:0 }
交易已经成功提交了。我们可以停止挖矿了,因为它太占CPU了: > miner.stop()
现在切换回truffle那个终端,部署过程也正确地执行完了。
4.7 启动DApp
执行以下命令来启动DApp: ~/demo$ npm run dev
在浏览器里访问 http://localhost:8080即可。 如果你希望从别的机器也可以访问你的DApp应用,修改一下package.json: { scripts:{ "dev": "webpack-dev-server –-host 0.0.0.0" } }
相关教程 以太坊DApp开发环境搭建-Ubuntu平台 以太坊DApp开发环境搭建 - Windows windows以太坊私有链搭建教程
区块链
2018-04-23 17:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
MetaMask是一个Chrome插件钱包,只能在Chrome浏览器中使用,但很受欢迎。
MetaMask 评价(5★):
安装设置:★★★★
界面操作:★★★★
备份方式:★★
支持币种:★★(只显示 ETH,但能保存其他 ERC-20 币种)
其他功能:★★(可直接与 DApp 互动)
语言支持:★(目前只支持英文)
可用平台:Chrome 插件
官方网站: metamask.io

Google Chrome 目前最多人使用的浏览器,因为它支持最新的网页标准,而且有一个强大的插件系統,现在说的 ETH 钱包 MetaMask,也是一个 Chrome 的插件。
MetaMask 除了是一个简单的钱包,它主要卖点是让使用者可以很容易跟以太坊的智能合约互动,或者说说MetaMask 可以用来参加 ICO,大家可能会感觉更爽些。
跟大部份钱包一样,MetaMask 也不会存钱包资料,所有钱包的私钥和密码都由使用者本身持有,就算是 MetaMask 停止更新,大家也可以用手上的私钥到其他钱包拿回自己的币。
安装步骤
使用 Chrome 浏览器到 MetaMask 官方网站 按照向导安装就好。安装完成后会在网址栏右边看到插件图示,按一下狐狸的图标就可以 MetaMask ,第一次使用要确认使用条款,按「Accept」继续。


然后建一个钱包密码,一定要记住。

接着是备份密码,这个是恢复钱包要用到,这个记在一个永久的地方。

接着就是主界面了。

「Account 1」下面就是 ETH 钱包地址,地址右边有一堆橘色小图标:
在 etherscan 网站看交易明细。
copy钱包地址。

钱包地址 QR Code,方便手机钱包扫描。
显示钱包私钥,用来恢复钱包、或在 MyEtherWallet 等打开钱包。
如果你想要发送 ETH 给交易所或者朋友,可以直接按「Send」,输入对方的地址和 ETH 数量就可以,而「Transaction Data」可以不用理。而「Buy」选项我们基本上没用,因为用法定货币购买 ETH 的服务商Coinbase 只支持美国,唯一可用就是 Shapeshift 将其他币换成 ETH。

如何浏览和存取 MetaMask 钱包的其他 ERC-20 token?

如果大家用 MetaMask 接收了其他以太坊 token、甚至用来参加 ICO,你会发现在 MetaMask 看不到这些token,这时候可以用 ethplorer 网站 ,输入你的 MetaMask 钱包地址,就可以看到除了 ETH 以外、还有什么币在其中了。

如果大家要需要用 token,就需要 MyEtherWallet ,大家需要先拿到 MetaMask 钱包的私钥,输入到 MyEtherWallet 中就可以。
小结
MetaMask 可以说是一个相当简单的钱包,但目前它是链接 DApp 与钱包的最好方式,而作为钱包它该有的功能都有了应该会升级支持其它以太坊 token,会更加方便。
分享一个以太坊开发的入门教程给大家:
http://xc.hubwiz.com/course/5a952991adb3847553d205d1
区块链
2018-04-22 22:22:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
温馨提示:需要在单机上部署好 Ontology,详情请见 Ubuntu 14.04 下,Ontology 开发环境构建 、部署及测试
安装Java 下载Java
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
打开上述链接, ,下载 特定的 Java8 版本。笔者选择的是 jdk-8u172-linux-x64.tar.gz。 配置Java环境变量
将 jdk-8u172-linux-x64.tar.gz 解压至指定目录 blockchain@ThinkPad-T460:~$ tar -zxvf ~/Downloads/jdk-8u172-linux-x64.tar.gz -C ~
编辑 主目录下的 .bashrc 文件 blockchain@ThinkPad-T460:~$ vim ~/.bashrc
在文件末尾添加 export JAVA_HOME=$HOME/jdk1.8.0_172 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=$PATH:${JAVA_HOME}/bin
使配置立即生效 blockchain@ThinkPad-T460:~$ source ~/.bashrc
查看Java版本 blockchain@ThinkPad-T460:~$ java -version java version "1.8.0_172" Java(TM) SE Runtime Environment (build 1.8.0_172-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)
安装Maven 下载Maven
http://www-eu.apache.org/dist/maven/maven-3/
打开上述链接, ,进入相应目录,下载 特定的 Maven 版本。笔者选择的是 apache-maven-3.3.9-bin.tar.gz。 配置Maven环境变量
将 apache-maven-3.3.9-bin.tar.gz 解压至指定目录 blockchain@ThinkPad-T460:~$ tar -zxvf ~/Downloads/apache-maven-3.3.9-bin.tar.gz -C ~
编辑 主目录下的 .bashrc 文件 blockchain@ThinkPad-T460:~$ vim ~/.bashrc
在文件末尾添加 export MAVEN_HOME=$HOME/apache-maven-3.3.9 export PATH=$PATH:${MAVEN_HOME}/bin
使配置立即生效 blockchain@ThinkPad-T460:~$ source ~/.bashrc
查看Maven版本 blockchain@ThinkPad-T460:~$ mvn -version Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00) Maven home: /home/blockchain/apache-maven-3.3.9 Java version: 1.8.0_172, vendor: Oracle Corporation Java home: /home/blockchain/jdk1.8.0_172/jre Default locale: en_US, platform encoding: UTF-8 OS name: "linux", version: "4.4.0-116-generic", arch: "amd64", family: "unix" 配置Maven镜像
编辑 MAVEN_HOME,conf 文件夹下的 settings.xml blockchain@ThinkPad-T460:~$ vim $MAVEN_HOME/conf/settings.xml
找到mirrors镜像节点,增加mirror节点,如下所示: alimaven aliyun maven http://maven.aliyun.com/nexus/content/groups/public/ central
构建 Ontology JAVA SDK 开发环境 下载ontology-java-sdk blockchain@ThinkPad-T460:~/GitClone$ git clone https://github.com/ontio/ontology-java-sdk Cloning into 'ontology-java-sdk'... remote: Counting objects: 2322, done. remote: Compressing objects: 100% (57/57), done. remote: Total 2322 (delta 19), reused 47 (delta 12), pack-reused 2240 Receiving objects: 100% (2322/2322), 592.08 KiB | 302.00 KiB/s, done. Resolving deltas: 100% (1264/1264), done. Checking connectivity... done. blockchain@ThinkPad-T460:~/GitClone$ ls -lt total 4 drwxrwxr-x 5 blockchain blockchain 4096 4月 26 16:56 ontology-java-sdk
ontology-java-sdk 是一个 Maven 工程。 下载IDEA社区版
https://www.jetbrains.com/idea/download/previous.html ,点击该链接
笔者选择的是 ideaIC-2017.3.5-no-jdk.tar.gz。 解压该压缩文件 blockchain@ThinkPad-T460:~$ tar -zxvf ~/Downloads/ideaIC-2017.3.5-no-jdk.tar.gz -C ~
启动IDEA blockchain@ThinkPad-T460:~$ idea-IC-173.4674.33/bin/idea.sh
进入如下界面,
点击 Open,打开 上面下载的 ontology-java-sdk 工程。
点击 OK。此时,会进入工程中。点击左上角的 File --> Settings
在下图中,选择 上一步 安装的 Maven 路径,然后确认。
右键点击 pom.xml,点击 Maven 选项中的 Reimport,此时 Maven 会拉取依赖的Jar包。
至此,Ontology 的 Java SDK 开发环境 搭建完成。下面,让我们运行其中的几个示例。
运行 demo 包下的,AccountDemo 实例。
运行 demo 包下的,Demo 实例。
运行 demo 包下的,ECIESDemo 实例。
运行 demo 包下的,MakeTxDemo 实例。
运行 demo 包下的,OntAssetDemo 实例。
运行 demo 包下的,RecordTxDemo 实例。
感兴趣的读者,可以把所有的示例都运行一遍,也许会有意外的收获哦~~
原文链接:转载请注明出处,谢谢!
获取更多信息,可关注微信公众号
区块链
2018-04-21 17:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
安装 Go
从 https://golang.org/dl/ 下载最新版本的Golang
笔者用的操作系统是 Ubuntu 14.04 LTS,所以下载的是 go1.10.1.linux-amd64.tar.gz。
解压该压缩文件 blockchain@ThinkPad-T460:~$ tar -zxf go1.10.1.linux-amd64.tar.gz
配置环境变量 blockchain@ThinkPad-T460:~$ vim ~/.bashrc
在文件末尾添加 export GOROOT=$HOME/go export GOPATH=$HOME/GoPath export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
注意 :GOPATH环境变量指定了你的工作空间位置。工作目录可以放在任何地方,但不能和Go的安装目录相同。 Go的代码必须放在工作空间内,其中包含了三个子目录: bin目录包含可执行命令 pkg目录包含包对象 src目录包含Go的源文件,它们被组织成包(每个目录对应一个包)
使环境变量立即生效 blockchain@ThinkPad-T460:~$ source ~/.bashrc
查看Go版本 blockchain@ThinkPad-T460:~$ go version go version go1.10.1 linux/amd64
至此,Golang安装成功。
安装第三方包管理工具glide blockchain@ThinkPad-T460:~$ sudo add-apt-repository ppa:masterminds/glide && sudo apt-get update blockchain@ThinkPad-T460:~$ sudo apt-get install glide
获取ontology
进入 $GOPATH 下的src 目录,从 GitHub上 下载 ontology 的源码。 blockchain@ThinkPad-T460:~$ cd $GOPATH/src blockchain@ThinkPad-T460:~/GoPath/src$ git clone https://github.com/ontio/ontology.git Cloning into 'ontology'... remote: Counting objects: 14252, done. remote: Compressing objects: 100% (20/20), done. remote: Total 14252 (delta 1), reused 15 (delta 1), pack-reused 14231 Receiving objects: 100% (14252/14252), 2.96 MiB | 428.00 KiB/s, done. Resolving deltas: 100% (9293/9293), done. Checking connectivity... done.
在 $GOPATH 下的src 目录创建 github.com/ontio 路径,将 ontology 源码目录 移至 该路径下。 blockchain@ThinkPad-T460:~/GoPath/src$ mkdir -p github.com/ontio blockchain@ThinkPad-T460:~/GoPath/src$ mv ontology/ github.com/ontio/
用第三方包管理工具glide拉取依赖库
进入 $GOPATH/src/github.com/ontio/ontology 目录,输入 glide install blockchain@ThinkPad-T460:~/GoPath/src$ cd $GOPATH/src/github.com/ontio/ontology blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ ll total 152 drwxrwxr-x 19 blockchain blockchain 4096 4月 17 17:18 ./ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:19 ../ drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 account/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 cli/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 common/ -rw-rw-r-- 1 blockchain blockchain 568 4月 17 17:18 config-dbft.json -rw-rw-r-- 1 blockchain blockchain 760 4月 17 17:18 config.json -rw-rw-r-- 1 blockchain blockchain 510 4月 17 17:18 config-solo.json drwxrwxr-x 7 blockchain blockchain 4096 4月 17 17:18 consensus/ drwxrwxr-x 12 blockchain blockchain 4096 4月 17 17:18 core/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 docs/ drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 errors/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 events/ drwxrwxr-x 8 blockchain blockchain 4096 4月 17 17:18 .git/ -rw-rw-r-- 1 blockchain blockchain 210 4月 17 17:18 .gitignore -rw-rw-r-- 1 blockchain blockchain 1295 4月 17 17:18 glide.yaml drwxrwxr-x 9 blockchain blockchain 4096 4月 17 17:18 http/ -rw-rw-r-- 1 blockchain blockchain 7651 4月 17 17:18 LICENSE -rw-rw-r-- 1 blockchain blockchain 5938 4月 17 17:18 main.go -rw-rw-r-- 1 blockchain blockchain 431 4月 17 17:18 Makefile drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 merkle/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 net/ -rw-rw-r-- 1 blockchain blockchain 1651 4月 17 17:18 nodectl.go -rw-rw-r-- 1 blockchain blockchain 9621 4月 17 17:18 README_CN.md -rw-rw-r-- 1 blockchain blockchain 9883 4月 17 17:18 README.md drwxrwxr-x 10 blockchain blockchain 4096 4月 17 17:18 smartcontract/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 test/ -rw-rw-r-- 1 blockchain blockchain 54 4月 17 17:18 TODO -rwxrwxr-x 1 blockchain blockchain 96 4月 17 17:18 .travis.yml* drwxrwxr-x 5 blockchain blockchain 4096 4月 17 17:18 txnpool/ drwxrwxr-x 7 blockchain blockchain 4096 4月 17 17:18 validator/ drwxrwxr-x 4 blockchain blockchain 4096 4月 17 17:18 vm/ blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ glide install [INFO] Lock file (glide.lock) does not exist. Performing update. [INFO] Downloading dependencies. Please wait... [INFO] --> Fetching updates for github.com/dnaproject/gopass. [INFO] --> Fetching updates for github.com/itchyny/base58-go. [INFO] --> Fetching updates for github.com/bitly/go-simplejson. [INFO] --> Fetching updates for github.com/hashicorp/golang-lru. [INFO] --> Fetching updates for github.com/gorilla/websocket. [INFO] --> Fetching updates for google.golang.org/genproto. [INFO] --> Fetching updates for github.com/pborman/uuid. [INFO] --> Fetching updates for github.com/syndtr/goleveldb. [INFO] --> Fetching updates for github.com/urfave/cli. [INFO] --> Fetching updates for github.com/orcaman/concurrent-map. [INFO] --> Fetching updates for github.com/whyrusleeping/tar-utils. [INFO] --> Fetching updates for golang.org/x/text. [INFO] --> Fetching updates for golang.org/x/crypto. [INFO] --> Fetching updates for github.com/Workiva/go-datastructures. [INFO] --> Fetching updates for github.com/AsynkronIT/goconsole. [INFO] --> Fetching updates for golang.org/x/sys. [INFO] --> Fetching updates for golang.org/x/net. [INFO] --> Fetching updates for google.golang.org/grpc. [INFO] --> Setting version for github.com/bitly/go-simplejson to v0.5.0. [INFO] --> Setting version for github.com/gorilla/websocket to v1.2.0. [INFO] --> Setting version for github.com/urfave/cli to v1.20.0. [INFO] --> Setting version for github.com/pborman/uuid to v1.1. [INFO] Resolving imports [INFO] --> Fetching updates for github.com/ontio/ontology-crypto. [INFO] --> Fetching updates for github.com/golang/crypto. [INFO] --> Fetching updates for github.com/emirpasic/gods. [INFO] --> Fetching updates for github.com/gogo/protobuf. [INFO] --> Fetching updates for github.com/ontio/ontology-eventbus. [INFO] --> Fetching updates for github.com/golang/snappy. [INFO] --> Fetching updates for github.com/davecgh/go-spew. [INFO] --> Fetching updates for github.com/pmezard/go-difflib. [INFO] Downloading dependencies. Please wait... [INFO] --> Fetching updates for github.com/valyala/bytebufferpool. [INFO] --> Fetching updates for github.com/stretchr/testify. [INFO] Setting references for remaining imports [INFO] Exporting resolved dependencies... [INFO] --> Exporting github.com/bitly/go-simplejson [INFO] --> Exporting github.com/dnaproject/gopass [INFO] --> Exporting github.com/gorilla/websocket [INFO] --> Exporting github.com/itchyny/base58-go [INFO] --> Exporting google.golang.org/genproto [INFO] --> Exporting github.com/pborman/uuid [INFO] --> Exporting github.com/ontio/ontology-crypto [INFO] --> Exporting github.com/urfave/cli [INFO] --> Exporting github.com/whyrusleeping/tar-utils [INFO] --> Exporting github.com/AsynkronIT/goconsole [INFO] --> Exporting github.com/Workiva/go-datastructures [INFO] --> Exporting github.com/emirpasic/gods [INFO] --> Exporting golang.org/x/text [INFO] --> Exporting golang.org/x/sys [INFO] --> Exporting github.com/ontio/ontology-eventbus [INFO] --> Exporting golang.org/x/net [INFO] --> Exporting golang.org/x/crypto [INFO] --> Exporting google.golang.org/grpc [INFO] --> Exporting github.com/hashicorp/golang-lru [INFO] --> Exporting github.com/gogo/protobuf [INFO] --> Exporting github.com/orcaman/concurrent-map [INFO] --> Exporting github.com/syndtr/goleveldb [INFO] --> Exporting github.com/golang/crypto [INFO] --> Exporting github.com/golang/snappy [INFO] --> Exporting github.com/stretchr/testify [INFO] --> Exporting github.com/valyala/bytebufferpool [INFO] --> Exporting github.com/pmezard/go-difflib [INFO] --> Exporting github.com/davecgh/go-spew [INFO] Replacing existing vendor dependencies [INFO] Project relies on 25 dependencies. blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$
用make编译源码
在 ontology 源码目录 下,输入 make blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ make go build -ldflags "-X github.com/ontio/ontology/common/config.Version=v0.6-98-g521b" -o ontology main.go go build -ldflags "-X main.Version=v0.6-98-g521b" nodectl.go blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$
成功编译后,会生成两个可执行程序。 blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ ll total 28468 drwxrwxr-x 20 blockchain blockchain 4096 4月 17 17:29 ./ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:19 ../ drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 account/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 cli/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 common/ -rw-rw-r-- 1 blockchain blockchain 568 4月 17 17:18 config-dbft.json -rw-rw-r-- 1 blockchain blockchain 760 4月 17 17:18 config.json -rw-rw-r-- 1 blockchain blockchain 510 4月 17 17:18 config-solo.json drwxrwxr-x 7 blockchain blockchain 4096 4月 17 17:18 consensus/ drwxrwxr-x 12 blockchain blockchain 4096 4月 17 17:18 core/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 docs/ drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 errors/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 events/ drwxrwxr-x 8 blockchain blockchain 4096 4月 17 17:28 .git/ -rw-rw-r-- 1 blockchain blockchain 210 4月 17 17:18 .gitignore -rw-rw-r-- 1 blockchain blockchain 3781 4月 17 17:26 glide.lock -rw-rw-r-- 1 blockchain blockchain 1295 4月 17 17:18 glide.yaml drwxrwxr-x 9 blockchain blockchain 4096 4月 17 17:18 http/ -rw-rw-r-- 1 blockchain blockchain 7651 4月 17 17:18 LICENSE -rw-rw-r-- 1 blockchain blockchain 5938 4月 17 17:18 main.go -rw-rw-r-- 1 blockchain blockchain 431 4月 17 17:18 Makefile drwxrwxr-x 2 blockchain blockchain 4096 4月 17 17:18 merkle/ drwxrwxr-x 6 blockchain blockchain 4096 4月 17 17:18 net/ -rwxrwxr-x 1 blockchain blockchain 11850671 4月 17 17:29 nodectl* -rw-rw-r-- 1 blockchain blockchain 1651 4月 17 17:18 nodectl.go -rwxrwxr-x 1 blockchain blockchain 17132760 4月 17 17:29 ontology* -rw-rw-r-- 1 blockchain blockchain 9621 4月 17 17:18 README_CN.md -rw-rw-r-- 1 blockchain blockchain 9883 4月 17 17:18 README.md drwxrwxr-x 10 blockchain blockchain 4096 4月 17 17:18 smartcontract/ drwxrwxr-x 3 blockchain blockchain 4096 4月 17 17:18 test/ -rw-rw-r-- 1 blockchain blockchain 54 4月 17 17:18 TODO -rwxrwxr-x 1 blockchain blockchain 96 4月 17 17:18 .travis.yml* drwxrwxr-x 5 blockchain blockchain 4096 4月 17 17:18 txnpool/ drwxrwxr-x 7 blockchain blockchain 4096 4月 17 17:18 validator/ drwxr-xr-x 5 blockchain blockchain 4096 4月 17 17:26 vendor/ drwxrwxr-x 4 blockchain blockchain 4096 4月 17 17:18 vm/ blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ ontology: 节点程序 nodectl: 以命令行方式提供的节点控制程序
创建ONT钱包文件
创建钱包 blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ ./nodectl wallet --create --name wallet.dat --password passwordtest Wallet File: 'wallet.dat' public key: 12020277d2895bab1eb2bbc1224374ea64af7e0603f3121fb18df7190895a74307e234 hex address: 01bc336ce9e0286f9d241f812bacc6892db38d55 base58 address: TA8P8gEZZcgX227CNfZKpMaRLSaXwfdJrq blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$
展示钱包 blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ ./nodectl wallet --list account Password: Wallet File: 'wallet.dat' public key: 12020277d2895bab1eb2bbc1224374ea64af7e0603f3121fb18df7190895a74307e234 hex address: 01bc336ce9e0286f9d241f812bacc6892db38d55 base58 address: TA8P8gEZZcgX227CNfZKpMaRLSaXwfdJrq blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$
服务器部署 单机部署 多机部署配置 在公共测试网上部署节点
单机部署
在主目录下新建一个目录 blockchain@ThinkPad-T460:~$ mkdir ~/node
进入 ontology 的 source目录 blockchain@ThinkPad-T460:~/GoPath/src/github.com/ontio/ontology$ pwd /home/blockchain/GoPath/src/github.com/ontio/ontology
把该目录下的: 节点程序ontology 节点控制程序nodectl 钱包文件wallet.dat config-solo.config 配置文件
复制到 新建的 node 文件夹下,将 config-solo.config 重命名为 config.json,编辑 config.json,修改Bookkeepers配置,用钱包公钥(public key)替换。 blockchain@ThinkPad-T460:~/node$ pwd /home/blockchain/node blockchain@ThinkPad-T460:~/node$ mv config-solo.json config.json blockchain@ThinkPad-T460:~/node$ vim config.json blockchain@ThinkPad-T460:~/node$
config.json 修改后的内容如下: { "Configuration": { "Magic": 7630401, "Version": 23, "SeedList": [ "127.0.0.1:20338" ], "Bookkeepers": [ "12020277d2895bab1eb2bbc1224374ea64af7e0603f3121fb18df7190895a74307e234" ], "HttpRestPort": 20334, "HttpWsPort":20335, "HttpJsonPort": 20336, "HttpLocalPort": 20337, "NodePort": 20338, "NodeConsensusPort": 20389, "PrintLevel": 1, "IsTLS": false, "MaxTransactionInBlock": 50000, "MultiCoreNum": 4, "ConsensusType":"solo" } }
此时的目录结构为: blockchain@ThinkPad-T460:~$ tree node/ node/ ├── config-solo.json ├── nodectl ├── ontology └── wallet.dat
启动节点,并在出现 Password: 提示后输入节点的钱包密码 blockchain@ThinkPad-T460:~/node$ ./nodectl --h NAME: nodectl - command line tool for Ontology blockchain USAGE: nodectl [global options] command [command options] [args] VERSION: v0.6-98-g521b COMMANDS: test run test routine transfer user ont transfer wallet user wallet operation help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --ip value node's ip address (default: "localhost") --port value node's RPC port (default: "20337") --help, -h show help --version, -v print the version blockchain@ThinkPad-T460:~/node$ ./nodectl -v nodectl version v0.6-98-g521b blockchain@ThinkPad-T460:~/node$ ./ontology 2018/04/17 20:20:09.187038 [TRACE] GID 1, main() main.go:76 Node version: v0.6-98-g521b 2018/04/17 20:20:09.187168 [INFO ] GID 1, 0. Open the account Password: 2018/04/17 20:20:16.264077 [INFO ] GID 1, 1. Loading the Ledger 2018/04/17 20:20:16.572088 [INFO ] GID 1, InitCurrentBlock currentBlockHash a7055f33ef44581180d4e73ba0e2a9b421395ea462ad2918b36b4c5ed2db2850 currentBlockHeight 5 2018/04/17 20:20:16.572833 [INFO ] GID 1, 3. Start the transaction pool server 2018/04/17 20:20:16.574710 [INFO ] GID 1, 4. Start the P2P networks 2018/04/17 20:20:16.574895 [INFO ] GID 1, Init node ID to 0xab5b89d277020212 2018/04/17 20:20:16.574985 [INFO ] GID 76, txpool actor started and be ready to receive txPool msg 2018/04/17 20:20:16.575106 [INFO ] GID 77, txpool-tx actor started and be ready to receive tx msg 2018/04/17 20:20:16.575163 [INFO ] GID 1, WaitForSyncBlkFinish... current block height is 5 ,current header height is 5 2018/04/17 20:20:16.575223 [INFO ] GID 1, 5. Start Consensus Services 2018/04/17 20:20:16.575336 [INFO ] GID 1, ConsensusType:solo 2018/04/17 20:20:16.575416 [INFO ] GID 79, statefull-validator: started and be ready to receive txn 2018/04/17 20:20:16.574755 [INFO ] GID 75, txpool-verify actor: started and be ready to receive validator's msg 2018/04/17 20:20:16.574904 [INFO ] GID 78, stateless-validator: started and be ready to receive txn 2018/04/17 20:20:16.575480 [WARN ] GID 84, p2p actor started 2018/04/17 20:20:16.575629 [WARN ] GID 90, solo actor started 2018/04/17 20:20:21.575664 [INFO ] GID 1, --Start the RPC interface 2018/04/17 20:20:22.575948 [INFO ] GID 37, incr validator block height 4294967295 != ledger block height 5 2018/04/17 20:20:22.576096 [INFO ] GID 37, current block Height 5, incrValidateHeight 5 2018/04/17 20:20:22.616021 [INFO ] GID 37, solo actor receives block complete event. block height= 6 2018/04/17 20:20:27.576408 [INFO ] GID 29, BlockHeight = 6 2018/04/17 20:20:28.576309 [INFO ] GID 100, current block Height 6, incrValidateHeight 6 2018/04/17 20:20:28.617860 [INFO ] GID 100, solo actor receives block complete event. block height= 7 2018/04/17 20:20:33.576793 [INFO ] GID 29, BlockHeight = 7 2018/04/17 20:20:34.575948 [INFO ] GID 112, current block Height 7, incrValidateHeight 6 2018/04/17 20:20:34.619755 [INFO ] GID 112, solo actor receives block complete event. block height= 8 2018/04/17 20:20:39.576459 [INFO ] GID 29, BlockHeight = 8 2018/04/17 20:20:40.575903 [INFO ] GID 123, current block Height 8, incrValidateHeight 6 2018/04/17 20:20:40.621813 [INFO ] GID 123, solo actor receives block complete event. block height= 9 2018/04/17 20:20:45.576501 [INFO ] GID 29, BlockHeight = 9 2018/04/17 20:20:46.575818 [INFO ] GID 56, current block Height 9, incrValidateHeight 6 2018/04/17 20:20:46.615421 [INFO ] GID 56, solo actor receives block complete event. block height= 10
至此,单机部署大功告成,此时的目录结构为: blockchain@ThinkPad-T460:~$ tree node/ node/ ├── ActorLog │   ├── 2018-04-17_19.52.43_LOG.log │   ├── 2018-04-17_19.53.58_LOG.log │   ├── 2018-04-17_20.15.45_LOG.log │   ├── 2018-04-17_20.18.01_LOG.log │   └── 2018-04-17_20.20.09_LOG.log ├── Chain │   ├── block │   │   ├── 000004.ldb │   │   ├── 000005.log │   │   ├── CURRENT │   │   ├── LOCK │   │   ├── LOG │   │   └── MANIFEST-000006 │   ├── ledgerevent │   │   ├── 000004.ldb │   │   ├── 000005.log │   │   ├── CURRENT │   │   ├── LOCK │   │   ├── LOG │   │   └── MANIFEST-000006 │   ├── merkle_tree.db │   └── states │   ├── 000002.ldb │   ├── 000005.ldb │   ├── 000006.log │   ├── CURRENT │   ├── LOCK │   ├── LOG │   └── MANIFEST-000007 ├── config.json ├── Log │   ├── 2018-04-17_19.52.43_LOG.log │   ├── 2018-04-17_19.53.58_LOG.log │   └── 2018-04-17_20.20.09_LOG.log ├── nodectl ├── ontology └── wallet.dat 6 directories, 32 files blockchain@ThinkPad-T460:~$
多机部署配置
网络环境下,最少需要4个节点(共识节点)完成部署。由于笔者资源有限,此处省略一万字。
在公共测试网上部署
只需 将 单机部署的 config.json 中的内容 改为: { "Configuration": { "Magic": 7630401, "Version": 23, "SeedList": [ "139.219.108.204:20338", "139.219.111.50:20338", "139.219.69.70:20338", "40.125.165.118:20338" ], "Bookkeepers": [ "1202021c6750d2c5d99813997438cee0740b04a73e42664c444e778e001196eed96c9d", "12020339541a43af2206358714cf6bd385fc9ac8b5df554fec5497d9e947d583f985fc", "120203bdf0d966f98ff4af5c563c4a3e2fe499d98542115e1ffd75fbca44b12c56a591", "1202021401156f187ec23ce631a489c3fa17f292171009c6c3162ef642406d3d09c74d" ], "HttpRestPort": 20334, "HttpWsPort":20335, "HttpJsonPort": 20336, "HttpLocalPort": 20337, "NodePort": 20338, "NodeConsensusPort": 20339, "PrintLevel": 1, "IsTLS": false, "MaxTransactionInBlock": 60000, "MultiCoreNum": 4 } }
然后启动 ontology 程序,即可。 blockchain@ThinkPad-T460:~/node$ ./ontology 2018/04/18 21:57:54.244387 [TRACE] GID 1, main() main.go:76 Node version: v0.6-98-g521b 2018/04/18 21:57:54.247436 [INFO ] GID 1, 0. Open the account Password: 2018/04/18 21:58:02.643795 [INFO ] GID 1, 1. Loading the Ledger 2018/04/18 21:58:02.948874 [INFO ] GID 1, InitCurrentBlock currentBlockHash 0000000000000000000000000000000000000000000000000000000000000000 currentBlockHeight 0 2018/04/18 21:58:02.998638 [INFO ] GID 1, 3. Start the transaction pool server 2018/04/18 21:58:03.001169 [INFO ] GID 1, 4. Start the P2P networks 2018/04/18 21:58:03.001337 [INFO ] GID 62, txpool-tx actor started and be ready to receive tx msg 2018/04/18 21:58:03.001338 [INFO ] GID 1, Init node ID to 0xab5b89d277020212 2018/04/18 21:58:03.001473 [INFO ] GID 63, stateless-validator: started and be ready to receive txn 2018/04/18 21:58:03.001595 [INFO ] GID 64, statefull-validator: started and be ready to receive txn 2018/04/18 21:58:03.001665 [INFO ] GID 61, txpool actor started and be ready to receive txPool msg 2018/04/18 21:58:03.002260 [WARN ] GID 75, p2p actor started 2018/04/18 21:58:03.004022 [INFO ] GID 60, txpool-verify actor: started and be ready to receive validator's msg 2018/04/18 21:58:03.005939 [INFO ] GID 73, Connect node 192.168.30.215:43184 connect with 139.219.69.70:20338 with tcp 2018/04/18 21:58:03.005984 [INFO ] GID 74, Connect node 192.168.30.215:49756 connect with 40.125.165.118:20338 with tcp 2018/04/18 21:58:03.006177 [INFO ] GID 72, Connect node 192.168.30.215:34600 connect with 139.219.111.50:20338 with tcp 2018/04/18 21:58:03.006416 [INFO ] GID 71, Connect node 192.168.30.215:60956 connect with 139.219.108.204:20338 with tcp 2018/04/18 21:58:03.010987 [INFO ] GID 84, Node info: 2018/04/18 21:58:03.012163 [INFO ] GID 99, Node info: 2018/04/18 21:58:03.012696 [INFO ] GID 84, state = 4 2018/04/18 21:58:03.014218 [INFO ] GID 84, id = 0xaf431a5439030212 2018/04/18 21:58:03.014307 [INFO ] GID 84, addr = 139.219.111.50 2018/04/18 21:58:03.014509 [INFO ] GID 84, conn = &{{0xc4200d4380}} 2018/04/18 21:58:03.014584 [INFO ] GID 84, cap = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2018/04/18 21:58:03.014638 [INFO ] GID 84, version = 0 2018/04/18 21:58:03.014691 [INFO ] GID 84, services = 0 2018/04/18 21:58:03.014744 [INFO ] GID 84, port = 20338 2018/04/18 21:58:03.014797 [INFO ] GID 84, relay = true 2018/04/18 21:58:03.014849 [INFO ] GID 84, height = 284191 2018/04/18 21:58:03.014901 [INFO ] GID 84, conn cnt = 0 2018/04/18 21:58:03.012915 [INFO ] GID 115, Node info: 2018/04/18 21:58:03.015069 [INFO ] GID 115, state = 4 2018/04/18 21:58:03.015119 [INFO ] GID 115, id = 0x186f150114020212 2018/04/18 21:58:03.015170 [INFO ] GID 115, addr = 40.125.165.118 2018/04/18 21:58:03.015280 [INFO ] GID 115, conn = &{{0xc42021c080}} 2018/04/18 21:58:03.015353 [INFO ] GID 115, cap = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2018/04/18 21:58:03.015406 [INFO ] GID 115, version = 0 2018/04/18 21:58:03.015459 [INFO ] GID 115, services = 0 2018/04/18 21:58:03.015511 [INFO ] GID 115, port = 20338 2018/04/18 21:58:03.015563 [INFO ] GID 115, relay = true 2018/04/18 21:58:03.015613 [INFO ] GID 115, height = 284191 2018/04/18 21:58:03.015672 [INFO ] GID 115, conn cnt = 0 2018/04/18 21:58:03.014138 [INFO ] GID 41, Node info: 2018/04/18 21:58:03.015764 [INFO ] GID 41, state = 4 2018/04/18 21:58:03.015818 [INFO ] GID 41, id = 0xc5d250671c020212 2018/04/18 21:58:03.015861 [INFO ] GID 41, addr = 139.219.108.204 2018/04/18 21:58:03.015941 [INFO ] GID 41, conn = &{{0xc4200d4280}} 2018/04/18 21:58:03.015994 [INFO ] GID 41, cap = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2018/04/18 21:58:03.016124 [INFO ] GID 41, version = 0 2018/04/18 21:58:03.016198 [INFO ] GID 41, services = 0 2018/04/18 21:58:03.016267 [INFO ] GID 41, port = 20338 2018/04/18 21:58:03.016329 [INFO ] GID 41, relay = true 2018/04/18 21:58:03.016386 [INFO ] GID 41, height = 284191 2018/04/18 21:58:03.016440 [INFO ] GID 41, conn cnt = 0 2018/04/18 21:58:03.016571 [INFO ] GID 99, state = 4 2018/04/18 21:58:03.016819 [INFO ] GID 99, id = 0xf966d9f0bd030212 2018/04/18 21:58:03.016917 [INFO ] GID 99, addr = 139.219.69.70 2018/04/18 21:58:03.017029 [INFO ] GID 99, conn = &{{0xc4201c6d80}} 2018/04/18 21:58:03.017136 [INFO ] GID 99, cap = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2018/04/18 21:58:03.017197 [INFO ] GID 99, version = 0 2018/04/18 21:58:03.017217 [INFO ] GID 102, The ip address is 10.0.0.5:20338 id is 0x186f150114020212 2018/04/18 21:58:03.017272 [INFO ] GID 99, services = 0 2018/04/18 21:58:03.017343 [INFO ] GID 99, port = 20338 2018/04/18 21:58:03.017293 [INFO ] GID 102, The ip address is 10.0.0.8:20338 id is 0x4708d88e2030212 2018/04/18 21:58:03.017414 [INFO ] GID 99, relay = true 2018/04/18 21:58:03.017480 [INFO ] GID 99, height = 284191 2018/04/18 21:58:03.017543 [INFO ] GID 99, conn cnt = 0 2018/04/18 21:58:03.017442 [INFO ] GID 102, The ip address is 10.0.0.6:20338 id is 0xc5d250671c020212 2018/04/18 21:58:03.017747 [INFO ] GID 102, The ip address is 10.0.0.7:20338 id is 0xf966d9f0bd030212 2018/04/18 21:58:03.017809 [INFO ] GID 102, The ip address is 61.48.40.26:20338 id is 0xab5b89d277020212 2018/04/18 21:58:03.017842 [INFO ] GID 105, The ip address is 10.0.0.4:20338 id is 0xaf431a5439030212 2018/04/18 21:58:03.017891 [INFO ] GID 105, The ip address is 10.0.0.6:20338 id is 0xc5d250671c020212 2018/04/18 21:58:03.017933 [INFO ] GID 105, The ip address is 10.0.0.8:20338 id is 0x4708d88e2030212 2018/04/18 21:58:03.017977 [INFO ] GID 105, The ip address is 10.0.0.7:20338 id is 0xf966d9f0bd030212 2018/04/18 21:58:03.018023 [INFO ] GID 105, The ip address is 61.48.40.26:20338 id is 0xab5b89d277020212 2018/04/18 21:58:03.021864 [INFO ] GID 107, The ip address is 10.0.0.8:20338 id is 0x4708d88e2030212 2018/04/18 21:58:03.022122 [INFO ] GID 107, The ip address is 10.0.0.6:20338 id is 0xc5d250671c020212 2018/04/18 21:58:03.022200 [INFO ] GID 107, The ip address is 10.0.0.4:20338 id is 0xaf431a5439030212 2018/04/18 21:58:03.022261 [INFO ] GID 107, The ip address is 10.0.0.5:20338 id is 0x186f150114020212 2018/04/18 21:58:03.022325 [INFO ] GID 107, The ip address is 61.48.40.26:20338 id is 0xab5b89d277020212 2018/04/18 21:58:03.022521 [INFO ] GID 109, The ip address is 61.48.40.26:20338 id is 0xab5b89d277020212 2018/04/18 21:58:03.022595 [INFO ] GID 109, The ip address is 10.0.0.7:20338 id is 0xf966d9f0bd030212 2018/04/18 21:58:03.022656 [INFO ] GID 109, The ip address is 10.0.0.8:20338 id is 0x4708d88e2030212 2018/04/18 21:58:03.022717 [INFO ] GID 109, The ip address is 10.0.0.4:20338 id is 0xaf431a5439030212 2018/04/18 21:58:03.022775 [INFO ] GID 109, The ip address is 10.0.0.5:20338 id is 0x186f150114020212 2018/04/18 21:58:06.001887 [INFO ] GID 1, WaitForSyncBlkFinish... current block height is 0 ,current header height is 0 2018/04/18 21:58:07.470968 [INFO ] GID 167, inv request block hash: 27906e0ddda8afb1a82f0aa14793e586a0b75628bfc256e933a51158d1b063aa
原文链接:转载请注明出处,谢谢!
获取更多信息,可关注微信公众号
区块链
2018-04-06 21:37:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链银行应用探索(Hyperledger fabric)
本文节选自电子书《Netkiller Blockchain 手札》
Netkiller Blockchain 手札
本文作者提供有偿顾问服务,有意向致电 13113668890
Mr. Neo Chan, 陈景峯(BG7NYT)
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890
文档始创于2018-02-10
版权 © 2018 Netkiller(Neo Chan). All rights reserved.
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
微信订阅号 netkiller-ebook (微信扫描二维码) QQ:13721218 请注明“读者”
QQ群:128659835 请注明“读者” 网站:http://www.netkiller.cn
内容摘要
这一部关于区块链开发及运维的电子书。
为什么会写区块链电子书?因为2018年是区块链年。
这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。
这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。
电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master
本文采用碎片化写作,原文会不定期更新,请尽量阅读原文。
http://www.netkiller.cn/blockchain/index.html
您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html
==============================
33.8. Hyperledger fabric 银行应用探索
一直想写这篇文章,可是我个人对银行系统了解甚少,网上很多文章有多拿银行来举例,铺天盖地的文章,却没有一篇告诉你究竟如何落地。
其中不少文章中提到银行SWIFT系统,什么事 SWIFT 呢?
33.8.1. 电汇年代
这要从电汇说起,年轻时候上学,每个学期都有一笔学费,那时主要交通是铁路,携带现金非常不便,母亲将5000元人民币缝在我的贴身内裤边上。到了学校拆开线取出,味道好极了,呵呵。
后来从同学那里得知,可以使用邮局汇款,首先去邮局,拿一个特殊信封填好地址,然后将钱交给工作人员。一周以后信封皮会记挂号信到收款人手里,那个这个信封去指定邮局取款。
记得第二学期就出现电汇,银行提供的电汇服务比邮政的速度快,也比邮局更方便。 用户A ----- 汇票 -----> 用户B | | 申请 提款 | | V V 银行A ----- 划账 -----> 银行B
电汇是用户A银行提出申请,将钱交给A银行。银行马上通过网络通知B银行,用户B就可以提款。没有多久邮政的系统也换成这套系统了。
那个年代只有拨号网络,帧中继,ATM(是一种网络,不是ATM取款机) 等窄带网络,现在用宽带上网的90后无法想法那个网速。
33.8.2. 通存通取年代
ISDN,DDN 专线的出现,才有了稳定窄带通信,银行网点互联成为可能。
MasterCard万事达,Visa维萨卡率先进入中国。很快银行内部就能实现转账了,ATM机互联成为可能,这时结算全部使用 MasterCard万事达,Visa维萨卡。银行需向MasterCard万事达,Visa维萨机构支付费用。促使一些有实力银行研发自己的内部系统,但是这些系统仅限内部使用,如果银行卡上没有MasterCard/Visa 的只能本银行使用,无法跨行。没有实力的银行则会选择门槛稍低的 INTERLINK、PLUS 等机构合作。
98年在学校寝室,一位同学拿了一张牡丹万事达卡,全寝室传看,那个年代在哈尔滨提款机都不普及,只有较大的银行网点才有。
2000来深圳,那时有个深银联,深圳首先实现了跨行业务,2年后(2002年)银联出现,国家强推,所有银行开始支持银联。当时银行卡上同时有两个标示,MasterCard/Visa 和 银联。
同年招商银行率先推出网上银行。招商银行电脑部一度成为IT红黑榜上评价最高的公司,可惜没有跟上互联网的步伐......
目前新开的银行卡已经看不到 MasterCard/Visa 标志了,甚至香港的银行卡也默认使用银联,出国旅游耍刷 UnionPay 跨境支付非常方便。
33.8.3. 跨境汇款
跨境汇款你别想像银联一样,填写一个姓名一个账号,点击一下转账按钮这样的操作。每个国家的政策也不同,政策不允许这样操作。
跨境只能汇款,无法转账,又回到了电汇时代,只是不再主要纸质的汇票了 用户A ----- SWIFT CODE -----> 用户B | | 申请 提款 | | V V 银行A -------- SWIFT --------> 银行B
跨境汇款必须依赖 SWIFT 系统,由于我国的政策问题,个人很少涉及跨境业务,所以多数人对 SWIFT 不是很了解。 如果你在香港开一张银行卡例如汇丰银行,拿到银行给的信封里,就会有一个 SWIFT 码。
你会遇到这个问题,无法输入对方的名字,例如: Nickname:netkiller English name: Neo chen Nippon name: ちんけいほう (音訳) Korean name: 천징봉 Thailand name: ภูมิภาพภูเขา Vietnam: Trần Cảnh Phong
所以就需要每个银行一个代码,每个账号一个代码,由于全世界银行太多,银行系统各种各样,每个国家的语言也不同,难以达成一致,联合国也没有能力统一标准。新建一套系统不太可能,所以80年代的标准仍然沿用至今。
使用 SWIFT 面临的问题 网络速度慢 手续费高 技术落后 不能实时到账 脆弱容易被攻击
SWIFT的诞生甚至早于数字时代,可以说是目前最好的跨境通讯和交易系统,但它的确需要与时俱进。
牵头做一个世界银联不太可能,世界各国银行无法想政府一样,一个红头文件,下面招办,行政手段推动。且业务差异大,系统复杂超乎想象,这个中心数据库谁来管理呢?
SWIFT早就意识到了这些问题,并宣布进军区块链,同时加入超级账本项目(Hyperledger Project)成为会员。可以肯定下一个版本的SWIFT灰使用区块链技术,一步一步逐渐取代就系统。
33.8.4. 区块链能做什么
区块链可以解决银行哪些痛点,先说说 SWIFT 2.0 (区块链SWIFT)我想SWIFT仍然会兼容现有的协议。SWIFT CODE协议仍然会保留。短时间不可能被取代SWIFT CODE因为这个体系已经使用很多年。 用户A ----- SWIFT CODE -----> 用户B | | 申请 提款 | | V V 银行A -------- 划账 --------> 银行B \ / \ / V V +---------------------------------+ | Hyperledger Fabric 盟链 | +---------------------------------+ | Smart Contract | +---------------------------------+
后端将会被区块链取代
另外银行的跨国业务将会走自己的区块链,不再依赖 SWIFT, 因为费用等问题。 用户A --------- 转账 ---------> 用户B | | 申请 提款 | | V V 英国渣打银行 ----- 划账 -----> 深圳渣打银行 \ / \ / V V +---------------------------------+ | Hyperledger Fabric 盟链 | +---------------------------------+ | Smart Contract | +---------------------------------+
33.8.5. 智能合约怎么落地
我对银行业务实在不了解,这里只能设想一下场景。下面是我之前写的一个Token合约,我将它应用到这个场景中 package main import ( "encoding/json" "fmt" "strconv" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type Msg struct{ Status bool `json:"Status"` Code int `json:"Code"` Message string `json:"Message"` } type Currency struct{ TokenName string `json:"TokenName"` TokenSymbol string `json:"TokenSymbol"` TotalSupply float64 `json:"TotalSupply"` } type Token struct { Lock bool `json:"Lock"` Currency map[string]Currency `json:"Currency"` } func (token *Token) transfer (_from *Account, _to *Account, _currency string, _value float64) []byte{ var rev []byte if (token.Lock){ msg := &Msg{Status: false, Code: 0, Message: "锁仓状态,停止一切转账活动"} rev, _ = json.Marshal(msg) return rev } if(_from.Frozen ) { msg := &Msg{Status: false, Code: 0, Message: "From 账号冻结"} rev, _ = json.Marshal(msg) return rev } if( _to.Frozen) { msg := &Msg{Status: false, Code: 0, Message: "To 账号冻结"} rev, _ = json.Marshal(msg) return rev } if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ = json.Marshal(msg) return rev } if(_from.BalanceOf[_currency] >= _value){ _from.BalanceOf[_currency] -= _value; _to.BalanceOf[_currency] += _value; msg := &Msg{Status: true, Code: 0, Message: "转账成功"} rev, _ = json.Marshal(msg) return rev }else{ msg := &Msg{Status: false, Code: 0, Message: "余额不足"} rev, _ = json.Marshal(msg) return rev } } func (token *Token) initialSupply(_name string, _symbol string, _supply float64, _account *Account) []byte{ if _,ok := token.Currency[_symbol]; ok { msg := &Msg{Status: false, Code: 0, Message: "已经存在"} rev, _ := json.Marshal(msg) return rev } if _account.BalanceOf[_symbol] > 0 { msg := &Msg{Status: false, Code: 0, Message: "账号中存在代币"} rev, _ := json.Marshal(msg) return rev }else{ token.Currency[_symbol] = Currency{TokenName: _name, TokenSymbol: _symbol, TotalSupply: _supply} _account.BalanceOf[_symbol] = _supply msg := &Msg{Status: true, Code: 0, Message: "货币初始化成功"} rev, _ := json.Marshal(msg) return rev } } func (token *Token) mint(_currency string, _amount float64, _account *Account) []byte{ if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ := json.Marshal(msg) return rev } cur := token.Currency[_currency] cur.TotalSupply += _amount; token.Currency[_currency] = cur _account.BalanceOf[_currency] += _amount; msg := &Msg{Status: true, Code: 0, Message: "货币增发成功"} rev, _ := json.Marshal(msg) return rev } func (token *Token) burn(_currency string, _amount float64, _account *Account) []byte{ if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ := json.Marshal(msg) return rev } if(token.Currency[_currency].TotalSupply >= _amount){ cur := token.Currency[_currency] cur.TotalSupply -= _amount; token.Currency[_currency] = cur _account.BalanceOf[_currency] -= _amount; msg := &Msg{Status: false, Code: 0, Message: "货币回收成功"} rev, _ := json.Marshal(msg) return rev }else{ msg := &Msg{Status: false, Code: 0, Message: "回收失败,回收额度不足"} rev, _ := json.Marshal(msg) return rev } } func (token *Token) isCurrency(_currency string) bool { if _, ok := token.Currency[_currency]; ok { return true }else{ return false } } func (token *Token) setLock(_look bool) bool { token.Lock = _look return token.Lock } type Account struct { Name string `json:"Name"` Frozen bool `json:"Frozen"` BalanceOf map[string]float64 `json:"BalanceOf"` } func (account *Account) balance (_currency string) map[string]float64{ bal := map[string]float64{_currency:account.BalanceOf[_currency]} return bal } func (account *Account) balanceAll() map[string]float64{ return account.BalanceOf } // ----------- const TokenKey = "Token" // Define the Smart Contract structure type SmartContract struct { } func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) pb.Response { token := &Token{Currency: map[string]Currency{}} tokenAsBytes, err := json.Marshal(token) err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Init Token %s \n", string(tokenAsBytes)) } return shim.Success(nil) } func (s *SmartContract) Query(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() if function == "balance" { return s.balance(stub, args) } else if function == "balanceAll" { return s.balanceAll(stub, args) } else if function == "showAccount" { return s.showAccount(stub, args) } return shim.Error("Invalid Smart Contract function name.") } func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response { // Retrieve the requested Smart Contract function and arguments function, args := stub.GetFunctionAndParameters() // Route to the appropriate handler function to interact with the ledger appropriately if function == "initLedger" { return s.initLedger(stub, args) } else if function == "createAccount" { return s.createAccount(stub, args) } else if function == "initCurrency" { return s.initCurrency(stub, args) } else if function == "setLock" { return s.setLock(stub, args) } else if function == "transferToken" { return s.transferToken(stub, args) } else if function == "frozenAccount" { return s.frozenAccount(stub, args) } else if function == "mintToken" { return s.mintToken(stub, args) } else if function == "balance" { return s.balance(stub, args) } else if function == "balanceAll" { return s.balanceAll(stub, args) } else if function == "showAccount" { return s.showAccount(stub, args) } else if function == "showToken" { return s.showToken(stub, args) } return shim.Error("Invalid Smart Contract function name.") } func (s *SmartContract) createAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } key := args[0] name := args[0] existAsBytes,err := stub.GetState(key) fmt.Printf("GetState(%s) %s \n", key, string(existAsBytes)) if string(existAsBytes) != "" { fmt.Println("Failed to create account, Duplicate key.") return shim.Error("Failed to create account, Duplicate key.") } account := Account{ Name: name, Frozen: false, BalanceOf: map[string]float64{}} accountAsBytes, _ := json.Marshal(account) err = stub.PutState(key, accountAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("createAccount %s \n", string(accountAsBytes)) return shim.Success(accountAsBytes) } func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface, args []string) pb.Response { return shim.Success(nil) } func (s *SmartContract) showToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("GetState(%s)) %s \n", TokenKey, string(tokenAsBytes)) } return shim.Success(tokenAsBytes) } func (s *SmartContract) initCurrency(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } _name := args[0] _symbol:= args[1] _supply,_:= strconv.ParseFloat(args[2], 64) _account := args[3] coinbaseAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes)) coinbase := &Account{} json.Unmarshal(coinbaseAsBytes, &coinbase) token := Token{} existAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("GetState(%s)) %s \n", TokenKey, string(existAsBytes)) } json.Unmarshal(existAsBytes, &token) result := token.initialSupply(_name,_symbol,_supply, coinbase) tokenAsBytes, _ := json.Marshal(token) err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Init Token %s \n", string(tokenAsBytes)) } coinbaseAsBytes, _ = json.Marshal(coinbase) err = stub.PutState(_account, coinbaseAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes)) return shim.Success(result) } func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } _from := args[0] _to := args[1] _currency := args[2] _amount,_ := strconv.ParseFloat(args[3], 32) if(_amount <= 0){ return shim.Error("Incorrect number of amount") } fromAsBytes,err := stub.GetState(_from) if err != nil { return shim.Error(err.Error()) } fmt.Printf("fromAccount %s \n", string(fromAsBytes)) fromAccount := &Account{} json.Unmarshal(fromAsBytes, &fromAccount) toAsBytes,err := stub.GetState(_to) if err != nil { return shim.Error(err.Error()) } fmt.Printf("toAccount %s \n", string(toAsBytes)) toAccount := &Account{} json.Unmarshal(toAsBytes, &toAccount) tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token %s \n", string(toAsBytes)) token := Token{Currency: map[string]Currency{}} json.Unmarshal(tokenAsBytes, &token) result := token.transfer(fromAccount, toAccount, _currency, _amount) fmt.Printf("Result %s \n", string(result)) fromAsBytes, err = json.Marshal(fromAccount) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_from, fromAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("fromAccount %s \n", string(fromAsBytes)) } toAsBytes, err = json.Marshal(toAccount) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_to, toAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("toAccount %s \n", string(toAsBytes)) } return shim.Success(result) } func (s *SmartContract) mintToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 3") } _currency := args[0] _amount,_ := strconv.ParseFloat(args[1], 32) _account := args[2] coinbaseAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes)) } coinbase := &Account{} json.Unmarshal(coinbaseAsBytes, &coinbase) tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token before %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) result := token.mint(_currency, _amount, coinbase) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token after %s \n", string(tokenAsBytes)) coinbaseAsBytes, _ = json.Marshal(coinbase) err = stub.PutState(_account, coinbaseAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes)) } fmt.Printf("mintToken %s \n", string(tokenAsBytes)) return shim.Success(result) } func (s *SmartContract) setLock(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 2") } _look := args[0] tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) if(_look == "true"){ token.setLock(true) }else{ token.setLock(false) } tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("setLock - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) frozenAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } _account := args[0] _status := args[1] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) account := Account{} json.Unmarshal(accountAsBytes, &account) var status bool if(_status == "true"){ status = true; }else{ status = false } account.Frozen = status accountAsBytes, err = json.Marshal(account) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_account, accountAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("frozenAccount - end %s \n", string(accountAsBytes)) } return shim.Success(nil) } func (s *SmartContract) showAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } return shim.Success(accountAsBytes) } func (s *SmartContract) balance(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] _currency := args[1] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } account := Account{} json.Unmarshal(accountAsBytes, &account) result := account.balance(_currency) resultAsBytes, _ := json.Marshal(result) fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes)) return shim.Success(resultAsBytes) } func (s *SmartContract) balanceAll(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } account := Account{} json.Unmarshal(accountAsBytes, &account) result := account.balanceAll() resultAsBytes, _ := json.Marshal(result) fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes)) return shim.Success(resultAsBytes) } // The main function is only relevant in unit test mode. Only included here for completeness. func main() { // Create a new Smart Contract err := shim.Start(new(SmartContract)) if err != nil { fmt.Printf("Error creating new Smart Contract: %s", err) } }
部署链码,然后实例化链码 peer chaincode install -n token3 -v 1.0 -p chaincodedev/chaincode/token peer chaincode instantiate -C myc -n token3 -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')"
首先初始化账号,需要将现有账号同步到区块链上,这是上链操作。账号分为两种,一个是 coinbase 银行的总账号,另外是用户账号 peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["coinbase"]}'
初始化外币,银行有多少外币存量,初始化到 coinbase 账号 peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Chain","RMB","1000000000","coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Japan","JPY","1000000000","coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["USA","USD","1000000000","coinbase"]}'
为用户创建账号 peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["netkiller"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["neo"]}'
同步用户账号中的外币 peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","RMB","10000"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","USD","100"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","neo","RMB","10000"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","neo","USD","100"]}'
现在区块链上的用户资金已经跟数据库中的资金同步。
现在netkiller这个用户需要给neo转账 50 美金 peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["netkiller","neo","USD","50"]}'
现在 netkiller 账号中又 50美金,neo 账号中有 150美金。 peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["netkiller"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["neo"]}'
如果 neo 账号被法院申请冻结了,怎么办?可以使用下面方法设置冻结账号 peer chaincode invoke -C myc -n token3 -c '{"function":"frozenAccount","Args":["neo","true"]}'
此时 neo 账号被冻结,资金无法转入,转出。
33.8.6. 总结
以上仅仅是提供一个区块链学历案例,作者也设想了一个场景。但是真是情况怎样作者也不清楚。
上面通正合约没有考虑到汇率和中间价问题,需要进一步完善。
另外还需要一个守护进程订阅 Event 状态,每做一笔转账交易,event 会受到然后将这个操作同步到中心化数据库。 用户A ------------- 转账 ------------> 用户B | | 申请 提款 | | V V 英国渣打银行 --------- 划账 ---------> 深圳渣打银行 / \ / \ / \ / \ V V V V +----------+ +---------------------------------+ +----------+ | | | Hyperledger Fabric 盟链 | | | | Database | +---------------------------------+ | Database | | | | Smart Contract | | | +----------+ +---------------------------------+ +----------+ ^ o o ^ | | | | +----- Event -----+ +----- Event -----+
作者相关文章:
Hyperledger Fabric 积分代币上链方案
Hyperledger fabric Chaincode 开发详解
Hyperledger也能实现Token代币
食品安全溯源区块链解决方案探索
征信区块链解决方案探索(Hyperledger)
使用代币替代传统积分系统
竞猜活动区块链方案探索
游戏领域区块链探索
传统数据库也能实现区块链存储
区块链
2018-03-27 09:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hyperledger Fabric 积分代币上链方案
本文节选自电子书《Netkiller Blockchain 手札》
Netkiller Blockchain 手札
本文作者提供有偿顾问服务,有意向致电 13113668890
Mr. Neo Chan, 陈景峯(BG7NYT)
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890
文档始创于2018-02-10
版权 © 2018 Netkiller(Neo Chan). All rights reserved.
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
微信订阅号 netkiller-ebook (微信扫描二维码) QQ:13721218 请注明“读者”
QQ群:128659835 请注明“读者” 网站:http://www.netkiller.cn
内容摘要
这一部关于区块链开发及运维的电子书。
为什么会写区块链电子书?因为2018年是区块链年。
这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。
这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。
电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master
本文采用碎片化写作,原文会不定期更新,请尽量阅读原文。
http://www.netkiller.cn/blockchain/index.html
您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html
==============================
传统币 Point (点) 仅仅是一个数字,数字存在数据库中,例如 Username | Point (Integer) ----------------------- Neo | 1000 Jam | 500
因为仅仅是一个数字,管理员可以随意修改,黑客也可随意修改,例如 update member set point = 1000000000000 where username = 'Neo'
瞬间就有 1000000000000 点。由于是在数据库中修改,没有日志,不知道谁操作的,可能是开发人员,可以是管理员,也可能是黑客。
如何消费“点呢”,例如消费 100 个点: update member set point = point - 100 where username = 'Neo'
传统币“点”,只是一个数字做加法和减法运算,安全性主要依赖于开发团队的能(期望别出BUG),运维团队的能力(被别黑客攻击),以及DBA(数据库管理员)的节操。
审计全靠开发人员打印出的日志。
现在我们再看看数字货币,跟很多朋友聊天中发现,他们还没有理解什么是币,他们认为数字代币花掉就没了(消失了),然后通过挖矿不停的产生新币,这种理解完全错误。
数字币是这样运作的,首先发行时设置了币的总量例如 1000000,然后将所有代币存入发行者账号,例如 account 1 account | coin --------------------------------- account1 | 1000000 account2 | 0 account3 | 0 account4 | 0 account5 | 0
现在 account2 游戏在线1小时奖励 10 个币,系统从账号account1转账5个币给 account2 account | coin --------------------------------- account1 | 999990 account2 | 10 account3 | 0 account4 | 0 account5 | 0
以此类推,从 account1 转账给其他账号。 account | coin --------------------------------- account1 | 999960 account2 | 10 account3 | 10 account4 | 10 account5 | 10
现在 account3 消费 5个币买了装备。从 account3 转 5 个币到 account1 account | coin --------------------------------- account1 | 999965 account2 | 10 account3 | 5 account4 | 10 account5 | 10
现在你应该看懂了把,代币是流通的,总量是不变的。account1 账号负责币的发行,回收等等工作。
同时任何转账将产生区块,历史数据永久记录。
-----------
借用以太坊思维,将以太坊代币合约搬到 hyperledger 上,一样可以实现代币的功能,这个代币除了不能上交易所,基本满足我们替代积分系统的需求,下面是我写了这样一个合约,在超级账本上实现类似以太坊的代币转账功能。
合约实现代币转账,额度查询,增发代币,冻结账号,锁仓等等服务器,功能与 ERC20 Token 相仿。
合约实例化所有代币打入了 coinbase 账号,分发代币需要使用转账功能从 coinbase 想普通账号转账
普通账号消费可以在将代币转到 coinbase 账号中,这样就完成了代币流通,形成一个闭环。
package main /* -------------------------------------------------- Author: netkiller Home: http://www.netkiller.cn Data: 2018-03-19 -------------------------------------------------- */ import ( "encoding/json" "fmt" "strconv" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type Token struct { Owner string `json:"Owner"` TotalSupply int `json:"TotalSupply"` TokenName string `json:"TokenName"` TokenSymbol string `json:"TokenSymbol"` BalanceOf map[string]int `json:"BalanceOf"` FrozenAccount map[string]int `json:"FrozenAccount"` Lock bool `json:"Lock"` } func (token *Token) initialSupply(){ if(token.TotalSupply == 0){ token.BalanceOf[token.Owner] = token.TotalSupply; } } func (token *Token) transfer (_from string, _to string, _value int){ if(token.Lock) return if(token.FrozenAccount[_from] || token.FrozenAccount[_to]) return if(token.BalanceOf[_from] >= _value){ token.BalanceOf[_from] -= _value; token.BalanceOf[_to] += _value; } } func (token *Token) balance (_from string) int{ return token.BalanceOf[_from] } func (token *Token) burn(_value int) { if(token.Lock) return if(token.BalanceOf[token.Owner] >= _value){ token.BalanceOf[token.Owner] -= _value; token.TotalSupply -= _value; } } func (token *Token) burnFrom(_from string, _value int) { if(token.Lock) return if(token.BalanceOf[_from] >= _value){ token.BalanceOf[_from] -= _value; token.TotalSupply -= _value; } } func (token *Token) mint(_value int) { if(token.Lock) return token.BalanceOf[token.Owner] += _value; token.TotalSupply += _value; } func (token *Token) setLock(_lock bool) { token.Lock = _look; } func (token *Token) frozen(_account string, _status bool) { token.FrozenAccount[_account] = _status; } type Account struct { Owner string `json:"Owner"` TokenName string `json:"TokenName"` TokenSymbol string `json:"TokenSymbol"` Balance int `json:"BalanceOf"` Frozen bool `json:"FrozenAccount"` } // Define the Smart Contract structure type SmartContract struct { } func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response { return shim.Success(nil) } func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 2") } symbol:= args[0] name := args[1] supply,_:= strconv.Atoi(args[2]) token := &Token{ Owner: "coinbase", TotalSupply: supply, TokenName: name, TokenSymbol: symbol, BalanceOf: map[string]int{}, FrozenAccount: map[string]int{}, Lock: false} token.initialSupply() tokenAsBytes, _ := json.Marshal(token) err := stub.PutState(symbol, tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Init %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } _from := args[1] _to := args[2] _amount,_ := strconv.Atoi(args[3]) if(_amount <= 0){ return shim.Error("Incorrect number of amount") } tokenAsBytes,err := stub.GetState(args[0]) if err != nil { return shim.Error(err.Error()) } fmt.Printf("transferToken - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) token.transfer(_from, _to, _amount) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(args[0], tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("transferToken - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) mintToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes,err := stub.GetState(args[0]) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) _amount,_ := strconv.Atoi(args[1]) token.mint(_amount) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(args[0], tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("mintToken - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) setLock(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes,err := stub.GetState(args[0]) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) token.setLock(args[1]) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(args[0], tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("setLock - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) frozenAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes,err := stub.GetState(args[0]) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) var status bool if(args[1] == "true"){ status = true; }else{ status = false } token.frozen(args[1],status) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(args[0], tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("frozenAccount - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes,err := stub.GetState(args[0]) if err != nil { return shim.Error(err.Error()) } token := Token{} json.Unmarshal(tokenAsBytes, &token) amount := token.balance(args[1]) status := token.FrozenAccount[args[1]] account := Account{ Owner: token.Owner, TokenName: token.TokenName, TokenSymbol: token.TokenSymbol Balance: amount, Frozen: status} // value := strconv.Itoa(amount) tokenAsBytes, _ := json.Marshal(account) fmt.Printf("%s balance is %s \n", args[1], value) return shim.Success(tokenAsBytes) } func (s *SmartContract) Query(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() if function == "balanceToken" { return s.balanceToken(stub, args) } else { } } func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response { // Retrieve the requested Smart Contract function and arguments function, args := stub.GetFunctionAndParameters() // Route to the appropriate handler function to interact with the ledger appropriately if function == "initLedger" { return s.initLedger(stub, args) } else if function == "setLock" { return s.setLock(stub, args) } else if function == "transferToken" { return s.transferToken(stub, args) } else if function == "frozenAccount" { return s.frozenAccount(stub, args) } else if function == "mintToken" { return s.mintToken(stub, args) } return shim.Error("Invalid Smart Contract function name.") } // The main function is only relevant in unit test mode. Only included here for completeness. func main() { // Create a new Smart Contract err := shim.Start(new(SmartContract)) if err != nil { fmt.Printf("Error creating new Smart Contract: %s", err) } }
测试步骤 peer chaincode install -n token -v 1.0 -p chaincodedev/chaincode/token peer chaincode instantiate -C myc -n token -v 1.0 -c '{"Args":[""]}' peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Token","Netkiller Coin","1000000"]}' peer chaincode invoke -C myc -n token -c '{"function":"transferToken","Args":["Token","coinbase","netkiller","100"]}' peer chaincode invoke -C myc -n token -c '{"function":"balanceToken","Args":["Token","netkiller"]}' peer chaincode invoke -C myc -n token -c '{"function":"transferToken","Args":["Token","netkiller","jerry","100"]}' peer chaincode query -C myc -n token -c '{"function":"balanceToken","Args":["Token","netkiller"]}'
这个合约用户可以创建多套代币,Args":["Token" 的第一参数 Token就是代币名称 peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Apple","水果币","1000000"]}' peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Token","蔬菜币","1000000"]}' peer chaincode invoke -C myc -n token -c '{"function":"initLedger","Args":["Oil","粮油币","1000000"]}'
这个方案仍有不足之处,作者还不清楚如果用户上线是多少,达到临界值后,Hyperledger Fabric 无法在提供服务。
可能 chaincode_example02 做法更靠谱,就是不用 map 保存数据,将每个用户存储在 State 数据上。
这里需要创建多套代币,所以使用了一个key 来存储所有账号。如果像 chaincode_example02 那样就需要部署多个 chaincode 在 channel 中。管理起来比较复杂。
测试日志 Init {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":1000000}} transferToken - begin {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":1000000}} transferToken - end {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":999900,"netkiller":100}} netkiller balance is 100 transferToken - begin {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":999900,"netkiller":100}} transferToken - end {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":999900,"netkiller":100}} transferToken - begin {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":999900,"netkiller":100}} transferToken - end {"Owner":"coinbase","TotalSupply":1000000,"TokenName":"Netkiller Coin","TokenSymbol":"Token","BalanceOf":{"coinbase":999900,"jerry":100,"netkiller":0}}
延伸阅读 《使用代币替代传统积分系统》
以太坊和超级账本各有优势,虽然超级账本的Token功能无法和以太坊相比,但是使用超级账本实现的Token交易不用矿工费。同事超级账本还有一个优势,就是可以在合约中调用另一个合约,这样一来可以做出很多复杂的需求。
例如我们在订票的合约中,就可以直接从Token合约中直接扣款。
--------------------- 2018-03-20 11:11 PM 更新 -----------------------
由于上面合约使用map 保存所有账号信息存在缺陷,经过一天的努力终于完成了Token合约改进 package main /* -------------------------------------------------- Author: netkiller Home: http://www.netkiller.cn Data: 2018-03-20 11:00 PM -------------------------------------------------- CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=token3:1.0 chaincode/token/token3 peer chaincode install -n token3 -v 1.0 -p chaincodedev/chaincode/token peer chaincode instantiate -C myc -n token3 -v 1.0 -c '{"Args":[""]}' -P "OR ('Org1MSP.member','Org2MSP.member')" peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["coinbase"]}' peer chaincode invoke -C myc -n token3 -v 1.0 -c '{"function":"showAccount","Args":["coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"balanceAll","Args":["coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["Netkiller Token","NKC","1000000","coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"initCurrency","Args":["NEO Token","NEC","1000000","coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"setLock","Args":["true"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"setLock","Args":["false"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"mintToken","Args":["NKC","5000","coinbase"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"createAccount","Args":["netkiller"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"transferToken","Args":["coinbase","netkiller","NKC","100"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"balance","Args":["netkiller","NKC"]}' peer chaincode invoke -C myc -n token3 -c '{"function":"frozenAccount","Args":["netkiller","true"]}' -------------------------------------------------- */ import ( "encoding/json" "fmt" "strconv" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" ) type Msg struct{ Status bool `json:"Status"` Code int `json:"Code"` Message string `json:"Message"` } type Currency struct{ TokenName string `json:"TokenName"` TokenSymbol string `json:"TokenSymbol"` TotalSupply float64 `json:"TotalSupply"` } type Token struct { Lock bool `json:"Lock"` Currency map[string]Currency `json:"Currency"` } func (token *Token) transfer (_from *Account, _to *Account, _currency string, _value float64) []byte{ var rev []byte if (token.Lock){ msg := &Msg{Status: false, Code: 0, Message: "锁仓状态,停止一切转账活动"} rev, _ = json.Marshal(msg) return rev } if(_from.Frozen ) { msg := &Msg{Status: false, Code: 0, Message: "From 账号冻结"} rev, _ = json.Marshal(msg) return rev } if( _to.Frozen) { msg := &Msg{Status: false, Code: 0, Message: "To 账号冻结"} rev, _ = json.Marshal(msg) return rev } if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ = json.Marshal(msg) return rev } if(_from.BalanceOf[_currency] >= _value){ _from.BalanceOf[_currency] -= _value; _to.BalanceOf[_currency] += _value; msg := &Msg{Status: true, Code: 0, Message: "转账成功"} rev, _ = json.Marshal(msg) return rev }else{ msg := &Msg{Status: false, Code: 0, Message: "余额不足"} rev, _ = json.Marshal(msg) return rev } } func (token *Token) initialSupply(_name string, _symbol string, _supply float64, _account *Account) []byte{ if _,ok := token.Currency[_symbol]; ok { msg := &Msg{Status: false, Code: 0, Message: "代币已经存在"} rev, _ := json.Marshal(msg) return rev } if _account.BalanceOf[_symbol] > 0 { msg := &Msg{Status: false, Code: 0, Message: "账号中存在代币"} rev, _ := json.Marshal(msg) return rev }else{ token.Currency[_symbol] = Currency{TokenName: _name, TokenSymbol: _symbol, TotalSupply: _supply} _account.BalanceOf[_symbol] = _supply msg := &Msg{Status: true, Code: 0, Message: "代币初始化成功"} rev, _ := json.Marshal(msg) return rev } } func (token *Token) mint(_currency string, _amount float64, _account *Account) []byte{ if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ := json.Marshal(msg) return rev } cur := token.Currency[_currency] cur.TotalSupply += _amount; token.Currency[_currency] = cur _account.BalanceOf[_currency] += _amount; msg := &Msg{Status: true, Code: 0, Message: "代币增发成功"} rev, _ := json.Marshal(msg) return rev } func (token *Token) burn(_currency string, _amount float64, _account *Account) []byte{ if(!token.isCurrency(_currency)){ msg := &Msg{Status: false, Code: 0, Message: "货币符号不存在"} rev, _ := json.Marshal(msg) return rev } if(token.Currency[_currency].TotalSupply >= _amount){ cur := token.Currency[_currency] cur.TotalSupply -= _amount; token.Currency[_currency] = cur _account.BalanceOf[_currency] -= _amount; msg := &Msg{Status: false, Code: 0, Message: "代币回收成功"} rev, _ := json.Marshal(msg) return rev }else{ msg := &Msg{Status: false, Code: 0, Message: "代币回收失败,回收额度不足"} rev, _ := json.Marshal(msg) return rev } } func (token *Token) isCurrency(_currency string) bool { if _, ok := token.Currency[_currency]; ok { return true }else{ return false } } func (token *Token) setLock(_look bool) bool { token.Lock = _look return token.Lock } type Account struct { Name string `json:"Name"` Frozen bool `json:"Frozen"` BalanceOf map[string]float64 `json:"BalanceOf"` } func (account *Account) balance (_currency string) map[string]float64{ bal := map[string]float64{_currency:account.BalanceOf[_currency]} return bal } func (account *Account) balanceAll() map[string]float64{ return account.BalanceOf } // ----------- const TokenKey = "Token" // Define the Smart Contract structure type SmartContract struct { } func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) pb.Response { token := &Token{Currency: map[string]Currency{}} tokenAsBytes, err := json.Marshal(token) err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Init Token %s \n", string(tokenAsBytes)) } return shim.Success(nil) } func (s *SmartContract) Query(stub shim.ChaincodeStubInterface) pb.Response { function, args := stub.GetFunctionAndParameters() if function == "balance" { return s.balance(stub, args) } else if function == "balanceAll" { return s.balanceAll(stub, args) } else if function == "showAccount" { return s.showAccount(stub, args) } return shim.Error("Invalid Smart Contract function name.") } func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) pb.Response { // Retrieve the requested Smart Contract function and arguments function, args := stub.GetFunctionAndParameters() // Route to the appropriate handler function to interact with the ledger appropriately if function == "initLedger" { return s.initLedger(stub, args) } else if function == "createAccount" { return s.createAccount(stub, args) } else if function == "initCurrency" { return s.initCurrency(stub, args) } else if function == "setLock" { return s.setLock(stub, args) } else if function == "transferToken" { return s.transferToken(stub, args) } else if function == "frozenAccount" { return s.frozenAccount(stub, args) } else if function == "mintToken" { return s.mintToken(stub, args) } else if function == "balance" { return s.balance(stub, args) } else if function == "balanceAll" { return s.balanceAll(stub, args) } else if function == "showAccount" { return s.showAccount(stub, args) } else if function == "showToken" { return s.showToken(stub, args) } return shim.Error("Invalid Smart Contract function name.") } func (s *SmartContract) createAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } key := args[0] name := args[0] existAsBytes,err := stub.GetState(key) fmt.Printf("GetState(%s) %s \n", key, string(existAsBytes)) if string(existAsBytes) != "" { fmt.Println("Failed to create account, Duplicate key.") return shim.Error("Failed to create account, Duplicate key.") } account := Account{ Name: name, Frozen: false, BalanceOf: map[string]float64{}} accountAsBytes, _ := json.Marshal(account) err = stub.PutState(key, accountAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("createAccount %s \n", string(accountAsBytes)) return shim.Success(accountAsBytes) } func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface, args []string) pb.Response { return shim.Success(nil) } func (s *SmartContract) showToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("GetState(%s)) %s \n", TokenKey, string(tokenAsBytes)) } return shim.Success(tokenAsBytes) } func (s *SmartContract) initCurrency(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } _name := args[0] _symbol:= args[1] _supply,_:= strconv.ParseFloat(args[2], 64) _account := args[3] coinbaseAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes)) coinbase := &Account{} json.Unmarshal(coinbaseAsBytes, &coinbase) token := Token{} existAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("GetState(%s)) %s \n", TokenKey, string(existAsBytes)) } json.Unmarshal(existAsBytes, &token) result := token.initialSupply(_name,_symbol,_supply, coinbase) tokenAsBytes, _ := json.Marshal(token) err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Init Token %s \n", string(tokenAsBytes)) } coinbaseAsBytes, _ = json.Marshal(coinbase) err = stub.PutState(_account, coinbaseAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes)) return shim.Success(result) } func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 4 { return shim.Error("Incorrect number of arguments. Expecting 4") } _from := args[0] _to := args[1] _currency := args[2] _amount,_ := strconv.ParseFloat(args[3], 32) if(_amount <= 0){ return shim.Error("Incorrect number of amount") } fromAsBytes,err := stub.GetState(_from) if err != nil { return shim.Error(err.Error()) } fmt.Printf("fromAccount %s \n", string(fromAsBytes)) fromAccount := &Account{} json.Unmarshal(fromAsBytes, &fromAccount) toAsBytes,err := stub.GetState(_to) if err != nil { return shim.Error(err.Error()) } fmt.Printf("toAccount %s \n", string(toAsBytes)) toAccount := &Account{} json.Unmarshal(toAsBytes, &toAccount) tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token %s \n", string(toAsBytes)) token := Token{Currency: map[string]Currency{}} json.Unmarshal(tokenAsBytes, &token) result := token.transfer(fromAccount, toAccount, _currency, _amount) fmt.Printf("Result %s \n", string(result)) fromAsBytes, err = json.Marshal(fromAccount) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_from, fromAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("fromAccount %s \n", string(fromAsBytes)) } toAsBytes, err = json.Marshal(toAccount) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_to, toAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("toAccount %s \n", string(toAsBytes)) } return shim.Success(result) } func (s *SmartContract) mintToken(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 3") } _currency := args[0] _amount,_ := strconv.ParseFloat(args[1], 32) _account := args[2] coinbaseAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Coinbase before %s \n", string(coinbaseAsBytes)) } coinbase := &Account{} json.Unmarshal(coinbaseAsBytes, &coinbase) tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token before %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) result := token.mint(_currency, _amount, coinbase) tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("Token after %s \n", string(tokenAsBytes)) coinbaseAsBytes, _ = json.Marshal(coinbase) err = stub.PutState(_account, coinbaseAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Coinbase after %s \n", string(coinbaseAsBytes)) } fmt.Printf("mintToken %s \n", string(tokenAsBytes)) return shim.Success(result) } func (s *SmartContract) setLock(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 2") } _look := args[0] tokenAsBytes,err := stub.GetState(TokenKey) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) token := Token{} json.Unmarshal(tokenAsBytes, &token) if(_look == "true"){ token.setLock(true) }else{ token.setLock(false) } tokenAsBytes, err = json.Marshal(token) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(TokenKey, tokenAsBytes) if err != nil { return shim.Error(err.Error()) } fmt.Printf("setLock - end %s \n", string(tokenAsBytes)) return shim.Success(nil) } func (s *SmartContract) frozenAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 2") } _account := args[0] _status := args[1] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) } // fmt.Printf("setLock - begin %s \n", string(tokenAsBytes)) account := Account{} json.Unmarshal(accountAsBytes, &account) var status bool if(_status == "true"){ status = true; }else{ status = false } account.Frozen = status accountAsBytes, err = json.Marshal(account) if err != nil { return shim.Error(err.Error()) } err = stub.PutState(_account, accountAsBytes) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("frozenAccount - end %s \n", string(accountAsBytes)) } return shim.Success(nil) } func (s *SmartContract) showAccount(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } return shim.Success(accountAsBytes) } func (s *SmartContract) balance(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 2 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] _currency := args[1] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } account := Account{} json.Unmarshal(accountAsBytes, &account) result := account.balance(_currency) resultAsBytes, _ := json.Marshal(result) fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes)) return shim.Success(resultAsBytes) } func (s *SmartContract) balanceAll(stub shim.ChaincodeStubInterface, args []string) pb.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } _account := args[0] accountAsBytes,err := stub.GetState(_account) if err != nil { return shim.Error(err.Error()) }else{ fmt.Printf("Account balance %s \n", string(accountAsBytes)) } account := Account{} json.Unmarshal(accountAsBytes, &account) result := account.balanceAll() resultAsBytes, _ := json.Marshal(result) fmt.Printf("%s balance is %s \n", _account, string(resultAsBytes)) return shim.Success(resultAsBytes) } // The main function is only relevant in unit test mode. Only included here for completeness. func main() { // Create a new Smart Contract err := shim.Start(new(SmartContract)) if err != nil { fmt.Printf("Error creating new Smart Contract: %s", err) } }
测试日志 2018-03-20 14:46:11.957 UTC [shim] SetupChaincodeLogging -> INFO 001 Chaincode log level not provided; defaulting to: INFO 2018-03-20 14:46:11.957 UTC [shim] SetupChaincodeLogging -> INFO 002 Chaincode (build level: ) starting up ... Coinbase before {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":4000,"NBA":999900}} Token before {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":20000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Token after {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":24000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900}} mintToken {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":24000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Account balance {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900}} jam balance is {"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900} Coinbase before {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900}} Token before {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":24000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Token after {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":28000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":12000,"NBA":999900}} mintToken {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":28000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Account balance {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900}} jam balance is {"BBC":10000,"CCE":10000,"EJB":8000,"NBA":999900} Coinbase before {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":12000,"NBA":999900}} Token before {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":28000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Token after {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":30000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900}} mintToken {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":30000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Account balance {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900}} jam balance is {"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900} Coinbase before {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900}} Token before {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":30000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Token after {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":20000,"NBA":999900}} mintToken {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36000},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Account balance {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900}} jam balance is {"BBC":10000,"CCE":10000,"EJB":14000,"NBA":999900} frozenAccount - end {"Name":"netkiller","Frozen":true,"BalanceOf":{"NKC":300}} fromAccount {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":20000,"NBA":999900}} toAccount {"Name":"netkiller","Frozen":true,"BalanceOf":{"NKC":300}} Token {"Name":"netkiller","Frozen":true,"BalanceOf":{"NKC":300}} Result {"Status":false,"Code":0,"Message":"To 账号冻结"} fromAccount {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":20000,"NBA":999900}} toAccount {"Name":"netkiller","Frozen":true,"BalanceOf":{"NKC":300}} frozenAccount - end {"Name":"netkiller","Frozen":false,"BalanceOf":{"NKC":300}} fromAccount {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":20000,"NBA":999900}} toAccount {"Name":"netkiller","Frozen":false,"BalanceOf":{"NKC":300}} Token {"Name":"netkiller","Frozen":false,"BalanceOf":{"NKC":300}} Result {"Status":true,"Code":0,"Message":"转账成功"} fromAccount {"Name":"jam","Frozen":false,"BalanceOf":{"BBC":10000,"CCE":10000,"EJB":19900,"NBA":999900}} toAccount {"Name":"netkiller","Frozen":false,"BalanceOf":{"EJB":100,"NKC":300}} Account balance {"Name":"netkiller","Frozen":false,"BalanceOf":{"EJB":100,"NKC":300}} netkiller balance is {"NKC":300} Account balance {"Name":"netkiller","Frozen":false,"BalanceOf":{"EJB":100,"NKC":300}} netkiller balance is {"NKCs":0} Account balance {"Name":"netkiller","Frozen":false,"BalanceOf":{"EJB":100,"NKC":300}} netkiller balance is {"NKC":300} Coinbase before {"Name":"coinbase","Frozen":false,"BalanceOf":{"NKC":4600}} GetState(Token)) {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36400},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NAS":{"TokenName":"NAS Token","TokenSymbol":"NAS","TotalSupply":2000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Init Token {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36400},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NAS":{"TokenName":"NAS Token","TokenSymbol":"NAS","TotalSupply":2000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"coinbase","Frozen":false,"BalanceOf":{"NKC":4600}} Coinbase before {"Name":"coinbase","Frozen":false,"BalanceOf":{"NKC":4600}} GetState(Token)) {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36400},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NAS":{"TokenName":"NAS Token","TokenSymbol":"NAS","TotalSupply":2000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Init Token {"Lock":false,"Currency":{"BBC":{"TokenName":"NEO Token","TokenSymbol":"BBC","TotalSupply":10000},"CCE":{"TokenName":"NEO Token","TokenSymbol":"CCE","TotalSupply":10000},"EJB":{"TokenName":"NEO Token","TokenSymbol":"EJB","TotalSupply":36400},"K8":{"TokenName":"888","TokenSymbol":"K8","TotalSupply":1000000},"NAS":{"TokenName":"NAS Token","TokenSymbol":"NAS","TotalSupply":2000},"NBA":{"TokenName":"NEO Token","TokenSymbol":"NBA","TotalSupply":1000000},"NEC":{"TokenName":"NEO Token","TokenSymbol":"NEC","TotalSupply":1000000},"NKC":{"TokenName":"Netkiller Token","TokenSymbol":"NKC","TotalSupply":1005000},"NKCKK":{"TokenName":"Netkillersss","TokenSymbol":"NKCKK","TotalSupply":1000000},"TTC":{"TokenName":"NEO Token","TokenSymbol":"TTC","TotalSupply":1000000}}} Coinbase after {"Name":"coinbase","Frozen":false,"BalanceOf":{"NKC":4600}}
区块链
2018-03-21 09:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hyperledger fabric Chaincode 开发详解
本文节选自电子书《Netkiller Blockchain 手札》
Netkiller Blockchain 手札
本文作者最近在找工作,有意向致电 13113668890
Mr. Neo Chan, 陈景峯(BG7NYT)
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890
文档始创于2018-02-10
版权 © 2018 Netkiller(Neo Chan). All rights reserved.
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
微信订阅号 netkiller-ebook (微信扫描二维码) QQ:13721218 请注明“读者”
QQ群:128659835 请注明“读者” 网站:http://www.netkiller.cn
内容摘要
这一部关于区块链开发及运维的电子书。
为什么会写区块链电子书?因为2018年是区块链年。
这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。
这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。
电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master
本文采用碎片化写作,原文会不定期更新,请尽量阅读原文。
http://www.netkiller.cn/blockchain/index.html
您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html
==============================
16.7. Chaincode 结构
shim.ChaincodeStubInterface 接口有三个方法,分别是:Init、Query 和 Invoke
16.7.1. 包 package main
16.7.2. 导入库 import ( "fmt" "strconv" "github.com/hyperledger/fabric/core/chaincode/shim" pb "github.com/hyperledger/fabric/protos/peer" )
16.7.3. 定义类 type SimpleChaincode struct { }
16.7.4. Init 方法
负责初始化工作,链码首次部署到区块链网络时调用,将由部署自己的链代码实例的每个对等节点执行。此方法可用于任何与初始化、引导或设置相关的任务。 func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response { }
16.7.5. Query
只要在区块链状态上执行任何读取/获取/查询操作,就需要调用 Query 方法。如果尝试在 Query 方法内修改区块链的状态,将会抛出异常。
16.7.6. Invoke
此方法主要是做修改操作,但是很多例子中一些用户也在 Invoke 做查询。
put, get, del 等操作都在可以在 Invoke 中运行 func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response { }
参考例子 func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response { // Retrieve the requested Smart Contract function and arguments function, args := stub.GetFunctionAndParameters() // Route to the appropriate handler function to interact with the ledger appropriately if function == "balanceToken" { return s.balanceToken(stub, args) } else if function == "initLedger" { return s.initLedger(stub) } else if function == "transferToken" { return s.transferToken(stub, args) } return shim.Error("Invalid Smart Contract function name.") }
在 Invoke 函数中,首先使用 stub.GetFunctionAndParameters() 获取合约函数 function, args := stub.GetFunctionAndParameters() 然后判断函数名称,实现对应的逻辑关系。 if function == "balanceToken" { return s.balanceToken(stub, args) } else if function == "initLedger" { return s.initLedger(stub) } else if function == "transferToken" { return s.transferToken(stub, args) }
16.7.7. func main()
任何 Go 程序的都需要 main 函数,他是程序的入口,因此该函数被用于引导/启动链代码。当对peer节点部署chaincode并实例化时,就会执行 main 函数。 func main() { err := shim.Start(new(SimpleChaincode)) if err != nil { fmt.Printf("Error starting Simple chaincode: %s", err) } }
shim.Start(new(SampleChaincode)) 启动链代码并注册到peer 节点。

16.8. shim.ChaincodeStubInterface 接口
Hyperledger提供基于key/value的数据存储,其中key是字符串,value则是二进制字节数组,Hyperledger的Go API提供了三个方法用于数据存取:PutState(key, value)用于向Hyperledger中存储数据, GetState(key)用于从Hyperledger中提取数据,而DelState(key)则从Hyperledger中删除数据。
写入数据如果是 struct 结构体,需要序列化,通常使用 json,其他形式的序列化也可以,只要能反序列化即可。
16.8.1. 曾,删,查 操作
16.8.1.1. PutState(key, value)写入区块
写入区块联系 func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response { token := &Token{ Owner: "netkiller", TotalSupply: 10000, TokenName: "代币通正", TokenSymbol: "COIN", BalanceOf: map[string]uint{}} token.initialSupply() tokenAsBytes, _ := json.Marshal(token) stub.PutState("Token", tokenAsBytes) fmt.Println("Added", tokenAsBytes) return shim.Success(nil) }
16.8.1.2. GetState(key) 读取区块
通过key获取区块信息 func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } tokenAsBytes, _ := stub.GetState(args[0]) token := Token{} json.Unmarshal(tokenAsBytes, &token) amount := token.balance(args[1]) return shim.Success(amount) }
16.8.1.3. DelState(key) 删除区块
删除区块信息 func (s *SmartContract) deleteData(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } err= stub.DelState(args[0]) if err != nil { return shim.Error("Failed to delete Student from DB, key is: "+key) } return shim.Success(nil) }
16.8.1.4. 修改数据
State 数据库并没有提供修改功能,修改数据可以先读取,再修改,最后写入 func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes, _ := stub.GetState(args[0]) token := Token{} json.Unmarshal(tokenAsBytes, &token) token.transfer(args[1],args[2],args[3]) tokenAsBytes, _ = json.Marshal(token) stub.PutState(args[0], tokenAsBytes) return shim.Success(nil) }
16.8.1.5. GetStateByRange(startKey, endKey) 范围查找
区块链是一个线性的数据结构,只要知道开始位置,结束位置,就能够取出中间部分的数据。 func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response { startKey := "CAR0" endKey := "CAR999" resultsIterator, err := APIstub.GetStateByRange(startKey, endKey) if err != nil { return shim.Error(err.Error()) } defer resultsIterator.Close() // buffer is a JSON array containing QueryResults var buffer bytes.Buffer buffer.WriteString("[") bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return shim.Error(err.Error()) } // Add a comma before array members, suppress it for the first array member if bArrayMemberAlreadyWritten == true { buffer.WriteString(",") } buffer.WriteString("{\"Key\":") buffer.WriteString("\"") buffer.WriteString(queryResponse.Key) buffer.WriteString("\"") buffer.WriteString(", \"Record\":") // Record is a JSON object, so we write as-is buffer.WriteString(string(queryResponse.Value)) buffer.WriteString("}") bArrayMemberAlreadyWritten = true } buffer.WriteString("]") fmt.Printf("- queryAllCars:\n%s\n", buffer.String()) return shim.Success(buffer.Bytes()) }
16.8.1.6. GetQueryResult(query string) CouchDB 查询
GetQueryResult 能查询 json 里面的数据
下面例子是 Name = Neo Chen 的所有数据。 func (t *SimpleChaincode) getQueryResult(stub shim.ChaincodeStubInterface, args []string) pb.Response{ name:="Neo Chen" //需要查询的名字 queryString := fmt.Sprintf("{\"selector\":{\"Name\":\"%s\"}}", name) resultsIterator,err:= stub.GetQueryResult(queryString)//必须是CouchDB才行 if err!=nil{ return shim.Error("query failed") } person,err:=getListResult(resultsIterator) if err!=nil{ return shim.Error("query failed") } return shim.Success(person) }
16.8.1.7. stub.GetHistoryForKey
通过key获取历史数据 func (t *SimpleChaincode) historyQuery(stub shim.ChaincodeStubInterface, args []string) pb.Response{ if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } it,err:= stub.GetHistoryForKey(args[0]) if err!=nil{ return shim.Error(err.Error()) } var result,_= getHistoryListResult(it) return shim.Success(result) }
16.8.1.8. shim.HistoryQueryIteratorInterface 接口 func getHistoryListResult(resultsIterator shim.HistoryQueryIteratorInterface) ([]byte,error){ defer resultsIterator.Close() // buffer is a JSON array containing QueryRecords var buffer bytes.Buffer buffer.WriteString("[") bArrayMemberAlreadyWritten := false for resultsIterator.HasNext() { queryResponse, err := resultsIterator.Next() if err != nil { return nil, err } // Add a comma before array members, suppress it for the first array member if bArrayMemberAlreadyWritten == true { buffer.WriteString(",") } item,_:= json.Marshal( queryResponse) buffer.Write(item) bArrayMemberAlreadyWritten = true } buffer.WriteString("]") fmt.Printf("queryResult:\n%s\n", buffer.String()) return buffer.Bytes(), nil }
16.8.2. 复合键
16.8.2.1. 创建复合键 // maintain the index indexName := "color~name" colorNameIndexKey, err := stub.CreateCompositeKey(indexName, []string{marbleJSON.Color, marbleJSON.Name}) if err != nil { return shim.Error(err.Error()) } // Delete index entry to state. err = stub.DelState(colorNameIndexKey) if err != nil { return shim.Error("Failed to delete state:" + err.Error()) }
16.8.2.2. 分解复合键 // get the color and name from color~name composite key objectType, compositeKeyParts, err := stub.SplitCompositeKey(responseRange.Key) if err != nil { return shim.Error(err.Error()) } returnedColor := compositeKeyParts[0] returnedMarbleName := compositeKeyParts[1]
16.8.3. stub.SetEvent(key, value) 事件
Hyperledger Fabic 事件实现了发布/订阅消息队列。您可以自由地在链码中创建和发出自定义事件。例如,区块链的状态发生改变,就会生成一个事件。通过向区块链上的事件中心注册一个事件适配器,客户端应用程序可以订阅和使用这些事件。 func (t *SimpleChaincode) testEvent(stub shim.ChaincodeStubInterface, args []string) pb.Response{ message := "Event send data is here!" err := stub.SetEvent("evtsender", []byte(message)) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) } func (t *SimpleChaincode) testEvent(stub shim.ChaincodeStubInterface, args []string) pb.Response{ event := &Token{ Owner: "netkiller", TotalSupply: 10000, TokenName: "代币通正", TokenSymbol: "COIN", BalanceOf: map[string]uint{}} eventBytes, err ;= json.Marshal(&event) if err != nil { return nil, err } err := stub.SetEvent("evtSender", eventBytes) if err != nil { fmt.Println("Could not set event for loan application creation", err) } }
16.8.4. 调用其他链码
在当前连码中调用另一个连码,调用连码需要提供连码名和通道名 stub.InvokeChaincode("连码名",调用函数,"通道") func (t *SimpleChaincode) testInvokeChainCode(stub shim.ChaincodeStubInterface, args []string) pb.Response{ trans:=[][]byte{[]byte("invoke"),[]byte("transfer"),[]byte("netkiller"),[]byte("neo"),[]byte("100")} response:= stub.InvokeChaincode("token",trans,"mychannel") fmt.Println(response.Message) return shim.Success([]byte( response.Message)) } 16.9. 案例 16.9.1. 模仿以太坊 ERC20 规范的 Hyperledger Fabric 实现 package main import ( "bytes" "encoding/json" "fmt" "strconv" "github.com/hyperledger/fabric/core/chaincode/shim" sc "github.com/hyperledger/fabric/protos/peer" ) // Define the Smart Contract structure type SmartContract struct { } type Token struct { Owner string `json:"Owner"` TotalSupply uint `json:"TotalSupply"` TokenName string `json:"TokenName"` TokenSymbol string `json:"TokenSymbol"` BalanceOf map[string]uint `json:"BalanceOf"` } func (token *Token) initialSupply(){ token.BalanceOf[token.Owner] = token.TotalSupply; } func (token *Token) transfer (_from string, _to string, _value uint){ if(token.BalanceOf[_from] >= _value){ token.BalanceOf[_from] -= _value; token.BalanceOf[_to] += _value; } } func (token *Token) balance (_from string) uint{ return token.BalanceOf[_from] } func (token *Token) burn(_value uint) { if(token.BalanceOf[token.Owner] >= _value){ token.BalanceOf[token.Owner] -= _value; token.TotalSupply -= _value; } } func (token *Token) burnFrom(_from string, _value uint) { if(token.BalanceOf[_from] >= _value){ token.BalanceOf[_from] -= _value; token.TotalSupply -= _value; } } func (token *Token) mint(_value uint) { token.BalanceOf[token.Owner] += _value; token.TotalSupply += _value; } func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response { return shim.Success(nil) } func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response { token := &Token{ Owner: "netkiller", TotalSupply: 10000, TokenName: "代币通正", TokenSymbol: "COIN", BalanceOf: map[string]uint{}} token.initialSupply() tokenAsBytes, _ := json.Marshal(token) stub.PutState("Token", tokenAsBytes) fmt.Println("Added", tokenAsBytes) return shim.Success(nil) } func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 3 { return shim.Error("Incorrect number of arguments. Expecting 2") } tokenAsBytes, _ := stub.GetState(args[0]) token := Token{} json.Unmarshal(tokenAsBytes, &token) token.transfer(args[1],args[2],args[3]) tokenAsBytes, _ = json.Marshal(token) stub.PutState(args[0], tokenAsBytes) return shim.Success(nil) } func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response { if len(args) != 1 { return shim.Error("Incorrect number of arguments. Expecting 1") } tokenAsBytes, _ := stub.GetState(args[0]) token := Token{} json.Unmarshal(tokenAsBytes, &token) amount := token.balance(args[1]) return shim.Success(amount) } func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response { // Retrieve the requested Smart Contract function and arguments function, args := stub.GetFunctionAndParameters() // Route to the appropriate handler function to interact with the ledger appropriately if function == "balanceToken" { return s.balanceToken(stub, args) } else if function == "initLedger" { return s.initLedger(stub) } else if function == "transferToken" { return s.transferToken(stub, args) } return shim.Error("Invalid Smart Contract function name.") } // The main function is only relevant in unit test mode. Only included here for completeness. func main() { // Create a new Smart Contract err := shim.Start(new(SmartContract)) if err != nil { fmt.Printf("Error creating new Smart Contract: %s", err) } }
区块链
2018-03-16 22:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
中本聪的论文发表于2008年11月,第一个区块生成于2009年1月3日18:15:05。
比特币的每个区块相当于一个账页,每个账页通过区块头哈希首尾相连,整个比特币区块链相当于一个大账簿,具有不能篡改性, 当前比特币这个大账簿已经有513,342个以上的账页 。
区块链
2018-03-15 17:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
食品安全溯源区块链解决方案探索

本文节选自电子书《Netkiller Blockchain 手札》
Netkiller Blockchain 手札
本文作者最近在找工作,有意向致电 13113668890
Mr. Neo Chan, 陈景峯(BG7NYT)
中国广东省深圳市龙华新区民治街道溪山美地 518131 +86 13113668890
文档始创于2018-02-10
版权 © 2018 Netkiller(Neo Chan). All rights reserved.
版权声明
转载请与作者联系,转载时请务必标明文章原始出处和作者信息及本声明。
微信订阅号 netkiller-ebook (微信扫描二维码) QQ:13721218 请注明“读者”
QQ群:128659835 请注明“读者” 网站:http://www.netkiller.cn
内容摘要
这一部关于区块链开发及运维的电子书。
为什么会写区块链电子书?因为2018年是区块链年。
这本电子书是否会出版(纸质图书)? 不会,因为互联网技术更迭太快,纸质书籍的内容无法实时更新,一本书动辄百元,很快就成为垃圾,你会发现目前市面的上区块链书籍至少是一年前写的,内容已经过时,很多例子无法正确运行。所以我不会出版,电子书的内容会追逐技术发展,及时跟进软件版本的升级,做到内容最新,至少是主流。
这本电子书与其他区块链书籍有什么不同?市面上大部分区块链书籍都是用2/3去讲区块链原理,只要不到 1/3 的干货,干货不够理论来凑,通篇将理论或是大谈特谈区块链行业,这些内容更多是头脑风暴,展望区块链,均无法落地实施。本书与那些书籍完全不同,不讲理论和原理,面向应用落地,注重例子,均是干货。
电子书更新频率?每天都会有新内容加入,更新频率最迟不会超过一周,更新内容请关注 https://github.com/netkiller/netkiller.github.io/commits/master
本文采用碎片化写作,原文会不定期更新,请尽量阅读原文。
http://www.netkiller.cn/blockchain/index.html
您的打赏是我的写作动力:http://www.netkiller.cn/blockchain/donations.html
==============================
33.2. 食品溯源案例
33.2.1. 背景
需求是通过区块链跟踪产品,实现产品产地,生产,流通等环节溯源。
需求归纳,需要实现下面几点:
产品具备通用的属性,例如名称,价格,重量,颜色,体积等等
生产销售链条跟踪
涉及环节,农产品的供应链是一个非常复杂的过程,涉及多方,农业局、卫生局、药监局、工商局、环保局等多个部门交织其中。
参与者角色,我们为每个环节的参与者分配一个以太坊账号,例如每个供应商一个账号,每个代理商一个账号。这样任何一方经手后都会使用自己的账号想合约中添加数据。
33.2.2. 安全问题
我将安全划分为六层,分别是: +----------+-----------------------------+ | 实体层 | 物 | +----------+-----------------------------+ | 用户层 | 人 | +----------+-----------------------------+ | 网络层 | 网络 | +----------+-----------------------------+ | 应用层 | 操作系统,应用服务器 | +----------+-----------------------------+ | 业务逻辑层 | 功能,业务逻辑 | +----------+-----------------------------+ | 存储层 | 物理存储,文件系统,硬盘 | +----------+-----------------------------+
并不是实施了区块链技术就安全无忧了,安全分为很多层,区块链只能做到存储层的安全。区块链无法解决用户层,应用层,逻辑层等安全问题,他只能保证存储在硬盘上的区块不被修改。
因为区块链仅仅能解决数据存储层的安全问题,不能保证上链的数据是真实的,上链前绝对不会被篡改;所以仅仅朔源,不考虑防伪是没有意义的,防伪仍然是重中之重。
33.2.3. 防伪问题
如何做防伪呢,这个领域很多公司已经探索多年,各种高科技应用,武装到牙齿,但仍没有解决假货问题。
区块链的出现很可能是一个突破,我们只需将现有成熟的防伪技术与区块链结合即可。
现在流行的访问技术太多了,我倾向于采用二维码技术,二维码与互联网紧密相连。
33.2.4. 性能问题
区块链目前的底层只适合做,低频高价值的业务。
区块链的读取性能通常是没有问题的,但是区块链的写入实际上无论你用多少个服务器节点都不能提升,因为写入区块需要做共识算法,这步操作,会在所有节点上进行,同时还需要加密运算,这些操作都是 CPU 密集型操作。所以写入操作是存在瓶颈的。
解决这个问题,我想出了几种方案:
性能解决方案 通过消息队列技术异步写入,将需要写入的区块放入队列,异步完成上链操作。 并行写入,我们可以建设多个区块链平台。多个平台同时服务于业务。
为了达到去中心化并行写入,我们将在客户端通过算法,匹配服务器。而不是在两个平台前面增加负载均衡。因为这样又回到了中心化系统。
33.2.5. 颗粒度问题
朔源的颗粒度问题,例如“红酒”的溯源,我们是将单位溯源做到箱呢?还是打,或是瓶呢?
我们用“四象限法则”分析 高价值 o | | o | 低频率 --------------+------------- 高频率 操作频率 | o | o | 低价值 物品价值
通过观察上面图,我们可以看到可以有四种情况,低频低价值,低频高价值,高频高价值,高频低价值
我认为对于低频高价值和高频高价值的业务,尽量做到最小颗粒度。
而对于低频低价值和高频低价值的业务,可以颗粒度更粗。
33.2.6. 存储规划
如果是高频低价值的业务,那么溯源数据源源将会不断的被添加到区块,以此同时区块的访问率极低。迟早会达到一个临界值。
所以你要规划存储,例如溯源数据的过期时间,对于 hyperledger 可以使用 DelState(key) 删除历史数据。
如果是高频高价值的业务是否要考虑永久保留数据呢?
这些问题都是需要考虑的。因为目前我们还不知道区块链的存储临界值。
33.2.7. 大数据问题
区块链替代不了数据库,它与数据库是互补关系。
对于低频的业务,通常传统数据库足以应付。那么对于高频操作的业务呢?暂时可能没有问题,但总有一天会遇到瓶颈。
综上所述,溯源项目数据库规划决不能少。同时还要考虑数据仓库和后期数据挖掘。因为用户使用微信或者我们的APP扫描二维码,我们可以获得很多有价值的数据。
手上没有 Vision 使用文本简单的绘制了一幅图 +------------------------+ | User -> QR Code | +------------------------+ | | V V +---------------+ +---------------+ +---------------+ | Search Engine |<-- | Microservice | | Microservice | +---------------+ +---------------+ +---------------+ | | +----------------------------------+ | | | | | V V V V +----------+ +------------+ +-------------+ | Database | | Big Data | | Blockchain | +----------+ +------------+ +-------------+ | MySQL | | Hadoop | | Hyperledger | | NoSQL | | Hive/Hbase | | Chaincode | +----------+ +------------+ +-------------+ | | ^ ^ | +------ ETL -----| | | | +----------- Message Queue ----------o
区块链之外的很多复杂的需求我们需要借助大数据系统和搜索技术。
区块链的弱点是无法做复杂的查询,这里我们会用到搜索引擎技术解决,实际上搜索引擎角色是给区块链做索引。
上图数据写入时,保存了四份,分别在搜索引擎,关系型数据库,数据仓库和区块的
具体怎么实现,有很多方式,这里就不讨论了,否则就跑题了。
33.2.8. BI商业智能
数据采集,大数据分析
溯源信息的查询是通过用户手机终端实现,有几种途径,微信扫二维码,APP扫二维码,微信小程序等等。
我们可以收集到很多有价值的数据,例如地理位置,手机号码,性别,年龄等等......
有了这些数据便可以挖掘出有价值的数据。例如用户行为分析,消费与地理分析的关系,年龄段与购买力的关系等等....
33.2.9. 采集终端
溯源数据怎么录入呢?
例如我们开发一个设备,二维码扫描枪,内置安卓系统。
我们不清楚他们的教育背景以及学习能力,所以设计原则是尽量傻瓜化,降低数据录入难度和学习难度,终端开机后互动教学,走一遍流程即可上手。
首先将溯源环节的每个节点通过后台事先写入数据库,接下来通过GIS地理信息系统匹配。 UUID -> 二维码 -> 设备扫描二维码激活-> 入数据库 -> 异步消息队列 -> 上链 > ---+ ^ | | | +------------------- 追加数据 ------------------+
终端会帮助用户欲录入信息,用户可以在信息基础上修改或者重写。同时终端支持图片,图像记录上传。对于图片还能实现 EXIF 数据保存,包括图片描述信息,地理信息等等......
多媒体数据
这里我们需要考虑是否需要记录多媒体数据,这里的多媒体指图像,声音,甚至3D扫描数据等等......
对于图片、音频与视频,我们可以将它集成到采集终端上,然后异步上传到去中心化的分布式文件系统中。
去中心化的分布式文件系统能实现,一张图片一个hash值,通过hash值访问图片,图片被同步到相邻节点实现去中心化,图片被修改hash值随之变化数据便无效。
33.2.10. 物流接口
使用物流单好通过物流公司提供的借口获得物流数据,然后写入到区块。

33.2.12. 如何激励用户
防伪技术做了,区块链溯源也做了,那么对于用户来说,他可能懒得去扫你的二维码,怎么办呢?
这里需要激励用户,怎样激励用户,我的方案是送代币。
首先代币不仅能够购买物品,还能交易,流通,形成一个小的商业闭环。其次目前代币已经泛滥 99% 可能是空气币,这里我们需要将代币的价值与物品对价,类似金本位/银本位。
怎样操作呢?例如一个代币等于一斤水果,无论代币怎样炒作,最终用户不想玩下去了,就来换水果,也可以是大米,食用油等等...
关于怎样使用代币来做积分系统请参考我的另一篇文章《使用代币替代传统积分系统》,你可以在搜索引擎中找到,或者访问 https://cloud.tencent.com/developer/article/1057118
扫描二维码显示溯源防伪信息的同时我们有很多可以操作空间,可以获取用户地理位置,手机号码等等信息,为后面大数据分析埋点。
33.2.11. 以太坊解决方案
我们设计一个简单的合约,模拟上面提到的解决方案 pragma solidity ^0.4.20; contract Trace { enum State { Origin, Factory, QA, Shipping, Received, Pending } string name; uint price; uint weight; bool lock = false; //合约锁 bool close = false; //合约状态 uint number = 1; uint attr_number = 1; mapping (address => string) guestbook; //客户留言本 struct Attribute { address owner; // 供应商 string name; // 属性的名字 string date; // 生产日期 string desc; // 描述信息 } mapping (uint => Attribute) attribute; struct Logistics { address owner; // 中转站 string date; // 转运日期 State status; // 状态 string message; // 留言信息 } mapping (uint => Logistics) stations; function Trace(string _name, uint _price, uint _weight) public { name = _name; price = _price; weight = _weight; } // 名称 function getName() public view returns(string){ return name; } // 价格 function getPrice() public view returns(uint){ return price; } // 重量 function getWeight() public view returns(uint){ return weight; } // 增加商品属性 function putAttribute(address _owner,string _name, string _date, string _desc ) public{ if(lock == false){ Attribute memory item = Attribute(_owner, _name,_date,_desc); attribute[attr_number] = item; attr_number = attr_number + 1; } } // 获得属性 function getAttribute(uint _attr_number) public view returns(address, string, string, string) { require(_attr_number < attr_number); Attribute memory item = attribute[_attr_number]; return (item.owner, item.name, item.date, item.desc); } // 增加物流中转信息 function putLogistics(address _owner,string _date, State _status, string _message ) public{ if(close == false){ Logistics memory node = Logistics(_owner,_date,_status,_message); stations[number] = node; number = number + 1; lock = true; } if (_status == State.Received) { close = true; } } // 获得中转信息 function getLogistics(uint _number) public view returns(address, string, State, string) { require(_number < number); Logistics memory node = stations[_number]; return (node.owner, node.date, node.status, node.message); } // 或者转中站数量 function getLogisticsCount() public view returns(uint){ return number; } // 客户留言 function addGuestbook(address _owner, string message) public{ guestbook[_owner] = message; } }
怎样使用这个合约呢?合约部署,需要输入三个参数,分别是名称,价格和装量 Trace(string _name, uint _price, uint _weight)
产品属性可以在出厂前设置,一旦出厂进入物流阶段就不允许在更改了。
33.2.11.1. 应用场景一
调用合约案例一,这是没有经过深加工的原产品案例。例如 Trace("山羊肉", 25, 50) var contract; Trace.deployed().then(function(instance){contract=instance;}); contract.getName(); contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","颜色", "", "黑色") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","产地", "", "内蒙古") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","出生", "2017-01-12", "XXX牧场") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","宰杀", "2018-02-12", "XXX宰杀") contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧场"); contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX屠宰公司"); contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX检验检疫"); contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一级经销商"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二级经销商"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批发中心"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市"); contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用户包裹收到"); contract.getNode(); // 获得物流经过的转运站数量
33.2.11.2. 应用场景二
调用合约案例二,这是深加工的产品案例。例如 Trace("牦牛肉干", 80, 500) var contract; Trace.deployed().then(function(instance){contract=instance;}); contract.getName(); contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","调和油", "2016-10-10", "银龙鱼牌") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","辣椒粉", "2016-10-30", "西藏XXX公司生产") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","生抽", "2016-01-12", "XXX生抽,XXX生产") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","山梨酸钾", "2017-02-12", "XXX生产") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","防腐剂", "2017-02-12", "XXX生产") contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","牦牛肉", "2017-02-12", "XXX牧场") contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧场"); contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX公司生产"); contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX通过QA、QC"); contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一级经销商"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二级经销商"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批发中心"); contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市"); contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用户包裹收到"); contract.getNode(); // 获得物流经过的转运站数量
33.2.11.3. 用户留言 contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","东西好吃,下次还买,给好评");
33.2.12. Hyperledger 解决方案
由于家里在刷墙,服务器收起来了,没有开发环境,只能提供部分参考代码,给大家一个思路
将代码放到合约中,使用PutState存储即可 package main import "fmt" import "encoding/json" const ( Origin = iota // 0 Factory // 1 QA // 2 Shipping // 3 Received // 4 Pending // 5 Supermarket // 6 ) type structElement struct { Name string `json:"name"` Company string `json:"company"` Description string `json:"description"` } type structLogistics struct { Stations string `json:"stations"` // 中转站 Date string `json:"date"` // 转运日期 Status uint8 `json:"status"` // 状态 Message string `json:"message"` // 留言信息 } type Trace struct { Name string `json:"name"` Address string `json:"address"` Attribute map[string]string `json:"attribute"` Element []structElement `json:"element"` Logistics map[string]structLogistics `json:"logistics"` } func (trace *Trace) setName(_name string) { trace.Name = _name } func (trace *Trace) getName() string { return trace.Name } func (trace *Trace) putAttribute(_key string, _value string) { trace.Attribute[_key] = _value } func (trace *Trace) putLogistics(_key string, _value structLogistics) { trace.Logistics[_key] = _value } func main(){ trace := &Trace{ Name: "牦牛肉干", Address: "内蒙古呼和浩特", Attribute: map[string]string{}, Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料制品有限公司", Description: "外包装"},structElement{Name:"辣椒粉",Company: "XXX调味品有限公司", Description: "采摘年份2016-10-10"},structElement{Name:"调和油",Company: "XXX调味品有限公司", Description: "生产日期2016-10-10"}}, Logistics: map[string]structLogistics{}} trace.putAttribute("Color","Red") trace.putAttribute("Size","10") trace.putAttribute("Weight","100kg") trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "牦牛收购"}) trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "牦牛宰杀"}) trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "经过质检"}) trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "运输中"}) trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷库"}) trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"}) trace.putLogistics("5", structLogistics{"龙华区","2016-10-15", Received, "用户签收"}) traceJson, _ := json.Marshal(trace) fmt.Println(string(traceJson)) }
现在是晚上12点,大脑一片浆糊,已经无法在继续写下去了,就写到这里。洗洗睡了
区块链
2018-03-14 08:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
EOS智能合约安全终极指南。当世界上最大的ICO,EOS于2018年6月推出时,加密社区变得持怀疑态度,并且由于软件错误而被冻结了2天。但快进4个月,EOS今天占了以太网今天所做交易的两倍以上。通过免费和更快速交易的承诺,EOS最顶级的Dapp拥有大约13,000个每日活跃用户,而以太网的最顶级Dapp只有2,000个。
一些常见的智能合约漏洞几乎适用于所有平台。与以太坊一样,在EOS上编写的智能合约需要在主网上上线之前进行审核。合约中的致命错误可以在合约没有经过足够的测试时被利用。在本指南中,我们将帮助你避免在EOS上制作下一个杀手dApp的过程中常见的陷阱。
在阅读本指南之前,了解有关EOS开发的一些先决条件信息非常重要,这些信息在你阅读本指南时会很方便。了解C++是必须的。开始智能合约开发的最佳位置是EOSIO自己的文档。
ABI调用处理 extern "C" { void apply(uint64_t receiver, uint64_t code, uint64_t action) { class_name thiscontract(receiver); if ((code == N(eosio.token)) && (action == N(transfer))) { execute_action(&thiscontract, &class_name::transfer); return; } if (code != receiver) return; switch (action) { EOSIO_API(class_name, (action_1)(action_n))}; eosio_exit(0); } }
上面是修改后的ABI调用程序的示例代码。如下所示的更简单的ABI调用程序用于简化合约的操作处理。 EOSIO_ABI( class_name, (action_1)(action_n) );
ABI调用程序/交易处理程序允许合约收听传入的 eosio.token 交易时间,以及与智能合约的正常交互。为了避免异常和非法调用,绑定每个键操作和代码以满足要求是很重要的。
一个例子是由于他们的ABI转发源代码中的错误而发生在dApp EOSBet Casino上的黑客攻击 。 if( code == self || code == N(eosio.token) ) { TYPE thiscontract( self ); switch( action ) { EOSIO_API( TYPE, MEMBERS ) } }
上面检查ABI转发源代码的apply动作处理程序允许攻击者完全绕过 eosio.token::transfer() 函数,并在放置之前直接调用 contract::transfer() 函数而不将EOS转移到合约中。打赌。对于损失,他没有得到任何报酬,但一无所获。然而,对于胜利,他从合约中支付了真正的EOS。
他们通过在传入操作请求合约之前添加 eosio.token 合约转移操作检查来修复上述错误。 if( code == self || code == N(eosio.token) ) { if( action == N(transfer) ){ eosio_assert( code == N(eosio.token), "Must transfer EOS"); } TYPE thiscontract( self ); switch( action ) { EOSIO_API( TYPE, MEMBERS ) } }
使用语句 require_auth(account) 非常重要;进入只需要授权帐户才能执行的操作。 require_auth(_self) ;用于仅授权合约的所有者签署交易。
操作中的授权 void token::transfer( account_name from, account_name to, asset quantity) { auto sym = quantity.symbol.name(); require_recipient( from ); require_recipient( to ); auto payer = has_auth( to ) ? to : from; sub_balance( from, quantity ); add_balance( to, quantity, payer ); }
上面的示例代码允许任何人调用该操作。要解决它,请使用 require_auth(from) ,声明授权付款人调用该行动​​。
尽量避免修改eosio.token合约
最近一位白帽黑客因其eosio.token合约中的方法调用问题而设法获得了10亿美元的dapp代币。Dapp Se7ens(现在处于非活动状态)在eosio.token合约中声明了一种新方法,用于将其代币空投到用户帐户中。合约没有反映 eosio.token 合约的问题或转移操作的变化,因此资金神奇地出现在用户的帐户上。其次,他们忘记在转移之前验证方法中的金额,这允许黑客在此过程中索取10亿个代币。
除了更改最大供应和代币符号之外,建议避免为自定义函数修改它,因为eosio.token合约中的错误可能是致命的。为了便于安全地进行空投,将空投代币转移到一个单独的帐户并从那里分发。
修改多索引表属性
EOS当前将数据存储在共享内存数据库中,以便跨操作共享。 struct [[eosio::table]] person { account_name key; std::string first_name; std::string last_name; std::string street; std::string city; std::string state; uint64_t primary_key() const { return key; } }; typedef eosio::multi_index address_index;
上面的示例代码创建了一个名为 people 的 multi_index 表 ,该表基于使用 struct person 的该表的单行的数据结构。部署后,EOS目前不允许修改表属性。 eosio_assert_message 断言失败将是将被抛出的错误。因此,在部署表之前需要完全考虑属性。否则,需要创建具有不同名称的新表,并且在从旧表迁移到新表时需要特别小心。如果不这样做可能会导致数据丢失。
数值外溢检查
在进行算术运算时,如果没有足够负责地检查边界条件,则值可能会溢出,从而导致用户资产丢失。 void transfer(symbol_name symbol, account_name from, account_names to, uint64_t balance) { require_auth(from); account fromaccount; eosio_assert(is_balance_within_range(balance), "invalid balance"); eosio_assert(balance > 0, "must transfer positive balance"); uint64_t amount = balance * 4; //Multiplication overflow }
在上面的示例代码中,使用 uint64_t 表示用户余额可能会在值乘以时导致溢出。因此,尽量避免使用 uint64_t 来表示余额并对其执行算术运算。使用 eosiolib 中定义 的asset结构进行操作,而不是处理溢出条件的精确余额。
考虑合约中的假设
在执行合约时会有假设需要断言。如果断言失败,使用 eosio_assert 将事先处理条件并停止执行特定操作。举个例子: void assert_roll_under(const uint8_t& roll_under) { eosio_assert(roll_under >= 2 && roll_under <= 96, "roll under overflow, must be greater than 2 and less than 96"); }
上面的断言语句假设 roll_under 整数大于2且小于96.但如果不是,则抛出上述消息并停止执行。没有发现像上面这样的局部问题可能会成为规则制定的全局灾难。
生成正确随机数
如果没有准确完成,在EOS区块链上生成正确的随机数仍然存在风险。如果没有正确地做到这一点,将导致对手预测结果,在整个过程中对整个系统进行游戏。像 Oracalize.it 这样的服务可以提供来自外部源的随机数,但它们很昂贵并且可能出现单点故障。人们过去曾使用Blockchain的上下文变量(块编号,块时间戳等)来生成以太坊智能合约中的随机数,这只是在游戏中使用。为了正确地进行生成,程序必须提供一种单一方无法单独控制的组合随机性。目前最好的方法之一是Dan Larimar自己生成随机数时建议的方法。 string sha256_to_hex(const checksum256& sha256) { return to_hex((char*)sha256.hash, sizeof(sha256.hash)); } string sha1_to_hex(const checksum160& sha1) { return to_hex((char*)sha1.hash, sizeof(sha1.hash)); } template Inline void hash_combine(std::size_t& seed, const T& v) { std::hash hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); }
上面的示例代码给出了1到100之间的优化随机数生成 .seed1 是种子, seed2 是上面的用户种子。作为参考, Dappub 和 EOSBetCasino 开放了他们的完整合约,随机数发生器实现了玩家和开发商之间的公平骰子游戏。 uint8_t compute_random_roll(const checksum256& seed1, const checksum160& seed2) { size_t hash = 0; hash_combine(hash, sha256_to_hex(seed1)); hash_combine(hash, sha1_to_hex(seed2)); return hash % 100 + 1; }
EOSBet最近再次遭到65,000EOS的攻击,当一个对手欺骗他们的 eosio.token 合约时,只要他在自己的钱包之间进行交易,就将EOS送到他的钱包。 eosio.token 合约代码 通知EOS代币的发送者和接收者有传入代币。为了模仿这种行为并促进黑客攻击,对手创建了两个帐户,让我们假设A和B。A与智能合约的操作有声明 require_recipient(N(eosbetdice11)) 。当A通过操作调用促进了从A到B的交易时,它通知合约中的转移功能,就好像该呼叫来自 eosio.token 合约一样。由于EOS没有真正转移到合约中,每当黑客输掉赌注时,他什么都没有丢失,但是在赢得赌注时他获得了奖励。因此,仅检查合约名称和操作名称是不够的。
检查合约中的通知
为了缓解这个问题,该函数应检查合约是否确实是代币的接收者。 eosio_assert(transfer_data.from == _self || transfer_data.to == _self, "Must be incoming or outgoing transfer");
在EOS上制定智能合约时应遵循的最佳做法是什么?
错误是任何软件不可避免的一部分。它的后果在去中心化的环境中被放大,特别是如果它涉及价值交易。除了上面讨论的EOS特定保护措施之外,以下是新智能合约开发人员应该记住的一些一般预防措施和最佳实践: 在主网上发布之前,始终独立于第三方智能合约审计公司审计合约。 在发布到testnet之前,进行必要的Caveman调试(当前调试合约的唯一方法)。EOSIO文档有一个很好的指南。 设置提款限额转移率,避免主网启动初期出现过多损失。有白帽黑客负责任披露的bug赏金计划。 当检测到错误时,有一个killswitch来冻结合约。
为了实现它,我们在 multi_index 表中保留一个标志。我们使用只能由合约所有者调用的操作来设置标志。然后我们检查每个公共行动是否将标志设置为冻结。下面给出了该函数的示例实现。 struct st_frozen { uint64_t frozen; }; typedef singleton tb_frozen; tb_frozen _frozen; uint64_t getFreezeFlag() { st_frozen frozen_st{.frozen = 0}; return _frozen.get_or_create(_self, frozen_st); } void setFreezeFlag(const uint64_t& pFrozen) { st_frozen frozen_st = getFreezeFlag(); frozen_st.frozen = pFrozen; _frozen.set(frozen_st, _self); } // public Action void freeze() { require_auth(_self); setFreezeFlag(1); } // public Action void unfreeze() { require_auth(_self); setFreezeFlag(0); } // any public action void action(...) { eosio_assert(getFreezeFlag().frozen == 1, "Contract is frozen!"); ... }
随时了解库中的安全性增强或平台上的漏洞披露。必要时立即更新库。至少开源合约代码,以便在项目中保持公平性,独立开发人员可以更快地发现错误。
EOS智能合约安全:结论
自EOS推出仅仅5个月,它已经超出了预期。它所取得的DPOS,可变智能合约,21个采矿节点等,当然受到权力下放极端主义者的严厉批评。然而,考虑到平台今天提供的可扩展性,它并没有阻止基于以太坊的dApp转向EOS。无论是EOS还是以太坊赢得战争还有待确定,但EOS肯定赢得了这场战斗。它将保持不变,直到以太坊设法达到运行“世界计算机”所需的世界所需的可扩展性。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 EOS智能合约安全终极指南
区块链
2018-12-17 09:50:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
以太坊应用开发接口指的是以太坊节点软件提供的API接口,去中心化应用可以利用这个接口访问以太坊上的智能合约。以太坊应用开发接口采用JSON-PRC标准,通常是通过HTTP或websocket提供给应用程序调用。
JSON-RPC是一种无状态轻量级远程过程调用(RPC)协议,规范定义了数据结构及相应的处理规则,规范使用JSON(RFC 4627)数据格式,规范本身是传输无关的,可以用于进程内通信、socket套接字、HTTP 或各种消息通信环境。
以太坊应用开发接口的配置
不同节点软件的应用开发接口访问点可能有所区别。常见以太坊节点软件的的默认JSON-RPC端结点如下: Geth - http://localhost:8545 Parity - http://localhost:8545 Pytheapp - http://localhost:4000
以最常见的geth节点软件为例,可以使用--rpc选项启动其基于HTTP的JSON-RPC应用开发接口。 ~$ geth --rpc
可以使用--rpcaddr和--rpcport选项修改默认的监听端口(8545)和监听地址(localhost): ~$ geth --rpc --rpcaddr --rpcport
如果需要从浏览器中访问RPC接口,需要正确设置CORS,否则由于同源策略的限制,javascript调用将失败: ~$ geth --rpc --rpccorsdomain "http://localhost:3000"
也可以在geth控制台使用 admin.startRPC(addr,port) 命令来启动JSON RPC。
以太坊应用开发接口的调用
利用标准的HTTP协议就可以调用以太坊应用开发接口,例如在命令行可以使用curl工具: ~$ curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":67}' http://127.0.0.1:8545
你可以点击这里查看 以太坊应用开发接口中文手册 。
以太坊应用开发接口的封装开发包
为便于在不同语言的代码中调用以太坊应用开发接口,以太坊社区涌现了不同语言的开发包,例如: javascript:Web3.js,教程: 以太坊Dapp开发入门 php:Web3.php, 教程: php以太坊开发详解 python:Web3.py,教程: python以太坊开发详解 java:Web3j,教程: Web3j以太坊开发详解 c#:Nethereum,教程: c#以太坊开发详解
可以根据你的需要选择合适的开发包来调用以太坊应用开发接口,这样可以大大缩短对接以太坊 节点的所需要的时间。
原文链接: 以太坊应用开发接口
区块链
2018-12-16 16:51:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在使用Eos的keosd钱包软件时,如果要删除EOS中指定名称的钱包,最简单的办法是 直接删除钱包文件,不过在删除钱包之前,需要先停止钱包软件的运行。 学习EOS应用开发要选这个: 【EOS智能合约与DApp开发教程】 ,不仅内容很系统化,而且有助教 在线答疑 !
EOS删除钱包之前,首先使用kill或pkill命令结束keosd进程的运行: ~$ pkill keosd
钱包文件保存在keosd的数据目录中,默认是 ~/eosio-wallet 。例如,对于默认钱包 default ,对应的钱包文件为 ~/eosio-wallet/default.wallet 。如果你创建了一个名称为 test 的钱包,那么对应的钱包文件为 ~/eosio-wallet/test.wallet 。
可以先使用 ls 命令查看数据目录的内容: ~$ ls ~/eosio-wallet
直接删除钱包对应的文件即可。例如删除默认钱包: ~$ rm ~/eosio-wallet/default.wallet
如果你启动keosd时使用 -d 选项设置了不同的数据目录,例如设置为 /my-wallet ,那么在删除钱包时,应当使用这个自定义目录。例如删除默认钱包: ~$ rm /my-wallet/default.wallet
原文链接: Eos如何删除钱包
区块链
2018-12-16 11:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
使用eos docker镜像是部署本地EOS开发环境的最轻松愉快的方法。使用官方提供的eos docker镜像,你可以快速建立一个eos开发环境,可以迅速启动开发节点和钱包服务器、创建账户、编写智能合约... 如果要学习EOS应用开发,可以访问这个 【EOS智能合约与DApp开发教程】 ,教程内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发、部署与交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
EOS Wiki提供了有关如何使用docker容器编译最新版本代码的说明。但可能有它自己的一些问题,因此我们鼓励你在学习时引用下面镜像。这样最初会更容易,更快。
如果你还没有安装docker,请在 此处 下载。
1.获取docker镜像
以下语句将下载包含已编译软件的Ubuntu镜像。 docker pull eosio/eos
作为快速测试,运行镜像并获取对bash shell的访问权限,请执行以下操作: docker run --rm -it eosio/eos bash
如果可行,你应该得到如下所示的提示,输入 cleos 应返回cleos工具的帮助: root@a5f9eafaab74:/#cleos ERROR: RequiredError: Subcommand required Command Line Interface to EOSIO Client Usage: cleos [OPTIONS] SUBCOMMAND Options: -h,--help Print this help message and exit -u,--url TEXT=http://localhost:8888/ the http/https URL where nodeos is running --wallet-url TEXT=http://localhost:8888/ the http/https URL where keosd is running -v,--verbose output verbose actions on error Subcommands: version Retrieve version information create Create various items, on and off the blockchain get Retrieve various items and information from the blockchain set Set or update blockchain state transfer Transfer EOS from account to account net Interact with local p2p network connections wallet Interact with local wallet sign Sign a transaction push Push arbitrary transactions to the blockchain multisig Multisig contract commands system Send eosio.system contract action to the blockchain. root@a5f9eafaab74:/# root@a5f9eafaab74:/#exit
键入 exit 退出镜像。
2.创建一个docker网络
创建一个docker网络,允许容器相互通信。 docker network create eosnetwork
3.运行容器
运行服务器软件(在端口7777上): docker run --name server --network=eosnetwork --rm -p 7777:7777 -i eosio/eos /bin/bash -c "nodeos -e -p eosio --plugin eosio::producer_plugin --plugin eosio::chain_api_plugin --plugin eosio::http_plugin -d /mnt/dev/data --http-server-address=0.0.0.0:7777 --access-control-allow-origin=*"
要运行钱包软件(在端口5555上): docker run --name wallet --network=eosnetwork --rm -p 5555:5555 -i eosio/eos /bin/bash -c "keosd --http-server-address=0.0.0.0:5555"
让我们打开一个bash shell,以便我们可以测试一些工具。 docker run --name tools --network=eosnetwork --rm -it eosio/eos /bin/bash
4.测试下以便确定它是否全部正常工作
现在让我们确保服务器正在运行: http://localhost:7777/v1/chain/get_info 应该可以在本地Web浏览器中使用。 从工具docker实例运行此命令应该工作: $ cleos -u http://server:7777 get info # Expected response { "server_version": "749a6759", "head_block_num": 1953, "last_irreversible_block_num": 1952, "last_irreversible_block_id": "000007a0c1ae4e28480dcbeef36e9d4970987969f850453dcf8e244b569d6325", "head_block_id": "000007a1fc0d5b3dd16ebfe18ab9a288ac8bc7d03caee050a58a502577d25560", "head_block_time": "2018-05-16T02:04:08", "head_block_producer": "eosio", "virtual_block_cpu_limit": 701979, "virtual_block_net_limit": 7389096, "block_cpu_limit": 99900, "block_net_limit": 1048576 } $ cleos --wallet-url http://wallet:5555 wallet list keys # We have not created any wallets yet, so this is the expected response Wallets: [] []
原文链接: 使用docker学习EOS
区块链
2018-12-16 01:27:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
EOS Factory包含一个完整的EOS测试框架,可以进行智能合约的开发和测试。由Tokenika于创建于2017年的这个基于Python的EOS测试框架可以轻松地完成智能合约的开发、部署与测试。 如果你希望马上学习EOS智能合约的开发,可以访问这个 【EOS智能合约开发教程】 ,教程内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发、部署与交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
随着EOS的不断发展,我们已经习惯了其基础代码的突然变化,并且随着我们的进展而逐渐减少诅咒;)今天,这一天终于来了,我们非常高兴能够将我们的工作交给EOS社区。我们的新生婴儿将以 EOS Factory 的名义出现,旨在成为一个完整的,完全记录的跨平台IDE,使用简单的命令行界面,你将能够: 启动私有 EOS testnet。 编译 EOS 智能合约。 通过单元测试运行它。 调整它直到你觉得它准备好了。 在 EOS 上部署它。 跨平台兼容性。
我们的主要目标之一是使EOSFactory与所有主要操作系统兼容,允许开发人员在从Linux到OSX到Windows10的所有内容上运行EOS节点并与之交互。我们很高兴地告诉大家这一点已经成功。据我们所知,没有其他EOS开发解决方案能够提供。EOSFactory与Visual StudioCode完美配合。这将允许你在一个强大的IDE中编写EOS智能合约,运行和单元测试。
为什么需要它?
EOSFactory提供的所有功能都可以通过官方EOS工具集完成,即cleos和eosiocpp。EOSFactory只是另一种工具吗?并不是的。
代码开发和单元测试涉及需要执行数百次的任务,并且每次都以完全相同的方式和上下文执行。因此,这些任务需要完全自动化,否则浪费了大量时间,更糟糕的是,引入了许多额外的不确定性。手动执行的操作容易出错。
这就是EOSFactory实际带来的内容:一种简单直观的方式来自动化处理智能合约的过程。以Python脚本的形式记下需要以完全相同的方式和上下文多次完成的操作,然后运行脚本。EOSFactory将负责其他所有事情:它将编译你的智能合约,创建新的测试网络,部署合约,调用其方法并验证响应,然后下线测试网络,最后报告结果。所有这一切都在几秒钟内完成。
结构
在EOSFactory中,我们使用Python与智能合约进行交互。但是,在内核里,我们的工具集由C++提供支持。
因此,EOSFactory由两层组成: 名称为 teos 的C++桥连接到运行私有testnet的EOS节点。 名称为 Pyteos 的Python封装器充当方便的人机界面。
换句话说,我们在外部使用Python,而C++则支持内部。
开发周期
这就是智能合约开发周期的样子: 1.写一份智能合约(用EOS的原生C++编写)。 2.编写单元测试(在Python中)。 3.编译你的智能合约。 4.启动一个新的单节点testnet。 5.使用Bios合约和几个测试帐户初始化testnet。 6.部署智能合约。 7.运行单元测试。 8.下线testnet。 9.修改智能合约或单元测试并跳转到第3步。
在EOSFactory中,上述过程的每一步都由Python类和方法完全自动化。作为开发人员,你只提供创意部分,即智能合约和单元测试的内容。单元测试旨在用Python编写,而智能合约当然是用C++编写的。Visual Studio Code完美支持这两种语言。
面向对象与过程
感谢Python,你在EOSFactory中处理的是类和对象。例如,智能合约是一个对象,你可以使用其方法处理它,例如 contract.build() , constract.deploy() , contract.push_action() 和 contract.get_table() 。这与用于EOS的官方CLI的 cleos 中使用的过程命令相反。
文档
你可以在此存储库中找到EOSFactory的 源代码 ,此处还有可用 文档列表 。
路线图
EOSFactory仍在开发中,事情正在得到解决和改进。现在我们正在研究以下功能: 升级到更高级的机制来创建和管理单元测试。它们将被存储为Python文件的层次结构,其方式类似于以太坊的truffle框架中的流程,只是我们使用Python而不是JavaScript。此外,我们计划添加对其他IDE的支持,例如Eclipse。 其次,我们正在考虑将Python层直接连接到 cleos (官方EOS CLI)的优缺点。当我们开始研究EOSFactory时, cleos (当时名为eosc)状况不佳,因此不适合我们的需求,所以我们必须为EOS节点构建自己的C ++接口。随着事情变得更加顺利,我们可能会重新考虑依赖清单。 最后,我们正考虑将Ricardian Contracts整合到我们的单元测试中。这是EOS智能合约的一个非常有趣的(在这个阶段可能并不广为人知)方面。有关更多信息,请参阅 EOSIO文档 。
我们正计划推出EOSFactory的下一个版本,最好是在EOS推出之前。
我们希望这是有帮助的,任何反馈,尤其是批评,都是非常受欢迎的。如果你发现我们的工作有用,你可以通过在即将到来的EOS Block Producer选举中投票选举Tokenika来表达你的支持——我们正在筹备更多令人兴奋的EOS项目。
原文链接: EOS智能合约开发和测试框架EOSFactory
区块链
2018-12-16 00:38:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
EOS智能合约开发需要使用 llvm 和 abigen 来生成 abi 文件, 为此eos提供了一个名为 eosiocpp 的工具。 在这篇文章中,我们介绍如何使用这个工具来开发、部署并调用一个EOS版本的 hello world 智能合约。 如果你希望马上学习EOS智能合约的开发,可以访问这个 【EOS智能合约开发教程】 ,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发、部署与交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
智能合约撰写
首先,编写一个ahello.cpp文件 - EOS的合约开发使用C++语言: # mkdir /home/centos/sc/ahello # cd /home/centos/sc/ahello # vim ahello.cpp
将以下内容插入到ahello.cpp文件中: #include #include using namespace eosio; class hello : public eosio::contract { public: using contract::contract; /// @abi action void hi( account_name user ) { print( "Hello, World", name{user} ); } }; EOSIO_ABI( hello, (hi) )
接下来,编译并创建一个wast(web程序集)文件和一个abi文件。 # eosiocpp -o ahello.wast ahello.cpp # eosiocpp -g ahello.abi ahello.cpp
智能合约的部署与交互
在部署合约之前,我们需要创建测试用的钱包、密钥和帐户。
首先,使用EOS客户端 cleos 创建一个名为 scuser 钱包,EOS使用钱包管理密钥: # cleos wallet create -n scuser Creating wallet: scuser Save password to use in the future to unlock this wallet. Without password imported keys will not be retrievable. "PW5JzRwAUN-----------------------------nAuCRWvHx4XnMPmGf9Kz "
接下来同样使用 cleos 创建一个密钥对: # cleos create key Private key: 5KZzUHNFNvf------------------------------vuF5z7d29uAUbsdn Public key: EOS63ndkvF-----------------------9ZVcByP9nfZcwMLzbMpn
然后将密钥保存在之前创建的钱包 scuser 中: # cleos wallet import -n scuser 5KZzUHNFNvf---------------------d29uAUbsdn
还需要创建一个额外的账户进行测试: # cleos create key Private key: 5JbriTGYsnrpNDvL------------------LgniHVgyTnS5ommxo Public key: EOS8XZoG2248Gu42-------------ps7JoW8tdHQwCsV
然后使用 wallet 子命令把这第二个密钥也存入钱包: # cleos wallet import -n scuser 5JbriTGYsnrpND----------HVgyTnS5ommxo
接下来,使用 create account 子命令创建一个账户 eosio —— 你需要使用账户与EOS区块链交互: #./cleos create account eosio scuser EOS63ndkvF---------cByP9nfZcwMLzbMpn EOS8XZo-------wJnieps7JoW8tdHQwCsV
现在使用 set contract 子命令部署智能合约: # cleos set contract scuser /home/centos/sc/ahello Reading WAST/WASM from /home/centos/sc/ahello/ahello.wasm... Using already assembled WASM... Publishing contract... executed transaction: 053a4883d9c191c2754656544dd045da17bd869250af13a00284a613eed3d23b 1792 bytes 601 us # eosio <= eosio::setcode {"account":"scuser","vmtype":0,"vmversion":0,"code":"0061736d01000000013b0c60027f7e006000017e60027e7... # eosio <= eosio::setabi {"account":"scuser","abi":{"types":[],"structs":[{"name":"hi","base":"","fields":[{"name":"user","ty... warning: transaction executed locally, but may not be confirmed by the network yet
合约部署成功后,使用 push action 子命令来执行合约方法 hi : # cleos push action scuser hi '["user1"]' -p scuser executed transaction: 9ed2894aef0f476687ad893ed16594588cc7a813c524d4b8497ba9f50793b151 104 bytes 330 us # scuser <= scuser::hi {"user":"user1"} >> Hello, World user1 warning: transaction executed locally, but may not be confirmed by the network yet
你应该可以看到类似下图的结果:
代码分析
以上示例代码是EOS智能合约的基本模板。 我们现在将逐步分析上面编写的代码。 #include #include
上述代码引入eos智能合约的头文件。 using namespace eosio;
上述代码使用eosio作为默认命名空间,因此在后续代码中可以直接使用诸如 account_name 之类的数据类型。 class hello:public eosio :: contract {
创建一个hello类,继承自eosio预置的contract。 public: using contract::contract; /// @abi action
这显示了指定操作时在区块链中实际执行的功能。 void hi( account_name user ) { print( "Hello, World", name{user} ); } }; EOSIO_ABI( hello, (hi) )
EOSIO_ABI是一个包含以前版本中的 apply() 函数的宏。
在这篇文章中,我们介绍了如何编写一个EOS版本的Hellow World智能合约,希望有助于你初步了解EOS智能合约的开发。
原文链接: 开发第一个EOS智能合约
区块链
2018-12-16 00:25:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
BtcTool 是一个基于第三方服务和离线裸交易实现的PHP比特币应用开发包,适合不希望部署本地 节点旳PHP开发者,开发包主要包含以下特性: 利用第三方服务获取指定地址的utxo集合 离线生成消费裸交易 利用第三方服务广播裸交易
BtcTool目前支持的第三方服务包括,并且可以非常简单进行扩展: blockchain.info btc.com
BtcTool下载地址: http://sc.hubwiz.com/codebag/btc-php-lib/
1. 代码清单
BtcTool运行在**Php 7.1+**环境下,当前版本1.0.0,主要代码文件清单如下:
代码文件说明
btctool/src/Wallet.php离线钱包类,开发包入口
btctool/src/Utxo.php未消费交易输出类
btctool/src/UtxoBag.phpUtxo集合类
btctool/src/UtxoCollector.phpUtxo收集器接口
btctool/src/CloudUtxoCollector.php基于第三方服务的Utxo收集器实现
btctool/src/UtxoSelector.phpUtxo筛选器接口
btchtool/src/DefaultUtxoSelector.php默认的Utxo筛选器实现
btctool/src/Broadcaster.php交易广播器接口
btctool/src/CloudBroadcaster.php基于第三方服务的交易广播器实现
btctool/src/Utils.php常用辅助函数
demo/wallet-init.php本地钱包初始化
demo/wallet-demo.php钱包载入、裸交易构造和广播
vendor第三方依赖包目录
composer.jsoncomposer配置文件

2. 使用说明
BtcTool的入口是 Wallet 类,基本的使用步骤如下: 创建一个Wallet实例 将私钥加入该Wallet实例 使用Wallet实例的**sendTx()**方法获取编码后的裸交易 使用Wallet实例的**broadcast()**方法广播裸交易
示例例代码如下,说明见注释: addKey($prvKey); $toAddr = 'mgYPLmNuZymK...e2XUNF6VFnT' //地址应当与testnet/mainnet保持一致 $amount = 10000; //单位:satoshi $rawtx = $wallet->sendTx($toAddr,$amount); //构造裸交易,返回16进制字符串 $ret = $wallet->broadcast($rawtx); //广播裸交易
注意: Wallet实例利用钱包中的私钥生成地址列表,并利用这些地址从第三方服务获取utxo信息。 因此需要钱包中的私钥对应地址在链上有utxo存在,Wallet对象才能够成功构造裸交易。 在调用Wallet对象的sendTx()方法构造裸交易时,可以指定找零地址和手续费,例如: $rawtx = $wallet->sendTx($toAddr,$amount,$changeAddr,$fee);
如果未指定后面两个参数,找零地址默认为钱包中第一个私钥对应的地址,手续费默认为 10000satoshi(高费率)。 3. 转账目标地址应当与创建Wallet对象时指定的链ID一致,例如mainnet的p2pkh地址,前缀应当为1
3. 主要模型
3.1 UtxoCollector接口
利用钱包地址列表,获取候选UTXO。
接口方法: collect():提取候选UTXO,返回TUXO集合
当前实现类: CloudUtxoCollector
3.2 UtxoSelector接口
根据目标金额从候选UTXO中选择可消费UTXO
接口方法: select():选择可消费UTXO,返回UtxoBag对象
当前实现类: DefaultUtxoSelector
3.3 Broadcaster接口
裸交易广播器
接口方法: broadcast():广播裸交易
当前实现类: CloudBroadcaster
3.4 Wallet类
管理私钥、地址和脚本,同时提供构造和广播裸交易的方法
主要方法: ::load(): 静态方法,利用硬盘的钱包文件构造Wallet对象 addKey():添加私钥 save():将Wallet对象保存到钱包文件 sendTx():构造裸交易,返回裸交易码流,该方法可自动搜索可用UTXO集合 broadcast():广播裸交易,调用Broadcaster完成
原文链接: 比特币PHP离线交易开发包
区块链
2018-12-15 12:06:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 本文由云+社区发表
当区块链遇到零知识证明
什么是零知识证明
零知识证明的官方定义是能够在不向验证者任何有用的信息的情况下,使验证者相信某个论断是正确的。这个定义有点抽象,下面笔者举几个例子,来帮助读者理解这个概念
例子一:小明有钥匙
地主有一个漂亮的女儿,一直没有嫁出去,有一天地主捡到一把锁,他灵光闪现,何不让老天爷来决定我闺女嫁给谁,于是乎他就发布告示,如果有谁拥有这把锁的钥匙,他就把女儿嫁给他。
小明看到那把锁,心想这不是我前两天扔的那把锁吗?我有钥匙啊,但是他又不想把钥匙拿出来让大家看到,因为钥匙上有不可告人的秘密。于是他找到地主,说出了自己的苦衷,地主略微思考了一下,想到了一个好办法。
他让人搬了一个密封的大木箱,把自己的贴身玉佩放到了箱子里,然后用锁锁起来,说如果小明第二天把玉佩交给他,那小明就可以娶到他的女儿。
小明晚上偷偷用钥匙打开了箱子,然后把玉佩取出来,第二天交到了地主手中。然后和美女幸福的生活在了一起。
这个故事中小明并没有把钥匙给地主看,也没有当着地主把锁打开,但是却证明了自己确实有钥匙。
例子二:哥德巴赫猜想的证明
众所周知,哥德巴赫猜想是一个世界级别的数学难题,有一天一个中国大学生小明找到了证明哥德巴赫猜想的方法。但是他说我能证明出来,没人相信他,甚至没人愿意看他的证明过程,因为名气太小了。这时候他就想到可以让导师出面证明他已经找出了证明方法,但是他遇到了一个两难的问题:
如果他把证明方法给导师看,导师可能直接说这个证明方法是他发现的。
如果他不把证明方法给导师看,导师也不相信他能找到哥德巴赫猜想的证明方法。
实在是难坏了小明!!!!!!!!
零知识证明可以帮助小明解决他的难题,零知识证明的世界里,小明可以不把实际的证明过程给导师看,只需要提供给导师另外一段数据,导师拿到这段数据经过验证可以知道小明真的有哥德巴赫猜想的证明方法,但是导师并没有看到真正的证明过程。
比特币的困境
假如你对比特币有了解的话应该知道,比特币就是一个共有的账本,A转给B一笔钱就是在区块链上写一条记录“A转给B 10块钱”,A的10块钱从哪儿来呢?要求必须是以前某人C在区块链上写一条记录“C转给A 10块钱”。于是比特币的区块链上就是存放了一条一条的账本条目:
C转给A 10块钱
A转给B 10块钱
B转给D 5块钱
B转给E 5块钱
E转给D 5块钱
D转给A 10块钱
……
但是这样会带来一个问题,号称匿名的比特币系统却做不到真的匿名,因为账本是公开的,所以大家都能看到A,B,C,D,E都有多少钱,并且是在什么时间得到的这笔钱的。而所谓的匿名性其实是不存在的。
PS:比特币的匿名性其实是指一般无法把A对应到真正的交易人实体,A只是一个账户标号,而不是类似“小明”这样的实际个人。但是假如小明是A账户的拥有者,而小明最终有可能因为操作账户A兑换了人民币或者购买什么东西,而被发现其实A就是小明。所以这个匿名性并不是真正的匿名性。
ZCASH怎么结合零知识证明到区块链
为了解决比特币的非匿名问题,零知识证明被ZCASH引入到比特币系统中。那么它是怎么实现匿名性的呢?且看娓娓道来。
假设有一个大池子,里面有一大堆的箱子,每一个箱子都被锁住并且只有一把钥匙可以打开,而且箱子中都有一张纸条,内容格式如下:
“这个箱子值x块钱”
我们暂且叫这个大池子为“大池子账本”。
ZCASH整个交易系统就是这样一个大池子,当A要转给B 10块钱的时候,A会填写一张字条:“这个箱子值10块钱”,然后把这个字条放到一个箱子里,锁上,把箱子和箱子对应的钥匙一起给了B,B拿到箱子和钥匙,用钥匙打开了箱子一看字条的内容是,“这个箱子值10块钱”B就知道他确实得到了10块钱,然后把箱子扔到大池子里,而钥匙他保留了下来,这样B就相当于在“大池子账本”里记录了他拥有了10块钱这个事实。
因为B有钥匙,所以他能知道自己有多少钱,并且在适当的时候花掉这笔钱,但是别人(除了A)只能看到箱子,看不到纸条的内容,并不知道箱子值多少钱,并且也不知道箱子到底属于谁(B有钥匙这件事儿他们不知道)。也就是ZCASH这个“大池子账本”系统,做到了隐藏两个比特币系统不能隐藏的事实:
1、 交易的参与者是谁
2、 交易的金额是多少
如果你想从系统中找出B有多少钱的信息,因为你打不开箱子,甚至都不知道哪些箱子是B的,所以你会一无所获
聪明的读者可能会发现这个系统有一个关键的问题没有解决,A凭啥就可以做一个箱子并且说这个箱子值10块钱?B和别的参与者凭啥相信B这个箱子真的值10块钱,这里就要靠零知识证明的强大力量了。
其实大池子中有一个箱子a是属于A的,A拥有这个箱子的钥匙akey,并且箱子里面的纸条上写着:“这个箱子值10块钱”。当A创建箱子b和对应的钥匙bkey的时候也会创建一个零知识证明的凭条akill,这个凭条的作用就是作废掉箱子a。
这个零知识凭条akill的神器功能就是:
1、所有参与者看到这个akill凭条就知道它对应到大池子里的一个箱子
2、所有参与者看到akill就知道它又生成了一个箱子b,并且箱子的纸条是合法的
3、除了A之外的所有参与者并不知道akill对应的就是a这个箱子
4、akey和akill是一一对应的关系
有点绕,简单来说,就是A在不提供钥匙akey只提供零知识证明凭条akill的情况下证明两件事
1、 自己拥有一把可以打开大池子中某个箱子的钥匙
2、 证明箱子b是合法的
这样就简单了,A把箱子b给B的时候,先把akill扔到了大池子里,让大家看,B看到了akill被放到了大池子里,他就相信b箱子是合法的,他确实值10块钱。
余下最后一个问题?如果A每次都说自己拥有打开某个箱子的钥匙,一笔钱被花了很多次怎么办?
akill和akey是一一对应的,并且akill被放到大池子里了,所有参与者都能看见,如果下次他又想给C一个箱子c并且用akill来证明,因为C看到akill已经在大池子里了,所以C就不相信A了。
PS:这里可能部分人会疑惑另外一个问题,当A扔akill到大池子里,或者B扔箱子到大池子里的时候他们的身份不就暴漏了吗?
这里可以认为他们是偷偷扔的,具体对应到网络上的实现就是B随便找了一台网络设备把箱子b对应的数据上传到区块链上。而想通过这台设备找到B很难,并且B也可以托C上传箱子b的数据。
此文已由作者授权腾讯云+社区发布
区块链
2018-12-14 19:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
区块链的重要性已经毋庸置疑,但对大多数跃跃欲试的开发者而言,去中心化思想、 非对称加密、共识算法等技术点的理解和运用,都是入门区块链开发的挑战。合适 的区块链开发教程可以极大地缩短区块链开发的学习周期,因此,本文汇总整理了以太坊、 比特币、EOS和Tendermint这四种流行的区块链的开发教程,推荐给有意进入区块链 开发领域的初学者者。
一、以太坊/ethereum
1.1 以太坊DApp开发入门
教程内容涵盖以太坊智能合约与去中心化应用(DApp)开发相关的诸多概念,如区块链、 ganache仿真器、Solidity语言、solc编译器、web3.js库、truffle开发框架、通证(代币) 发行等,并将手把手地教大家如何构建一个基于以太坊的完整去中心化应用 —— 区块链投票系统。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5a952991adb3847553d205d1
1.2 以太坊电商DApp实战
教程面向有一定基础的以太坊DApp开发者,通过一个去中心化电商DApp的完整开发过程, 引导学习者在实战中深入理解并掌握如何基于以太坊开发去中心化应用,内容涵盖以太坊、 IPFS、MongDB、Express等诸多技术点,采用敏捷开发思路,内容深入浅出,是不可多得的以太坊 DApp实战课程。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6
1.3 web3j以太坊开发详解
教程详细讲解如何使用web3j为Java应用或Android App增加以太坊区块链支持,内容即涉及 以太坊中的核心概念,例如账户管理、状态与交易、智能合约开发与交互、过滤器和事件等,同时 也详细说明如何使用web3j提供的开发接口与以太坊进行交互,是java工程师学习以太坊应用开发的 不二选择。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b2b6e82c02e6b6a59171de2
1.4 Php以太坊开发详解
教程详细讲解如何使用Php开发语言为网站增加以太坊区块链支持,内容即涉及以太坊中的核心概念, 例如账户管理、状态与交易、智能合约开发与交互、过滤器和事件等,同时也详细说明如何使用web3.Php与 以太坊进行交互,是Php工程师学习以太坊应用开发的不二选择。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b36629bc02e6b6a59171de3
1.5 Python以太坊开发详解
教程详细讲解如何使用Python开发以太坊应用,内容即涉及以太坊中的核心概念,例如账户管理、 状态与交易、智能合约开发与交互、过滤器和事件等,同时也详细说明如何使用web3.py与以太坊进行交互, 是Python工程师学习以太坊应用开发的不二选择。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b40462cc02e6b6a59171de4
1.6 C#以太坊开发详解
教程详细讲解如何使用C#开发基于.Net的以太坊应用,课程内容即涉及以太坊中的核心概念,例如账户管理、状态与交易、 智能合约开发与交互、过滤器和事件等,同时也详细说明如何使用Nethereum框架与以太坊进行交互,是C#工程师学习以 太坊应用开发的不二选择。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b6048c3c02e6b6a59171dee
二、柚子/EOS
2.1 EOS智能合约与DApp开发入门
这个教程可以帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、智能合约开发与部署、 使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b52c0a2c02e6b6a59171ded
三、比特币/Bitcoin
3.1 PHP比特币开发详解
教程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时 也详细讲解如何在Php代码中使用bitcoin-php开发包集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php 工程师不可多得的比特币开发学习资料。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5b9e779ac02e6b6a59171def
3.2 Java比特币开发详解
教程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等, 同时也详细讲解如何使用Bitcoinj在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等, 是Java工程师不可多得的比特币开发学习资料。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5bb35c90c02e6b6a59171df0
四、tendermint
4.1 tendermint区块链开发详解
适合希望使用tendermint进行区块链开发的工程师,内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、 默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
教程包含演示源代码,地址: http://xc.hubwiz.com/course/5bdec63ac02e6b6a59171df3
原文链接: 区块链开发教程推荐
区块链
2018-12-14 10:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。
有时候,时间也可以用作一种很好的安全机制。以下代码基于DAO区块链大会,但有不同的变化。不是每个操作需要X个成员批准,而是任何交易都可以由单个成员发起,但它们在执行之前都需要最少的延迟,这取决于交易的支持。提案的批准越多,就越早执行。会员可以对交易进行投票,这意味着它将取消其他一个已批准的签名。
时间锁定Multisig
这意味着如果你没有紧急程度,则执行任何交易可能只需要一个或两个签名。但是,如果单个密钥被泄露,其他密钥可以将该交易延迟数月或数年,甚至可以阻止其执行。
这个怎么运作
所有密钥都已批准的交易可以在十分钟后执行(此金额是可配置的),并且每5%未投票的成员每次需要的时间加倍(如果他们主动投票,则为四倍)反对)。如果它是一个简单的ether交易,只要支持投票将其置于所需的时间内,就会执行交易,但更复杂的交易将要求使用正确的字节码手动执行交易。这些是默认值,但在创建合约时可以设置不同的值:
批准交易的成员数量:近似时间延迟 100%批准:10分钟(最低默认值) 90%批准:40分钟 80%:2小时40分钟 50%:大约一周 40%:1个月 30%:4个月 20%:超过一年 10%或更少:5年或从不 一旦最短的时间过去,任何人都可以执行交易(参见“国会”以获得更完整的步行)。这是故意的,因为它允许某人安排交易或雇用其他人来执行交易。
代码: pragma solidity >=0.4.22 <0.6.0; contract owned { address public owner; constructor() public { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { owner = newOwner; } } contract tokenRecipient { event receivedEther(address sender, uint amount); event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, address(this), _value)); emit receivedTokens(_from, _value, _token, _extraData); } function () payable external { emit receivedEther(msg.sender, msg.value); } } interface Token { function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); } contract TimeLockMultisig is owned, tokenRecipient { Proposal[] public proposals; uint public numProposals; mapping (address => uint) public memberId; Member[] public members; uint minimumTime = 10; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter, string justification); event ProposalExecuted(uint proposalID, int result, uint deadline); event MembershipChanged(address member, bool isMember); struct Proposal { address recipient; uint amount; string description; bool executed; int currentResult; bytes32 proposalHash; uint creationDate; Vote[] votes; mapping (address => bool) voted; } struct Member { address member; string name; uint memberSince; } struct Vote { bool inSupport; address voter; string justification; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyMembers { require(memberId[msg.sender] != 0); _; } /** * Constructor * * First time setup */ constructor( address founder, address[] memory initialMembers, uint minimumAmountOfMinutes ) payable public { if (founder != address(0)) owner = founder; if (minimumAmountOfMinutes !=0) minimumTime = minimumAmountOfMinutes; // It’s necessary to add an empty first member addMember(address(0), ''); // and let's add the founder, to save a step later addMember(owner, 'founder'); changeMembers(initialMembers, true); } /** * Add member * * @param targetMember address to add as a member * @param memberName label to give this member address */ function addMember(address targetMember, string memory memberName) onlyOwner public { uint id; if (memberId[targetMember] == 0) { memberId[targetMember] = members.length; id = members.length++; } else { id = memberId[targetMember]; } members[id] = Member({member: targetMember, memberSince: now, name: memberName}); emit MembershipChanged(targetMember, true); } /** * Remove member * * @param targetMember the member to remove */ function removeMember(address targetMember) onlyOwner public { require(memberId[targetMember] != 0); for (uint i = memberId[targetMember]; i proposalDeadline(proposalNumber) && p.currentResult > 0 && p.proposalHash == keccak256(abi.encodePacked(p.recipient, p.amount, '')) && supportsProposal) { executeProposal(proposalNumber, ''); } } function proposalDeadline(uint proposalNumber) public view returns(uint deadline) { Proposal storage p = proposals[proposalNumber]; uint factor = calculateFactor(uint(p.currentResult), (members.length - 1)); return p.creationDate + uint(factor * minimumTime * 1 minutes); } function calculateFactor(uint a, uint b) public pure returns (uint factor) { return 2**(20 - (20 * a)/b); } /** * Finish vote * * Count the votes proposal #`proposalNumber` and execute it if approved * * @param proposalNumber proposal number * @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it */ function executeProposal(uint proposalNumber, bytes memory transactionBytecode) public { Proposal storage p = proposals[proposalNumber]; require(now >= proposalDeadline(proposalNumber) // If it is past the voting deadline && p.currentResult > 0 // and a minimum quorum has been reached && !p.executed // and it is not currently being executed && checkProposalCode(proposalNumber, p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... p.executed = true; (bool success, ) = p.recipient.call.value(p.amount)(transactionBytecode); require(success); // Fire Events emit ProposalExecuted(proposalNumber, p.currentResult, proposalDeadline(proposalNumber)); } }
部署和使用
像以前一样在这些教程上部署该代码。在部署参数上,将最小时间留空将默认为30分钟,如果你想要更快的锁定时间,则放1分钟。上传后,执行“添加成员”功能以添加组的新成员,他们可以是你认识的其他人,也可以是不同计算机上的帐户或离线存储。
设置为所有者owner的帐户非常强大,因为它可以随意添加或删除成员。因此,在添加主成员后,我们建议你通过执行 Transfer Membership 功能将 owner 设置为另一个帐户。如果你希望对所有成员的添加或删除进行投票,则将其设置为multisig本身,就像任何其他交易一样。另一种方法是将其设置为另一个受信任的 multisig 钱包,如果你希望永久修复成员数,则可以设置为0x000。请记住,此合约上的资金仅与“所有者”帐户一样安全。
与上述任何DAO一样,此合约可以持有以太币,任何基于以太坊的代币并执行任何合约。为此,请检查如何在国会DAO上执行复杂的提案。
警告和改进
为简单起见,对提案的投票仅仅算得少一点支持。如果你愿意,你可以玩弄负面投票更重要的想法,但这意味着少数成员可以对任何提议的交易拥有有效的否决权!
你怎么能改善这个合约?
我们去探索吧!
你已经到了本教程的末尾,但这只是一次伟大冒险的开始。回顾一下,看看你取得了多少成就:你创造了一个活生生的,有说服力的机器人,你自己的加密货币,通过无信息的众筹筹集资金,并用它来启动你自己的个人民主组织。
接下来会发生什么? 你仍然控制的代币可以在分散的交易所出售,或者交易商品和服务,以资助第一份合约的进一步发展和发展组织。 你的DAO可以在名称注册商处拥有自己的名称,然后更改它重定向的位置,以便在代币持有者批准时自行更新。 该组织不仅可以拥有醚类,还可以拥有在以太坊上创造的任何其他类型的硬币,包括其价值与比特币或美元相关的资产。 可以对DAO进行编程,以允许具有多个交易的提案,其中一些预定在未来。它还可以拥有其他DAO的股份,这意味着它可以对更大的组织投票或成为DAO联盟的一部分。 代币合约可以重新编程为持有以太或持有其他代币并将其分发给代币持有者。这会将代币的价值与其他资产的价值联系起来,因此只需将资金转移到代币地址即可实现支付股息。
这一切都意味着你创造的这个小社会可以成长,从第三方获得资金,支付经常性工资,拥有任何类型的加密资产,甚至使用众筹为其活动提供资金。所有这些都具有完全透明,完全的问责制和完全免受任何人为干扰。当网络存在时,合约将完全执行它们被创建的代码,而没有任何例外,永远执行。
那么你的合约是什么?它会是一个国家,一个公司,一个非营利组织吗?你的代码会做什么?
随你,由你决定。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 以太坊DAO之时间锁定的Multisig
区块链
2018-12-14 09:56:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
由于SuperNode超级节点社区建立在EOS之上,我们希望引导我们的社区成员设置EOS钱包和帐户,以便充分参与我们的生态系统。
虽然设置过程可能不如其他区块链系统那么简单,但不要担心。本指南旨在帮助你在10分钟内逐步设置EOS钱包和帐户。
本指南分为三个部分: 使用 Scatter 设置EOS钱包以生成EOS公钥和私钥。 将EOS公钥分配给EOS帐户。 使用Scatter钱包配置EOS帐户。
完成所有这些步骤后,你将成功设置EOS帐户,该帐户支持资金转帐,投票和接收基于EOS的代币(例如 SNCT )等功能。
第1部分:使用Scatter设置EOS钱包
市场上有几个EOS钱包,但为了这个演示,我们将使用Scatter,因为它易于使用并被EOS社区广泛采用。Scatter是Google Chrome扩展程序,因此如果你目前没有使用Chrome浏览器,则需要下载 Chrome 。
转到Google Chrome扩展程序的 Scatter页面 ,然后点击添加到chrome即“Add to Chrome”
2.单击Google Chrome右上角的Scatter徽标
3.设置你选择的密码并单击新建一个账号即“create new Scatter”
4.记下助记符并将所有12个单词保存在安全的位置
5.单击跳过基本设置即“Skip Basic Setup”
6.点击密钥对即“Key Pairs”
7.单击新建“New”
8.按照以下步骤生成密钥对: 1).输入你喜欢的名字。 2).单击生成密钥对即“Generate Key Pair”。完成此步骤后,将显示公钥和私钥。 3).单击复制“Copy”并将两个密钥保存在安全位置。 4).点击保存“Save”。
9.钥匙对是激活的
10.重复步骤7-9以创建另一个密钥对
可以使用与不同权限对应的不同密钥配置EOS帐户。在EOS中,所有者和活动权限是每个帐户的标准权限。因此,在此步骤中,我们将为活动权限创建另外一个密钥对。重复步骤7-9后,你应该能够看到以下内容:
第2部分:将你的EOS公共密钥分配给EOS帐户
从Scatter获取公钥后,下一步是设置EOS帐户。有两种推荐方式: 1.)询问拥有现有EOS帐户的朋友为你创建一个帐户。 2.)使用EOS帐户生成工具。
在本指南中,我们将向你展示如何使用第二个选项创建EOS帐户。我们建议使用EOS帐户创建器 EOS Account Creator ,它简单明了,并支持多种付款方式。
要设置帐户,需要4KB的RAM。在安装过程中,你需要在EOS中支付一小笔一次性费用。但是,与以太网每笔交易都需要付费不同的是,因此在设置帐户后,EOS不会产生后续交易费用。
1.转到EOS帐户创建器,然后单击开始使用即“Get Started”。
2.选择一个帐户名称
获取你的EOS帐户名称时应遵守以下要求:1.)正好包含12个字符。2.)小写字符。3.)数字到5。
3.提供所有者和活动公钥
注意:EOS帐户设置只需要公钥。切勿将你的私钥发送给任何人。
4.支付
EOS Account Creator是一个方便的工具,支持所有流行的支付选项。其中,从交易账户使用EOS自然是最便宜的选择。
5.按照这些说明结算付款
如果你使用EOS结算付款,则支付RAM和投注后的剩余余额将记入你的帐户。
6.你应该在付款完成后不到一分钟内看到确认
7.转到EOS Block Explorer检查你的帐户状态
由于我们使用工具来帮助创建帐户,因此检查准确性非常重要。最好的方法是直接通过 EOS Block Explorer 查看EOS区块链。 1.)转到EOS Block Explorer并输入你的EOS帐户名称
2.)检查您的余额和帐户状态
3.)向下滚动并单击帐户权限即“Account Permissions”。仔细检查公钥是否正确。
第3部分:使用Scatter钱包配置EOS帐户
创建EOS帐户并检查区块链资源管理器后,你可以使用钱包配置EOS帐户。
1.打开Scatter并点击“Identities”
2.点击修改即“Edit”
3.在帐户即“Account”下,选择你的有效密钥对
4.单击导入即“Import”
5.选择“your_eos_account_name @ active”,然后单击“使用所选帐户”
6.点击保存即“Save”
7.单击圆圈以检查帐户中的EOS余额

恭喜!如果你已经到这里,那么你的帐户现已成功配置,你可以在适当的时候使用EOS执行大多数功能并接收SNCT之类的代币了。
了解有关SuperNode社区项目的更多信息,该项目被选为2018年第四季度顶级区块链项目之一 : https://www.snct.io/
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 如何在10分钟内设置EOS钱包和帐户:分步指南
区块链
2018-12-14 09:35:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。
投票支付合约的所有费用和行动需要时间,并要求用户始终保持活跃,知情和专注。另一个有趣的方法是选择一个可以控制合约的指定账户,然后能够迅速做出决定。
流动民主合约
我们将实施一种通常称为流动民主 Liquid Democracy 的版本,这是一种更灵活的代议制民主。在这种民主制度下,任何选民都可以成为潜在的代表:你只需说出你信任哪位选民就可以为你处理这一决定。你的投票权重被委托给他们,他们可以将其委托给他们信任的另一个选民,依此类推。最终结果应该是投票最多的账户是与最大数量的选民有信任关系的账户。
代码: pragma solidity >=0.4.22 <0.6.0; contract token { mapping (address => uint256) public balanceOf; } contract LiquidDemocracy { token public votingToken; bool underExecution; address public appointee; mapping (address => uint) public voterId; mapping (address => uint256) public voteWeight; uint public delegatedPercent; uint public lastWeightCalculation; uint public numberOfDelegationRounds; uint public numberOfVotes; DelegatedVote[] public delegatedVotes; string public forbiddenFunction; event NewAppointee(address newAppointee, bool changed); struct DelegatedVote { address nominee; address voter; } /** * Constructor */ constructor( address votingWeightToken, string memory forbiddenFunctionCall, uint percentLossInEachRound ) public { votingToken = token(votingWeightToken); delegatedVotes.length++; delegatedVotes[0] = DelegatedVote({nominee: address(0), voter: address(0)}); forbiddenFunction = forbiddenFunctionCall; delegatedPercent = 100 - percentLossInEachRound; if (delegatedPercent > 100) delegatedPercent = 100; } /** * Vote for an address * * Send your vote weight to another address * * @param nominatedAddress the destination address receiving the sender's vote */ function vote(address nominatedAddress) public returns (uint voteIndex) { if (voterId[msg.sender]== 0) { voterId[msg.sender] = delegatedVotes.length; numberOfVotes++; voteIndex = delegatedVotes.length++; numberOfVotes = voteIndex; } else { voteIndex = voterId[msg.sender]; } delegatedVotes[voteIndex] = DelegatedVote({nominee: nominatedAddress, voter: msg.sender}); return voteIndex; } /** * Perform Executive Action * * @param target the destination address to interact with * @param valueInWei the amount of ether to send along with the transaction * @param bytecode the data bytecode for the transaction */ function execute(address target, uint valueInWei, bytes32 bytecode) public { require(msg.sender == appointee // If caller is the current appointee, && !underExecution // if the call is being executed, && bytes4(bytecode) != bytes4(keccak256(abi.encodePacked(forbiddenFunction))) // and it's not trying to do the forbidden function && numberOfDelegationRounds >= 4); // and delegation has been calculated enough underExecution = true; (bool success, ) = target.call.value(valueInWei)(abi.encode(bytecode)); // Then execute the command. require(success); underExecution = false; } /** * Calculate Votes * * Go thruogh all the delegated vote logs and tally up each address's total rank */ function calculateVotes() public returns (address winner) { address currentWinner = appointee; uint currentMax = 0; uint weight = 0; DelegatedVote storage v = delegatedVotes[0]; if (now > lastWeightCalculation + 90 minutes) { numberOfDelegationRounds = 0; lastWeightCalculation = now; // Distribute the initial weight for (uint i=1; i< delegatedVotes.length; i++) { voteWeight[delegatedVotes[i].nominee] = 0; } for (uint i=1; i< delegatedVotes.length; i++) { voteWeight[delegatedVotes[i].voter] = votingToken.balanceOf(delegatedVotes[i].voter); } } else { numberOfDelegationRounds++; uint lossRatio = 100 * delegatedPercent ** numberOfDelegationRounds / 100 ** numberOfDelegationRounds; if (lossRatio > 0) { for (uint i=1; i< delegatedVotes.length; i++){ v = delegatedVotes[i]; if (v.nominee != v.voter && voteWeight[v.voter] > 0) { weight = voteWeight[v.voter] * lossRatio / 100; voteWeight[v.voter] -= weight; voteWeight[v.nominee] += weight; } if (numberOfDelegationRounds>3 && voteWeight[v.nominee] > currentMax) { currentWinner = v.nominee; currentMax = voteWeight[v.nominee]; } } } } if (numberOfDelegationRounds > 3) { emit NewAppointee(currentWinner, appointee == currentWinner); appointee = currentWinner; } return currentWinner; } }
部署
首先,你需要一个代币。如果你已按照上面的股东协会 Shareholder association 教程,你可以使用与之前相同的代币,否则只需部署 新代币 并在某些帐户中分配。复制代币地址。
部署民主合约,并将代币地址放在投票权重代币上 ,将75作为每轮中的百分比损失,并将转让所有权(地址)(没有任何空格或额外字符!)作为禁止功能 。
选择代表
现在部署Liquid民主并转到其页面。首先,任何股东都会投票决定他们信任谁代表本合约做出决定。如果你想成为最终决策者,你可以自己投票,或者如果你宁愿没有人代表你担任这个角色,你可以在零地址上投票。
在足够多的人投票后,你可以执行计算投票功能,以便计算每个人的投票权重。这个功能需要多次运行,所以第一次运行它只会将每个人的体重设置为所选代币中的余额。在下一轮中,投票权重将转到你投票指定的人,在下一轮它会去由你选择的人投票的人等等。为了防止无限循环的投票授权,每次投票都会转发,它会失去一些权力,在percentLossInEachRound合约启动时设定。因此,如果损失设定为75%,则意味着你投票的人获得了100%的体重,但如果他们将投票委托给其他人,则只有75%的体重被转发。那个人可以委托给别人,但他们只能获得56%的投票权,依此类推。如果比率低于100%,将会有一个有限的时刻,重新计算投票授权将不再改变结果,但如果它是100%,则意味着投票权重将简单地围绕任何潜在的循环进行循环。
如果这一轮调用计算投票开始已超过一个半小时,所有权重将重置并将根据原始代币余额重新计算,因此如果你最近收到更多代币,则应再次执行此功能。
众议机构
投票代表团的所有优点是什么?例如,你可以在关联上使用它而不是代币权重。首先,获取股东协会的代码,但替换描述代币的第一行: contract Token { mapping (address => uint256) public balanceOf; function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); }
进入这个: contract Token { mapping (address => uint256) public voteWeight; uint public numberOfDelegationRounds; function balanceOf(address member) public view returns (uint256 balance) { if (numberOfDelegationRounds < 3) return 0; else return this.voteWeight(member); } function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); }
在编写合约时,你可以描述主合约使用的多个其他合约。有些可能是已在目标合约上定义的函数和变量,如 voteWeight 和 numberOfDelegationRounds 。但请注意, balanceOf 是一个新功能,它在 Liquid Democracy 或 Association 合约上都不存在,我们现在正在定义它,作为一个函数,如果至少计算了三轮,它将返回 voteWeight 。
使用 Liquid democracy 作为代币地址而不是原始代币,并像往常一样继续部署股东协会。就像以前一样,用户可以针对这些问题创建新的提案或投票,但现在,我们使用委托流程,不是使用代币余额作为投票权。所以,如果你是一个代币持有者,你可以选择一个你信任的人并指定他们,而不是让你自己不断地通知所有问题,然后他们可以选择他们信任的人:结果就是你的代表,而不是被限制在给定的任意地理位置附近,将是你社交距离的人。
此外,这意味着你可以随时切换投票:如果你的代表在某些问题上投票反对你的利益,你可以在提案投票结算之前,转换你的被任命者,或者只是选择代表你自己处理问题并投自己投票。
行政部门
代议制民主国家是选择代表的一种很好的方式,但对于一些重要或简单的决策,对个别提案进行投票可能太慢:这就是为什么大多数民主政府都有一个行政部门,而被任命的人有权代表国家。
在四轮代表之后,更多权重的地址将被设定为被任命者。如果有许多委托投票,那么可能需要再多几轮计算投票才能在最终指定的地址中解决。
被任命者是唯一可以调用Execute函数的地址,它可以执行(几乎)任何代表整个民主的函数。如果液体民主合约中存有任何以太或代币,被任命者将被允许在任何地方移动。
如果你遵循我们的示例并使用此流动民主作为代币创建了股东协会 ,那么你应该能够以有趣的方式使用行政部门:转到主协会地址并执行转移所有权功能到流动民主。
传输完成后,将功能切换为更改投票规则。这允许你更改一些基本的投票规则,例如投票通过所需的最低法定人数或新提案需要留在场上的时间。尝试更改这些设置并单击执行:当弹出确认窗口时,它将告诉你该交易无法执行。当然,这种情况发生,因为只有设置为所有者的地址才能更改这些设置,合约将拒绝此交易尝试。因此,不是键入密码而是复制数据字段上的代码并将其保存到文本文件中。单击取消,滚动到顶部并单击复制地址,并将其保存到文本文件。
现在转到Liquid民族页面并选择执行。在目标上放置关联合约的地址,将 ether amount 保留为0并将先前复制的代码粘贴到字节码数据字段中。确保你从作为被任命者设置的帐户执行它,然后单击执行。
一旦交易被收回,Liquid民主将把订单传递给协会,并且新的投票规则可能适用。被任命者有绝对的权力去做液体民主合约可以执行的任何事情。你可以使用相同的技术创建委派民主所拥有的Mintable代币,然后允许被任命者填写代币或冻结帐户。
为了防止滥用权力,你可以设置一个被禁止的人无法做的禁止功能。如果你按照我们的例子,禁止的功能是 transferOwnership (地址),以防止被任命者将协会的所有权转让给他们自己(在政治上,当总统利用他的行政权力转移给自己以前属于总统,这是政变或贪污)。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 以太坊DAO之流动民主智能合约
区块链
2018-12-13 09:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
1. 准备工作

1.NEO-GUI
2.NEO-CLI
3..NET Core Runtime (不能是2.x版本,官方建议是1.12,实际上我用1.14也是没有问题的)
4.四台windows操作系统的虚拟机(本文是基于AWS的,理论上本地跑虚拟机也是没问题的)

2. 部署 NEO 节点

先给出官方文档的链接--NEO节点部署(官方文档),可以对照这个文档一起进行。
1.先准备好要充当节点的虚拟机。这里以AWS为例,打开AWS首页,注册登录,登录成功后,选择AWS管理控制台,然后再点击左上角的服务,选择EC2



2.新页面中点启动实例(PS:右上角有个节点选择,可以自由切换,我选择的是新加坡的),然后选择 windows server 2016 Base>>> 选择有符合条件的免费套餐 , 后面的默认配置就可以。关键是最后一步--- 核实实例启动 ,这里选择创建一个新密钥对,并保存好。




3.创建成功后,回到控制台,稍等几分钟,等虚拟机准备好,然后选择刚刚创建的虚拟机,点连接,会出现一个弹框,点击下载选择桌面文件,密码那里按提示操作获取密码


4.打开刚刚下载的远程桌面文件,按提示粘贴上一步获取到的密码,成功连接后,等系统初始化完成就可以操作了。初始化完成后先把虚拟机的IE安全设置关掉(不然无法下载文件,而且每打开一个网页都要警告一次,贼烦),点击左下角的搜索按钮,输入server manager,然后在Local server面板找到IE Enhanced SecurityConfiguration,点击ON,然后在弹出的面板里选择OFF,关闭掉IE安全设置



5.打开NEO节点部署(官方文档),下载NEO-GUI,NEO-CLI,.NET Core Runtime。 这里说一下,为什么要下载 NEO-GUI ? 按照官方文档的说明,理论上我们只下载NEO-CLI就可以的了。但是官方文档没有指明NEO-CLI运行需要的两个库文件具体在哪里可以下载。官方文档这里的第一步的程序包应该指的就是NEO-GUI,然后再NEO-GUI目录下的x64(或者x86,根据你的操作系统来选择)文件下可以找到这两个库文件。将这两个文件复制到NEO-CLI文件目录下。



6.安装.NET Core Runtime ,安装完成后,打开命令行,我这里用的是PowerShell,用命令行切换到NEO-CLI目录,输入 dotnet neo-cli.dll ,不出意外的话,NEO节点就部署成功了,如图所示。


3. 搭建私链(或者联盟链)

官方文档在这---NEO私链搭建(官方文档),可以对照着一起来做。
1.文档中有指出需要4台虚拟机,所以我们需要4台虚拟机,并且都是部署好NEO节点的。可以按照上一步教程,将另外3台配置好。这里的给虚拟机命名的时候最好自己弄个简单的标识排序,不然后面操作起来可能会乱,比如我这里的4台虚拟机分别命名是neo-window,neo-window1,neo-window2,neo-window3。当然这些看个人习惯来。
NEO 私有链的部署至少需要 4 台服务器才能取得共识,每台服务器对应一个共识节点,每台服务器上有一个NEO 钱包文件

2.另外3台部署成功后,打开其中一台虚拟机,我这里打开的是第一台(neo-window),然后启动NEO-CLI,用 create wallet xxxx.db3 新建4个钱包,我这里分别创建的是wallet1.db3 ~wallet4.db3。新建好后将四个 pubkey 保存到一个txt文件里,后面会用到。然后将其它3个钱包文件分别复制粘贴到相应的虚拟机上。 注意要放在跟 neo-cli.dll 同一个目录下 。



3.后台打开AWS控制台,将4台虚拟机的IP地址记录下来。这一步后面修改节点配置文件的时候会用到。如图所示,要把IPv4 公有IP记录下来。


4. 如果你之前有启动过 neo-cli.dll ,那么要先删除掉 NEO-CLI 目录下的 Chain 目录,不然做到最后面可能会出现有连接数,但是区块高度为 0 的状况 。 同样的,如果你因为好奇心打开过 NEO-GUI ,你也需要删除相应的 Chain 目录。如果没有,可以跳过删除这一步 。之后打开NEO-CLI目录,用txt方式打开目录下的 protocol.json 文件,这个是节点的配置文件。按照官方文档的格式进行修改


下面是我修改好的文件,修改好后,保存,并 复制替换掉其它虚机上的客户端的节点配置文件 。
{
"ProtocolConfiguration":{

"Magic":66123456,
"AddressVersion":23,

"StandbyValidators":[
"0264e5b51fa2af8392292fd13e0381d913288e0d197086d12aef195d6823a349fa",
"034a879e08069f89cdbc34067ff0df614d36bef7a30b014509dfe3170c6a9d7974",
"0332c8d77ca8e2a7847325af55457f2793d285e2a914e349bc329c94e301c01dd6",
"020a02297371c867c845471dc0030277dc669c89ae80071424442787535295c7a4"
],

"SeedList":[
"54.255.212.167:10333",
"52.221.255.41:10333",
"13.229.109.145:10333",
"13.228.25.57:10333"
],

"SystemFee":{
"EnrollmentTransaction":1000,
"IssueTransaction":500,
"PublishTransaction":500,
"RegisterTransaction":10000
}
}
}


5.修改虚拟机的防火墙入站规则,这里要注意的是 不仅需要修改虚拟机的入站规则,而且要去 AWS 控制台修改安全组里的入站规则, 由于我4台虚拟机用的都是同一个安全组,所以只要修改一个就行了。虚拟机里windows操作系统的入站规则我就不具体写了,在控制面板里操作,这个比较简单。其实,如果想简单粗暴点,直接关掉windows的防火墙应该就可以了。



6.在四台虚拟机上分别用neo-cli打开相对应的钱包,这里以第一台虚拟机为例。运行 dotnet neo-cil.dll 命令,如果你想检查当前的区块状态,可以使用 show state ,初始状态应该是高度为0,节点为0。然后打开本机对应的钱包文件,这台虚拟机对应的wallet1.db3。

输入命令 open wallet wallet1.db3 打开钱包
然后输入 start consensus 开启共识

其他虚拟机一样操作,分别打开对应的钱包文件,然后输入命令开启共识。不出意外的话就会出现如图所示的界面。关掉其中一台的话,其它三台依然可以产生共识



7.利用NEO-GUI提取NEO和GAS。安装好NEO-GUI后,将NEO-CLI目录下的 protocol.json 文件覆盖掉NEO-GUI目录下的 protocol.json ,然后运行neo-gui.exe。进行多方签名的设置。



但是有一个问题,这里有点比较坑的地方,文档里也没有明说。 NEO-CLI 和 NEO-GUI 是不能同时启动的,也就是说用 dotnet neo-cli.dll 启动了 neo-cli 的时候,不能运行 neo-gui.exe ,反之亦然。所以当产生共识之后,我们需要关闭其中一个节点的 NEO-CLI ,然后在这个节点上的 NEO-GUI 客户端进行操作。依次打开 4 个钱包,进行多方签名的设置和重建钱包索引的操作,这个操作是在同一台虚拟机上完成的。这个一定要注意。
然后把 NEO 从合约地址转到普通地址中,打开 4 个钱包中的任意一个,点击 交易 >>> 转账 输入标准地址和数额,将 1 亿 NEO 转到这个地址中。

然后系统会提示“交易构造完成,但没有足够的签名”,然后将代码复制下来,打开第二个钱包,点击交易签名粘贴刚才复制的代码,点击签名,然后将代码复制下来,打开第三个钱包,点击交易签名粘贴刚才复制的代码,点击签名,这时你会发现窗口中出现了一个广播按钮,代表交易已经签名完成(达到多方签名合约要求的最少签名数量)可以广播,点击广播后转账交易开始广播,约 15 秒后转账成功。

上面的操作也是在同一个客户端完成的。这里多说一句,我第一次弄的时候没有注意,我是在 4 个节点客户端上分别操作的,也就是说多方签名的设置和重建钱包索引操作,我是在 4 台虚拟机上分别完成的。这就造成了我没有正在运行的节点了 ( 因为 neo-cli 和 neo-gui 不能同时运行,而 neo-gui 没有共识功能 ) 。然后,到了提取 NEO 和 GAS 这一步时,就造成了,我的交易签名成功了,而且广播了,但是并没有转帐成功。造成这样的原因是因为我虽然广播了,但是此时没有节点帮我把这个交易打包上去。所以,我的交易转账当然不成功,在普通地址上也就没看到那些相应的 NEO 。这点是因为自己对区块链的概念不够深,我这里被困扰了很久。


8.GAS的提取教程基本上差不多,这里的不多说废话了,官方教程已经说得很详细了。

作者:人称卢哥哥
转自NEL新经济实验室: 基于NEO的私链(Private Blockchain)
区块链
2018-12-12 20:29:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
什么是DAPP
DAPP 是以太坊发明的词汇 Decentralized Application.
目前基于区块链技术开发的应用程序广泛的接受使用了这一名称。
NEL将为开发DAPP提供全面的服务
什么是NEL
NEL是 “NewEconoLab新经济实验室”的简称,成立于2017.11.9日,是一个年轻的社区型组织。
致力于 培养社区开发人才,推动项目落地,发展NEO社区生态
主要活动区域是:https://github.com/NewEconoLab/
NEL会为NEO的DAPP开发提供全面的支持,欢迎学习、交流、参与。
DAPP开发的几种模式
目前DAPP开发有几种模式

之前做了一次基于全节2点开发DAPP的培训,效果不太好。去的人都太帅了,颜值超越了程序员太多。
所以这里补上说明,退一步,更加系统的来说一下DAPP的开发。
很多时候,很多事情,并不是只差一个程序员的状态。往往,你差好几个程序员。
我们将分这几种模式来讨论一下。下文列出的需要程序员都可以一人身兼N职。
模式一、基于全节点客户端开发DAPP
基于全节点客户端开发DAPP是工作量最少的模式,你只需要修改NEO GUI 源码,加上你的DAPP专属的代码.
如果你是要测试一个DAPP的概念,这种方式比较适合,快。
需要程序员:
1.winform 程序员,会c#的程序员多半可以用几个小时掌握winform开发。
2.智能合约程序员,基于NEO区块链开发智能合约,目前用c#开发智能合约是最成熟的。
NEL 已经为此提供了项目NEO-GUI NEL版。
源码 https://github.com/NewEconoLab/neo-gui-nel
这个版本增加了插件机制。
1.你不需要去阅读neo-gui源码了,关键部分通过插件接口可以知道。
2.项目更加可控,多个dapp也不用整合代码了
3.有例子可以参考
模式二、快速全节点客户端开发DAPP
这个模式是在同步区块链时有所选择,只同步和自己的钱包有关的,所以同步速度较快。
但还是要同步。NEO官方有用ts实现一版,代码比较复杂。
官方已放弃此项目,这个模式 NEL也不会提供支持。
模式三、轻钱包前后台模式开发DAPP
首先很多介入DAPP开发的爱好者们基本上都低估了轻钱包前后台模式的开发难度。如果一头扎进来,基本上几个月的时间扔进去还是一头雾水。
蓝鲸淘或者区块链浏览器或者别的什么采用这种模式的项目开发,都不是一个小工程。
他们基本的系统结构是这样

看了这个系统结构,我相信DAPP的开发者能够更清醒的认识到开发一个这种模式的DAPP,并非一件轻松愉快的事情。
虽然这种开发模式体验最好,但是还没有形成标准,所以后台几层需要自行开发。
有一些浏览器开放了他们的API出来,但对整个系统缺乏说明。
采用这种模式你需要如下程序员
1.能修改或者部署neo-cli节点的程序员或者网管
2.能开发爬虫之类程序的程序员,会写数据库
3.能开发网页API的程序员
4.前端程序员
5.智能合约程序员,推荐c#
很不幸 2 3 4 很难列出推荐的语言或者技术,因为实现这类功能的方案实在太多了。
NEL将会为这种开发模式提供一整套的方案,助力DAPP开发。
很多项目还在开发中,后文会有介绍
DAPP开发的基础
理解区块链
区块链是分布式的账本。
需要了解区块链的共识过程,每一个区块都是在每一个节点上被执行。
和传统的服务器不一样,每一个节点都要逐一执行区块链上的每一笔交易。
而所有的DAPP,最终也体现为一笔笔的交易。
理解NEO智能合约
调用NEO智能合约也是一笔交易。
智能合约主要的输出方式是写入storage。
这些我们以后会专门开一篇来说
调试NEO智能合约 如何编写合约见NEO文档 如何发布合约见NEO文档 如何调用合约NEOGUI 有提供调用合约的测试功能,见NEO文档 如何调试合约
鉴于NEO智能合约的执行是在链上,而发布调用交易和交易被执行的过程是分离的,而且还不是本机执行。所以智能合约的调试一直缺乏良好的方法
NEL为此专门开设了区块链浏览器项目

可以分析已经执行过的智能合约交易,对其执行的过程完全复盘,并对应到源码
智能合约浏览器还在开发中,目前已经可以使用,欢迎开发者入坑。以后我们会专门撰文来说这个话题。
NEL的一些支持工作
Neo-gui nel 版本
源码 , https://github.com/NewEconoLab/neo-gui-nel
(neo-gui项目)
目标 简化开发基于neo-gui的dapp
状态 已完成,将关注neo 官方 neo项目 neovm项目 neo gui项目,随时升级同步。
Neo-cli nel版本
源码 https://github.com/NewEconoLab/neo-gui-nel
(neo-cli项目,和上面的在同一个仓库)
目标 在NEOCIL的基础上提供智能合约调试的数据。
状态 已完成,随时升级同步
NEL 节点统计入库程序
目标 开发一个通用化、容易插件化扩展的节点数据统计入库程序。
状态 计划中
NEL 查询API
目标 为轻钱包前后台模式的DAPP 或一个者neo浏览器 提供查询功能支持
状态 计划中
NEL 智能合约交易浏览器
源码 https://github.com/NewEconoLab/SmartContractBrowser
目标 提供已经完成的智能合约交易中精确的交易执行情况的检查,作为一个非常有效的智能合约调测程序,补充智能合约开发环境。
状态 开发中,完成50%,已经可以配合Neo-gui nel版本 或者neo-cil nel版本 进行本地化调测。
等NEL 查询API上线,就可以开发online版本。
NEL 浏览器插件钱包
目标 解决各个浏览器DAPP 各自实现各自钱包导致的各种问题。 用户的私钥转来转去都在浏览器缓存中,风险较大的问题。插件钱包加密保存,DApp 找插件要地址就行了 私钥加密保存在浏览器插件中,永远不会给DAPP。需要对数据块签名时,DAPP 发送数据给浏览器插件进行签名。签名后返还给DAPP。DAPP 不会得到私钥,降低私钥泄露风险 每次签名插件会弹出提示,让用户确认,避免DAPP在用户不知情的情况下发送交易。 对接硬件钱包只需要插件支持,所有DAPP无需额外工作即可得到硬件钱包的保护。
状态 计划中

作者:李剑英
原文链接: https://www.cnblogs.com/crazylights/p/8016653.html
区块链
2018-12-12 19:42:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
我们的目标是通过编程学习让你更了解区块链技术。这将对区块链开发初学者一次很好的体验。这里需要强调一下,编程零基础也能学会。
我们将以小组的形式,由教练带领学员完成DAPP开发。每位学员在指导下自主完成程序开发任务,结束后会得到一个自己的DAPP:

本次活动的开发基于Nervos的Appchain,Nervos Appchain是一套高性能的开源区块链解决方案,作为成熟产品在过去两年中成功地支撑了多家银行和金融机构的区块链创新业务。其中的核心组件CITA针对各类商业应用做了改进,包括支持灵活的激励机制(经济模型)和治理机制(通过智能合约进行记账节点配置、权重分配等)。
通过本次学习,你不但会学到如何使用Solidity开发智能合约,也会学到如何使用React开发前端页面,并熟悉整个的DAPP开发流程。
1
时间地点
**时间:**2018年12月23日 13:00-18:00
地址 :(北京昌平)回龙观东大街3号楼珠峰培训
2
如何参与
提交个人信息并报名。成功后,请添加我们的管理员,并微信转账20元给管理员,管理员会拉你入群。
此次活动为免费活动,但为了保证参与质量与人数,此次报名费用为押金,当天参加活动的报名者将退还押金。如未参与活动的报名者,押金不予以退还。
3
活动流程
12:30 - 13:00 报到
13:00-13:10 light talk
13:10 - 13:40 教练 & 学员分组
13:40 - 17:00 coding together
17:00 - 17:40 成果展示 & 分享
17:40 - 18:00 合影 & 自由交流
4
注意事项
活动需要每个人自己准备电脑。
需要对区块链有基础认知。
为了节省当天时间,我们会在群里指导预先安装开发环境。所以缴费的小伙伴一定要加管理员入群,不要空降。
5
联合主办
珠峰培训
HiBlock社区
haoqicat.com
小芦蜂区块链培训
区块链
2018-12-12 13:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。
在 上一节 中,我们为了展示什么是DAO创建了一个合约,就像一个采用邀请制的俱乐部,会员被总统的心血来潮邀请或禁止。但这有一些缺点:如果有人想改变他的主要地址怎么办?如果一些成员比其他成员更重要?怎么办? 如果你真的想在公开市场上交易或出售会员资格或股票怎么办?如果你希望你的组织作为股东的不改变决策的持续工作,该怎么办?
股东协会智能合约
我们将修改我们的合约以将其连接到特定代币,该代币将作为合约的持有份额。首先,我们需要创建此代币:转到代币教程并创建一个简单代币,初始供应为100,小数为0,百分号(%)为符号。如果你希望能够以百分比的百分比进行交易,则将供应量增加100倍或1000倍,然后将相应数量的零添加为小数。部署此合约并将其地址保存在文本文件中。
那么修改后的股东协会合约代码: pragma solidity >=0.4.22 <0.6.0; contract owned { address public owner; constructor() public { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { owner = newOwner; } } contract tokenRecipient { event receivedEther(address sender, uint amount); event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, address(this), _value)); emit receivedTokens(_from, _value, _token, _extraData); } function () payable external { emit receivedEther(msg.sender, msg.value); } } contract Token { mapping (address => uint256) public balanceOf; function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); } /** * The shareholder association contract itself */ contract Association is owned, tokenRecipient { uint public minimumQuorum; uint public debatingPeriodInMinutes; Proposal[] public proposals; uint public numProposals; Token public sharesTokenAddress; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter); event ProposalTallied(uint proposalID, uint result, uint quorum, bool active); event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress); struct Proposal { address recipient; uint amount; string description; uint minExecutionDate; bool executed; bool proposalPassed; uint numberOfVotes; bytes32 proposalHash; Vote[] votes; mapping (address => bool) voted; } struct Vote { bool inSupport; address voter; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyShareholders { require(sharesTokenAddress.balanceOf(msg.sender) > 0); _; } /** * Constructor * * First time setup */ constructor(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) payable public { changeVotingRules(sharesAddress, minimumSharesToPassAVote, minutesForDebate); } /** * Change voting rules * * Make so that proposals need to be discussed for at least `minutesForDebate/60` hours * and all voters combined must own more than `minimumSharesToPassAVote` shares of token `sharesAddress` to be executed * * @param sharesAddress token address * @param minimumSharesToPassAVote proposal can vote only if the sum of shares held by all voters exceed this number * @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed */ function changeVotingRules(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) onlyOwner public { sharesTokenAddress = Token(sharesAddress); if (minimumSharesToPassAVote == 0 ) minimumSharesToPassAVote = 1; minimumQuorum = minimumSharesToPassAVote; debatingPeriodInMinutes = minutesForDebate; emit ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, address(sharesTokenAddress)); } /** * Add Proposal * * Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. * * @param beneficiary who to send the ether to * @param weiAmount amount of ether to send, in wei * @param jobDescription Description of job * @param transactionBytecode bytecode of transaction */ function newProposal( address beneficiary, uint weiAmount, string memory jobDescription, bytes memory transactionBytecode ) onlyShareholders public returns (uint proposalID) { proposalID = proposals.length++; Proposal storage p = proposals[proposalID]; p.recipient = beneficiary; p.amount = weiAmount; p.description = jobDescription; p.proposalHash = keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode)); p.minExecutionDate = now + debatingPeriodInMinutes * 1 minutes; p.executed = false; p.proposalPassed = false; p.numberOfVotes = 0; emit ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); numProposals = proposalID+1; return proposalID; } /** * Add proposal in Ether * * Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. * This is a convenience function to use if the amount to be given is in round number of ether units. * * @param beneficiary who to send the ether to * @param etherAmount amount of ether to send * @param jobDescription Description of job * @param transactionBytecode bytecode of transaction */ function newProposalInEther( address beneficiary, uint etherAmount, string memory jobDescription, bytes memory transactionBytecode ) onlyShareholders public returns (uint proposalID) { return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode); } /** * Check if a proposal code matches * * @param proposalNumber ID number of the proposal to query * @param beneficiary who to send the ether to * @param weiAmount amount of ether to send * @param transactionBytecode bytecode of transaction */ function checkProposalCode( uint proposalNumber, address beneficiary, uint weiAmount, bytes memory transactionBytecode ) view public returns (bool codeChecksOut) { Proposal storage p = proposals[proposalNumber]; return p.proposalHash == keccak256(abi.encodePacked(beneficiary, weiAmount, transactionBytecode)); } /** * Log a vote for a proposal * * Vote `supportsProposal? in support of : against` proposal #`proposalNumber` * * @param proposalNumber number of proposal * @param supportsProposal either in favor or against it */ function vote( uint proposalNumber, bool supportsProposal ) onlyShareholders public returns (uint voteID) { Proposal storage p = proposals[proposalNumber]; require(p.voted[msg.sender] != true); voteID = p.votes.length++; p.votes[voteID] = Vote({inSupport: supportsProposal, voter: msg.sender}); p.voted[msg.sender] = true; p.numberOfVotes = voteID +1; emit Voted(proposalNumber, supportsProposal, msg.sender); return voteID; } /** * Finish vote * * Count the votes proposal #`proposalNumber` and execute it if approved * * @param proposalNumber proposal number * @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it */ function executeProposal(uint proposalNumber, bytes memory transactionBytecode) public { Proposal storage p = proposals[proposalNumber]; require(now > p.minExecutionDate // If it is past the voting deadline && !p.executed // and it has not already been executed && p.proposalHash == keccak256(abi.encodePacked(p.recipient, p.amount, transactionBytecode))); // and the supplied code matches the proposal... // ...then tally the results uint quorum = 0; uint yea = 0; uint nay = 0; for (uint i = 0; i < p.votes.length; ++i) { Vote storage v = p.votes[i]; uint voteWeight = sharesTokenAddress.balanceOf(v.voter); quorum += voteWeight; if (v.inSupport) { yea += voteWeight; } else { nay += voteWeight; } } require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached if (yea > nay ) { // Proposal passed; execute the transaction p.executed = true; (bool success, ) = p.recipient.call.value(p.amount)(transactionBytecode); require(success); p.proposalPassed = true; } else { // Proposal failed p.proposalPassed = false; } // Fire Events emit ProposalTallied(proposalNumber, yea - nay, quorum, p.proposalPassed); } }
部署和使用
代码的部署几乎与前面的代码完全相同,但你还需要放置一个共享代币地址 shares token address ,该地址是代币的地址,它将作为具有投票权的共享。
注意这些代码行:首先我们描述新合约的代币合约。由于它只使用了 balanceOf 函数,我们只需要添加那一行。 contract Token { mapping (address => uint256) public balanceOf; }
然后我们定义一个类型标记的变量,这意味着它将继承我们之前描述的所有函数。最后,我们将代币变量指向区块链上的地址,因此它可以使用它并请求实时信息。这是使一个合约在以太坊中理解另一个的最简单方法。 contract Association { token public sharesTokenAddress; // ... constructor(token sharesAddress, uint minimumSharesForVoting, uint minutesForDebate) { sharesTokenAddress = token(sharesAddress);
这个协会 association 提出了前一届大会 congress 没有的挑战:因为任何拥有代币的人都可以投票而且余额可以很快变化,当股东投票时,提案的实际分数不能计算,否则有人能够通过简单地将他的份额发送到不同的地址来多次投票。因此,在本合约中,仅记录投票位置,然后在执行提案阶段计算实际得分。 uint quorum = 0; uint yea = 0; uint nay = 0; for (uint i = 0; i < p.votes.length; ++i) { Vote v = p.votes[i]; uint voteWeight = sharesTokenAddress.balanceOf(v.voter); quorum += voteWeight; if (v.inSupport) { yea += voteWeight; } else { nay += voteWeight; } }
计算加权投票的另一种方法是创建一个单一的有符号整数来保持投票得分并检查结果是正面还是负面,但你必须使用int将无符号整数 voteWeight转换为有符号整数 得分= int(voteWeight);
使用这个DAO就像以前一样:成员创建新的提案,对它们进行投票,等到截止日期过去,然后任何人都可以计算投票并执行它。
但是我如何限制所有者的权力呢?
在此合约中,设置为所有者 owner 的地址具有一些特殊权力:他们可以随意添加或禁止成员,更改获胜所需的保证金,更改辩论所需的时间以及投票通过所需的法定人数。但这可以通过使用业主拥有的另一种权力来解决:改变所有权。
所有者可以通过将新所有者指向0x00000来将所有权更改为任何人....这将保证规则永远不会改变,但这是不可逆转的行动。所有者还可以将所有权更改为合约本身:只需单击复制地址 copy address 并将其添加到新所有者 new owner 字段即可。这将使得所有者的所有权力都可以通过创建提案来执行。
如果你愿意,你也可以设置一个合约作为另一个合约的所有者:假设你想要一个公司结构,你想要一个有权任命董事会成员的终身总统,然后可以发行更多的股票,最后这些股票被投票关于如何花费预算。你可以创建一个关联合约 Association ,该合约 mintable token 使用最终由单个帐户拥有的会议 congress 所拥有的mintable代币。
但是如果你想要不同的投票规则呢?也许改变投票规则你需要80%的共识,或者成员可能不同。在这种情况下,你可以创建另一个相同的DAO或使用其他一些源代码并将其作为第一个的所有者插入。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 以太坊DAO之股东协会
区块链
2018-12-12 09:01:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
neo-boa编译器介绍
neo-boa编译器可将Python文件编译为.avm格式,在 Neo虚拟机 中运行。NEO虚拟机可在 Neo区块链 上执行合约。
编译器支持Python语言子集(python语言子集与python的关系类似于蟒蛇与蟒属的关系)。
目前功能 将Python语言子集编译成.avm格式,在Neo虚拟机上运行。 适用于Python3.4与3.5
未来功能 编译更广泛的Python语言子集 适用于Python3.6
已支持的Python功能
下文为目前支持的Python功能一览。详细介绍请参见boa.tests.src目录中的案例。 流控制
If、Else、Elif、While、Break、Methodcalls、Lamdbas、for x in 用于整数运算的算数运算符与相等运算符
ADD、SUB、MUL、DIV、ABS、LSHIFT、RSHIFT、AND、OR、XOR、MODULO、INVERT、GT、GTE、LT、LTE、EQ、NOTEQ
使用自定义内置功能进行列表创建。注意列表一经创建,其长度便不可更改。 from boa.code.builtins import list
·
· # this works
· x = list(length=10)
· x[3] = 84
·
· # this also works
· x = [1,3,65,23]
·
· # this does NOT work
· x = [ ]
· x.append(1)
· 支持列表(building slices)操作
· x = [1,2,46,56]
· y = x[1:3]
在可能的情况下,Python的某些__builtins__已经根据NEO虚拟机的特点以自定义的方式实现 fromboa.code.builtins import range
·
· xrange = range(1, 30)
·
· # this also works
· fori in range(2, 21):
· i = i + 1
安装
使用pip pipinstallneo-boa复制代码
手动安装
克隆存储库,进入项目目录后创建Python3虚拟环境,并通过以下指令激活。 python3 -mvenvvenv复制代码 sourcevenv/bin/activate复制代码
或单独安装Python3.5 virtualenv -p /usr/local/bin/python3.5venv复制代码 sourcevenv/bin/activate复制代码
接着,通过以下指令安装需求 pipinstall -rrequirements.txt复制代码
基本用途
编译器使用指南如下 fromboa.compiler import Compiler复制代码 Compiler.load_and_save('path/to/your/file.py')复制代码
参见boa.compiler.Compiler与其他模块了解其他高级用途。
许可证
· 开源 MIT (Github地址:/CityOfZion/neo-python/blob/master/LICENSE.md)
· 主作者为 localhuman (Github地址:https://github.com/localhuman)
boa.compiler.Compiler
下文将介绍Compiler的具体实现细则。
classboa.compiler.Compiler [source]
主编译器接口类
通过下列程序加载python文件,编译为.avm格式,并与python文件存储在一个地方。
fromboa.compiler import Compiler
Compiler.load_and_save(‘path/to/your/file.py’)
# return the compiler object for inspection
compiler = Compiler.load(‘path/to/your/file.py’)
# retrieve the default module for inpection
default_module = compiler.default
# retreive the default/entry method for the smartcontract
entry_method = default_module.main
default
取回默认或“入口”模块。
返回值: 默认反回值为boa.code.Module对象,异常时无返回值
staticinstance()[source]
取回当前编译器对象的实例(如有),否则创建一个实例
返回值: 编译器对象的单个实例 staticload(path)[source]
调用load来加载需编译但无需写为.avm格式的Python文件
参数: path–Python文件的编译路径
返回值: 编译器实例
用途: 通过下述程序返回编译器对象进行检查
fromboa.compiler import Compiler
compiler = Compiler.load(‘path/to/your/file.py’)
staticload_and_save(path, output_path=None)[source]
调用load_and_save来加载需编译为.avm格式的Python文件,并保存结果。默认情况下,最终生成的.avm文件将与源文件存储在一个地方。
参数:
· path——Python文件的编译路径
· output_path——已编译的.avm文件的可选保存路径
返回值: 返回编译器实例
用途: 通过下述代码返回编译器对象进行检查
fromboa.compiler import Compiler
Compiler.load_and_save(‘path/to/your/file.py’)
write()[source]
返回值: 已编译的Python程序的字节串 staticwrite_file(data, path)[source]
通过指定路径将输出数据存储至文件系统
参数:
· data——待写入磁盘的数据字节串
· path——文件写入路径
boa.code.module.Module
下文将介绍Module的具体实现细则。
classboa.code.module.Module (path, module_name=”, is_sys_module=False, items_to_import=None)[source]
模块是包含代码对象的顶层组件。例如,在path/to/my/file.py的编译过程中,file.py中包含的项目即为模块。一个可执行项可包含多个模块。上述案例中的“默认”或“入口”模块即为file.py。
调用Compiler.load_and_save(‘path/to/file.py’)时会专门为file.py创建一个模块。若file.py导入了其他任何功能,那些模块也会被添加至可执行项中,并置于Module.loaded_modules属性中。
在模块被当做方法处理,方法被当做基本块处理,基本块被处理为标记后,主模块或default模块的write()方法即被调用,将可执行项写为字节串,返回磁盘并存储。
如果您想检查模块内容,可使用Compiler.load(‘path/to/file.py’),该功能将返回一个编译器实例。获取该实例后,您便可以访问编译器的default模块,从而访问该默认模块中装入的其他模块。
各模块(以及各方法对象)均包含byteplay3对象bp的引用,该对象包含可在Python解释器中显示的指令集。
您可对具备bp属性的任意对象调用print(module.bp.code),结果将输出一段Python解释器代码。
>>> fromboa.compiler import Compiler
>>> module = Compiler.load(‘./boa/tests/src/AddTest1.py’).default
>>> print(module.bp.code)
2 1 LOAD_CONST
2 LOAD_CONST ‘Main’
3 MAKE_FUNCTION 0
4 STORE_NAME Main
5 LOAD_CONST None
6 RETURN_VALUE
对可执行项进行处理与标记化后,便会生成虚拟机标记集,虚拟机标记虽与byteplay3标记相类似,但仍存在显著区别。这些标记均包含在该模块的all_vm_tokens属性中。
您可调用module.to_s()来查看该程序,因为该程序已根据NEO虚拟机的特点进行了标记化。 >>> module.to_s()复制代码 4 31 LOAD_FAST a [data]复制代码 36 LOAD_CONST 2 [data]复制代码 37 BINARY_MULTIPL [data]复制代码 38 STORE_FAST a2 [data]复制代码 6 45 LOAD_FAST b [data]复制代码 50 LOAD_CONST 1 [data]复制代码 51 BINARY_ADD [data]复制代码 52 STORE_FAST b2 [data]复制代码 8 59 LOAD_FAST c [data]复制代码 64 LOAD_CONST 2 [data]复制代码 65 BINARY_TRUE_DIVIDE [data]复制代码 66 STORE_FAST c2 [data]复制代码 10 73 LOAD_FAST d [data]复制代码 78 LOAD_CONST 1 [data]复制代码 79 BINARY_SUBTRACT [data]复制代码 80 STORE_FAST d2 [data]复制代码 13 87 243 b'' [data] 3 90 LOAD_FAST a2 [data]复制代码 95 LOAD_FAST b2 [data]复制代码 100 BINARY_ADD [data]复制代码 101 LOAD_FAST c2 [data]复制代码 106 BINARY_ADD [data]复制代码 107 LOAD_FAST d2 [data]复制代码 112 BINARY_ADD [data]复制代码 113 NOP [data]复制代码 114 241 [data]复制代码 115 242 [data]复制代码 116 RETURN_VALUE [data] 复制代码
add_method(method)[source]
在模块中添加方法如下:
Parameters: method (boa.code.method.Method) ——模块中待添加的方法对象
返回值: 显示是否已添加该方法
返回值类型: 布尔值
build()[source]
将bp.code对象拆分成行,并合并多行,生成不同的项目。
link_methods()[source]
关联各方法地址
main
返回该模块的默认方法
返回值:该模块的默认方法
返回值类型:boa.code.method.Method
method_by_name(method_name)[source]
在模块的methods列表中查找方法名称:param method_name:
待查找的方法名称:typemethod_name: str
返回值: 方法(如有)
返回值类型: boa.code.method.Method
module_path
返回该模块的文件路径
返回值: 模块路径
返回值类型: str
orderered_methods
方法序列表
返回值: 该模块中的方法序列表
返回值类型: 列表
process_action(lineset)[ source ]
处理模块中的动作,样本如下,其目的类似于创建下列事件:
fromboa.blockchain.vm.Neo.Action import RegisterAction
# Register the action.
onRefund = RegisterAction(‘refund’,’to_address’,’amount’)
# Dispatch an action.
onRefund(my_address,100)
参数: lineset (list) – 包含应用程序调用注册功能的行集
process_import(import_item)[source]
处理该模块中的导入语句
Parameters: import_item (boa.code.items.Import subclass) –
process_method(lineset)[source]
处理包含byteplay3代码对象的行集
参数: lineset (list) – 需处理与添加的行集
process_smart_contract_app_registration(lineset)[source]
在智能合约中调用另一个智能合约时处理智能合约应用程序注册事宜:
fromboa.blockchain.vm.Neo.App import RegisterAppCall
# register the contract
otherContract = RegisterAppCall(‘contract_hash’,’param1′,’param2′)
# call the contract
result = otherContract(a,b )
参数: lineset (list) – 包含应用程序调用注册功能的行集
split_lines()[source]
将模块中的行集拆分成可编译的对象集
to_s()[source]
该方法的目的在于以可读/标记化的格式打印可执行项的输出值,样本如下: >>> from boa.compiler import Compiler复制代码 >>> module = Compiler.load('./boa/tests/src/LambdaTest.py').default复制代码 >>> module.write()复制代码 >>> module.to_s()复制代码 12 3 LOAD_CONST 9 [data]复制代码 4 STORE_FAST j [data]复制代码 22 11 LOAD_FAST j [data]复制代码 17 CALL_FUNCTION Main..q_1[] [data] 复制代码 22 20 STORE_FAST m [data]复制代码 24 27 243 b'' [data] 复制代码 3 30 LOAD_FAST m [data]复制代码 35 NOP [data]复制代码 36 241 [data]复制代码 37 242 [data]复制代码 38 RETURN_VALUE [data]复制代码 20 49 243 b'' [data] 复制代码 3 52 LOAD_FAST x [data]复制代码 57 LOAD_CONST 1 [data]复制代码 58 BINARY_ADD [data]复制代码 59 NOP [data]复制代码 60 241 [data]复制代码 61 242 [data]复制代码 62 RETURN_VALUE [data]复制代码
write()[source]
将当前模块写入字节串
注: 如果您使用的是Compiler.load(‘path/to/file.py’),执行module.write()调用后方可对模块进行检查。
返回值: 代表当前模块的字节串
返回值类型: 字节
write_methods()[source]
将当前模块的所有方法写入字节串
返回值:该模块中当前存在的所有方法的字节串
返回值类型:字节 boa.code.method.Method
下文为Method的具体实现细则。
classboa.code.method.Method(code_object, parent, make_func_name=None)[source]
方法是主功能单元。
任何方法均可给多个自变量赋予0值,并返回1。
各方法包含的行数不同, boa.code.block.Block对象数也不同,意味着功能单元是互不相关的。
每行可被拆分为独立的boa.code.pytoken.PyToken对象,这些对象即为Python解释器使用的标记。
之后,每行便可形成一个区块。一个完整的区块生成后,将经历一系列的处理进程,最终将转化为可被NEO虚拟机理解的语言。
区块处理完毕后,我们可将其串联在一起,并发送给VMTokenizer。
VMTokenizer负责将PyToken对象转化为VMToken对象。
对方法进行标记化后,各标记将在该方法内生成地址。生成完整的地址后,将调用convert_jumps方法来判断需跳转的流控制操作(地址)。
args
返回该方法中的自变量列表
返回值: 该方法的自变量列表
返回值类型: 列表
code
返回byteplay3代码对象
返回值:该方法的byteplay3代码对象
返回值类型:byteplay3.Code
convert_jumps()[source]
转换流控制项目中发生的跳转,如if、else、forloops、while loops及for breaks
firstlineno
获取该方法的起始行号
返回值: 起始行号
返回值类型: int
full_name
获取该方法的全称
返回值:该方法模块的命名空间名称
返回值类型:str
module
取回包含该方法的模块
返回值: 包含该方法的模块
返回值类型: boa.code.module.Module
name
获取该方法的名称
返回值:该方法的名称
返回值类型:str
print()[source]
打印该方法的byteplay3对象在python解释器中的输出值,并与boa.code.method.Method.to_dis()输出值对比,您会发现两者间微妙的区别。
输出样本 >>> method.print()复制代码 2 STORE_FAST j复制代码 12 1 LOAD_CONST 9复制代码 14 4 LOAD_CONST 复制代码 5 LOAD_CONST 'Main..q'复制代码 6 MAKE_FUNCTION 0复制代码 7 STORE_FAST q复制代码 22 9 LOAD_FAST q复制代码 10 LOAD_FAST j复制代码 11 CALL_FUNCTION 1复制代码 12 STORE_FAST m复制代码 24 14 LOAD_FAST m复制代码 15 RETURN_VALUE复制代码
process_block_groups()[source]
获取并处理当前区块(类似于方法中的行),以便区块能被适当地标记化
read_initial_tokens()[source]
从byteplay3代码对象中获取初始标记集,并将其转化为区块
read_module_variables()[source]
获取所有模块的global变量,并允许该方法访问这些变量。
to_dis()[source]
打印该方法在python编译器中的输出值,并与boa.code.method.Method.print()的输出值对比,您会发现两者间微妙的区别。 >>> method.to_dis()复制代码 3 STORE_FAST 0 (j)复制代码 12 0 LOAD_CONST 1 (9)复制代码 14 6 LOAD_CONST 2 ()复制代码 9 LOAD_CONST 3 ('Main..q')复制代码 12 MAKE_FUNCTION 0复制代码 15 STORE_FAST 1 (q)复制代码 22 18 LOAD_FAST 1 (q)复制代码 21 LOAD_FAST 0 (j)复制代码 24 CALL_FUNCTION 1 (1 positional, 0 keyword pair)复制代码 27 STORE_FAST 2 (m)复制代码 24 30 LOAD_FAST 2 (m)复制代码 33 RETURN_VALUE复制代码
tokenize()[source]
将boa.code.pytoken.PyToken对象集转化为boa.code.vmtoken.VMToken对象。
total_lines
获取该方法的总行(即区块)数
返回值: 总行数
返回值类型: int
total_module_variables
获取局部变量总数
返回值: 该模块中的变量总数
返回值类型: int
vm_tokens
返回该方法中的虚拟机标记列表
返回值: 该方法中的虚拟机标记列表
返回值类型: 列表
write()[source]
将标记器当前的状态写为字节串
返回值: 当前标记器的字节串
返回值类型: 字节
Python文件样本
添加
本案例展示了如何添加数字,同时这也是一个可接受多个参数的智能合约案例(4)。
boa.tests.src.AddTest1.Main(a, b, c, d)[source]
参数:
· a –
· b –
· c –
· d –
返回值:
列表
本案例展示了如何创建与操作列表
boa.tests.src.ArrayTest.Main()[source]
返回值:
boa.tests.src.ArrayTest.get_items_from_range(items, index)[source]
参数:
· items –
· index –
返回值:
boa.tests.src.ArrayTest.get_thing()[source]
返回值:
二元操作符
本案例展示了如何使用二元操作符
boa.tests.src.BinopTest.Main(a, b, c, d)[source]
参数:
· a –
· b –
· c –
· d –
返回值:
NEP-5代币
本案例展示了如何生成NEP-5代币。
将该文件编译为.avm格式后,需检查其是否符合NEO区块链上实施的的NEP-5代币标准。
查看代币标准议案NEP-5代币标准议案:https://github.com/neo-project/proposals/blob/master/nep-5.mediawiki。
可通过以下步骤进行编译 >>> fromboa.compiler import Compiler复制代码 >>> Compiler.load_and_save('./boa/tests/src/NEP5Test.py')复制代码
下方为Python的编译实现
boa.tests.src.NEP5Test.BalanceOf(account)[source]
某地址当前余额数值的返回方法
参数: account (bytearray) ——需获取余额的账户地址
返回值: 某地址的当前余额
返回值类型: int
boa.tests.src.NEP5Test.CurrentSwapRate()[source]
NEO/NEP-5代币的当前“费率”或兑换率计算方法
返回值: 当前费率
返回值类型: int
boa.tests.src.NEP5Test.Decimals()[source]
NEP-5代币小数点返回方法
返回值: NEP-5代币的小数点
返回值类型: int
boa.tests.src.NEP5Test.Deploy()[source]
NEP-5代币持有人将初始代币部署至自己地址的方法
返回值:部署成功与否
返回值类型:布尔值
boa.tests.src.NEP5Test.DoTransfer(t_from, t_to, amount)[source]
将一定量的NEP-5代币从一个账户转至另一个账户的方法
参数:
· t_from (bytearray) —— 转出地址
· t_to (bytearray) ——转入地址
· amount (int) ——待转账的NEP-5代币量
返回值: 转账成功与否
返回值类型: 布尔值
boa.tests.src.NEP5Test.Main(operation, args)[source]
此处是智能合约的主要入口点
参数:
· operation (str) —— 待执行的操作 如 mintTokens、transfer等)
· args (list) ——可选自变量列表
返回值: 显示智能合约已成功执行
返回值类型: 布尔值
boa.tests.src.NEP5Test.MintTokens()[source]
某地址将NEO存入NEP-5代币持有者的账户以换取一定量的NEP-5代币的调用方法
返回值: 铸币成功与否
返回值类型: 布尔值
boa.tests.src.NEP5Test.Name()[source]
NEP-5代币名称返回方法
返回值: 代币名称
返回值类型: str
boa.tests.src.NEP5Test.Symbol()[source]
NEP-5代币符号返回方法
返回值: 代币符号
返回值类型: str
boa.tests.src.NEP5Test.TotalSupply()[source]
流通中的NEP-5代币总量数值返回方法
返回值: 流通中的代币总量
返回值类型: int
【交互操作】NEO区块链
下列各操作的目的在于收集区块链中包含的状态数据。因为下列各项均是在NEO虚拟机中实现的,因此这里无法查找其源。若欲了解具体实现方法,请参考neo-python项目。
项目地址: github.com/CityOfZion/…
区块链
boa.blockchain.vm.Neo.Blockchain.GetAccount(script_hash) →boa.blockchain.vm.Neo.Account.Account[source]
参数:script_hash –
boa.blockchain.vm.Neo.Blockchain.GetAsset(asset_id) →boa.blockchain.vm.Neo.Asset.Asset[source]
参数:asset_id –
boa.blockchain.vm.Neo.Blockchain.GetBlock(height_or_hash) →boa.blockchain.vm.Neo.Block.Block[source]
参数:height_or_hash –
boa.blockchain.vm.Neo.Blockchain.GetContract(script_hash) →boa.blockchain.vm.Neo.Contract.Contract[source]
参数:script_hash –
boa.blockchain.vm.Neo.Blockchain.GetHeader(height_or_hash) →boa.blockchain.vm.Neo.Header.Header[source]
参数:height_or_hash –
boa.blockchain.vm.Neo.Blockchain.GetHeight() → int[source]
boa.blockchain.vm.Neo.Blockchain.GetTransaction(hash) →boa.blockchain.vm.Neo.Transaction.Transaction[source]
参数:hash –
boa.blockchain.vm.Neo.Blockchain.GetValidators() → [][source]
区块头
区块头对象包含了所有区块信息,但不包含交易数据。
boa.blockchain.vm.Neo.Header.GetConsensusData(header: boa.blockchain.vm.Neo.Header.Header) →bytearray[source]
获取共识地址
boa.blockchain.vm.Neo.Header.GetHash(header: boa.blockchain.vm.Neo.Header.Header) →bytearray[source]
获取区块头哈希值
boa.blockchain.vm.Neo.Header.GetMerkleRoot(header: boa.blockchain.vm.Neo.Header.Header) →bytearray[source]
获取区块中交易信息的默克尔根值
boa.blockchain.vm.Neo.Header.GetNextConsensus(header: boa.blockchain.vm.Neo.Header.Header) →bytearray[source]
获取下一个共识发生的地址
boa.blockchain.vm.Neo.Header.GetPrevHash(header:boa.blockchain.vm.Neo.Header.Header) → bytearray[source]
获取区块链中上一个区块头的哈希值
boa.blockchain.vm.Neo.Header.GetTimestamp(header: boa.blockchain.vm.Neo.Header.Header) →int[source]
获取区块头创建时间戳
boa.blockchain.vm.Neo.Header.GetVersion(header: boa.blockchain.vm.Neo.Header.Header) →int[source]
获取区块头的版本号
区块
区块对象包含区块中的交易数据
boa.blockchain.vm.Neo.Block.GetTransaction(block:boa.blockchain.vm.Neo.Block.Block, index:int)→ boa.blockchain.vm.Neo.Transaction.Transaction[source]
参数:
· block——包含该交易的区块
· index——区块中交易的指数
boa.blockchain.vm.Neo.Block.GetTransactionCount(block: boa.blockchain.vm.Neo.Block.Block) →int[source]
返回区块中的交易数
boa.blockchain.vm.Neo.Block.GetTransactions(block:boa.blockchain.vm.Neo.Block.Block) → list[source]
返回区块中包含的交易列表
账户
账户对象代表区块上的地址
boa.blockchain.vm.Neo.Account.GetBalance(account, asset_id)[source]
参数:
· account –
· asset_id –
boa.blockchain.vm.Neo.Account.GetScriptHash(account)[source]
参数:
account –
boa.blockchain.vm.Neo.Account.GetVotes(account)[source]
参数:
account –
boa.blockchain.vm.Neo.Account.SetVotes(account, votes)[source]
参数:
· account –
· votes –
动作
动作对象用于在区块链上注册动作/事件监听器
boa.blockchain.vm.Neo.Action.RegisterAction(event_name, *args) [source]
参数:
· event_name –
· args –
应用程序
应用程序对象用于调用区块链上的其他合约
boa.blockchain.vm.Neo.App.RegisterAppCall(smart_contract_hash, *args, **kwargs)[source]
参数:
· smart_contract_hash –
· args –
· kwargs –
资产
资产对象用于检索NEO或GAS等本地资产信息
boa.blockchain.vm.Neo.Asset.Create(asset_type, name, amount, precision, owner, admin, issuer)[source]
参数:
· asset_type –
· name –
· amount –
· precision –
· owner –
· admin –
· issuer –
boa.blockchain.vm.Neo.Asset.GetAdmin(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetAmount(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetAssetId(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetAssetType(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetAvailable(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetIssuer(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetOwner(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.GetPrecision(asset)[source]
参数:
asset –
boa.blockchain.vm.Neo.Asset.Renew(asset, years)[source]
参数:
· asset –
· years –
【交互操作】执行引擎
下列各操作的目的在于收集NEO虚拟机当前运行状态的参考数据。因为下列各项均是在NEO虚拟机中实现的,因此这里无法查找其出处。若欲了解具体实现方法,请参考neo-python项目。
方法
classboa.blockchain.vm.System.ExecutionEngine.ExecutionEngine[source]
未使用
boa.blockchain.vm.System.ExecutionEngine.GetCallingScriptHash()[source]
获取脚本(智能合约)的哈希值,开始执行当前脚本
返回值: 脚本(智能合约)的哈希值,开始执行当前脚本
返回值类型: bytearray
boa.blockchain.vm.System.ExecutionEngine.GetEntryScriptHash()[source]
获取脚本(智能合约)的哈希值,开始执行智能合约
返回值: 脚本(智能合约)的哈希值,开始执行智能合约
返回值类型: bytearray
boa.blockchain.vm.System.ExecutionEngine.GetExecutingScriptHash()[source]
获取执行中的脚本(智能合约)的哈希值
· 该方法在NEO虚拟机内部实现
返回值:执行中的脚本(智能合约)的哈希值
返回值类型:bytearray
boa.blockchain.vm.System.ExecutionEngine.GetScriptContainer()[source]
返回当前执行智能合约的Script Container,它是boa.blockchain.vm.Neo.Transaction对象
返回值: 当前执行智能合约的Script Container
返回值类型: boa.blockchain.vm.Neo.Transaction
原文出处: https://github.com/localhuman/neo-python
区块链
2018-12-11 19:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。
“在区块链上,没有人知道你是一台冰箱”——理查德布朗
到目前为止,我们列出的所有合约都是由人类持有的其他账户拥有和执行的。但是在以太坊生态系统中不存在对机器人或人类的歧视,合约可以像任何其他帐户一样创造任意行为。合约可以拥有代币,参与众筹,甚至是其他合约的投票成员。
在本节中,我们将建立一个去中心化的民主组织机构,仅存在于区块链上,但这可以做任何简单账户所能做到的事情。该组织有一个中央经理,负责决定谁是成员和投票规则,但正如我们所看到的,这也可以改变。
这种特殊民主的运作方式是它拥有一个像管理员,首席执行官或总统一样工作的所有者 Owner 。所有者可以向组织添加(或删除)投票成员。任何成员都可以提出一个提议,该提议以以太坊交易的形式发送以太或执行某些合约,其他成员可以投票支持或反对该提案。一旦预定的时间量和一定数量的成员投票,就可以执行提案:合约计票,如果有足够的票数,它将执行给定的交易。
区块链大会
文末附全部代码。 pragma solidity >=0.4.22 <0.6.0; contract owned { address public owner; constructor() public { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { owner = newOwner; } } contract tokenRecipient { event receivedEther(address sender, uint amount); event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, address(this), _value)); emit receivedTokens(_from, _value, _token, _extraData); } function () payable external { emit receivedEther(msg.sender, msg.value); } } interface Token { function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); } contract Congress is owned, tokenRecipient { // Contract Variables and events uint public minimumQuorum; uint public debatingPeriodInMinutes; int public majorityMargin; Proposal[] public proposals; uint public numProposals; mapping (address => uint) public memberId; Member[] public members; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter, string justification); event ProposalTallied(uint proposalID, int result, uint quorum, bool active); event MembershipChanged(address member, bool isMember); event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, int newMajorityMargin); struct Proposal { address recipient; uint amount; string description; uint minExecutionDate; bool executed; bool proposalPassed; uint numberOfVotes; int currentResult; bytes32 proposalHash; Vote[] votes; mapping (address => bool) voted; } .....
如何部署
打开钱包(如果你只是测试,请转到菜单开发>网络>testnet),转到合约选项卡 Contracts ,然后点击部署合约 deploy contract ,在 solidity code box 上粘贴上面的代码。在合约选择器上,选择 Congress ,你将看到设置变量。 提案的最低法定人数是提案在执行之前需要的最低票数。 争论的时间是在执行之前需要经过的最短时间(以分钟为单位)。 多数票的保证金如果超过50%的票数加上保证金,则提案通过。在简单多数时保留为0,将其设为成员数-1要求绝对共识。
你可以稍后更改这些参数。首先,你可以选择5分钟进行辩论,并将剩余参数保留为0。在页面上稍微低一点,你将看到在以太网中部署合约的成本估算值。如果要保存,可以尝试降低价格,但这可能意味着必须等待更长时间才能创建合约。 单击部署 Deploy ,键入密码并等待。
几秒钟后,你将被带到仪表板dashboard,向下滚动,你将能够看到正在创建的交易。在不到一分钟的时间内,你将看到交易成功,并且将创建一个新的唯一图标。单击合约的名称以查看它(你可以随时在合约选项卡 Contracts 上找到它)。
与他人分享
如果你想与他人共享你的DAO,那么他们需要合约地址和接口文件,这是一个小文本字符串,作为合约的使用说明书。单击复制地址 copy address 以获取前者并 显示界面 show interface 以显示后者。
在另一台计算机上,进入合约选项卡 Contracts ,然后单击监视合约 watch contract 。添加正确的地址和界面,然后单击 OK 。
与合约互动
在从合约中读取 Read from contract 中,你可以看到合约中可以免费执行的所有功能,因为它们只是从区块链中读取信息。例如,你可以在此处查看合约的当前所有者 owner (应该是上载合约的帐户)。
在写入合约 Write to contract 中,你有一个列表,其中列出了将尝试进行某些计算以将数据保存到区块链的所有函数,因此将花费以太。选择 New Proposal ,它将显示该功能的所有选项。
在与合约交互之前,你需要添加新成员才能投票。 在 Select function 选择器上,选择 Add Member 。添加你要成为会员的人的地址(要删除会员,请选择 Remove Member 功能)。 在 execute from 时,请确保你拥有与所有者相同的帐户,因为这只是主要管理员可以执行的操作。点击执行 execute 并等待几秒钟,以便下一个块进行更改。
没有成员列表,但你可以通过将其地址放在 Read from contract 列的 Members 功能上来检查是否有人是成员。
此外,如果你想让合约有自己的钱,你需要存入一些以太(或其他代币),否则你将拥有一个非常无聊的组织。按右上角的 transfer Ether & Tokens 。
添加一个简单的提案:发送以太
现在让我们将第一个提案添加到合约中。在函数选择器上,选择 New Proposal 。
对于“受益人”,添加你要发送以太的人的地址,并在标有“Wei Amount”的框中输入你要发送的数量。Wei是以太的最小单位,等于10^-18以太,并且必须始终作为整数给出。例如,如果要发送1以太,请输入1000000000000000000(即18个零)。最后,添加一些描述你要执行此操作的原因的文本。暂时将“Transaction bytecode”留空。单击执行 execute 并键入密码。几秒钟后,numProposals将增加到1,第一个提案编号0将出现在左列上。当你添加更多提案时,只需将提案编号放在 proposals 字段中即可看到其中的任何提案,你可以阅读所有提案。
投票提案也很简单。在函数选择器上选择 Vote 。在第一个框中键入提议编号,如果你同意,请选中 Yes 框(或将其留空以对其进行投票)。点击 execute 发送你的投票。
投票时间过后,你可以选择 executeProposal 。如果提案只是发送以太,那么你也可以将 transactionBytecode 字段留空。在点击 execute 之后但在输入密码之前,请注意出现的屏幕。
如果 estimated fee consumption 即估计费用消耗字段上有警告,则表示由于某种原因,被调用的函数将不会执行并将突然终止。这可能意味着许多事情,但在本合约的上下文中,只要你在截止日期过后尝试执行合约,或者用户尝试发送的字节码数据与原始提案不同,就会显示此警告。出于安全原因,如果发生任何这些事情,合约执行将突然终止,并且尝试非法交易的用户将失去他发送的用于支付交易费用的所有以太费用。
如果交易被执行,那么几秒钟之后你应该能够看到结果:执行将变为真,并且应该从该合约的余额和收件人地址中减去正确的以太量。
添加复杂提案:拥有另一个代币
你可以使用此民主方式来执行以太坊上的任何交易,只要你能够找出该交易生成的字节码即可。幸运的是,你可以使用钱包做到这一点!
在这个例子中,我们将使用一个代币来表明这个合约可以容纳多于以太,并且可以在任何其他基于以太坊的资产中进行交易。首先,创建一个属于你的普通帐户的代币 。在合约页面上,单击转移以太网和代币以将其中一些转移到新的 congress 合约中(为简单起见,不要将超过一半的硬币发送到你的DAO)。之后,我们将模拟你要执行的操作。因此,如果你想建议DAO向个人发送500毫克黄金代币作为付款,请按照你从你拥有的帐户执行该交易的步骤,然后点击 send 但是当确认屏幕时弹出,不要输入密码 。
而是单击显示原始数据 SHOW RAW DATA 链接并复制 RAW DATA 字段上显示的代码并将其保存到文本文件或记事本中。取消交易。你还需要你将要为该操作调用的合约的地址,在这种情况下是代币合约。你可以在 Contracts 选项卡上找到它:将其保存在某处。
现在回到 congress 合约并使用以下参数创建新提案: 作为受益人,请填写你的代币地址(如果它是相同的图标请注意)。 将以太币量留空。 在工作描述上,只需写下你想要完成内容的描述。 在 Transaction Bytecode 上 ,粘贴你在上一步中从数据字段中保存的字节码。
几秒钟后,你应该能够看到提案的详细信息。你会注意到交易字节码不会在那里显示,而是只有一个交易哈希 transaction hash 。与其他字段不同,字节码可能非常冗长,因此存储在区块链上非常昂贵,因此稍后执行调用的人将提供字节码,而不是对其进行存档。
但是,这当然会造成一个安全漏洞:如果没有实际代码,投票如何投票?什么阻止用户在提案投票后执行不同的代码?这就是交易哈希的用武之地。在从合约中读取 read from contract 功能列表中滚动一下,你会看到一个提议检查功能,任何人都可以放置所有的功能参数并检查它们是否与被投票的匹配。这也保证了除非字节码的hash与提供的代码上的hash完全匹配,否则不会执行提议。
任何人都可以通过遵循相同的步骤来获取正确的字节码,然后将提议编号和其他参数添加到从合约中读取 read from contract 底部的名为 检查提案代码 Check proposal code 的功能,从而可以非常轻松地检查提案。
其余的投票过程保持不变:所有成员都可以投票,在截止日期之后,有人可以执行该投标。唯一的区别是,这次你必须提供之前提交的相同字节码。注意确认窗口上的任何警告:如果它说它不会执行你的代码,请检查截止日期是否已经过去,是否有足够的投票以及你的交易字节码是否已检出。
让它更好
以下是当前DAO的一些缺点,我们将其作为练习留给读者: 你可以将会员列表公开并编入索引吗? 你能否允许成员改变他们的选票(在投票后但在投票结果出来之前)? 目前投票信息仅在日志上可见,你是否可以创建一个显示所有投票的功能?
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 以太坊DAO之区块链大会
附代码: pragma solidity >=0.4.22 <0.6.0; contract owned { address public owner; constructor() public { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner public { owner = newOwner; } } contract tokenRecipient { event receivedEther(address sender, uint amount); event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); function receiveApproval(address _from, uint256 _value, address _token, bytes memory _extraData) public { Token t = Token(_token); require(t.transferFrom(_from, address(this), _value)); emit receivedTokens(_from, _value, _token, _extraData); } function () payable external { emit receivedEther(msg.sender, msg.value); } } interface Token { function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); } contract Congress is owned, tokenRecipient { // Contract Variables and events uint public minimumQuorum; uint public debatingPeriodInMinutes; int public majorityMargin; Proposal[] public proposals; uint public numProposals; mapping (address => uint) public memberId; Member[] public members; event ProposalAdded(uint proposalID, address recipient, uint amount, string description); event Voted(uint proposalID, bool position, address voter, string justification); event ProposalTallied(uint proposalID, int result, uint quorum, bool active); event MembershipChanged(address member, bool isMember); event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, int newMajorityMargin); struct Proposal { address recipient; uint amount; string description; uint minExecutionDate; bool executed; bool proposalPassed; uint numberOfVotes; int currentResult; bytes32 proposalHash; Vote[] votes; mapping (address => bool) voted; } struct Member { address member; string name; uint memberSince; } struct Vote { bool inSupport; address voter; string justification; } // Modifier that allows only shareholders to vote and create new proposals modifier onlyMembers { require(memberId[msg.sender] != 0); _; } /** * Constructor */ constructor ( uint minimumQuorumForProposals, uint minutesForDebate, int marginOfVotesForMajority ) payable public { changeVotingRules(minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority); // It’s necessary to add an empty first member addMember(address(0), ""); // and let's add the founder, to save a step later addMember(owner, 'founder'); } /** * Add member * * Make `targetMember` a member named `memberName` * * @param targetMember ethereum address to be added * @param memberName public name for that member */ function addMember(address targetMember, string memory memberName) onlyOwner public { uint id = memberId[targetMember]; if (id == 0) { memberId[targetMember] = members.length; id = members.length++; } members[id] = Member({member: targetMember, memberSince: now, name: memberName}); emit MembershipChanged(targetMember, true); } /** * Remove member * * @notice Remove membership from `targetMember` * * @param targetMember ethereum address to be removed */ function removeMember(address targetMember) onlyOwner public { require(memberId[targetMember] != 0); for (uint i = memberId[targetMember]; i p.minExecutionDate // If it is past the voting deadline && !p.executed // and it has not already been executed && p.proposalHash == keccak256(abi.encodePacked(p.recipient, p.amount, transactionBytecode)) // and the supplied code matches the proposal && p.numberOfVotes >= minimumQuorum); // and a minimum quorum has been reached... // ...then execute result if (p.currentResult > majorityMargin) { // Proposal passed; execute the transaction p.executed = true; // Avoid recursive calling (bool success, ) = p.recipient.call.value(p.amount)(transactionBytecode); require(success); p.proposalPassed = true; } else { // Proposal failed p.proposalPassed = false; } // Fire Events emit ProposalTallied(proposalNumber, p.currentResult, p.numberOfVotes, p.proposalPassed); } }
区块链
2018-12-11 09:51:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在本文中,我们将看看以太坊节点是什么,并探索最受欢迎的节点之一,称为Geth。
为了与区块链进行通信,我们必须使用区块链客户端。客户端是能够与其他客户建立p2p通信信道,签署和广播交易,挖掘,部署和与智能合约交互等的软件。客户端通常被称为节点。
以太坊节点必须遵循的功能的正式定义在以太坊黄皮书中定义。黄皮书定义了网络上节点所需的函数,挖掘算法,私钥/公钥ECDSA参数。它定义了使节点与以太坊客户端完全兼容的全部功能。
基于 以太坊黄皮书 ,任何人都能够以他们认为合适的语言创建自己的以太坊节点实现。
这里可以看到完整的 客户端列表 。
迄今为止最受欢迎的客户是 Geth 和 Parity 。实现的不同之处主要在于选择的编程语言:Geth使用Golang,而Parity使用Rust。
由于Geth是目前最受欢迎的客户端实现,我们现在将重点关注它。
节点类型
当你加入以太坊网络时,你可以选择运行各种类型的节点。目前的选项是: light节点 full节点 Archive节点
Archive节点是full节点的特例,因此我们不会详细介绍它。我发现的节点类型的最佳摘要之一是在 Stack Exchange 上:
通常,我们可以将节点软件划分为两种类型:完整节点和轻(重量)节点。完整节点验证广播到网络上的块。也就是说,它们确保块中包含的交易(以及块本身)遵循以太坊规范中定义的规则。它们维护网络的当前状态(根据以太坊规范定义)。
不遵循规则的交易和块不用于确定以太坊网络的当前状态。例如,如果A尝试向B发送100以太,但A有0个ethers,并且一个块包含此交易,则完整节点将意识到这不遵循以太坊的规则并拒绝该块为无效。特别是,智能合约的执行是交易的一个例子。每当在交易中使用智能合约(例如,发送ERC-20代币)时,所有完整节点都必须运行所有指令以确保它们到达区块链的正确的,商定的下一状态。
到达同一个状态有多种方式。例如,如果A有101个以太,并且在一次交易中将其中的一百个给了B以支付1个以太的gas,那么最终结果将是如果A每次向B发送100个1以太的交易,每次交易支付0.01以太(无视谁收到交易费用)。要知道B现在是否允许发送100以太,就足以知道B的当前余额是多少。保留整个交易历史记录的完整节点称为完整归档节点。这些必须存在于网络上才能保持健康。
节点也可以选择丢弃旧数据;如果B想要向C发送100以太,那么如何获得以太并不重要,只要B的账号包含100以太。相反,轻节点不会验证每个块或交易,也可能没有当前区块链状态的副本。他们依靠完整的节点为他们提供缺失的细节(或者只是缺少特定的功能)。轻型节点的优势在于它们可以更快地启动和运行,可以在更多计算/内存受限的设备上运行,并且不会占用几乎同样多的存储空间。在缺点方面,其他节点存在信任因素(它根据客户端和概率方法/启发式方法而有所不同,可用于降低风险)。一些完整的客户端包括具有更快同步的功能(例如,Parity的warp sync)。
安装Geth
可以在 此处 找到Geth在各种平台(Windows,macOS,Linux)上的安装说明。该列表非常全面,并且保持最新,所以我不会在文章中介绍它。
运行Geth
为了启动Geth节点,你唯一需要做的就是转到终端窗口并运行geth。当你这样做时,你应该得到类似于这样的输出: ~ geth INFO [06-03|11:03:13] Maximum peer count ETH=25 LES=0 total=25 INFO [06-03|11:03:13] Starting peer-to-peer node instance=Geth/v1.8.10-stable/darwin-amd64/go1.10.2 INFO [06-03|11:03:13] Allocated cache and file handles database=/Users/mjvr/Library/Ethereum/geth/chaindata cache=768 handles=128 INFO [06-03|11:03:13] Writing default main-net genesis block INFO [06-03|11:03:14] Persisted trie from memory database nodes=12356 size=2.34mB time=48.31016ms gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B INFO [06-03|11:03:14] Initialised chain configuration config="{ChainID: 1 Homestead: 1150000 DAO: 1920000 DAOSupport: true EIP150: 2463000 EIP155: 2675000 EIP158: 2675000 Byzantium: 4370000 Constantinople: Engine: ethash}" INFO [06-03|11:03:14] Disk storage enabled for ethash caches dir=/Users/mjvr/Library/Ethereum/geth/ethash count=3 INFO [06-03|11:03:14] Disk storage enabled for ethash DAGs dir=/Users/mjvr/.ethash count=2 INFO [06-03|11:03:14] Initialising Ethereum protocol versions="[63 62]" network=1 INFO [06-03|11:03:14] Loaded most recent local header number=0 hash=d4e567…cb8fa3 td=17179869184 INFO [06-03|11:03:14] Loaded most recent local full block number=0 hash=d4e567…cb8fa3 td=17179869184 INFO [06-03|11:03:14] Loaded most recent local fast block number=0 hash=d4e567…cb8fa3 td=17179869184 INFO [06-03|11:03:14] Regenerated local transaction journal transactions=0 accounts=0 INFO [06-03|11:03:14] Starting P2P networking INFO [06-03|11:03:16] UDP listener up self=enode://a4cb08519bc2bceecb8ad421871c624d5212888653bbaee309fda960f3c87ca7aa9855ee14684d521836ae88ad1986b8ca944348e976760d2bd1247ed3ca7628@[::]:30303 INFO [06-03|11:03:16] RLPx listener up self=enode://a4cb08519bc2bceecb8ad421871c624d5212888653bbaee309fda960f3c87ca7aa9855ee14684d521836ae88ad1986b8ca944348e976760d2bd1247ed3ca7628@[::]:30303 INFO [06-03|11:03:16] IPC endpoint opened url=/Users/mjvr/Library/Ethereum/geth.ipc
在此之后,你应该看到定期出现新行,Geth说“导入新状态”或“导入新区块头”或“导入新收据”。状态,块头和交易是 Ethereum’s tree tries 的一部分:必须下载它们才能使你的节点与以太坊区块链同步。
这个过程可能需要很长时间,因此你可以选择运行这样的轻型节点。 geth --light
Geth现在需要做的只是拉动最新的块头并依赖其他完整节点来通过使用 merkle证明 来验证交易。
访问Geth控制台
现在你已经创建了一个节点,你可以通过在终端中打开一个新选项卡并运行以下命令来访问它: geth attach
这将把Geth控制台(一个用于与区块链通信的Javascript环境)连接到你的运行节点。这可以在完全客户端模式和轻模式下完成。
打开控制台后,键入以下内容: web3.eth.blockNumber
你应该输出一个数字(例如5631487),表示以太坊网络的当前块号。
创建一个新帐户
要使用区块链,你需要拥有一个帐户。使用Geth,你可以通过在终端中运行以下命令来实现: geth account new
完成后,它会询问你输入密码,以保护你的帐户。确保使用安全密码并安全存储。
运行 geth account new 时Geth所做的是更新Geth数据目录中的文件(Geth存储所有必要数据的目录,包括块和块头信息)。目录在每个平台的位置: macOS: ~/Library/Ethereum Linux: ~/.ethereum Windows: %APPDATA%\Ethereum
从其他客户端访问Geth
当你启动Geth时,客户端会自动在端口 8545 启动RPC服务器。你可以通过使用 web3js 或 web3j 等库连接到 localhost:8545 或使用 curl 或 wget 手动调用它来访问此端口上的RPC服务器及其 方法 。
要了解如何与正在运行的Geth实例(在启动你自己的区块链时是私有的,或在上面的说明中公开)的外部工具的连接,请参阅 此文章 。
结论
在这篇简短的介绍中,我们介绍了Geth,以太坊节点的类型及其目的。你现在可以运行自己的Geth节点,并使用第三方工具对其进行增强。在以后的文章中,我们将介绍运行专用网络(你自己的以太网区域链与Geth)以及更多内容。
======================================================================
分享一些以太坊、EOS、比特币等区块链相关的交互式在线编程实战教程: java以太坊开发教程 ,主要是针对java和android程序员进行区块链以太坊开发的web3j详解。 python以太坊 ,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。 php以太坊 ,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和交易等内容。 以太坊入门教程 ,主要介绍智能合约与dapp应用开发,适合入门。 以太坊开发进阶教程 ,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。 C#以太坊 ,主要讲解如何使用C#开发基于.Net的以太坊应用,包括账户管理、状态与交易、智能合约开发与交互、过滤器和交易等。 EOS教程 ,本课程帮助你快速入门EOS区块链去中心化应用的开发,内容涵盖EOS工具链、账户与钱包、发行代币、智能合约开发与部署、使用代码与智能合约交互等核心知识点,最后综合运用各知识点完成一个便签DApp的开发。 java比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Java代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Java工程师不可多得的比特币开发学习课程。 php比特币开发教程 ,本课程面向初学者,内容即涵盖比特币的核心概念,例如区块链存储、去中心化共识机制、密钥与脚本、交易与UTXO等,同时也详细讲解如何在Php代码中集成比特币支持功能,例如创建地址、管理钱包、构造裸交易等,是Php工程师不可多得的比特币开发学习课程。 tendermint区块链开发详解 ,本课程适合希望使用tendermint进行区块链开发的工程师,课程内容即包括tendermint应用开发模型中的核心概念,例如ABCI接口、默克尔树、多版本状态库等,也包括代币发行等丰富的实操代码,是go语言工程师快速入门区块链开发的最佳选择。
汇智网原创翻译,转载请标明出处。这里是原文 Geth介绍及如何运行以太坊节点
区块链
2018-12-05 08:19:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
区块链太复杂,那我们就讲点简单的。用JS来构建你自己的区块链系统,寥寥几行代码就可以说明区块链的底层数据结构、POW挖矿思想和交易过程等。当然了,真实的场景远远远比这复杂。本文的目的仅限于让大家初步了解、初步认识区块链。
文章内容主要参考视频: Building a blockchain with Javascript ( https://www.youtube.com/playlist?list=PLzvRQMJ9HDiTqZmbtFisdXFxul5k0F-Q4 )
感谢原作者,本文在原视频基础上做了修改补充,并加入了个人理解。
认识区块链
区块链顾名思义是由区块连接而成的链,因此最基本的数据结构是Block。每个Block都含有timestamp、data、hash、previousHash等信息。其中data用来存储数据,previousHash是前一个区块的hash值。示意如下:
hash是对区块信息的摘要存储,hash的好处是任意长度的信息经过hash都可以映射成固定长度的字符串,如可用sha256: calculateHash() { return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); }
Block的数据结构
Block的最基本数据结构如下: class Block { constructor(timestamp, data, previousHash = '') { this.timestamp = timestamp; this.data = data; this.previousHash = previousHash; //对hash的计算必须放在最后,保证所有数据赋值正确后再计算 this.hash = this.calculateHash(); } calculateHash() { return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString(); } }
BlockChain的数据结构
多个Block链接而成BlockChain,显然可用用数组或链表来表示,如: class BlockChain { constructor() { this.chain = []; } }
创世区块
正所谓万物始于一,区块链的第一个区块总是需要人为来手动创建,这个区块的previousHash为空,如: createGenesisBlock() { return new Block("2018-11-11 00:00:00", "Genesis block of simple chain", ""); }
区块链的构造方法也应改为: class BlockChain { constructor() { this.chain = [this.createGenesisBlock()]; } }
添加区块
每新加一个区块,必须保证与原有区块链连接起来,即: class BlockChain { getLatestBlock() { return this.chain[this.chain.length - 1]; } addBlock(newBlock) { //新区块的前一个hash值是现有区块链的最后一个区块的hash值; newBlock.previousHash = this.getLatestBlock().hash; //重新计算新区块的hash值(因为指定了previousHash); 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]; //重新计算当前区块的hash值,若发现hash值对不上,说明该区块有数据被篡改,hash值未重新计算 if (currentBlock.hash !== currentBlock.calculateHash()) { console.error("hash not equal: " + JSON.stringify(currentBlock)); return false; } //判断当前区块的previousHash是否真的等于前一个区块的hash,若不等,说明前一个区块被篡改,虽然hash值被重新计算正确,但是后续区块的hash值并未重新计算,导致整个链断裂 if (currentBlock.previousHash !== previousBlock.calculateHash) { console.error("previous hash not right: " + JSON.stringify(currentBlock)); return false; } } return true; }
Just run it
跑起来看看,即: let simpleChain = new BlockChain(); simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10})); simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20})); console.log(JSON.stringify(simpleChain, null, 4)); console.log("is the chain valid? " + simpleChain.isChainValid());
结果如下: ali-186590cc4a7f:simple-chain shanyao$ node main_1.js { "chain": [ { "timestamp": "2018-11-11 00:00:00", "data": "Genesis block of simple chain", "previousHash": "", "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89" }, { "timestamp": "2018-11-11 00:00:01", "data": { "amount": 10 }, "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89", "hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529" }, { "timestamp": "2018-11-11 00:00:02", "data": { "amount": 20 }, "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529", "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34" } ] } is the chain valid? true
注意看其中的previousHash与hash,确实是当前区块的previousHash指向前一个区块的hash。
篡改下试试
都说区块链不可篡改,是真的吗?让我们篡改第2个区块试试,如: let simpleChain = new BlockChain(); simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10})); simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20})); console.log("is the chain valid? " + simpleChain.isChainValid()); //将第2个区块的数据,由10改为15 simpleChain.chain[1].data = {amount: 15}; console.log("is the chain still valid? " + simpleChain.isChainValid()); console.log(JSON.stringify(simpleChain, null, 4));
结果如下: ali-186590cc4a7f:simple-chain shanyao$ node main_1.js is the chain valid? true hash not equal: {"timestamp":"2018-11-11 00:00:01","data":{"amount":15},"previousHash":"fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89","hash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529"} is the chain still valid? false { "chain": [ { "timestamp": "2018-11-11 00:00:00", "data": "Genesis block of simple chain", "previousHash": "", "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89" }, { "timestamp": "2018-11-11 00:00:01", "data": { "amount": 15 }, "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89", "hash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529" }, { "timestamp": "2018-11-11 00:00:02", "data": { "amount": 20 }, "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529", "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34" } ] }
显然,篡改了数据之后,hash值并未重新计算,导致该区块的hash值对不上。
再篡改下试试
那么,如果我们聪明点,篡改后把hash值也重新计算会如何? let simpleChain = new BlockChain(); simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10})); simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20})); console.log("is the chain valid? " + simpleChain.isChainValid()); //篡改后重新计算hash值 simpleChain.chain[1].data = {amount: 15}; simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash(); console.log("is the chain still valid? " + simpleChain.isChainValid()); console.log(JSON.stringify(simpleChain, null, 4));
结果如下: ali-186590cc4a7f:simple-chain shanyao$ node main_1.js is the chain valid? true previous hash not right: {"timestamp":"2018-11-11 00:00:02","data":{"amount":20},"previousHash":"150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529","hash":"274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34"} is the chain still valid? false { "chain": [ { "timestamp": "2018-11-11 00:00:00", "data": "Genesis block of simple chain", "previousHash": "", "hash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89" }, { "timestamp": "2018-11-11 00:00:01", "data": { "amount": 15 }, "previousHash": "fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89", "hash": "74d139274fb692495b7c805dd5822faa0c5b5e6058b6beef96e87e18ab83a6b1" }, { "timestamp": "2018-11-11 00:00:02", "data": { "amount": 20 }, "previousHash": "150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529", "hash": "274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34" } ] }
显然,第3个区块的previousHash并未指向第2个区块的hash。
是真的无法篡改吗
其实并不是,如果我们再聪明一点,把后续区块的hash值也重新计算一下,不就OK了吗? 确实如此,如: let simpleChain = new BlockChain(); simpleChain.addBlock(new Block("2018-11-11 00:00:01", {amount: 10})); simpleChain.addBlock(new Block("2018-11-11 00:00:02", {amount: 20})); console.log("is the chain valid? " + simpleChain.isChainValid()); //篡改第2个区块 simpleChain.chain[1].data = {amount: 15}; simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash(); //并把第3个区块也重新计算 simpleChain.chain[2].previousHash = simpleChain.chain[1].hash; simpleChain.chain[2].hash = simpleChain.chain[2].calculateHash(); console.log("is the chain still valid? " + simpleChain.isChainValid()); console.log(JSON.stringify(simpleChain, null, 4
原文链接
区块链
2018-12-04 14:01:00