赠书 | Go语言开发智能合约要几步?

Posted 区块链大本营

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了赠书 | Go语言开发智能合约要几步?相关的知识,希望对你有一定的参考价值。

作者 | 高野

责编 | 晋兆雨

头图 | 付费下载于视觉中国

智能合约开发

对于很多不了解区块链行业的人来说,听到智能合约就会有一种高大上的感觉。一般情况下,大多数人可能会发出三个灵魂拷问:什么是智能合约?为什么叫智能合约?如何写智能合约?学完本节后我们不仅可以回答这些问题,还可以学会搭建一套应用开发的环境、创建一个私链,并且很容易写出符合基本业务要求的智能合约。

合约开发环境搭建

在讲搭建环境之前,先来解答灵魂拷问的前2个问题。

第一问,什么是智能合约?

要回答这个问题,首先要了解以太坊的定位是什么。小V在介绍以太坊时,清晰地表示以太坊是“一台全球计算机”。之所以有这样的表述是因为以太坊的节点遍布全球,在这样的网络中运行计算就相当于在“一台全球计算机”中运行。在明确了以太坊的定位之后,就好理解智能合约是什么了。智能合约就是运行在以太坊这个“全球计算机”上的“进程”,是的,我们可以把它理解为特殊的“进程”。

第二问,为什么叫智能合约?

智能合约,也叫智能合同,也就是“smart  contract”的翻译。把智能合约拆成两部分来看,前半部分的智能主要体现在智能合约是可以自动化运行的,后半部分的合约则是因为以太坊的合约代码多会涉及一些资产转移,而现实世界中签订合同也多是伴随着资产转移。因此把这样的代码叫合约。在解决了这两个疑问后,也了解了(以太坊的)智能合约是运行在以太坊节点上的。因此,若要安装智能合约的开发环境,需要有一个以太坊节点。

温馨提示:在这里要明确一下,学习其他区块链平台的智能合约需要安装其对应区块链平台的节点。

需要明确的是,以太坊针对多种语言,都开发了对应的客户端软件,比如Go、C++、Rust、Java、Python等,不过最受欢迎的客户端还是Go语言编写Geth,它是go-ethereum工程的缩写名。接下来,介绍一下Geth在各个平台的安装方式(以1.9.10版本为例)。

Geth的官方下载地址为:https://geth.ethereum.org/downloads/,读者要有一定的耐心,这个网站打开时偶尔会有一些问题,不过也不必担心,后面提供的压缩包下载链接是可以直接使用的。

下面将分别介绍在Windows系统和Linux系统中安装Geth的方法。

(1)在Windows系统安装Geth。

虽然Geth可以在Windows系统运行,不过不太建议在Windows平台运行节点做测试,毕竟Windows系统做服务器不是特别稳定。

下面简单介绍一下如何在Windows环境中安装Geth,作为开发者不光要使用Geth,还要使用其他相关配套的工具。因此需要下载“Geth&Tools”工具,如下图所示,单击链接可以直接下载64位的“Geth & Tools 1.9.10”工具包。

Windows系统Geth下载示意图

下载完成后,将其解压缩,再将解压缩后Geth文件所在的目录配置到path环境变量中,这样就算是完成了安装,在命令行窗口就可以运行Geth了。

如果官方网址打开较慢,可以使用下面的链接直接下载:

https://gethstore.blob.core.windows.net/builds/geth-alltools-windows-amd64-1.9.10-58cf5686.zip。

(2)在Linux系统安装Geth。

与安装Go语言开发环境类似,安装Geth同样可以用命令行或下载可运行二进制文件的方式,当然,如果愿意源码安装也可以。这里,介绍命令行安装与下载二进制文件的方式。

命令行安装方式,以Ubuntu系统为例,只需要执行下面的三条指令就可以完成Geth的安装。

sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install ethereum

安装参考网址:https://geth.ethereum.org/docs/install-and-build/installing-geth#install-on-ubuntu-via-ppas。

