EOS智能合约开发系列(四)

本文是EOS开发入门系列的第四篇。前面几篇都是准备,从这一篇开始,编写我们的第一个智能合约,hello合约。功能比较简单,不过可以让我们对EOS上的智能合约有个简单的印象。我还将会学习如何部署这个合约,以及如果触发它的action。*

EOS代码的更新频度很高,差不多每周都有一个小版本出来;所以我们时不时会想更新一下代码,重新编译一下,本文就从这里开始。

如何更新代码并重新编译

更新代码用两个命令,第一个命令更新子模块,第二个更新主代码:

git pull origin master
git submodule update --init --recursive

如果有冲突,就解决。通常情况下,如果你没有修改过代码,是不会有冲突的。如果不巧,确实有冲突,而自己又不会修改怎么办呢?删除代码,按照第一章的方式重新下载全代码,再编译安装。这种方式耗时肯定会多一些,但肯定比你第一次全代码编译要快。因为依赖的库文件都已经在第一次装好了,编译脚本会自动检测,不用重新安装。

如果没有冲突,那最好了。还是按照上面第一章的方法开始编译,在eos代码根目录,依次执行下面两个命令:

./eosio_build.sh
./eosio_install.sh

在不羁的macOS上面,第一个命令大概需要20分钟,第二个命令大概1分钟。

如此编译完成后,运行nodeos,然后继续下面小节的命令。
不过呢,当运行nodeos的时候,有时候没那么顺利,可能会提示类似这样的错误:

database created by a different compiler, build, boost version, or operating system
rethrow database created by a different compiler, build, boost version, or operating system

怎么回事呢?原来是代码更新之后,操作数据库相关的库文件、编译器可能发生了改变,nodeos怕新的代码不兼容老代码生成的数据库文件,所以报了这么个错误。

这个问题也很好解决,只需要给nodeos加个参数,执行下面的命令就好了:

nodeos --replay-blockchain

这种命令会利用以前的区块链数据重建数据库,重建完成之后,nodeos会自动进入出块阶段,此时cleos就可以用了。

"hello world"合约

首先我们创建一个~/eos-workspace文件夹。被你发现了,我总是喜欢偷懒,喜欢直接在$HOME目录下操作,见谅见谅哈。然后再在~/eos-workspace下,建一个hello文件夹,然后进入此文件夹,创建hello.cpp文件,并把如下代码(此代码稍后分析)拷贝到该文件中:

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;

class hello : public eosio::contract {
  public:
      using contract::contract;

      /// @abi action 
      void hi( account_name user ) {
         print( "Hello, ", name{user} );
      }
};

EOSIO_ABI( hello, (hi) )

然后把它编译成web assambly(.wast)格式:

eosiocpp -o hello.wast hello.cpp

注意,上面的编译可能会产生一下警告(warning),不必管它。
然后生成abi:

eosiocpp -g hello.abi hello.cpp

如果你这两个命令在执行的过程中,提示某个头文件( 比如'stdint.h')找不到,那么恭喜你,你遇到了和我一样的情况,下面我专门来说说我是怎么解决这个问题的:

