查看原文
其他

课程回顾 | roysue:第一个智能合约、在树莓派上部署

小雪 看雪学院 2019-05-25


11月30日,【传统安全X区块链】群直播课程中,由看雪讲师、看雪区块链研究员roysue为区块链、安全领域爱好者分享了知识大宴《第一个智能合约 在树莓派上部署》,下面我们一起来回顾一下~

roysue:今天我们将学习第一个智能合约,就是说通过智能合约来实现前端和后端的交互。现在我们先来实现一个最基本的操作,从hello world入手。(2018/11/30)


我们的第一个智能合约


首先,我们将在以太坊建立 the Greeter, a Hello World 智能合约。它被描述为“存活在以太坊上的智能数字资产,可以和任何与他交互的人进行对话”,也就是你说啥、它也说啥。


在第一部分中,我们描述了如何用 solidity 编写智能合约,和Java类似,它会被编译为字节码,以太坊的虚拟机来执行这个字节码。现在我们来安装这个编译器。编译器也是在笔记本上来安装,命令行如下:

$ sudo add-apt-repository ppa:ethereum/ethereum

$ sudo apt-get update

$ sudo apt-get install solc



编译会占用大量系统资源,在桌面平台会比树莓派这样的平台快很多倍。这里安装的solc就是solidity语言的编译器。solc不只有一个版本,还有很多分支,我们这里装的是最原始的版本。从solidity官方网站看到,很多编译器都可以用,官方推荐的是Remix,安装步骤都有文档说明。


https://solidity.readthedocs.io/en/v0.4.25/installing-solidity.html


其他版本的安装我们就不演示了,我们只演示官方版本的安装。



solc编译器的安装


首先,新建一个文件夹,

```js

mkdir solc

cd sol/

`
``


然后,新建一个文件

```js

touch Greeter.sol

`
``


用你喜欢的编译器打开Greeter.sol文件,并将如下内容写进去。

```js

contract Mortal {

   /* Define variable owner of the type address */

   address owner;



   /* This function is executed at initialization and sets the owner of the contract */

   function Mortal() { owner = msg.sender; }



   /* Function to recover the funds on the contract */

   function kill() { if (msg.sender == owner) selfdestruct(owner); }

}



contract Greeter is Mortal {

   /* Define variable greeting of the type string */

   string greeting;



   /* This runs when the contract is executed */

   function Greeter(string _greeting) public {

       greeting = _greeting;

   }



   /* Main function */

   function greet() constant returns (string) {

       return greeting;

   }

}



编译:

```js

>$ solc -o target --bin --abi Greeter.sol


ABI可以理解为符号,在windows的PE里,符号是集成在windows的可执行文件中的。在编译器里面,符号是不会集成在可执行文件里的。



这张图片是它演示编译的一个结果。虽然有一些警告,但是会有一个target的文件夹会产生,包含四个文件:Greeter 和 Mortal 的ABI与bin文件。我们只需要有Greeter的这两个文件就可以了,因为它已经集成了Mortal的contract。


接下来就是把Greeter的ABI和bin打包进智能合约中去。可以一句话一句话的把所有命令都敲一遍进去,这样就可以打包进去;也可以把所有命令保存为一个脚本,命名为 greeter.js,直接加载脚本就已经把这个ABI和binary打包进智能合约。我们来操作一遍。


大家可以看到最后的编译结果是16进制,符号也标的很明显。

var greeterFactory = eth.contract([{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}])

var greeterCompiled = "0x" + "608060405234801561001057600080fd5b5060405161039b38038061039b83398101806040528101908080518201929190505050336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610089929190610090565b5050610135565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d157805160ff19168380011785556100ff565b828001600101855582156100ff579182015b828111156100fe5782518255916020019190600101906100e3565b5b50905061010c9190610110565b5090565b61013291905b8082111561012e576000816000905550600101610116565b5090565b90565b610257806101446000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610051578063cfae321714610068575b600080fd5b34801561005d57600080fd5b506100666100f8565b005b34801561007457600080fd5b5061007d610189565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100bd5780820151818401526020810190506100a2565b50505050905090810190601f1680156100ea5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415610187576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102215780601f106101f657610100808354040283529160200191610221565b820191906000526020600020905b81548152906001019060200180831161020457829003601f168201915b50505050509050905600a165627a7a72305820467ac32dfa208b06ba97f3a7f72d039f2f912938b54b9881c603d80f6b004df40029"

var _greeting = "Hello DesignSpark!"

var greeter = greeterFactory.new(_greeting,{from:eth.accounts[0],data:greeterCompiled,gas:1000000}, function(e, contract){

   if(e) {

     console.error(e); // If something goes wrong, at least we'll know.

     return;

   }



   if(!contract.address) {

     console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");



   } else {

     console.log("Contract mined! Address: " + contract.address);

     console.log(contract);

   }

})


var _greeting可以定义你任何想说的话,greeterFactory可以定义你新建的实例,gas是消耗的单位,我们默认的账户实例要加载到区块链上。 


console.error(e)是打印的错误。如果合约成功执行,我们就会打印一个log , 就是说合约已经执行了,hash加载成功。把Hash放出来,这个hash就会等在挖矿的人附近。如果合约部署失败了,或者说合约被挖到了,合约的地址就会暴露出来。

var greeterFactory = eth.contract(<contents of the file Greeter.abi>)

var greeterCompiled = "0x" + "<contents of the file Greeter.bin>"