压缩包的安装方式如下图所示,在官方网站可以找到Geth对应的Linux版本文件。

Linux系统Geth下载示意图

基于压缩包的安装其实也就是执行shell命令,具体步骤如下。

步骤01:下载压缩包,指令如下。

wget https://gethstore.blob.core.windows.net/builds/geth-alltools-linux-amd64-1.9.10-58cf5686.tar.gz

步骤02:解压缩下载包,指令如下。

tar zxvf geth-alltools-linux-amd64-1.9.10-58cf5686.tar.gz

步骤03:配置环境变量,指令如下。

mv geth-alltools-linux-amd64-1.9.10-58cf5686 ~/geth-home
export PATH=$HOME/geth-home:$PATH
echo `export PATH=$HOME/geth-home:$PATH` >> ~/.bashrc

步骤04:执行“geth --help”命令,验证geth效果。  

$ geth --help
NAME:   
geth - the go-ethereum command line interface   
Copyright 2013-2020 The go-ethereum AuthorsUSAGE:   
geth [options] command [command options] [arguments...]
VERSION:
1.9.10  -stable
COMMANDS:   
account                            Manage accounts   
attach                             Start an interactive 
javascript environment (connect to node)   
console                            Start an interactive 
JavaScript environment   
copydb                             Create a local chain from a 
target chaindata folder
......此处省略其他语句

当看到类似上面的效果时,就代表Geth的安装已经生效了。

(3)在macOS系统安装Geth。

在macOS系统安装Geth同样可以采用命令行或压缩文件的方式,命令行的安装方式需要借助brew工具,简单的2条指令就可以搞定。  

brew tap ethereum/ethereum
brew install ethereum

下载压缩包的方式,步骤如下。

步骤01:在官网找到macOS对应的Geth & Tools版本,鼠标右键单击版本号,如下图所示。

MacOS系统Geth下载示意图(1)

步骤02:右击鼠标后,在弹出的快捷菜单中选择“复制链接地址”选项,如下图所示。

MacOS系统Geth选择下载示意图(2)

完成上述操作后可以得到具体的下载地址:

https://gethstore.blob.core.windows.net/builds/geth-alltools-darwin-amd64-1.9.10-58cf5686.tar.gz。

步骤03:复制压缩包的下载地址后,在brew工具中输入如下命令行,开始下载。   

wget https://gethstore.blob.core.windows.net/builds/geth-alltools-darwin-amd64-1.9.10-58cf5686.tar.gz

步骤04:下载完成后,输入如下命令行,开始解压缩下载包。   

tar zxvf geth-alltools-darwin-amd64-1.9.10-58cf5686.tar.gz

步骤05:解压完成后,输入如下命令行开始配置环境变量。   

mv geth-alltools-darwin-amd64-1.9.10-58cf5686 ~/geth-home
export PATH=$HOME/geth-home:$PATH
echo `export PATH=$HOME/geth-home:$PATH` >> ~/.bash_profile

在macOS系统上安装基本与Linux相同,需要注意两点:首先,下载适合本系统的版本;其次,macOS系统的配置文件与Linux不同。安装完成后,同样可以执行“geth  –help”命令验证一下是否安装成功。这个help所展示的信息太多了,在此就不再展示了。

在安装好了Geth之后,接下来就需要知道如何启动它。不过在这之前,还需要区分几个概念。

(1)主网:以太坊真实节点运行的网络,节点遍布全球,此网络中使用的“ETH”是真实的虚拟数字货币,部署合约时需要消耗真金白银。

(2)测试网:测试网的节点没有主网节点那么多,主要是为以太坊开发者提供一个测试的平台环境,此网络上的“ETH”可以通过做任务获得。

(3)私网:私网是由开发者自行组建的网络,不与主网及测试网连通,独立存在,仅用于个人测试或企业项目使用。