这个问题,是由于eosiocpp引起的,所以我们先找到这个工具的源文件。在代码目录下,tools/eosiocpp.in就是eosiocpp的源文件了,我们打开这个文件,看看里面的内容(下面只截取其中一部分):

 for file in $@; do
        name=`basename $file`
        filePath=`dirname $file`

        ($PRINT_CMDS; @WASM_CLANG@ -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc \
                                   -DBOOST_DISABLE_ASSERTS -DBOOST_EXCEPTION_DISABLE \
                                   -nostdlib -nostdlibinc -ffreestanding -nostdlib -fno-threadsafe-statics -fno-rtti \
                                   -fno-exceptions -I ${EOSIO_INSTALL_DIR}/include \
                                   -I${EOSIO_INSTALL_DIR}/include/libc++/upstream/include \
                                   -I${EOSIO_INSTALL_DIR}/include/musl/upstream/include \
                                   -I${BOOST_INCLUDE_DIR} \
                                   -I $filePath \
                                   ${EOSIOCPP_CFLAGS} \
                                   -c $file -o $workdir/built/$name
        )

    done

    ($PRINT_CMDS; @WASM_LLVM_LINK@ -only-needed -o $workdir/linked.bc $workdir/built/* \
                                   ${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/eosiolib.bc \
                                   ${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/libc++.bc \
                                   ${EOSIO_INSTALL_DIR}/usr/share/eosio/contractsdk/lib/libc.bc


    )
    

可以看出,这是个shell脚本,这是编译 .wast的部分,你可以看到前半部分是编译所有的cpp文件,后面再把生成的目标文件链接起来。在它编译的时候,会把查找头文件的目录作为参数传进去,类似于下面这样:

-I${EOSIO_INSTALL_DIR}/include/libc++/upstream/include \

可惜的是,在eos官方提供这部分脚本中,作为参数传进来的这些目录中,没有一个包含stdint.h,所以我在执行eosiocpp命令的时候,就报错说找不到stdint.h

知道了原因,修改起来也就相当简单了,在eos目录下找到stdint.h文件的位置:

find . -name 'stdint.h'

找到位置之后,把绝对路径,按照原有的格式,添加到编译参数里面就可以了。
修改完之后,进入build目录,执行

sudo make install

执行完成之后,再运行下上面的编译.wast命令和.abi命令。如果还报相同的错误,说明你上面的路径没加对,如果有新的文件找不到,按照同样的方式操作,直到解决所有的问题为止。如果编译阶段通过了,链接阶段找不到库文件,也是类似的操作,只不过要把库文件的绝对位置加载链接命令的后面。

生成abi的过程如果出错,也是类似的,找到生成abi的命令,加上对应的绝对路径就好了。

我为什么要用绝对路径呢?

这是因为,当用绝对路径的时候,我们就可以用eosiocpp编译任何位置的合约代码了。

我想到这里,你应该已经解决问题了。

下面开始部署合约。

部署hello world合约

此时/Users/james/eos-workspace/hello文件夹下面应该有这些文件了:

hello.abi      hello.wasm     
hello.cpp      hello.wast    

很好,我可以部署这个合约了,我部署在user账号下面:

cd ~/eos-workspace/hello
cleos set contract user ../hello -p user@active

注意,当前目录已经是hello文件夹了,我们在传递合约目录的时候,传递的是../hello,而不是./,为什么呢?

因为cleos是根据文件夹的名字来找合约文件的,所以我们的路径中必须包含文件夹的名字,同时我们生成的合约目标文件(.wast和.abi),必须使用所属文件夹的名字。

我们分析下这个合约的代码,看注释部分:

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
//上面的头文件提供一些常用的接口,这两个头文件是经常用到的,一般情况下,他们都是必备的
using namespace eosio;

//合约类必须继承自eosio::contract
class hello : public eosio::contract {
  public:
      //使用父类的构造函数
      using contract::contract;

      /// @abi action 
      void hi( account_name user ) {
         //这里我们处理'hi'action的代码了
         //确保调用这个action的账户拥有user的权限,注意此user是个变量,具体的账户名依传进来的参数而定
         require_auth( user );
         //打印函数。name是一个类,使用user来初始化
         // 这里的user实际上是一个数字,name类的to_string函数能把它转化为原有的字符串形式。
         //我们知道eos中,账户名是有长度限制的,不能超过13位,这个限制也是为了确保它能够和64位的整数互相转换
         print( "Hello, ", name{user} );
      }
};

//EOSIO_ABI是官方定义的一个宏。这句代码是非常必要的,它声明hello合约中,哪些函数是处理action的
//如果有多个action处理函数,就在下面多加几个参数。
EOSIO_ABI( hello, (hi) )

触发合约的action

我通过上面的代码分析,知道了hello合约提供了一个hi action,那么我们就可以用命令触发这个action:

➜  cleos push action user hi '["tester"]' -p tester@active
executed transaction: bc0666f7ea0e4987677ce43a361b543f00525a73ea382f53404e1a0dbe524a6c  104 bytes  908 us
#          user <= user::hi                     {"user":"tester"}
>> Hello, tester

很成功,对吧?我们再试试这个:

➜  cleos push action user hi '["tester"]' -p user@active
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.

提示我没有权限,为什么呢?
这就是那句代码require_auth( user );的作用了。上面的action执行时,我们传入的是tester,而触发action的权限是user@active,所以失败了。

好了,今天到此结束了。

简介:不羁,一名程序员;专研EOS技术,玩转EOS智能合约开发。
微信公众号:know_it_well
知识星球地址:https://t.zsxq.com/QvbuzFM

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 1. 概述 官方关于智能合约介绍的文档:https://developers.eos.io/eosio-cpp/d...
    yuyangray阅读 1,746评论 0 1
  • 1 EOS智能合约的介绍1.1 所需背景知识1.2 EOS智能合约基础知识1.3 技术局限性 2 智能合约文件2....
    cenkai88阅读 30,474评论 5 28
  • 华子的人生蜕变之旅原创第488篇 2018年4月15日 星期日 晴(农历二月三十) 今早有幸和正能量的瑜...
    誉仔妈妈阅读 742评论 7 6
  • 华为人力资源管理2.0 1.以客户为中心,以生存为底线的管理体系。实现外部压力无衰减的内部传递,提升响应客户需求的...
    ERICmomy阅读 300评论 0 0
  • 2018年已经过了半个月了,这半个月里,带赞赞去郑州11天,体验陌生环境带孩子的生活,对我和赞赞处处都有挑战,也有...
    终身成长的舒可阅读 236评论 0 1