`ABI`是接口,可以理解为符号,`bin`就纯粹的是二进制。所以接下来的编译,一部分是符号,一部分纯粹的是二进制。


查看版本信息

solc --version


0.4.24这个版本的信息还是蛮重要的,现在我们先来把这个实验操作完成。


安装一个可以读目录的一个工具:

sudo apt install tree



接下来,如果我们确保在合适的节点上运行矿工,然后解锁我们希望部署的联系人的计算机帐户。 在我们的例子中,我们再次在笔记本电脑的geth控制台上执行了 miner.start(),并决定通过 Raspberry Pi 部署合同。


所以我们在树莓派上输入:

> personal.unlockAccount(eth.coinbase, "")

>
loadScript("greeter.js")

成功!在同步消息中,我们可以看到正在发送的合约消息,等待挖掘的通知,然后通知完成。


我们可以用以下方法进行验证:

> eth.getCode(greeter.address)


我们检查树莓派上帐户的余额,发现略有减少。


资金将转到相应的矿工,在我们这个案例中,是笔记本电脑上的帐户。


最后,我们可以执行合约:


> greeter.greet();


如以太坊网站所述,这会立即返回,因为该调用不会改变区块链上的任何内容并且没有 Gas 成本。


在这种情况下,如果笔记本正在挖矿,把这个合约加载进去,把数据先传到树莓派上,内容在树莓派上进行部署,然后在树莓派上敲命令。运行节点信息,先把账户解锁,把greeter.js加载一次,加载上去后就能马上看到。

> personal.unlockAccount(eth.coinbase, "")

> loadScript("greeter.js")



挖矿的同时,会在节点显示同步信息。


我们先来进行挖矿,

miner.start()


查看挖矿速度,

htop


可以看到在合约地址上的内容。


因为我们部署智能合约是需要gas的。我们可以执行合约:

> greeter.greet();


合约查询是不会消耗wei的。如果涉及到合约存储、缓存,会消耗wei。所以我们可以在网上查询区块的信息,都是不会涉及到wei的消耗的。


在结束的时候会总结,其实这个合约是官方的教程。因为它没有在区块链上做任何改变,所以不会消耗gas。最后我们可以研究下文档和debug。



文档和debugging


它的建议是以太坊正在飞速的发展,任何合约可能在诞生不久后就会过时,各种官网和wiki也会过时,哪怕比较权威的人写的文章也会过时。一个典型的例子是,官网上的一个2017年的例子,已经不在维护中了,已经认为是过时了。


https://github.com/ethereum/go-ethereum/wiki/Contract-Tutorial


以太坊正在飞速运行,各种改进都会提高它的稳定性及效率。如果哪里做的不对,看看是不是自己的文档过期了,以太坊的版本是不是和文档相匹配的。在测试的链上,用旧的版本也没关系,但是在主网上旧版不一定适用。


如果想把刚刚做的实验全部删掉的话,执行下面的命令,就可以把文件夹全部删掉。

```js

$ geth removedb --datadir .designspark

`
``


重新建一个文件夹,重新建一个流程,就可以开始做你自己的私链,重新做一个创世区块。


最后我们总结一下,做了哪些事。


1.从理论上讲,树莓派运行智能合约,树莓派参加一个私链,哪怕树莓派参加私链用轻同步模式,参加主网,都是没问题的。


2.一个私链是如何建起来的。


3.私链在两个账户之间转账,编译智能合约,在树莓派上把智能合约应用起来,调用了一个函数。


我们现在开始,就可以加入更多的节点,运行更多复杂的智能合约,做一些更加复杂的事情。我们这节课的任务就完成了。


我们后面将介绍智能合约的编写到部署,按要求来进行智能合约的开发,最后再讲智能合约安全开发的事情。



问答



接下来,我们一起来看一下,上次课程中又有哪些精彩问答吧~!




学员:桌面到底使用solc还是remix编译合约程序?


r0ysueremix是在云端的,在桌面是没有的。只是一个网页,其实我觉得这个随意哈。写完是这样:



然后编译,$ solc -o target --bin --abi Greeter.sol:



学员:用solc编译后的应该是一个中间代码吧?感觉你部署交叉编译环境......


r0ysue:这张图片是它演示编译的一个结果。你了解什么叫“交叉编译环境”吗?交叉编译环境是指在x86机器上,编译arm指令的代码。或者说我们在x86的机器上,编译跑在EVM虚拟机里的字节码。编译完之后的,就是EVM虚拟机的字节码。在这一点之上remix和solc功能是一样的。虽然有一些警告,但是会有一个target的文件架会产生。包含四个文件夹:Greeter和Mortal的ABI与bin文件。



学员:你后不是要拿去树莓派上用吗?你本机是x86的系统,树莓派是arm的系统,需要交叉编译的环境才能移植过去吧。


r0ysue:编译完成之后,是EVM的字节码。这个字节码可以跑在EVM的虚拟机里面的,不是直接跑在arm上面的,不需要arm的机器码。


......


本期教程视频已经上传到看雪学院,[传统安全X区块链]栏目


(https://www.kanxue.com/book-13-418.htm)



下期活动,邀你参与~



【传统安全X区块链】下期活动来啦~!


将于 2018年12月8日,19:00~20:00 开讲~


讲师:看雪讲师、看雪区块链小组成员、上海交通大学密码学实验室成员 xrosherat


扫描下图海报二维码,即可添加看雪工作人员,入群免费学习~!





- End -


往期热门资讯:                                        




公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存