需要明确的是,无论是主网、测试网还是私网,都可以使用Geth来启动。Geth直接运行,默认连接的就是以太坊主网,如果想要连接测试网可以连接Ropsten或rinkeby,指令参考如下:

// Ropsten 测试网络
geth --testnet --fast --cache=512 console
// Rinkeby 测试网络
geth --rinkeby --fast --cache=512 console

很多时候,开发者都习惯自己搭建一套私网,接下来重点介绍私网搭建的步骤。

步骤01:配置创世块文件。将如下内容保存为genesis.json文件。

{  
"config": {        
"chainId": 18,        
"homesteadBlock": 0,          
"eip150Block": 0,        
"eip155Block": 0,        
"eip158Block": 0    
},  
"alloc"      : {},  
"coinbase"   : "0x0000000000000000000000000000000000000000",  
"difficulty" : "0x2",  
"extraData"  : "",  
"gasLimit"   : "0xffffffff",  
"nonce"      : "0x0000000000000042",  
"mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",  
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",  
"timestamp"  : "0x00"
}

创世块文件的部分内容,可以简单了解一下。

(1)Coinbase:挖矿后获得奖励的账户地址。

(2)Difficulty:挖矿难度。

(3)gasLimit:一个区块所能容纳gas的上限,智能合约指令在执行时需要消耗gas,可通过以太币自动兑换。

(4)nonce:随机值。

(5)mixhash:一个256位的哈希证明,与nonce相结合,验证本块的有效性。

(6)extraData:附加信息,随意填写。

(7)parentHash:前一块的hash值,因为是创世块,所以为0。

步骤02:数据初始化。init是初始化的命令,--datadir则是用来指定数据存储路径。

geth init genesis.json --datadir ./data

在此步骤,主要是利用创世块进行文件初始化,指定一个数据目录,当看到类似下面的结果代表初始化成功。

INFO [02-14|16:42:36.647] Maximum peer count                       ETH=50 LES=0 total=50
INFO [02-14|16:42:36.796] Allocated cache and file handles         database=/Users/yk/ethdev/yekai1003/rungeth/data/geth/chaindata cache=16.00MiB handles=16
INFO [02-14|16:42:36.861] Writing custom genesis block 
INFO [02-14|16:42:36.862] Persisted trie from memory database     nodes=0 size=0.00B time=13.579μs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [02-14|16:42:36.866] Successfully wrote genesis state         database=chaindata hash=c1d47d...d9ea3e
INFO [02-14|16:42:36.872] Allocated cache and file handles         database=/Users/yk/ethdev/yekai1003/rungeth/data/geth/lightchaindata cache=16.00MiB handles=16
INFO [02-14|16:42:36.899] Writing custom genesis block 
INFO [02-14|16:42:36.899] Persisted trie from memory database     nodes=0 size=0.00B time=5.75μs   gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [02-14|16:42:36.900] Successfully wrote genesis state         database=lightchaindata hash=c1d47d...d9ea3e

此时在data目录下会有一些文件生成,通过tree命令可以查看,输入“tree data/”命令行即可。

root:rungeth yk$ tree data/data/
├── geth│
├── chaindata
││├── 000001.log
││├── CURRENT
││├── LOCK
││├── LOG
││└── MANIFEST-000000
│└── lightchaindata
│├── 000001.log
│├── CURRENT
│├── LOCK
│├── LOG
│└── MANIFEST-000000
└── keystore

【温馨提示】如果 tree 命令不存在,可以自行安装一下。Linux 系统与 macOS 系统会有如何安装的提示。

步骤03:输入如下命令行,启动Geth节点。

geth --datadir ./data --networkid 18 --port 30303 --rpc  --rpcport 8545 --rpcapi 'db,net,eth,web3,personal' --rpccorsdomain '*' --gasprice 0 --allow-insecure-unlock console 2> 1.log

这个命令的启动参数比较长,也需要针对参数进行介绍。

(1)datadir:指定之前初始化的数据目录文件。

(2)networkid:配置成与配置文件config内的chainId相同值,代表加入哪个网络,私网随意编号即可。

(3)port:P2P端口,也就是节点之间互相通信的端口。

(4)rpc:代表开启远程调用服务,这对我们很重要。

(5)rpcport:远程服务的端口,默认是8545。

(6)rpcapi:远程服务提供的远程调用函数集。

(7)rpccorsdomain:指定可以接收请求来源的域名列表(浏览器访问,必须开启)。

(8)gasprice:gas的单价。

(9)allow-insecure-unlock:新版本增加的选项,允许在Geth命令窗口解锁账户。

(10)console:进入管理台。

(11)2>  1.log:UNIX系统下的重定向,将Geth产生的日志输出都重定向到1.log中,以免刷日志影响操作。

启动后,将看到类似下面的结果:

Welcome to the Geth JavaScript console!
instance: Geth/v1.9.6-stable/darwin-amd64/go1.13.1
at block: 0 (Thu, 01 Jan 1970 08:00:00 CST) 
datadir:/Users/yk/ethdev/yekai1003/rungeth/data 
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>

终于大功告成!

由于命令很长,在这里向大家推荐一个更容易操作的启动方法。读者可以借助笔者的GitHub工程,直接下载,在工程内部有现成的创世块文件和启动脚本,操作起来更简单,步骤如下。

步骤01:下载GitHub工程,命令行如下。

mkdir ~/ethdev
cd ~/ethdev
git clone https://github.com/yekai1003/rungeth

步骤02:进入该目录,进行初始化,命令行如下。

cd rungeth
geth init genesis.json --datadir ./data

步骤03:执行脚本“./rungeth.sh”,启动Geth,结果如下。

root:rungeth yk$ ./rungeth.sh 
Welcome to the Geth JavaScript console!
instance: Geth/v1.9.6-stable/darwin-amd64/go1.13.1
at block: 0 (Thu, 01 Jan 1970 08:00:00 CST) 
datadir: /Users/yk/ethdev/yekai1003/rungeth/data 
modules: admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
>

至此,搞定了节点,可以准备智能合约的开发了。

初识Solidity

由于目前支持智能合约的区块链平台很多,智能合约的开发语言也有多种选择,不过,以太坊毕竟是第一个诞生智能合约的区块链平台,之后产生的很多区块链平台也多是参考以太坊平台的虚拟机技术,借鉴以太坊的智能合约开发环境。以太坊平台智能合约开发语言主要采用Solidity。因此Solidity也是目前多数主流区块链平台所采用的智能合约开发语言。

Solidity是一门面向对象、为实现智能合约而创建的高级编程语言,这门语言受到了C++、Py-thon和Javascript等语言的影响。在以太坊黄皮书披露的技术细节中,提到了以太坊虚拟机是一款图灵完备(Turing  Completeness)的虚拟机器,Solidity自然也就是一款图灵完备的高级开发语言。它的内部可以支持变量定义、容器、自定义类型、函数、循环、继承等高级语言的通用技术,在后面的内容中我们将逐渐展开介绍。

按照惯例,学习一门语言的第一个程序是介绍“hello-world”,这就当是和Solidity的初识吧。

pragma solidity^0.6.0;
contract hello {
    string public Msg;            
    constructor() public {
        Msg = "hello";
    }
}

这是最简单的智能合约代码,分别介绍一下具体的含义。第一行的作用是为了控制智能合约编译器的版本,“pragma”是Solidity的编译控制指令,“^0.6.0”代表的含义是可以使用0.6.x的版本对该代码进行编译,也就是说0.5.x和0.7.x的编译器版本不允许编译该智能合约的。此外,也可以使用类似“pragma solidity >0.4.99 <0.6.0;”这样的写法来表达对编译器版本的限制,这样看上去更加简单明了。

代码中“contract”是一个关键字,用来定义合约名字,它很像是某些语言里的类(class)定义方法。“hello”是本合约的名字,这个合约的主要功能是向区块链系统中存储一个Msg字符串。“constructor”是该合约的构造函数,当合约部署时,执行的也就是构造函数的代码,该构造函数的功能是将Msg初始化为“hello”。

当然,介绍还没有结束,这只是合约代码,需要将它部署后再看看效果。想要运行以太坊的智能合约,一般都会使用官方推荐的在线IDE环境remix,这是一个智能合约开发、测试、部署的集成环境。读者可以在浏览器输入:http://remix.ethereum.org/,打开后可以按照下面的步骤操作。步骤01:在打开的页面中,单击【Solidity】按钮,如下图所示。

Remix环境操作(1)

步骤02:在跳转后的窗口中单击【EVM Vesion】选项卡并选择【byzantium】选项,单击【文件浏览器】按钮,如下图所示。

Remix环境操作(2)

步骤03:在跳转后的窗口中,单击左上角的【+】按钮创建文件,并在弹出的对话框中输入文件名,然后单击【OK】按钮,如下图所示。

创建合约文件

步骤04:将前文复制的代码粘贴至窗口右侧的文本框中,使用快捷键进行保存(Windows用户使用【Ctrl+S】组合键保存,macOS用户使用【Command+S】组合键保存),并单击左侧选项栏的【】按钮切换到部署和运行页面,如下图所示。警告不用处理,可以忽略它。

合约部署操作(1)

步骤05:在切换到部署和运行页面后,单击【Environment】选项卡中选择运行环境,此处采用默认的【JavaScript VM】选项即可,然后单击【Deploy】按钮,部署合约,如下图所示。

合约部署操作(2)

对于Environment的三种选择,分别介绍一下。

(1)JavaScript VM:Remix内置的虚拟机,运行速度快,无须挖矿,测试方便。

(2)Injected Web3:单击时会连接浏览器安装的Metamask插件,该插件为以太坊浏览器钱包,很多时候,我们都是通过Metamask钱包将合约部署到主网或测试网。

(3)Web3 Provider:单击时,将代表要连接某个以太坊节点,需要指定Geth的连接地。

步骤06:部署完成后,在浏览器下部可以看到合约部署的信息及合约视图,单击下图所示的按钮查看合约视图。

合约部署效果查看

步骤07:在展开的合约视图中可以看到左侧的【Msg】按钮,它代表着Msg方法,单击该按钮进行调用,如下图所示。

合约调用(1)

步骤08:单击【Msg】按钮后可查看运行后的结果,得到“hello”,如下图所示。

合约调用(2)

此步骤执行完,合约部署到执行已经操作完成,该合约也就是简单地把“hello”存储到以太坊节点中,并通过查询函数Msg可以获得存储的值。

在步骤05时,在【Environment】选项栏中如果选择【Web3 Provider】选项,将会看到下图所示的效果。

Remix连接Geth环境

单击【OK】按钮,将会看到下图所示的效果。

Geth连接后效果图

接下来的操作基本和JavaScript VM时的操作一样,可以部署合约,调用函数。对于新启动的Geth,一般没有账户,可以在Geth窗口内创建一个,如下图所示。

账户创建

创建后,再回到Remix环境(浏览器窗口),可以看到创建的地址,它目前没有以太币,如下图所示。

Remix查看Geth账户

此时,单击【Deploy】按钮,部署会失败,如下图所示。

Remix部署合约到Geth操作(1)

对于Geth内的账户,需要解锁后方可使用。没办法,去Geth窗口去解锁一下,操作指令如下图所示。

解锁账户

如下图所示,解锁后,再来部署合约,不报错了,但是合约仍然处于“pending”状态,也就是说合约并没有被成功部署。原因是什么,读者想到了吗?

Remix部署合约到Geth操作(2)

区块链内的每次合约执行都是一个交易,这个交易需要挖矿才能被打包到区块中,只有被打包到了区块中,才代表交易执行成功了。需要启动挖矿,这也是区块链平台的最大特点!

使用下图中所示的指令启动挖矿。

启动挖矿

给它们一点时间,再回到浏览器,就可以看到合约被部署完成了,这时候Msg也可以调用了,如下图所示。

Remix部署合约到Geth成功示意图

至此,已经将Remix环境与安装的Geth节点相结合,可以进行后续合约的学习,此后的合约调用也基本都是此步骤。

Solidity有哪些数据类型

Solidity是一种静态类型的高级语言,每个变量在编译时都需要明确变量的类型。说到变量,就要介绍一下Solidity的变量类型,Solidity内部定义了多种基础类型及一些复杂的复合类型,先来说说基础类型。

Solidity支持的基础类型有整型(int,uint)、布尔类型(bool)、字符串类型(string)和字节类型(byte)。对于布尔类型和字符串类型,这里不再详述,它们和很多语言一样。整型类型是开发者比较熟悉的一种类型,之所以在这里单独强调一下,是因为Solidity在设计数据类型时精确到了每一个字节。因此其内部的整型光int类的就有int8、int16、int24、int32......int256等,如下图所示。当然,对于无符号数整数类型uint来说也是相同的待遇。仔细想想也可以理解,毕竟智能合约是运行在以太坊EVM中,存储到全球节点里的,空间能省就省。

合约代码编辑示意图

温馨提示整型类型最大定义长为 256 位,int 等价于 int256,uint 等价于 uint256。

除了基础类型,在Solidity中也可以使用一些复合类型,包括定长字节数组、变长字节数组和地址类型(address)。地址类型address是Solidity特有的数据类型,它对应了以太坊的账户地址。可以把地址理解为一个结构化的数据类型。

定长字节数组的待遇与int类似,从bytes1一直定义到bytes32,一个字节是8位,最终bytes32可以表达256位长度的数值。除了定长数组,也可以定义bytes[N]这样的变长数组。在Solidity中同样支持枚举类型,但编译器会将枚举类型进行转换。因此我们也可以忽略枚举类型。

在了解了相关数据类型之后,接下来谈谈变量的定义。说到变量,首先应该明确其存储位置。按照存储位置的不同,可将变量分为状态变量和局部变量。所谓状态变量,是指存储在以太坊节点中的变量,这类变量的数据存储需要支付费用,这类变量其实也就是合约的成员变量。我们先来说说状态变量的定义,语法如下:

Type [permission] identifier;//状态变量定义

其中“permission”用来修饰变量的访问权限,可以使用public或private,如果不写的时候默认为private。如果成员变量是public访问权限的,合约部署后会自动为我们提供该变量的查询方法。

局部变量主要就是在函数中使用,定义方式与状态变量类似。不过局部变量的修饰符主要强调的是该值是值传递还是引用传递,有时候编译器要求我们显式地声明到底是值传递(memory)还是引用传递(storage)。函数的参数及返回值都是memory类型传递,函数体内部根据需要可以使用storage传递或memory传递。

#欢迎留言在评论区和我们讨论#

看完本文,对于Go语言和区块链你有什么想说的?

欢迎在评论区留言

我们将在 6 月 9 日精选出 3 条优质留言

赠送《Go语言区块链应用开发从入门到精通》纸质书籍一本哦

更多阅读推荐

以上是关于赠书 | Go语言开发智能合约要几步?的主要内容,如果未能解决你的问题,请参考以下文章

hyperledger fabric 智能合约开发

Fabric基础架构原理:链码 | 赠书活动

GO 智能合约cannot use transactionRecordId + strconv.Itoa(id) (type string) as type byte in append(示例代码(代

学习区块链开发是学习go语言、hyper ledger fabric比较好、还是以太坊智能合约比较好或者公链开发?

Go语言入门经典|文末赠书

一学就会,手把手教你用Go语言调用智能合约